From a797349358c396ee7acee28a611abc802976f57f Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Mon, 15 Jun 2026 18:10:18 +0000 Subject: [PATCH] test(lbug): cover the sorted-output path in the byte-identity differential (#2215 review) The differential test only exercised the default insertion-order emit path. Add a case under GITNEXUS_SORT_GRAPH_OUTPUT=1 that feeds the oracle the same id-sorted order orderedRelationships() uses and asserts per-pair byte-identity, so within-pair row reordering on the sorted path can't slip past the gate. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../test/integration/csv-pipeline.test.ts | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/gitnexus/test/integration/csv-pipeline.test.ts b/gitnexus/test/integration/csv-pipeline.test.ts index 16ed577fb..0c7fd2db8 100644 --- a/gitnexus/test/integration/csv-pipeline.test.ts +++ b/gitnexus/test/integration/csv-pipeline.test.ts @@ -483,4 +483,62 @@ describe('streamAllCSVsToDisk — direct per-pair emit matches the split oracle' expect(split.totalValidRels).toBeLessThan(direct.totalValidRels); expect(split.skippedRels).toBeGreaterThan(direct.skippedRels); }); + + it('sorted path (GITNEXUS_SORT_GRAPH_OUTPUT=1): per-pair files byte-identical to the oracle', async () => { + // The earlier differential test covers the default (insertion-order) path. + // Here the sorted emit path must also match the oracle — fed the SAME + // id-sorted order orderedRelationships() uses (sort by rel.id). + process.env.GITNEXUS_SORT_GRAPH_OUTPUT = '1'; + try { + const graph = buildTestGraph( + [ + { id: 'File:a.ts', label: 'File', name: 'a.ts', filePath: 'a.ts' }, + { id: 'Function:a.ts:f:1', label: 'Function', name: 'f', filePath: 'a.ts' }, + { id: 'Function:a.ts:g:5', label: 'Function', name: 'g', filePath: 'a.ts' }, + ], + // Deliberately NOT in id-sorted order so the sort actually reorders rows. + [ + { sourceId: 'Function:a.ts:f:1', targetId: 'Function:a.ts:g:5', type: 'CALLS' }, + { sourceId: 'File:a.ts', targetId: 'Function:a.ts:g:5', type: 'CONTAINS' }, + { sourceId: 'File:a.ts', targetId: 'Function:a.ts:f:1', type: 'CONTAINS' }, + ], + ); + + const directDir = path.join(csvDir, 'sorted-direct'); + const oracleDir = path.join(csvDir, 'sorted-oracle'); + await fs.mkdir(oracleDir, { recursive: true }); + + const direct = await streamAllCSVsToDisk(graph, repoDir, directDir); + + // Oracle fed the same id-sorted order the sorted emit produces. + const sortedRels = [...graph.iterRelationships()].sort((a, b) => + a.id < b.id ? -1 : a.id > b.id ? 1 : 0, + ); + const relCsv = path.join(oracleDir, 'relations.csv'); + const lines = [REL_CSV_HEADER]; + for (const rel of sortedRels) lines.push(buildRelRow(rel)); + await fs.writeFile(relCsv, lines.join('\n') + '\n', 'utf-8'); + const split = await splitRelCsvByLabelPair( + relCsv, + oracleDir, + new Set(NODE_TABLES), + getNodeLabel, + ); + await Promise.all( + Array.from(split.pairWriteStreams.values()).map(async (ws) => { + ws.end(); + await finished(ws); + }), + ); + + expect([...direct.relsByPair.keys()].sort()).toEqual([...split.relsByPairMeta.keys()].sort()); + for (const key of direct.relsByPair.keys()) { + const directContent = await fs.readFile(direct.relsByPair.get(key)!.csvPath, 'utf-8'); + const oracleContent = await fs.readFile(split.relsByPairMeta.get(key)!.csvPath, 'utf-8'); + expect(directContent, `pair ${key} (sorted)`).toBe(oracleContent); + } + } finally { + delete process.env.GITNEXUS_SORT_GRAPH_OUTPUT; + } + }); });