mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-09 03:17:54 +00:00
test: add unit tests for Phase 8 SymbolTable features (39 tests, up from 19)
Cover all new branches: declaredType metadata, Property exclusion from globalIndex, conditional callableIndex invalidation, lookupFieldByOwner (happy path + edge cases), lookupFuzzyCallable filtering, and clear() with fieldByOwner. Fixes branch coverage threshold (21.8% → 23%+).
This commit is contained in:
parent
df0018042c
commit
c6ca847641
1 changed files with 174 additions and 1 deletions
|
|
@ -137,14 +137,187 @@ describe('SymbolTable', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('declaredType metadata', () => {
|
||||
it('stores declaredType in SymbolDefinition', () => {
|
||||
table.add('src/models.ts', 'address', 'prop:address', 'Property', {
|
||||
declaredType: 'Address',
|
||||
ownerId: 'class:User',
|
||||
});
|
||||
const def = table.lookupExactFull('src/models.ts', 'address');
|
||||
expect(def).toBeDefined();
|
||||
expect(def!.declaredType).toBe('Address');
|
||||
});
|
||||
|
||||
it('omits declaredType when not provided', () => {
|
||||
table.add('src/models.ts', 'name', 'prop:name', 'Property', { ownerId: 'class:User' });
|
||||
const def = table.lookupExactFull('src/models.ts', 'name');
|
||||
expect(def).toBeDefined();
|
||||
expect(def!.declaredType).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Property exclusion from globalIndex', () => {
|
||||
it('Property with ownerId is NOT added to globalIndex', () => {
|
||||
table.add('src/models.ts', 'name', 'prop:name', 'Property', {
|
||||
declaredType: 'string',
|
||||
ownerId: 'class:User',
|
||||
});
|
||||
// Should not appear in fuzzy lookup
|
||||
expect(table.lookupFuzzy('name')).toEqual([]);
|
||||
// But should still be in fileIndex
|
||||
expect(table.lookupExact('src/models.ts', 'name')).toBe('prop:name');
|
||||
});
|
||||
|
||||
it('Property without ownerId IS added to globalIndex', () => {
|
||||
table.add('src/models.ts', 'name', 'prop:name', 'Property');
|
||||
expect(table.lookupFuzzy('name')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('Property without declaredType is still added to fieldByOwner index only (not globalIndex)', () => {
|
||||
table.add('src/models.ts', 'name', 'prop:name', 'Property', { ownerId: 'class:User' });
|
||||
// No declaredType → not in fieldByOwner, but still excluded from globalIndex
|
||||
expect(table.lookupFuzzy('name')).toEqual([]);
|
||||
expect(table.lookupFieldByOwner('class:User', 'name')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('non-Property types are always added to globalIndex', () => {
|
||||
table.add('src/models.ts', 'save', 'method:save', 'Method', { ownerId: 'class:User' });
|
||||
expect(table.lookupFuzzy('save')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('conditional callableIndex invalidation', () => {
|
||||
it('adding a Function invalidates callableIndex', () => {
|
||||
table.add('src/a.ts', 'foo', 'func:foo', 'Function', { returnType: 'void' });
|
||||
// First call builds the index
|
||||
expect(table.lookupFuzzyCallable('foo')).toHaveLength(1);
|
||||
// Add another callable — should invalidate and rebuild
|
||||
table.add('src/a.ts', 'bar', 'func:bar', 'Method');
|
||||
expect(table.lookupFuzzyCallable('bar')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('adding a Property does NOT invalidate callableIndex', () => {
|
||||
table.add('src/a.ts', 'foo', 'func:foo', 'Function');
|
||||
// Build callable index
|
||||
expect(table.lookupFuzzyCallable('foo')).toHaveLength(1);
|
||||
// Add a Property — callable index should still be valid (foo still found)
|
||||
table.add('src/models.ts', 'name', 'prop:name', 'Property', {
|
||||
declaredType: 'string',
|
||||
ownerId: 'class:User',
|
||||
});
|
||||
expect(table.lookupFuzzyCallable('foo')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('adding a Class does NOT invalidate callableIndex', () => {
|
||||
table.add('src/a.ts', 'foo', 'func:foo', 'Function');
|
||||
expect(table.lookupFuzzyCallable('foo')).toHaveLength(1);
|
||||
table.add('src/models.ts', 'User', 'class:User', 'Class');
|
||||
// Class is not callable, should not trigger rebuild
|
||||
expect(table.lookupFuzzyCallable('foo')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('lookupFieldByOwner', () => {
|
||||
it('finds a Property by ownerNodeId and fieldName', () => {
|
||||
table.add('src/models.ts', 'address', 'prop:address', 'Property', {
|
||||
declaredType: 'Address',
|
||||
ownerId: 'class:User',
|
||||
});
|
||||
const def = table.lookupFieldByOwner('class:User', 'address');
|
||||
expect(def).toBeDefined();
|
||||
expect(def!.declaredType).toBe('Address');
|
||||
expect(def!.nodeId).toBe('prop:address');
|
||||
});
|
||||
|
||||
it('returns undefined for unknown owner', () => {
|
||||
table.add('src/models.ts', 'address', 'prop:address', 'Property', {
|
||||
declaredType: 'Address',
|
||||
ownerId: 'class:User',
|
||||
});
|
||||
expect(table.lookupFieldByOwner('class:Unknown', 'address')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns undefined for unknown field name', () => {
|
||||
table.add('src/models.ts', 'address', 'prop:address', 'Property', {
|
||||
declaredType: 'Address',
|
||||
ownerId: 'class:User',
|
||||
});
|
||||
expect(table.lookupFieldByOwner('class:User', 'email')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns undefined for empty table', () => {
|
||||
expect(table.lookupFieldByOwner('class:User', 'name')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('does not index Property without declaredType', () => {
|
||||
table.add('src/models.ts', 'name', 'prop:name', 'Property', { ownerId: 'class:User' });
|
||||
expect(table.lookupFieldByOwner('class:User', 'name')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('distinguishes fields by owner', () => {
|
||||
table.add('src/models.ts', 'name', 'prop:user:name', 'Property', {
|
||||
declaredType: 'string',
|
||||
ownerId: 'class:User',
|
||||
});
|
||||
table.add('src/models.ts', 'name', 'prop:repo:name', 'Property', {
|
||||
declaredType: 'RepoName',
|
||||
ownerId: 'class:Repo',
|
||||
});
|
||||
expect(table.lookupFieldByOwner('class:User', 'name')!.declaredType).toBe('string');
|
||||
expect(table.lookupFieldByOwner('class:Repo', 'name')!.declaredType).toBe('RepoName');
|
||||
});
|
||||
});
|
||||
|
||||
describe('lookupFuzzyCallable', () => {
|
||||
it('returns only callable types (Function, Method, Constructor)', () => {
|
||||
table.add('src/a.ts', 'foo', 'func:foo', 'Function');
|
||||
table.add('src/a.ts', 'bar', 'method:bar', 'Method');
|
||||
table.add('src/a.ts', 'Baz', 'ctor:Baz', 'Constructor');
|
||||
table.add('src/a.ts', 'User', 'class:User', 'Class');
|
||||
table.add('src/a.ts', 'IUser', 'iface:IUser', 'Interface');
|
||||
expect(table.lookupFuzzyCallable('foo')).toHaveLength(1);
|
||||
expect(table.lookupFuzzyCallable('bar')).toHaveLength(1);
|
||||
expect(table.lookupFuzzyCallable('Baz')).toHaveLength(1);
|
||||
expect(table.lookupFuzzyCallable('User')).toEqual([]);
|
||||
expect(table.lookupFuzzyCallable('IUser')).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns empty array for unknown name', () => {
|
||||
table.add('src/a.ts', 'foo', 'func:foo', 'Function');
|
||||
expect(table.lookupFuzzyCallable('unknown')).toEqual([]);
|
||||
});
|
||||
|
||||
it('rebuilds index after adding new callable', () => {
|
||||
table.add('src/a.ts', 'foo', 'func:foo', 'Function');
|
||||
expect(table.lookupFuzzyCallable('foo')).toHaveLength(1);
|
||||
expect(table.lookupFuzzyCallable('bar')).toEqual([]);
|
||||
table.add('src/a.ts', 'bar', 'func:bar', 'Function');
|
||||
expect(table.lookupFuzzyCallable('bar')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('filters non-callable types from mixed name entries', () => {
|
||||
table.add('src/a.ts', 'save', 'func:save', 'Function');
|
||||
table.add('src/b.ts', 'save', 'class:save', 'Class');
|
||||
const callables = table.lookupFuzzyCallable('save');
|
||||
expect(callables).toHaveLength(1);
|
||||
expect(callables[0].type).toBe('Function');
|
||||
});
|
||||
});
|
||||
|
||||
describe('clear', () => {
|
||||
it('resets all state', () => {
|
||||
it('resets all state including fieldByOwner', () => {
|
||||
table.add('src/a.ts', 'foo', 'func:foo', 'Function');
|
||||
table.add('src/b.ts', 'bar', 'func:bar', 'Function');
|
||||
table.add('src/models.ts', 'address', 'prop:address', 'Property', {
|
||||
declaredType: 'Address',
|
||||
ownerId: 'class:User',
|
||||
});
|
||||
table.clear();
|
||||
expect(table.getStats()).toEqual({ fileCount: 0, globalSymbolCount: 0 });
|
||||
expect(table.lookupExact('src/a.ts', 'foo')).toBeUndefined();
|
||||
expect(table.lookupFuzzy('foo')).toEqual([]);
|
||||
expect(table.lookupFieldByOwner('class:User', 'address')).toBeUndefined();
|
||||
expect(table.lookupFuzzyCallable('foo')).toEqual([]);
|
||||
});
|
||||
|
||||
it('allows re-adding after clear', () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue