mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-11-10 08:27:27 +00:00
* fix(ingestion): retain class-expression owners Keep named JS/TS expressions, members and scope declarations on matching graph identities. Distinguish expressions that reuse a local class name. Avoid root enumeration when no file-scope types are configured. 1088 focused tests pass. Full suite: 28407 passed, one pre-existing SSH-signing-sensitive golden failure, 91 skipped. * fix(ingestion): preserve type-alias owners Keep method-local type-alias member edges on the existing simple TypeAlias IDs when JS/TS class qualification is enabled. Otherwise native COPY drops both method and property relationships. Cover TS, TSX and Vue workers plus direct native-persistence reads. Refs #3725 * fix(bench): align JS/TS capture baselines Record the fingerprints introduced by qualified class and member identities. Same-corpus comparisons attribute every changed byte to the new qualified-name tags; removing those tags restores both previous hashes. Keep capture counts and scaling budgets unchanged. --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
94 lines
3.6 KiB
TypeScript
94 lines
3.6 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { parseFilesWithWorkers } from '../helpers/worker-parse.js';
|
|
|
|
describe('qualified class lookups', () => {
|
|
it('preserves nested names with and without file-level package scopes', async () => {
|
|
const { graph, model } = await parseFilesWithWorkers([
|
|
{
|
|
path: 'nested.ts',
|
|
content:
|
|
'class Outer { make() { class Inner { value = 1; run() { return this.value; } } return Inner; } }',
|
|
},
|
|
{
|
|
path: 'Nested.java',
|
|
content: 'package example.models; class Outer { class Inner {} }',
|
|
},
|
|
{
|
|
path: 'nested.rb',
|
|
content: 'module Root\n class Outer\n class Inner\n end\n end\nend\n',
|
|
},
|
|
]);
|
|
for (const name of ['Outer.Inner', 'example.models.Outer.Inner', 'Root.Outer.Inner']) {
|
|
expect(model.types.lookupClassByQualifiedName(name)).toHaveLength(1);
|
|
}
|
|
const ownerId = 'Class:nested.ts:Outer.Inner';
|
|
expect(graph.getNode(ownerId)).toBeDefined();
|
|
const members = graph.relationships.filter(
|
|
(edge) =>
|
|
edge.sourceId === ownerId && (edge.type === 'HAS_METHOD' || edge.type === 'HAS_PROPERTY'),
|
|
);
|
|
expect(members.map((edge) => graph.getNode(edge.targetId)?.properties.name).sort()).toEqual([
|
|
'run',
|
|
'value',
|
|
]);
|
|
for (const edge of members) expect(graph.getNode(edge.targetId)).toBeDefined();
|
|
});
|
|
|
|
it('derives canonical dot-separated names from namespaces, packages, and modules', async () => {
|
|
const { model } = await parseFilesWithWorkers([
|
|
{
|
|
path: 'src/Services/User.cs',
|
|
content: 'namespace Services.Auth;\npublic class User {}\n',
|
|
},
|
|
{
|
|
path: 'src/Data/User.cs',
|
|
content: 'namespace Data.Auth;\npublic class User {}\n',
|
|
},
|
|
{
|
|
path: 'src/models/Config.java',
|
|
content: 'package com.example.models;\nclass Config {}\n',
|
|
},
|
|
{
|
|
path: 'lib/admin/user.rb',
|
|
content: 'module Admin\n class User\n end\nend\n',
|
|
},
|
|
]);
|
|
|
|
const userMatches = model.types.lookupClassByName('User');
|
|
expect(userMatches).toHaveLength(3);
|
|
expect(userMatches.map((match) => match.qualifiedName).sort()).toEqual(
|
|
['Admin.User', 'Data.Auth.User', 'Services.Auth.User'].sort(),
|
|
);
|
|
|
|
const servicesUser = model.types.lookupClassByQualifiedName('Services.Auth.User');
|
|
expect(servicesUser).toHaveLength(1);
|
|
expect(servicesUser[0].filePath).toBe('src/Services/User.cs');
|
|
expect(servicesUser[0].qualifiedName).toBe('Services.Auth.User');
|
|
|
|
const dataUser = model.types.lookupClassByQualifiedName('Data.Auth.User');
|
|
expect(dataUser).toHaveLength(1);
|
|
expect(dataUser[0].filePath).toBe('src/Data/User.cs');
|
|
|
|
const javaConfig = model.types.lookupClassByQualifiedName('com.example.models.Config');
|
|
expect(javaConfig).toHaveLength(1);
|
|
expect(javaConfig[0].qualifiedName).toBe('com.example.models.Config');
|
|
|
|
const rubyUser = model.types.lookupClassByQualifiedName('Admin.User');
|
|
expect(rubyUser).toHaveLength(1);
|
|
expect(rubyUser[0].qualifiedName).toBe('Admin.User');
|
|
});
|
|
|
|
it('falls back to the simple name for top-level class-like symbols', async () => {
|
|
const { model } = await parseFilesWithWorkers([
|
|
{ path: 'src/plain-user.ts', content: 'export class User {}\n' },
|
|
]);
|
|
|
|
const simpleMatches = model.types.lookupClassByName('User');
|
|
expect(simpleMatches).toHaveLength(1);
|
|
expect(simpleMatches[0].qualifiedName).toBe('User');
|
|
|
|
const matches = model.types.lookupClassByQualifiedName('User');
|
|
expect(matches).toHaveLength(1);
|
|
expect(matches[0].qualifiedName).toBe('User');
|
|
});
|
|
});
|