mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-08-28 05:25:25 +00:00
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
* fix(scope-resolution): link Record graph nodes Register Record definitions and caller anchors so Java and C# record targets and initializer sources resolve to canonical nodes. Keep generated LadybugDB relation pairs and the production benchmark baseline synchronized. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(bench): harden schema-pair production gate Derive the production count from executable DDL, fail closed when its budget is missing, and independently pin Record-to-Property coverage. Keep benchmark evidence machine-scoped and correct stale schema counts. --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
332 lines
12 KiB
TypeScript
332 lines
12 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
NODE_TABLES,
|
|
REL_TABLE_NAME,
|
|
REL_TYPES,
|
|
EMBEDDING_TABLE_NAME,
|
|
NODE_SCHEMA_QUERIES,
|
|
REL_SCHEMA_QUERIES,
|
|
SCHEMA_QUERIES,
|
|
FILE_SCHEMA,
|
|
FOLDER_SCHEMA,
|
|
FUNCTION_SCHEMA,
|
|
CLASS_SCHEMA,
|
|
INTERFACE_SCHEMA,
|
|
METHOD_SCHEMA,
|
|
PROPERTY_SCHEMA,
|
|
CODE_ELEMENT_SCHEMA,
|
|
COMMUNITY_SCHEMA,
|
|
PROCESS_SCHEMA,
|
|
BASICBLOCK_SCHEMA,
|
|
RELATION_SCHEMA,
|
|
EMBEDDING_SCHEMA,
|
|
CREATE_VECTOR_INDEX_QUERY,
|
|
} from '../../src/core/lbug/schema.js';
|
|
import { parseRelationSchemaPairs } from '../../src/core/lbug/rel-pair-routing.js';
|
|
|
|
describe('LadybugDB Schema', () => {
|
|
describe('NODE_TABLES', () => {
|
|
it('includes all core node types', () => {
|
|
const core = [
|
|
'File',
|
|
'Folder',
|
|
'Function',
|
|
'Class',
|
|
'Interface',
|
|
'Method',
|
|
'CodeElement',
|
|
'Community',
|
|
'Process',
|
|
];
|
|
for (const t of core) {
|
|
expect(NODE_TABLES).toContain(t);
|
|
}
|
|
});
|
|
|
|
it('includes multi-language node types', () => {
|
|
const multiLang = [
|
|
'Struct',
|
|
'Enum',
|
|
'Macro',
|
|
'Typedef',
|
|
'Union',
|
|
'Namespace',
|
|
'Trait',
|
|
'Impl',
|
|
'TypeAlias',
|
|
'Const',
|
|
'Static',
|
|
'Variable',
|
|
'Property',
|
|
'Record',
|
|
'Delegate',
|
|
'Annotation',
|
|
'Constructor',
|
|
'Template',
|
|
'Module',
|
|
];
|
|
for (const t of multiLang) {
|
|
expect(NODE_TABLES).toContain(t);
|
|
}
|
|
});
|
|
|
|
it('includes the BasicBlock taint/PDG substrate node (issue #2080)', () => {
|
|
expect(NODE_TABLES).toContain('BasicBlock');
|
|
});
|
|
|
|
it('has expected total count', () => {
|
|
// 9 core + 19 multi-language + Route + Tool + BasicBlock = 32
|
|
expect(NODE_TABLES).toHaveLength(32);
|
|
});
|
|
});
|
|
|
|
describe('REL_TYPES', () => {
|
|
it('includes all expected relationship types', () => {
|
|
const expected = [
|
|
'CONTAINS',
|
|
'DEFINES',
|
|
'IMPORTS',
|
|
'CALLS',
|
|
'EXTENDS',
|
|
'IMPLEMENTS',
|
|
'MEMBER_OF',
|
|
'STEP_IN_PROCESS',
|
|
];
|
|
for (const t of expected) {
|
|
expect(REL_TYPES).toContain(t);
|
|
}
|
|
});
|
|
|
|
it('includes the taint/PDG substrate edge types (issue #2080)', () => {
|
|
for (const t of ['CFG', 'REACHING_DEF', 'TAINTED', 'SANITIZES', 'TAINT_PATH']) {
|
|
expect(REL_TYPES).toContain(t);
|
|
}
|
|
});
|
|
|
|
it('includes the control-dependence edge types (issue #2085 M5)', () => {
|
|
for (const t of ['CDG', 'POST_DOMINATE']) {
|
|
expect(REL_TYPES).toContain(t);
|
|
}
|
|
});
|
|
|
|
it('includes the DI collection-injection edge type (#2200)', () => {
|
|
expect(REL_TYPES).toContain('INJECTS');
|
|
});
|
|
|
|
it('includes Spring condition and auto-configuration edge types (#2415)', () => {
|
|
expect(REL_TYPES).toContain('CONDITIONAL_ON');
|
|
expect(REL_TYPES).toContain('DECLARES');
|
|
expect(REL_TYPES).toContain('ADVISED_BY');
|
|
expect(REL_TYPES).not.toContain('AUTO_REGISTERS');
|
|
});
|
|
});
|
|
|
|
describe('node schema DDL', () => {
|
|
it.each([
|
|
['FILE_SCHEMA', FILE_SCHEMA, 'File'],
|
|
['FOLDER_SCHEMA', FOLDER_SCHEMA, 'Folder'],
|
|
['FUNCTION_SCHEMA', FUNCTION_SCHEMA, 'Function'],
|
|
['CLASS_SCHEMA', CLASS_SCHEMA, 'Class'],
|
|
['INTERFACE_SCHEMA', INTERFACE_SCHEMA, 'Interface'],
|
|
['METHOD_SCHEMA', METHOD_SCHEMA, 'Method'],
|
|
['CODE_ELEMENT_SCHEMA', CODE_ELEMENT_SCHEMA, 'CodeElement'],
|
|
['COMMUNITY_SCHEMA', COMMUNITY_SCHEMA, 'Community'],
|
|
['PROCESS_SCHEMA', PROCESS_SCHEMA, 'Process'],
|
|
])('%s contains CREATE NODE TABLE for %s', (_, schema, tableName) => {
|
|
expect(schema).toContain('CREATE NODE TABLE');
|
|
expect(schema).toContain(tableName);
|
|
expect(schema).toContain('PRIMARY KEY');
|
|
});
|
|
|
|
it('Function schema has startLine and endLine', () => {
|
|
expect(FUNCTION_SCHEMA).toContain('startLine INT64');
|
|
expect(FUNCTION_SCHEMA).toContain('endLine INT64');
|
|
});
|
|
|
|
it('Function schema has isExported', () => {
|
|
expect(FUNCTION_SCHEMA).toContain('isExported BOOLEAN');
|
|
});
|
|
|
|
it('Property schema preserves declaredType', () => {
|
|
expect(SCHEMA_QUERIES).toContain(PROPERTY_SCHEMA);
|
|
expect(PROPERTY_SCHEMA).toContain('declaredType STRING');
|
|
});
|
|
|
|
it('BasicBlock schema is wired into SCHEMA_QUERIES (issue #2080, F1 guard)', () => {
|
|
// Defining BASICBLOCK_SCHEMA is not enough — it must be appended to
|
|
// NODE_SCHEMA_QUERIES (→ SCHEMA_QUERIES) or initLbug never creates the
|
|
// table and the bulk-COPY round-trip fails with "table does not exist".
|
|
expect(SCHEMA_QUERIES).toContain(BASICBLOCK_SCHEMA);
|
|
expect(BASICBLOCK_SCHEMA).toContain('CREATE NODE TABLE BasicBlock');
|
|
expect(BASICBLOCK_SCHEMA).toContain('filePath STRING');
|
|
expect(BASICBLOCK_SCHEMA).toContain('startLine INT64');
|
|
expect(BASICBLOCK_SCHEMA).toContain('endLine INT64');
|
|
expect(BASICBLOCK_SCHEMA).toContain('text STRING');
|
|
expect(BASICBLOCK_SCHEMA).toContain('PRIMARY KEY (id)');
|
|
});
|
|
|
|
it('Community schema has heuristicLabel and cohesion', () => {
|
|
expect(COMMUNITY_SCHEMA).toContain('heuristicLabel STRING');
|
|
expect(COMMUNITY_SCHEMA).toContain('cohesion DOUBLE');
|
|
});
|
|
|
|
it('Process schema has processType and stepCount', () => {
|
|
expect(PROCESS_SCHEMA).toContain('processType STRING');
|
|
expect(PROCESS_SCHEMA).toContain('stepCount INT32');
|
|
});
|
|
});
|
|
|
|
describe('relation schema', () => {
|
|
it('creates a single REL TABLE named CodeRelation', () => {
|
|
expect(RELATION_SCHEMA).toContain(`CREATE REL TABLE ${REL_TABLE_NAME}`);
|
|
});
|
|
|
|
it('has type, confidence, reason, step properties', () => {
|
|
expect(RELATION_SCHEMA).toContain('type STRING');
|
|
expect(RELATION_SCHEMA).toContain('confidence DOUBLE');
|
|
expect(RELATION_SCHEMA).toContain('reason STRING');
|
|
expect(RELATION_SCHEMA).toContain('step INT32');
|
|
});
|
|
|
|
// These four go through `parseRelationSchemaPairs`, not a raw substring:
|
|
// each of their pairs is now emitted by a cross product rather than
|
|
// hand-written, and the generated half backticks EVERY label
|
|
// (`FROM \`Function\` TO \`Function\``) while the hand-written half
|
|
// backticks only multi-language names. Asserting the runtime's own parse
|
|
// keeps them about the pair being declared, which is what LadybugDB
|
|
// enforces — a cosmetic DDL formatting change cannot fail them.
|
|
it('connects Function to Function (CALLS)', () => {
|
|
expect(parseRelationSchemaPairs(RELATION_SCHEMA).has('Function|Function')).toBe(true);
|
|
});
|
|
|
|
it('connects File to Function (CONTAINS/DEFINES)', () => {
|
|
expect(parseRelationSchemaPairs(RELATION_SCHEMA).has('File|Function')).toBe(true);
|
|
});
|
|
|
|
it('connects symbols to Community (MEMBER_OF)', () => {
|
|
const declaredPairs = parseRelationSchemaPairs(RELATION_SCHEMA);
|
|
expect(declaredPairs.has('Function|Community')).toBe(true);
|
|
expect(declaredPairs.has('Class|Community')).toBe(true);
|
|
});
|
|
|
|
it('connects symbols to Process (STEP_IN_PROCESS)', () => {
|
|
const declaredPairs = parseRelationSchemaPairs(RELATION_SCHEMA);
|
|
expect(declaredPairs.has('Function|Process')).toBe(true);
|
|
expect(declaredPairs.has('Method|Process')).toBe(true);
|
|
});
|
|
|
|
it('connects BasicBlock to BasicBlock (taint/PDG substrate edges, #2080)', () => {
|
|
expect(RELATION_SCHEMA).toContain('FROM BasicBlock TO BasicBlock');
|
|
});
|
|
|
|
it('persists class-like framework edges to synthetic declarations (#2416)', () => {
|
|
expect(RELATION_SCHEMA).toContain('FROM Class TO CodeElement');
|
|
expect(RELATION_SCHEMA).toContain('FROM Interface TO CodeElement');
|
|
});
|
|
|
|
it('declares the Swift enum/property member-containment pairs (#2769)', () => {
|
|
// Asserted through the runtime's own parser, not raw strings, so a
|
|
// cosmetic DDL formatting change cannot fail this without a semantic
|
|
// change (see PR #2769 review Finding 7). The Rust impl/trait and JS/TS
|
|
// object-literal pairs get their own tests below — narrower and named
|
|
// for the specific abort each one fixes.
|
|
const declaredPairs = parseRelationSchemaPairs(RELATION_SCHEMA);
|
|
const memberPairs = [
|
|
// Swift enum members (also reached by Java/PHP enum methods)
|
|
'Enum|Function',
|
|
'Enum|Method',
|
|
'Enum|Struct',
|
|
'Enum|Constructor',
|
|
'Enum|Property',
|
|
'Enum|TypeAlias',
|
|
'Property|Class',
|
|
'Property|Enum',
|
|
'Property|Function',
|
|
'Property|Struct',
|
|
'Method|Variable',
|
|
'Method|Const',
|
|
// #2801 moves this pair from structural DDL into the generated bridge.
|
|
'Record|Property',
|
|
];
|
|
|
|
for (const pair of memberPairs) {
|
|
expect(declaredPairs.has(pair)).toBe(true);
|
|
}
|
|
});
|
|
|
|
it('has all FROM/TO pairs needed for HAS_METHOD edges', () => {
|
|
// HAS_METHOD sources: Class, Interface, Struct, Trait, Impl, Record
|
|
// HAS_METHOD targets: Method, Constructor (Property is now HAS_PROPERTY)
|
|
const declaredPairs = parseRelationSchemaPairs(RELATION_SCHEMA);
|
|
const sources = ['Class', 'Interface'];
|
|
const backtickSources = ['Struct', 'Trait', 'Impl', 'Record'];
|
|
const targets = ['Method'];
|
|
const backtickTargets = ['Constructor'];
|
|
|
|
for (const src of [...sources, ...backtickSources]) {
|
|
for (const tgt of [...targets, ...backtickTargets]) {
|
|
expect(declaredPairs.has(`${src}|${tgt}`)).toBe(true);
|
|
}
|
|
}
|
|
});
|
|
|
|
it('has FROM/TO pairs for HAS_METHOD edges whose member is minted as Function, not Method (#2769)', () => {
|
|
// A Rust `impl`/`trait` method is minted as a `Function` node, not
|
|
// `Method` (tree-sitter-queries.ts), so `Impl|Method`/`Trait|Method`
|
|
// never fire for it — this was the reproduced abort on any Rust repo
|
|
// with an `impl` or `trait` block (PR #2769 review Finding 1).
|
|
const declaredPairs = parseRelationSchemaPairs(RELATION_SCHEMA);
|
|
for (const pair of ['Trait|Function', 'Impl|Function']) {
|
|
expect(declaredPairs.has(pair)).toBe(true);
|
|
}
|
|
});
|
|
|
|
it('has FROM/TO pairs for HAS_METHOD edges owned by a JS/TS object-literal binding (#2769)', () => {
|
|
// A `const x = { m() {} }` / `var x = { m() {} }` owner is labelled
|
|
// verbatim `Const`/`Variable` (ast-helpers.ts), and its shorthand
|
|
// method is a `Method` node — this was the second reproduced abort,
|
|
// hit on ordinary TS/JS code including GitNexus's own source tree
|
|
// (PR #2769 review Finding 1).
|
|
const declaredPairs = parseRelationSchemaPairs(RELATION_SCHEMA);
|
|
for (const pair of ['Const|Method', 'Variable|Method']) {
|
|
expect(declaredPairs.has(pair)).toBe(true);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('embedding schema', () => {
|
|
it('creates CodeEmbedding table', () => {
|
|
expect(EMBEDDING_SCHEMA).toContain(`CREATE NODE TABLE ${EMBEDDING_TABLE_NAME}`);
|
|
expect(EMBEDDING_SCHEMA).toContain('embedding FLOAT[384]');
|
|
});
|
|
|
|
it('has vector index query', () => {
|
|
expect(CREATE_VECTOR_INDEX_QUERY).toContain('CREATE_VECTOR_INDEX');
|
|
expect(CREATE_VECTOR_INDEX_QUERY).toContain('cosine');
|
|
});
|
|
});
|
|
|
|
describe('schema query ordering', () => {
|
|
it('NODE_SCHEMA_QUERIES has correct count', () => {
|
|
// 31 + BasicBlock = 32
|
|
expect(NODE_SCHEMA_QUERIES).toHaveLength(32);
|
|
});
|
|
|
|
it('REL_SCHEMA_QUERIES has one relation table', () => {
|
|
expect(REL_SCHEMA_QUERIES).toHaveLength(1);
|
|
});
|
|
|
|
it('SCHEMA_QUERIES includes all node + rel + embedding schemas', () => {
|
|
// 32 node + 1 rel + 1 embedding = 34
|
|
expect(SCHEMA_QUERIES).toHaveLength(34);
|
|
});
|
|
|
|
it('node schemas come before relation schemas in SCHEMA_QUERIES', () => {
|
|
const relIndex = SCHEMA_QUERIES.indexOf(RELATION_SCHEMA);
|
|
const lastNodeIndex = SCHEMA_QUERIES.indexOf(
|
|
NODE_SCHEMA_QUERIES[NODE_SCHEMA_QUERIES.length - 1],
|
|
);
|
|
expect(relIndex).toBeGreaterThan(lastNodeIndex);
|
|
});
|
|
});
|
|
});
|