import { resolveJoins } from './export-query.builder'; import { ExportDataset, ExportField } from './export.types'; const field = (key: string, requires?: string[]): ExportField => ({ key, label: key, type: 'string', group: 'g', select: `x.${key}`, requires, }); /** Entities are never dereferenced by resolveJoins — only the alias graph matters. */ const entity = {} as ExportDataset['joins'][number]['entity']; const dataset = ( joins: ExportDataset['joins'], alwaysJoin?: string[], ): ExportDataset => ({ key: 'test', joins, alwaysJoin, fields: [], }) as unknown as ExportDataset; describe('resolveJoins', () => { it('pulls in only the joins the selected fields ask for', () => { const ds = dataset([ { alias: 'a', entity, on: 'a.id = b.a_id' }, { alias: 'z', entity, on: 'z.id = b.z_id' }, ]); expect(resolveJoins(ds, [field('one', ['a'])]).map((j) => j.alias)).toEqual(['a']); }); it('selecting nothing still applies alwaysJoin — the count query relies on this', () => { const ds = dataset( [ { alias: 'a', entity, on: 'a.id = b.a_id' }, { alias: 'z', entity, on: 'z.id = b.z_id' }, ], ['a'], ); expect(resolveJoins(ds, []).map((j) => j.alias)).toEqual(['a']); }); it('resolves a transitive dependency, dependency first', () => { const ds = dataset([ { alias: 'ct', entity, on: 'ct.id = b.contract_id' }, { alias: 'ctc', entity, on: 'ctc.id = ct.company_id', requires: ['ct'] }, ]); expect(resolveJoins(ds, [field('x', ['ctc'])]).map((j) => j.alias)).toEqual(['ct', 'ctc']); }); it('resolves a multi-hop chain in order', () => { const ds = dataset([ { alias: 'a', entity, on: 'a.id = b.a_id' }, { alias: 'bb', entity, on: 'bb.id = a.b_id', requires: ['a'] }, { alias: 'cc', entity, on: 'cc.id = bb.c_id', requires: ['bb'] }, ]); expect(resolveJoins(ds, [field('x', ['cc'])]).map((j) => j.alias)).toEqual(['a', 'bb', 'cc']); }); it('emits a shared join once, not per field that needs it', () => { const ds = dataset([{ alias: 'a', entity, on: 'a.id = b.a_id' }]); const joins = resolveJoins(ds, [field('one', ['a']), field('two', ['a'])]); expect(joins.map((j) => j.alias)).toEqual(['a']); }); it('does not duplicate a join already pulled in by alwaysJoin', () => { const ds = dataset([{ alias: 'a', entity, on: 'a.id = b.a_id' }], ['a']); expect(resolveJoins(ds, [field('one', ['a'])]).map((j) => j.alias)).toEqual(['a']); }); it('throws on a cycle rather than looping forever', () => { const ds = dataset([ { alias: 'a', entity, on: 'a.id = bb.a_id', requires: ['bb'] }, { alias: 'bb', entity, on: 'bb.id = a.b_id', requires: ['a'] }, ]); expect(() => resolveJoins(ds, [field('x', ['a'])])).toThrow(/join cycle/); }); it('throws on an undeclared alias — a typo must fail loudly, not silently 42P01', () => { const ds = dataset([{ alias: 'a', entity, on: 'a.id = b.a_id' }]); expect(() => resolveJoins(ds, [field('x', ['ghost'])])).toThrow(/unknown join alias "ghost"/); }); it('a field with no requires pulls in no joins at all', () => { const ds = dataset([{ alias: 'a', entity, on: 'a.id = b.a_id' }]); expect(resolveJoins(ds, [field('plain')])).toEqual([]); }); });