mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-29 01:41:42 +00:00
fix(group): resolve custom manifest links against graph symbols (#1254)
This commit is contained in:
parent
8eebac2957
commit
0728a4ef58
2 changed files with 171 additions and 0 deletions
|
|
@ -268,6 +268,19 @@ export class ManifestExtractor {
|
|||
LIMIT 1`,
|
||||
{ contract: link.contract },
|
||||
);
|
||||
} else if (link.type === 'custom') {
|
||||
// V1: exact name-only match on code-definition nodes.
|
||||
// Positive allowlist mirrors other contract types. If multiple code
|
||||
// symbols share the same name, ORDER BY filePath ASC LIMIT 1 picks
|
||||
// the alphabetically-first occurrence deterministically.
|
||||
rows = await executor(
|
||||
`MATCH (n:Function|Method|Class|Interface|Struct|Enum|Trait|Constructor|TypeAlias|Impl|Macro|Union|Typedef|Property|Record|Delegate|Annotation|Template|Const|Static|CodeElement)
|
||||
WHERE n.name = $contract
|
||||
RETURN n.id AS uid, n.name AS name, n.filePath AS filePath
|
||||
ORDER BY n.filePath ASC
|
||||
LIMIT 1`,
|
||||
{ contract: link.contract },
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -578,6 +578,164 @@ describe('ManifestExtractor', () => {
|
|||
expect(lowerContractId).toBe(upperContractId);
|
||||
});
|
||||
|
||||
it('resolves custom manifest links by exact symbol name', async () => {
|
||||
const links: GroupManifestLink[] = [
|
||||
{
|
||||
from: 'parser/mathlex',
|
||||
to: 'engine/thales',
|
||||
type: 'custom',
|
||||
contract: 'Expression',
|
||||
role: 'provider',
|
||||
},
|
||||
];
|
||||
|
||||
const dbExecutors = new Map<
|
||||
string,
|
||||
(cypher: string, params?: Record<string, unknown>) => Promise<Record<string, unknown>[]>
|
||||
>([
|
||||
[
|
||||
'engine/thales',
|
||||
async (_cypher, params) => {
|
||||
if (params?.contract === 'Expression') {
|
||||
return [
|
||||
{
|
||||
uid: 'uid-expression-struct',
|
||||
name: 'Expression',
|
||||
filePath: 'src/expression.rs',
|
||||
},
|
||||
];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
],
|
||||
[
|
||||
'parser/mathlex',
|
||||
async (_cypher, params) => {
|
||||
if (params?.contract === 'Expression') {
|
||||
return [
|
||||
{
|
||||
uid: 'uid-expression-enum',
|
||||
name: 'Expression',
|
||||
filePath: 'src/ast.rs',
|
||||
},
|
||||
];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
const result = await extractor.extractFromManifest(links, dbExecutors);
|
||||
|
||||
const provider = result.contracts.find((c) => c.role === 'provider');
|
||||
const consumer = result.contracts.find((c) => c.role === 'consumer');
|
||||
|
||||
expect(provider?.symbolUid).toBe('uid-expression-enum');
|
||||
expect(provider?.symbolRef.filePath).toBe('src/ast.rs');
|
||||
|
||||
expect(consumer?.symbolUid).toBe('uid-expression-struct');
|
||||
expect(consumer?.symbolRef.filePath).toBe('src/expression.rs');
|
||||
|
||||
expect(result.crossLinks).toHaveLength(1);
|
||||
expect(result.crossLinks[0].matchType).toBe('manifest');
|
||||
});
|
||||
|
||||
it('falls back to synthetic uid when custom symbol not found in graph', async () => {
|
||||
const links: GroupManifestLink[] = [
|
||||
{
|
||||
from: 'core/units',
|
||||
to: 'engine/thales',
|
||||
type: 'custom',
|
||||
contract: 'Dimension',
|
||||
role: 'provider',
|
||||
},
|
||||
];
|
||||
|
||||
const dbExecutors = new Map<
|
||||
string,
|
||||
(cypher: string, params?: Record<string, unknown>) => Promise<Record<string, unknown>[]>
|
||||
>([
|
||||
['engine/thales', async () => []],
|
||||
['core/units', async () => []],
|
||||
]);
|
||||
|
||||
const result = await extractor.extractFromManifest(links, dbExecutors);
|
||||
|
||||
const provider = result.contracts.find((c) => c.role === 'provider');
|
||||
expect(provider?.symbolUid).toBe('manifest::core/units::custom::Dimension');
|
||||
});
|
||||
|
||||
it('custom contract query uses positive label allowlist (not negative exclusion)', async () => {
|
||||
const links: GroupManifestLink[] = [
|
||||
{
|
||||
from: 'parser/mathlex',
|
||||
to: 'engine/thales',
|
||||
type: 'custom',
|
||||
contract: 'Route',
|
||||
role: 'provider',
|
||||
},
|
||||
];
|
||||
let capturedCypher = '';
|
||||
const dbExecutors = new Map<
|
||||
string,
|
||||
(cypher: string, params?: Record<string, unknown>) => Promise<Record<string, unknown>[]>
|
||||
>([
|
||||
[
|
||||
'parser/mathlex',
|
||||
async (cypher) => {
|
||||
capturedCypher = cypher;
|
||||
return [];
|
||||
},
|
||||
],
|
||||
['engine/thales', async () => []],
|
||||
]);
|
||||
|
||||
await extractor.extractFromManifest(links, dbExecutors);
|
||||
|
||||
expect(capturedCypher).toContain('Function|Method|Class|Interface|Struct|Enum|Trait');
|
||||
expect(capturedCypher).not.toContain('NOT n:File');
|
||||
});
|
||||
|
||||
it('custom contract with ambiguous name returns first-by-filePath deterministically', async () => {
|
||||
const links: GroupManifestLink[] = [
|
||||
{
|
||||
from: 'parser/mathlex',
|
||||
to: 'engine/thales',
|
||||
type: 'custom',
|
||||
contract: 'Token',
|
||||
role: 'provider',
|
||||
},
|
||||
];
|
||||
|
||||
const dbExecutors = new Map<
|
||||
string,
|
||||
(cypher: string, params?: Record<string, unknown>) => Promise<Record<string, unknown>[]>
|
||||
>([
|
||||
[
|
||||
'parser/mathlex',
|
||||
async (_cypher, params) => {
|
||||
if (params?.contract === 'Token') {
|
||||
return [{ uid: 'uid-token-first', name: 'Token', filePath: 'src/ast.rs' }];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
],
|
||||
[
|
||||
'engine/thales',
|
||||
async (_cypher, params) => {
|
||||
if (params?.contract === 'Token') {
|
||||
return [{ uid: 'uid-token-consumer', name: 'Token', filePath: 'src/lexer.rs' }];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
const result = await extractor.extractFromManifest(links, dbExecutors);
|
||||
const provider = result.contracts.find((c) => c.role === 'provider');
|
||||
expect(provider?.symbolUid).toBe('uid-token-first');
|
||||
});
|
||||
|
||||
it('returns empty for no links', async () => {
|
||||
const result = await extractor.extractFromManifest([]);
|
||||
expect(result.contracts).toHaveLength(0);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue