mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-07 08:26:11 +00:00
Bug fixes (P0):
- Narrow Go /cmd/ entry-point detection to only match /main.go
- Fix Rust scoped grouped imports (use crate::models::{User, Repo}) resolution
- Filter PHP use function/use const from class-type namedImportMap bindings
Improvements (P1):
- Add C# resolveStandard fallback when .csproj discovery fails
- Change preprocessImportPath return type to string | null with caller guards
- Add Q_SIGNALS/Q_SLOTS (standard plural Qt macros)
- Fix stale "11 supported languages" comment → 13
API simplification (P2):
- Replace buildImportResolvers() factory with const importResolvers table
- Move configs onto ResolveCtx (extends ImportResolutionContext)
- Eliminate tsconfigPaths parameter threading through 6 non-TS resolvers
- Split utils.ts (1,476 lines) into ast-helpers.ts + call-analysis.ts + utils.ts
- Consolidate findChild/findChildByType into single source of truth
- Match multi-file import bindings to files by basename for namedImportMap
Cleanup (P3):
- EMPTY_INDEX returns shared frozen empty array instead of allocating per call
- Type appendKotlinWildcard parameter as SyntaxNode instead of any
- Document call-routing validation requirement on CallRouter type
Tests:
- Add unit tests for preprocessImportPath (13 tests)
- Add integration tests: Rust scoped multi-file, PHP use function/const,
C# without .csproj, Go cmd/ helper scoring (14 tests, 4 fixtures)
All 3579 tests pass.
149 lines
6.8 KiB
TypeScript
149 lines
6.8 KiB
TypeScript
/**
|
|
* Unit tests for import-resolution.ts
|
|
*
|
|
* Coverage notes:
|
|
* - `preprocessImportPath` is tested directly below (no tree-sitter required for most paths).
|
|
* - Rust scoped grouped import logic (`resolveRustImportDispatch`) requires a live file system
|
|
* and ResolveCtx — that path is covered by test/integration/resolvers/rust.test.ts.
|
|
* - PHP `use function` / `use const` filtering (via `extractPhpNamedBindings`) requires
|
|
* tree-sitter PHP nodes — covered by test/integration/resolvers/php.test.ts.
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import { preprocessImportPath } from '../../src/core/ingestion/import-resolution.js';
|
|
import { SupportedLanguages } from '../../src/config/supported-languages.js';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Minimal SyntaxNode stub — only the fields preprocessImportPath touches.
|
|
// For non-Kotlin languages preprocessImportPath never reads the node, so an
|
|
// empty stub satisfies the type requirement without loading tree-sitter.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function makeNode(overrides: Partial<{ childCount: number; child: (i: number) => any }> = {}): any {
|
|
return {
|
|
childCount: overrides.childCount ?? 0,
|
|
child: overrides.child ?? (() => null),
|
|
};
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// preprocessImportPath — universal cleaning behaviour
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('preprocessImportPath', () => {
|
|
describe('quote and bracket stripping', () => {
|
|
it('strips double quotes from a bare module path', () => {
|
|
const node = makeNode();
|
|
expect(preprocessImportPath('"foo"', node, SupportedLanguages.TypeScript)).toBe('foo');
|
|
});
|
|
|
|
it('strips single quotes from a bare module path', () => {
|
|
const node = makeNode();
|
|
expect(preprocessImportPath("'bar/baz'", node, SupportedLanguages.JavaScript)).toBe('bar/baz');
|
|
});
|
|
|
|
it('strips angle brackets from a C-style include path', () => {
|
|
const node = makeNode();
|
|
expect(preprocessImportPath('<stdio.h>', node, SupportedLanguages.C)).toBe('stdio.h');
|
|
});
|
|
|
|
it('strips mixed quote and angle bracket characters', () => {
|
|
const node = makeNode();
|
|
// Pathological input — all stripped characters removed
|
|
expect(preprocessImportPath('"<hello>"', node, SupportedLanguages.TypeScript)).toBe('hello');
|
|
});
|
|
});
|
|
|
|
describe('null returns for invalid inputs', () => {
|
|
it('returns null for an empty string (after cleaning)', () => {
|
|
const node = makeNode();
|
|
// Only quote characters — cleaned result is empty string
|
|
expect(preprocessImportPath('""', node, SupportedLanguages.TypeScript)).toBeNull();
|
|
});
|
|
|
|
it('returns null for a string containing control characters', () => {
|
|
const node = makeNode();
|
|
// \x01 is a control character that passes the length check but fails the regex guard
|
|
expect(preprocessImportPath('foo\x01bar', node, SupportedLanguages.Rust)).toBeNull();
|
|
});
|
|
|
|
it('returns null for a string containing a null byte', () => {
|
|
const node = makeNode();
|
|
expect(preprocessImportPath('foo\x00bar', node, SupportedLanguages.Go)).toBeNull();
|
|
});
|
|
|
|
it('returns null for a path exceeding 2048 characters', () => {
|
|
const node = makeNode();
|
|
const longPath = 'a'.repeat(2049);
|
|
expect(preprocessImportPath(longPath, node, SupportedLanguages.Python)).toBeNull();
|
|
});
|
|
|
|
it('accepts a path of exactly 2048 characters', () => {
|
|
const node = makeNode();
|
|
const maxPath = 'a'.repeat(2048);
|
|
expect(preprocessImportPath(maxPath, node, SupportedLanguages.Python)).toBe(maxPath);
|
|
});
|
|
});
|
|
|
|
describe('Kotlin wildcard pass-through', () => {
|
|
it('delegates to appendKotlinWildcard when language is Kotlin — no wildcard child', () => {
|
|
// Node with no children -> appendKotlinWildcard returns the path unchanged
|
|
const node = makeNode({ childCount: 0 });
|
|
const result = preprocessImportPath('com.example.models', node, SupportedLanguages.Kotlin);
|
|
// Without a wildcard_import child the path is returned as-is
|
|
expect(result).toBe('com.example.models');
|
|
});
|
|
|
|
it('delegates to appendKotlinWildcard when language is Kotlin — wildcard_import child present', () => {
|
|
// Simulate a node that has a wildcard_import child at index 0
|
|
const wildcardChild = { type: 'wildcard_import' };
|
|
const node = makeNode({
|
|
childCount: 1,
|
|
child: (i: number) => (i === 0 ? wildcardChild : null),
|
|
});
|
|
const result = preprocessImportPath('com.example.models', node, SupportedLanguages.Kotlin);
|
|
// appendKotlinWildcard appends .* when the wildcard_import child is found
|
|
expect(result).toBe('com.example.models.*');
|
|
});
|
|
});
|
|
|
|
describe('non-Kotlin languages are returned unchanged (after cleaning)', () => {
|
|
it('returns the cleaned path for Rust without modification', () => {
|
|
const node = makeNode();
|
|
expect(preprocessImportPath('"crate::models"', node, SupportedLanguages.Rust)).toBe('crate::models');
|
|
});
|
|
|
|
it('returns the cleaned path for PHP without modification', () => {
|
|
const node = makeNode();
|
|
expect(preprocessImportPath('"App\\\\Models\\\\User"', node, SupportedLanguages.PHP)).toBe('App\\\\Models\\\\User');
|
|
});
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Rust scoped grouped import logic (resolveRustImportDispatch)
|
|
// ---------------------------------------------------------------------------
|
|
// The dispatch function requires a live ResolveCtx with file lists — unit
|
|
// testing it without a file system would duplicate the integration fixtures.
|
|
// The following comment documents what the integration tests verify:
|
|
//
|
|
// test/integration/resolvers/rust.test.ts covers:
|
|
// - Top-level grouped: use {crate::a, crate::b}
|
|
// - Scoped grouped: use crate::models::{User, Repo}
|
|
// - Alias stripping: use crate::models::{User, Repo as R} -> resolves User + Repo
|
|
// - Prefix fallback: when no individual items resolve, resolves the prefix path
|
|
//
|
|
// The ::{ detection and alias-stripping logic lives in resolveRustImportDispatch()
|
|
// at import-resolution.ts lines 328-344.
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// PHP use function / use const filtering (extractPhpNamedBindings)
|
|
// ---------------------------------------------------------------------------
|
|
// extractPhpNamedBindings requires live tree-sitter PHP SyntaxNode objects.
|
|
// The filtering of `use function` and `use const` declarations is covered by:
|
|
//
|
|
// test/integration/resolvers/php.test.ts
|
|
//
|
|
// which runs the full ingestion pipeline over PHP fixture repositories and
|
|
// asserts that function/const use-declarations do not produce spurious IMPORTS
|
|
// edges to non-existent class files.
|