fix(test): remove literal NUL byte and cover streamGraphEmit phase gating

Two review findings, both verified before accepting.

1. The round-trip test contained a literal NUL byte as a key separator,
   which made Git treat the whole .ts file as BINARY —
   `git show --numstat` reported `-\t-` for it, so the file would not
   diff or blame and CI text tooling would skip it. Replaced with the
   escaped \\u0000 sequence; behaviour is identical, the file is text
   again. (Found by the Codex swarm lane.)

2. buildPhaseList's four new streamGraphEmit gating predicates and the
   flag-off default path had no test that would fail on revert — two
   review lanes flagged this independently. Reversing any enabledWhen
   condition would have passed the suite silently, which matters because
   an ungated taintSummaries yields an empty taint layer rather than an
   error.

Added four cases: the streamed run drops communities/processes/
taintSummaries/callSummaries; it keeps mro/di (their reads are all in
RETAINED_REL_TYPES); the flag-off list is untouched; and skipGraphPhases
still works independently.

Refs #2680
This commit is contained in:
Gergo Magyar 2026-07-24 22:01:16 +00:00
parent a2249147e9
commit 38d737bb50
2 changed files with 45 additions and 0 deletions

View file

@ -14,6 +14,7 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import { resolveStreamGraphEmit } from '../../src/core/run-analyze.js';
import { buildPhaseList } from '../../src/core/ingestion/pipeline.js';
import { createKnowledgeGraph } from '../../src/core/graph/graph.js';
import { pruneLocalValueSymbols } from '../../src/core/ingestion/local-symbol-pruner.js';
import type { GraphNode } from 'gitnexus-shared';
@ -115,3 +116,47 @@ describe('pruneLocalValueSymbols under streamed emit', () => {
expect(stats).toMatchObject({ candidateNodes: 1, prunedNodes: 1 });
});
});
describe('buildPhaseList under streamGraphEmit', () => {
const names = (o: Parameters<typeof buildPhaseList>[0]) => buildPhaseList(o).map((p) => p.name);
it('drops every phase that consumes the whole CALLS graph', () => {
// CALLS is exactly what streams out, so leaving these enabled yields
// silently empty results rather than an error.
const streamed = names({ streamGraphEmit: true, pdg: true, force: true });
expect(streamed).not.toContain('communities');
expect(streamed).not.toContain('processes');
expect(streamed).not.toContain('taintSummaries');
expect(streamed).not.toContain('callSummaries');
});
it('keeps mro and di, whose reads are all in the retained set', () => {
const streamed = names({ streamGraphEmit: true, pdg: true, force: true });
expect(streamed).toContain('mro');
expect(streamed).toContain('di');
expect(streamed).toContain('parse');
expect(streamed).toContain('scopeResolution');
expect(streamed).toContain('pruneLocalSymbols');
});
it('leaves the phase list untouched when the flag is off', () => {
// Guards the default path: the gating predicates must not filter anything
// for existing (flag-off) users.
const withPdg = names({ pdg: true, force: true });
expect(withPdg).toContain('communities');
expect(withPdg).toContain('processes');
expect(withPdg).toContain('taintSummaries');
expect(withPdg).toContain('callSummaries');
});
it('still honours skipGraphPhases independently of the streaming flag', () => {
const skipped = names({ skipGraphPhases: true });
expect(skipped).not.toContain('communities');
expect(skipped).not.toContain('processes');
expect(skipped).toContain('pruneLocalSymbols');
});
});