This commit is contained in:
Zander Raycraft 2026-03-18 17:36:39 -05:00
commit 16ec18aef1
12 changed files with 64 additions and 31 deletions

View file

@ -840,8 +840,9 @@ describe('Field type resolution (C++)', () => {
it('emits HAS_PROPERTY edges linking fields to classes', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(2);
expect(propEdges.length).toBe(3);
expect(edgeSet(propEdges)).toContain('User → address');
expect(edgeSet(propEdges)).toContain('User → name');
expect(edgeSet(propEdges)).toContain('Address → city');
});
@ -952,7 +953,7 @@ describe('Write access tracking (C++)', () => {
it('emits ACCESSES write edges for field assignments', () => {
const accesses = getRelationships(result, 'ACCESSES');
const writes = accesses.filter(e => e.rel.reason === 'write');
expect(writes.length).toBeGreaterThanOrEqual(2);
expect(writes.length).toBe(2);
const fieldNames = writes.map(e => e.target);
expect(fieldNames).toContain('name');
expect(fieldNames).toContain('address');

View file

@ -1221,7 +1221,7 @@ describe('Field type resolution (C#)', () => {
it('emits HAS_PROPERTY edges linking properties to classes', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(3);
expect(propEdges.length).toBe(3);
expect(edgeSet(propEdges)).toContain('User → Address');
expect(edgeSet(propEdges)).toContain('User → Name');
expect(edgeSet(propEdges)).toContain('Address → City');
@ -1300,7 +1300,7 @@ describe('Write access tracking (C#)', () => {
it('emits ACCESSES write edges for field assignments', () => {
const accesses = getRelationships(result, 'ACCESSES');
const writes = accesses.filter(e => e.rel.reason === 'write');
expect(writes.length).toBeGreaterThanOrEqual(2);
expect(writes.length).toBe(2);
const fieldNames = writes.map(e => e.target);
expect(fieldNames).toContain('Name');
expect(fieldNames).toContain('Address');

View file

@ -968,7 +968,10 @@ describe('Field type resolution (Go)', () => {
it('emits HAS_PROPERTY edges linking struct fields to structs', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(2);
expect(propEdges.length).toBe(3);
expect(edgeSet(propEdges)).toContain('User → Name');
expect(edgeSet(propEdges)).toContain('User → Address');
expect(edgeSet(propEdges)).toContain('Address → City');
});
it('resolves user.Address.Save() → Address#Save via field type', () => {
@ -1008,7 +1011,12 @@ describe('Deep field chain resolution (Go)', () => {
it('emits HAS_PROPERTY edges for nested type chain', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(3);
expect(propEdges.length).toBe(5);
expect(edgeSet(propEdges)).toContain('User → Name');
expect(edgeSet(propEdges)).toContain('User → Address');
expect(edgeSet(propEdges)).toContain('Address → City');
expect(edgeSet(propEdges)).toContain('Address → Street');
expect(edgeSet(propEdges)).toContain('City → ZipCode');
});
it('resolves 2-level chain: user.Address.Save() → Address#Save', () => {

View file

@ -1096,7 +1096,7 @@ describe('Field type resolution (Java)', () => {
it('emits HAS_PROPERTY edges linking properties to classes', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(3);
expect(propEdges.length).toBe(3);
expect(edgeSet(propEdges)).toContain('User → address');
expect(edgeSet(propEdges)).toContain('User → name');
expect(edgeSet(propEdges)).toContain('Address → city');

View file

@ -262,10 +262,11 @@ describe('Field type resolution (JavaScript)', () => {
it('emits HAS_PROPERTY edges linking fields to classes', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(3);
expect(propEdges.length).toBe(4);
expect(edgeSet(propEdges)).toContain('User → address');
expect(edgeSet(propEdges)).toContain('User → name');
expect(edgeSet(propEdges)).toContain('Address → city');
expect(edgeSet(propEdges)).toContain('Config → DEFAULT');
});
});
@ -285,7 +286,7 @@ describe('Write access tracking (JavaScript)', () => {
it('emits ACCESSES write edges for field assignments', () => {
const accesses = getRelationships(result, 'ACCESSES');
const writes = accesses.filter(e => e.rel.reason === 'write');
expect(writes.length).toBeGreaterThanOrEqual(2);
expect(writes.length).toBe(2);
const fieldNames = writes.map(e => e.target);
expect(fieldNames).toContain('name');
expect(fieldNames).toContain('address');

View file

@ -1246,7 +1246,7 @@ describe('Field type resolution (Kotlin)', () => {
it('emits HAS_PROPERTY edges linking properties to classes', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(3);
expect(propEdges.length).toBe(3);
expect(edgeSet(propEdges)).toContain('User → address');
expect(edgeSet(propEdges)).toContain('User → name');
expect(edgeSet(propEdges)).toContain('Address → city');
@ -1367,13 +1367,16 @@ describe('Write access tracking (Kotlin)', () => {
it('emits ACCESSES write edges for property assignments', () => {
const accesses = getRelationships(result, 'ACCESSES');
const writes = accesses.filter(e => e.rel.reason === 'write');
expect(writes.length).toBeGreaterThanOrEqual(2);
expect(writes.length).toBe(3);
const nameWrite = writes.find(e => e.target === 'name');
const addressWrite = writes.find(e => e.target === 'address');
const scoreWrite = writes.find(e => e.target === 'score');
expect(nameWrite).toBeDefined();
expect(nameWrite!.source).toBe('updateUser');
expect(addressWrite).toBeDefined();
expect(addressWrite!.source).toBe('updateUser');
expect(scoreWrite).toBeDefined();
expect(scoreWrite!.source).toBe('updateUser');
});
it('emits ACCESSES write edge for compound assignment (+=)', () => {

View file

@ -138,7 +138,7 @@ describe('PHP heritage & import resolution', () => {
it('emits OVERRIDES edge for User overriding log (inherited from BaseModel)', () => {
const overrides = getRelationships(result, 'OVERRIDES');
expect(overrides.length).toBeGreaterThanOrEqual(1);
expect(overrides.length).toBe(1);
const logOverride = overrides.find(e => e.source === 'User' && e.target === 'log');
expect(logOverride).toBeDefined();
});
@ -1189,7 +1189,7 @@ describe('Field type resolution (PHP)', () => {
it('emits HAS_PROPERTY edges linking properties to classes', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(3);
expect(propEdges.length).toBe(3);
});
it('resolves $user->address->save() → Address#save via field type', () => {
@ -1229,7 +1229,12 @@ describe('Deep field chain resolution (PHP)', () => {
it('emits HAS_PROPERTY edges for nested type chain', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(3);
expect(propEdges.length).toBe(5);
expect(edgeSet(propEdges)).toContain('User → name');
expect(edgeSet(propEdges)).toContain('User → address');
expect(edgeSet(propEdges)).toContain('Address → city');
expect(edgeSet(propEdges)).toContain('Address → street');
expect(edgeSet(propEdges)).toContain('City → zipCode');
});
it('resolves 2-level chain: $user->address->save() → Address#save', () => {
@ -1303,14 +1308,16 @@ describe('Write access tracking (PHP)', () => {
it('emits ACCESSES write edges for field assignments', () => {
const accesses = getRelationships(result, 'ACCESSES');
const writes = accesses.filter(e => e.rel.reason === 'write');
// user->name (x2: simple + compound), user->address, User::$count
expect(writes.length).toBeGreaterThanOrEqual(2);
expect(writes.length).toBe(3);
const nameWrite = writes.find(e => e.target === 'name');
const addressWrite = writes.find(e => e.target === 'address');
const countWrite = writes.find(e => e.target === 'count');
expect(nameWrite).toBeDefined();
expect(nameWrite!.source).toBe('updateUser');
expect(addressWrite).toBeDefined();
expect(addressWrite!.source).toBe('updateUser');
expect(countWrite).toBeDefined();
expect(countWrite!.source).toBe('updateUser');
});
it('emits ACCESSES write edge for static property assignment', () => {

View file

@ -720,10 +720,10 @@ describe('Python static/classmethod class resolution (issue #289)', () => {
const hasMethod = getRelationships(result, 'HAS_METHOD');
const userServiceMethods = hasMethod.filter(e => e.source === 'UserService');
expect(userServiceMethods.length).toBeGreaterThanOrEqual(3); // find_user, create_user, from_config
expect(userServiceMethods.length).toBe(3); // find_user, create_user, from_config
const adminServiceMethods = hasMethod.filter(e => e.source === 'AdminService');
expect(adminServiceMethods.length).toBeGreaterThanOrEqual(2); // find_user, delete_user
expect(adminServiceMethods.length).toBe(2); // find_user, delete_user
});
it('resolves unique static method calls (create_user, delete_user, from_config)', () => {
@ -1309,7 +1309,7 @@ describe('Field type resolution (Python)', () => {
it('emits HAS_PROPERTY edges linking attributes to classes', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(3);
expect(propEdges.length).toBe(3);
expect(edgeSet(propEdges)).toContain('User → address');
expect(edgeSet(propEdges)).toContain('User → name');
expect(edgeSet(propEdges)).toContain('Address → city');

View file

@ -105,7 +105,7 @@ describe('Ruby require_relative, heritage & property resolution', () => {
it('emits EXTENDS edge: User → BaseModel', () => {
const extends_ = getRelationships(result, 'EXTENDS');
expect(extends_.length).toBeGreaterThanOrEqual(1);
expect(extends_.length).toBe(1);
const edges = edgeSet(extends_);
expect(edges).toContain('User → BaseModel');
});
@ -620,7 +620,7 @@ describe('Ruby return type inference via function call', () => {
it('detects save method on both User and Repo (disambiguation required)', () => {
const methods = getNodesByLabel(result, 'Method');
// Both classes have save — fuzzy match alone cannot resolve this
expect(methods.filter(m => m === 'save').length).toBeGreaterThanOrEqual(2);
expect(methods.filter(m => m === 'save').length).toBe(2);
});
it('resolves user.save to User#save via YARD @return [User] on get_user()', () => {
@ -874,7 +874,7 @@ describe('Field type resolution (Ruby)', () => {
it('emits HAS_PROPERTY edges linking properties to classes', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(3);
expect(propEdges.length).toBe(3);
expect(edgeSet(propEdges)).toContain('User → address');
expect(edgeSet(propEdges)).toContain('User → name');
expect(edgeSet(propEdges)).toContain('Address → city');
@ -938,13 +938,16 @@ describe('Write access tracking (Ruby)', () => {
it('emits ACCESSES write edges for setter assignments', () => {
const accesses = getRelationships(result, 'ACCESSES');
const writes = accesses.filter(e => e.rel.reason === 'write');
expect(writes.length).toBeGreaterThanOrEqual(2);
expect(writes.length).toBe(3);
const nameWrite = writes.find(e => e.target === 'name');
const addressWrite = writes.find(e => e.target === 'address');
const scoreWrite = writes.find(e => e.target === 'score');
expect(nameWrite).toBeDefined();
expect(nameWrite!.source).toBe('update_user');
expect(addressWrite).toBeDefined();
expect(addressWrite!.source).toBe('update_user');
expect(scoreWrite).toBeDefined();
expect(scoreWrite!.source).toBe('update_user');
});
it('emits ACCESSES write edge for compound assignment (operator_assignment)', () => {

View file

@ -702,7 +702,7 @@ describe('Rust ::default() constructor resolution', () => {
it('detects save methods on both structs', () => {
const methods = [...getNodesByLabel(result, 'Function'), ...getNodesByLabel(result, 'Method')];
expect(methods.filter((m: string) => m === 'save').length).toBeGreaterThanOrEqual(2);
expect(methods.filter((m: string) => m === 'save').length).toBe(2);
});
it('resolves user.save() in process_with_new() via User::new() constructor', () => {
@ -774,7 +774,7 @@ describe('Rust async .await constructor binding resolution', () => {
it('detects save methods in separate files', () => {
const methods = [...getNodesByLabel(result, 'Function'), ...getNodesByLabel(result, 'Method')];
expect(methods.filter((m: string) => m === 'save').length).toBeGreaterThanOrEqual(2);
expect(methods.filter((m: string) => m === 'save').length).toBe(2);
});
it('resolves user.save() after .await to user.rs via return type of get_user()', () => {
@ -1322,7 +1322,10 @@ describe('Field type resolution (Rust)', () => {
it('emits HAS_PROPERTY edges linking fields to structs', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(2);
expect(propEdges.length).toBe(3);
expect(edgeSet(propEdges)).toContain('User → name');
expect(edgeSet(propEdges)).toContain('User → address');
expect(edgeSet(propEdges)).toContain('Address → city');
});
it('resolves user.address.save() → Address#save via field type', () => {
@ -1362,7 +1365,12 @@ describe('Deep field chain resolution (Rust)', () => {
it('emits HAS_PROPERTY edges for nested type chain', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(3);
expect(propEdges.length).toBe(5);
expect(edgeSet(propEdges)).toContain('User → name');
expect(edgeSet(propEdges)).toContain('User → address');
expect(edgeSet(propEdges)).toContain('Address → city');
expect(edgeSet(propEdges)).toContain('Address → street');
expect(edgeSet(propEdges)).toContain('City → zip_code');
});
it('resolves 2-level chain: user.address.save() → Address#save', () => {
@ -1396,10 +1404,11 @@ describe('Write access tracking (Rust)', () => {
it('emits ACCESSES write edges for field assignments', () => {
const accesses = getRelationships(result, 'ACCESSES');
const writes = accesses.filter(e => e.rel.reason === 'write');
expect(writes.length).toBeGreaterThanOrEqual(2);
expect(writes.length).toBe(3);
const fieldNames = writes.map(e => e.target);
expect(fieldNames).toContain('name');
expect(fieldNames).toContain('address');
expect(fieldNames).toContain('score');
const sources = writes.map(e => e.source);
expect(sources).toContain('update_user');
});

View file

@ -1726,10 +1726,11 @@ describe('Field type resolution (TypeScript)', () => {
it('emits HAS_PROPERTY edges linking properties to classes', () => {
const propEdges = getRelationships(result, 'HAS_PROPERTY');
expect(propEdges.length).toBeGreaterThanOrEqual(3);
expect(propEdges.length).toBe(4);
expect(edgeSet(propEdges)).toContain('User → address');
expect(edgeSet(propEdges)).toContain('User → name');
expect(edgeSet(propEdges)).toContain('Address → city');
expect(edgeSet(propEdges)).toContain('Config → DEFAULT');
});
it('resolves user.address.save() → Address#save via field type', () => {

View file

@ -554,7 +554,7 @@ class UserService {
}
findCalls(tree.rootNode);
expect(calls.length).toBeGreaterThanOrEqual(1);
expect(calls.length).toBe(1);
// $this should resolve to enclosing class 'UserService'
expect(typeEnv.lookup('$this', calls[0])).toBe('UserService');
});
@ -822,7 +822,7 @@ object AppConfig {
}
findCalls(tree.rootNode);
expect(calls.length).toBeGreaterThanOrEqual(1);
expect(calls.length).toBe(1);
expect(typeEnv.lookup('this', calls[0])).toBe('AppConfig');
});
});