Files
colbymchenry--codegraph/__tests__/object-registry-synthesizer.test.ts
T
wehub-resource-sync 5f98960d22
Deploy site to GitHub Pages / build (push) Failing after 1s
Deploy site to GitHub Pages / deploy (push) Has been skipped
chore: import upstream snapshot with attribution
2026-07-13 12:02:56 +08:00

84 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Object-literal registry dispatch synthesizer.
*
* A command registry maps keys → handler classes/functions in an object literal, then
* dispatches by a RUNTIME key (`new registry[command]().execute()`) that static parsing
* can't follow. The synthesizer links each dispatching method → each registered handler's
* callable entry. Validates: a class registry resolves to the handler's `.execute` method;
* the field-initializer form (`commands = {…}` matched against a `this.commands[k]` dispatch);
* and the dispatch GATE — a look-alike object literal that is only ever accessed statically
* (never `registry[var]`) yields no edges.
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as os from 'node:os';
import { CodeGraph } from '../src';
describe('object-registry synthesizer', () => {
let dir: string;
beforeEach(() => { dir = fs.mkdtempSync(path.join(os.tmpdir(), 'obj-registry-')); });
afterEach(() => { fs.rmSync(dir, { recursive: true, force: true }); });
it('links a dispatcher to each registered command classs execute method, gated on dynamic dispatch', async () => {
fs.writeFileSync(
path.join(dir, 'commands.ts'),
`export class AddCommand { execute() { return 'add'; } }
export class RemoveCommand { execute() { return 'remove'; } }
export class MoveCommand { execute() { return 'move'; } }
`
);
fs.writeFileSync(
path.join(dir, 'manager.ts'),
`import { AddCommand, RemoveCommand, MoveCommand } from './commands';
const Cmd = { ADD: 'add', REMOVE: 'remove', MOVE: 'move' };
class CommandManager {
commands = {
[Cmd.ADD]: AddCommand,
[Cmd.REMOVE]: RemoveCommand,
[Cmd.MOVE]: MoveCommand,
};
executeCommand(command: string) {
return new this.commands[command]().execute();
}
}
`
);
// A look-alike registry that is NEVER dynamically dispatched (only a static `.add`
// member access) — must yield NO edges. The dynamic `registry[var]` dispatch is the gate.
fs.writeFileSync(
path.join(dir, 'static.ts'),
`import { AddCommand, RemoveCommand } from './commands';
const table = { add: AddCommand, remove: RemoveCommand };
export function direct() { return new table.add().execute(); }
`
);
const cg = await CodeGraph.init(dir, { silent: true });
await cg.indexAll();
const db = (cg as any).db.db;
const rows = db
.prepare(
`SELECT s.name source_name, t.name target_name, t.kind target_kind, t.file_path target_file
FROM edges e
JOIN nodes s ON s.id = e.source
JOIN nodes t ON t.id = e.target
WHERE json_extract(e.metadata,'$.synthesizedBy') = 'object-registry'`
)
.all();
cg.close?.();
// Exactly the 3 dispatcher→handler-entry edges: executeCommand → {Add,Remove,Move}Command.execute.
expect(rows.length).toBe(3);
expect(rows.every((r: any) => r.source_name === 'executeCommand')).toBe(true);
expect(rows.every((r: any) => r.target_kind === 'method' && r.target_name === 'execute')).toBe(true);
expect(rows.every((r: any) => /commands\.ts$/.test(r.target_file))).toBe(true);
// The statically-accessed look-alike registry contributed nothing.
expect(rows.some((r: any) => /static\.ts$/.test(r.target_file))).toBe(false);
});
});