/** * Corpus-derived coverage for the HAND-DECLARED half of `RELATION_SCHEMA`. * * `test/unit/schema-pair-coverage.test.ts` derives its requirement from * schema.ts's two rules — the scope-resolution bridge cross product, and * `DEFINITION_ANCHOR_LABELS × ATTACHMENT_TARGET_LABELS` for the framework and * pipeline-phase overlays — so both generated halves are covered there. What * neither rule can reach is `STRUCTURAL_PAIR_DDL`: the containment, inheritance * and import pairs BETWEEN TWO DEFINITION LABELS. Eleven node tables are absent * from every rule's target side (`CodeElement`, `Impl`, `Namespace`, * `Template`, `TypeAlias`, `Typedef`, `Union`, `Static`, `Section`, `Folder`, * and the PDG-only `BasicBlock`), so a pair pointing at one is hand-declared or * it does not exist. No predicate describes that surface — any container can * hold any definition — so this asks the emitters directly: run the real * pipeline and require every FROM/TO pair it produces to be declared. * * Each entry also pins the pair it exists to guard. Without that the suite is * vacuous: `undeclared` is derived from what the pipeline emitted, so a fixture * that stopped emitting — renamed directory, grammar that failed to load, * swallowed parse error — yields an empty set and passes green while guarding * nothing. `sentinels` turns each case from "nothing undeclared" into "this * emitter still fires, and everything it emits is declared". * * Coverage is bounded by `NON_BRIDGE_CORPUS`: a sample, not a proof. A * language whose fixture is absent is unguarded, so a new structural emitter * should land with an entry here. * * Deliberately isolated from the resolver suites that already build three of * these graphs (`resolvers/cobol.test.ts`, `resolvers/vue.test.ts`, * `resolvers/php.test.ts`) — see NOTE below the corpus before re-raising that. */ import { describe, it, expect, vi, beforeAll, afterAll } from 'vitest'; import path from 'path'; import { NODE_TABLES } from 'gitnexus-shared'; import { FIXTURES, runPipelineFromRepo } from './resolvers/helpers.js'; import { RELATION_SCHEMA } from '../../src/core/lbug/schema.js'; import { parseRelationSchemaPairs, relPairKeyFor } from '../../src/core/lbug/rel-pair-routing.js'; import { DIST_WORKER_URL, distWorkerExists } from '../helpers/worker-parse.js'; vi.setConfig({ testTimeout: 180_000 }); const describeIfWorkerBuilt = distWorkerExists() ? describe : describe.skip; type CorpusEntry = { /** Fixture directory under `test/fixtures/lang-resolution`. */ readonly fixture: string; /** * The emitter this fixture exists to exercise, short enough that vitest does * not truncate it out of the case title (~36 chars). */ readonly emitter: string; /** * FROM|TO pairs the fixture MUST still emit. These are the anti-vacuity * check: they fail loudly when the fixture stops reaching the emitter, * which is the failure mode "no undeclared pairs" cannot see. */ readonly sentinels: readonly string[]; }; /** * Fixtures chosen to reach a structural emitter that no other suite drives. * * The sentinels are the anti-vacuity anchor: each names an emitter that must * still fire. Two of them (`CodeElement|Property`, `Module|Namespace`) are also * the only pairs here that no generated rule can reach; the other nine are * rule-derived, and stay because a rule DECLARING a pair says nothing about * whether any emitter still PRODUCES it — which is the failure this corpus * exists to catch. */ const NON_BRIDGE_CORPUS = [ { // `cobol-processor.ts`: CONTAINS/CALLS/ACCESSES over Module / Namespace / // Record / Property / CodeElement. fixture: 'cobol-app', emitter: 'cobol-processor containment', sentinels: ['CodeElement|Property', 'Module|Namespace', 'Module|Record', 'Record|Record'], }, { // Same processor, DECLARATIVES section: the USE-procedure Namespace // ACCESSES a file Record. fixture: 'cobol-declaratives', emitter: 'cobol-processor DECLARATIVES', sentinels: ['Namespace|Record'], }, { // `languages/vue/scope-resolver.ts`: the only edge whose target is a // `File`. Function→File from `