GitNexus/gitnexus/test/unit/model/field-registry.test.ts
Gergő Magyar 52924ef12c
perf(emit): drop six equivalent redundancies on Java-scale index path (#3129)
* perf(scope): reuse deferred sites and signatures for callable-value-flow

Pass the Phase 4 deferred-site collection and signature map into emitCallableValueFlow so the emit path does not rescan the same call sites.

Co-authored-by: Cursor <cursoragent@cursor.com>

* perf(ingestion): nest owner registries instead of composite string keys

Look up methods, fields, and nested types via Map<owner, Map<name, defs>> while keeping EMPTY identity and TypeRegistry miss [] semantics.

Co-authored-by: Cursor <cursoragent@cursor.com>

* perf(scope): memoize resolveDefGraphId per nodeLookup

Cache graph ids on a WeakMap keyed by lookup identity so heritage rebuild invalidates, with an env opt-out that skips the cache.

Co-authored-by: Cursor <cursoragent@cursor.com>

* perf(csv): prepare file content once and LRU-touch via Map order

Cache split lines and binary flags per source file so snippet and FTS extraction do not re-scan the same bytes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* perf(identity): allow in-process cache guards only when opted in or unwritable

Keep the ≥128-guard subprocess default on writable installs; use direct snapshots when the env flag is set or W_OK fails on the analyzer tree.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor: share empty CSV payload and restore spacing after memo key (U4, U3)

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(scope): assert resolveDefGraphId memo skips a second lookup walk

The ID-equality checks still passed with the memo disabled. Count Map#get on the lookup so a repeat call must hit the WeakMap cache. Also apply prettier on the remaining emit-path files CI flagged.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: pin identity W_OK probes and memo env isolation

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: document emit-path env knobs and fix registry headers

CONTRIBUTING requires README rows for new GITNEXUS_* variables; also drop stale owner\0name comments after the nested-map change.

---------

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 11:22:55 +00:00

81 lines
3.3 KiB
TypeScript

/**
* Unit tests for FieldRegistry (SM-20).
*
* FieldRegistry is the simplest of the three owner-scoped registries —
* one flat Map keyed on `ownerNodeId\0fieldName`. These tests pin the
* basic register/lookup/clear contract and the owner-scope isolation.
*/
import { describe, it, expect } from 'vitest';
import { createFieldRegistry } from '../../../src/core/ingestion/model/field-registry.js';
import type { SymbolDefinition } from 'gitnexus-shared';
import { makeDef as makeBaseDef } from './helpers.js';
const makeDef = (overrides: Partial<SymbolDefinition> = {}): SymbolDefinition =>
makeBaseDef({ nodeId: 'prop:test', type: 'Property', ...overrides });
describe('FieldRegistry', () => {
it('lookupFieldByOwner returns undefined when the registry is empty', () => {
const reg = createFieldRegistry();
expect(reg.lookupFieldByOwner('class:User', 'name')).toBeUndefined();
});
it('register + lookup round-trips the exact def reference', () => {
const reg = createFieldRegistry();
const def = makeDef({ nodeId: 'prop:User.name', declaredType: 'string' });
reg.register('class:User', 'name', def);
expect(reg.lookupFieldByOwner('class:User', 'name')).toBe(def);
});
it('isolates fields by ownerNodeId — same field name on two classes does not collide', () => {
const reg = createFieldRegistry();
const userName = makeDef({ nodeId: 'prop:User.name' });
const orderName = makeDef({ nodeId: 'prop:Order.name' });
reg.register('class:User', 'name', userName);
reg.register('class:Order', 'name', orderName);
expect(reg.lookupFieldByOwner('class:User', 'name')?.nodeId).toBe('prop:User.name');
expect(reg.lookupFieldByOwner('class:Order', 'name')?.nodeId).toBe('prop:Order.name');
});
it('accumulates multiple defs under the same (ownerNodeId, fieldName)', () => {
const reg = createFieldRegistry();
const first = makeDef({ nodeId: 'prop:User.name#first', type: 'Property' });
const second = makeDef({ nodeId: 'def:User.name#var', type: 'Variable' });
reg.register('class:User', 'name', first);
reg.register('class:User', 'name', second);
expect(reg.lookupFieldByOwner('class:User', 'name')?.nodeId).toBe('prop:User.name#first');
expect(reg.lookupAllByOwner('class:User', 'name')).toEqual([first, second]);
});
it('clear() empties the registry', () => {
const reg = createFieldRegistry();
reg.register('class:User', 'name', makeDef());
reg.register('class:Order', 'total', makeDef());
reg.clear();
expect(reg.lookupFieldByOwner('class:User', 'name')).toBeUndefined();
expect(reg.lookupFieldByOwner('class:Order', 'total')).toBeUndefined();
});
it('allows re-registration after clear', () => {
const reg = createFieldRegistry();
reg.register('class:User', 'name', makeDef({ nodeId: 'prop:first' }));
reg.clear();
reg.register('class:User', 'name', makeDef({ nodeId: 'prop:second' }));
expect(reg.lookupFieldByOwner('class:User', 'name')?.nodeId).toBe('prop:second');
});
it('returns the same frozen empty array on miss', () => {
const reg = createFieldRegistry();
expect(reg.lookupAllByOwner('class:Nope', 'x')).toBe(reg.lookupAllByOwner('class:Nope', 'y'));
expect(Object.isFrozen(reg.lookupAllByOwner('class:Nope', 'x'))).toBe(true);
});
});