Merge origin/main into delphi-support
Integrate main branch changes (WASM grammar architecture, centralized resolution caches, SQLite adapter) with delphi-support branch. Pascal grammar is now built as WASM and shipped in src/extraction/wasm/ for consistency with the WASM-based grammar loading approach. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,16 +4,20 @@
|
||||
* Tests for the tree-sitter extraction system.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { CodeGraph } from '../src';
|
||||
import { extractFromSource, scanDirectory, shouldIncludeFile } from '../src/extraction';
|
||||
import { detectLanguage, isLanguageSupported, getSupportedLanguages } from '../src/extraction/grammars';
|
||||
import { detectLanguage, isLanguageSupported, getSupportedLanguages, initGrammars } from '../src/extraction/grammars';
|
||||
import { normalizePath } from '../src/utils';
|
||||
import { DEFAULT_CONFIG } from '../src/types';
|
||||
|
||||
beforeAll(async () => {
|
||||
await initGrammars();
|
||||
});
|
||||
|
||||
// Create a temporary directory for each test
|
||||
function createTempDir(): string {
|
||||
return fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-test-'));
|
||||
|
||||
@@ -317,7 +317,7 @@ describe('Database Connection', () => {
|
||||
|
||||
const version = db.getSchemaVersion();
|
||||
expect(version).not.toBeNull();
|
||||
expect(version?.version).toBe(1);
|
||||
expect(version?.version).toBe(2);
|
||||
|
||||
db.close();
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* - CLI uninit command
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
@@ -24,8 +24,13 @@ import {
|
||||
getSupportedLanguages,
|
||||
clearParserCache,
|
||||
getUnavailableGrammarErrors,
|
||||
initGrammars,
|
||||
} from '../src/extraction/grammars';
|
||||
|
||||
beforeAll(async () => {
|
||||
await initGrammars();
|
||||
});
|
||||
|
||||
// Create a temporary directory for each test
|
||||
function createTempDir(): string {
|
||||
return fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-pr19-test-'));
|
||||
@@ -320,8 +325,9 @@ describe('Database Layer Improvements', () => {
|
||||
const { DatabaseConnection } = await import('../src/db');
|
||||
const { QueryBuilder } = await import('../src/db/queries');
|
||||
|
||||
const db = DatabaseConnection.initialize(testDir);
|
||||
const queries = new QueryBuilder(db.getDatabase());
|
||||
const dbPath = path.join(testDir, 'codegraph.db');
|
||||
const db = DatabaseConnection.initialize(dbPath);
|
||||
const queries = new QueryBuilder(db.getDb());
|
||||
|
||||
// Insert a node first (needed as foreign key)
|
||||
queries.insertNode({
|
||||
@@ -375,8 +381,9 @@ describe('Database Layer Improvements', () => {
|
||||
const { DatabaseConnection } = await import('../src/db');
|
||||
const { QueryBuilder } = await import('../src/db/queries');
|
||||
|
||||
const db = DatabaseConnection.initialize(testDir);
|
||||
const queries = new QueryBuilder(db.getDatabase());
|
||||
const dbPath = path.join(testDir, 'codegraph.db');
|
||||
const db = DatabaseConnection.initialize(dbPath);
|
||||
const queries = new QueryBuilder(db.getDb());
|
||||
|
||||
// Insert some nodes
|
||||
for (let i = 0; i < 3; i++) {
|
||||
@@ -405,8 +412,9 @@ describe('Database Layer Improvements', () => {
|
||||
it.skipIf(!HAS_SQLITE)('should set performance pragmas on initialization', async () => {
|
||||
const { DatabaseConnection } = await import('../src/db');
|
||||
|
||||
const db = DatabaseConnection.initialize(testDir);
|
||||
const rawDb = db.getDatabase();
|
||||
const dbPath = path.join(testDir, 'codegraph.db');
|
||||
const db = DatabaseConnection.initialize(dbPath);
|
||||
const rawDb = db.getDb();
|
||||
|
||||
// Check pragmas were set
|
||||
const synchronous = rawDb.pragma('synchronous', { simple: true });
|
||||
@@ -428,8 +436,9 @@ describe('Database Layer Improvements', () => {
|
||||
const { DatabaseConnection } = await import('../src/db');
|
||||
const { QueryBuilder } = await import('../src/db/queries');
|
||||
|
||||
const db = DatabaseConnection.initialize(testDir);
|
||||
const queries = new QueryBuilder(db.getDatabase());
|
||||
const dbPath = path.join(testDir, 'codegraph.db');
|
||||
const db = DatabaseConnection.initialize(dbPath);
|
||||
const queries = new QueryBuilder(db.getDb());
|
||||
|
||||
// Should not throw on empty array
|
||||
expect(() => queries.insertUnresolvedRefsBatch([])).not.toThrow();
|
||||
@@ -665,28 +674,21 @@ describe('CLI uninit', () => {
|
||||
// Tree-sitter Version Pinning
|
||||
// =============================================================================
|
||||
|
||||
describe('Tree-sitter Version Pinning', () => {
|
||||
it('should have exact versions (no caret) in package.json', () => {
|
||||
describe('Tree-sitter WASM Setup', () => {
|
||||
it('should use web-tree-sitter and tree-sitter-wasms in dependencies', () => {
|
||||
const pkgPath = path.join(__dirname, '..', 'package.json');
|
||||
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
||||
|
||||
const treeSitterDeps = Object.entries(pkg.dependencies as Record<string, string>)
|
||||
.filter(([name]) => name.startsWith('tree-sitter') || name.includes('tree-sitter'));
|
||||
|
||||
for (const [name, version] of treeSitterDeps) {
|
||||
// Skip github: references
|
||||
if (version.startsWith('github:')) continue;
|
||||
expect(version, `${name} should not use caret range`).not.toMatch(/^\^/);
|
||||
}
|
||||
expect(pkg.dependencies['web-tree-sitter']).toBeDefined();
|
||||
expect(pkg.dependencies['tree-sitter-wasms']).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have tree-sitter override pinned', () => {
|
||||
it('should not have native tree-sitter in dependencies', () => {
|
||||
const pkgPath = path.join(__dirname, '..', 'package.json');
|
||||
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
||||
|
||||
expect(pkg.overrides).toBeDefined();
|
||||
expect(pkg.overrides['tree-sitter']).toBeDefined();
|
||||
expect(pkg.overrides['tree-sitter']).not.toMatch(/^\^/);
|
||||
expect(pkg.dependencies['tree-sitter']).toBeUndefined();
|
||||
expect(pkg.overrides).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { execFileSync } from 'child_process';
|
||||
import CodeGraph from '../src/index';
|
||||
|
||||
describe('Sync Module', () => {
|
||||
@@ -150,4 +151,112 @@ describe('Sync Module', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Git-based sync', () => {
|
||||
let testDir: string;
|
||||
let cg: CodeGraph;
|
||||
|
||||
function git(...args: string[]) {
|
||||
execFileSync('git', args, { cwd: testDir, stdio: 'pipe' });
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-git-sync-'));
|
||||
|
||||
// Initialize a git repo with an initial commit
|
||||
git('init');
|
||||
git('config', 'user.email', 'test@test.com');
|
||||
git('config', 'user.name', 'Test');
|
||||
|
||||
const srcDir = path.join(testDir, 'src');
|
||||
fs.mkdirSync(srcDir);
|
||||
fs.writeFileSync(
|
||||
path.join(srcDir, 'index.ts'),
|
||||
`export function hello() { return 'world'; }`
|
||||
);
|
||||
|
||||
git('add', '-A');
|
||||
git('commit', '-m', 'initial');
|
||||
|
||||
// Initialize CodeGraph and index
|
||||
cg = CodeGraph.initSync(testDir, {
|
||||
config: {
|
||||
include: ['**/*.ts'],
|
||||
exclude: [],
|
||||
},
|
||||
});
|
||||
await cg.indexAll();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (cg) {
|
||||
cg.destroy();
|
||||
}
|
||||
if (fs.existsSync(testDir)) {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('should detect modified files via git', async () => {
|
||||
fs.writeFileSync(
|
||||
path.join(testDir, 'src', 'index.ts'),
|
||||
`export function hello() { return 'modified'; }`
|
||||
);
|
||||
|
||||
const result = await cg.sync();
|
||||
|
||||
expect(result.filesModified).toBe(1);
|
||||
expect(result.changedFilePaths).toContain('src/index.ts');
|
||||
});
|
||||
|
||||
it('should detect new untracked files via git', async () => {
|
||||
fs.writeFileSync(
|
||||
path.join(testDir, 'src', 'new.ts'),
|
||||
`export function newFunc() { return 42; }`
|
||||
);
|
||||
|
||||
const result = await cg.sync();
|
||||
|
||||
expect(result.filesAdded).toBe(1);
|
||||
expect(result.changedFilePaths).toContain('src/new.ts');
|
||||
|
||||
// Verify the function was indexed
|
||||
const nodes = cg.searchNodes('newFunc');
|
||||
expect(nodes.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should detect deleted files via git', async () => {
|
||||
fs.unlinkSync(path.join(testDir, 'src', 'index.ts'));
|
||||
|
||||
const result = await cg.sync();
|
||||
|
||||
expect(result.filesRemoved).toBe(1);
|
||||
|
||||
// Verify function is gone
|
||||
const nodes = cg.searchNodes('hello');
|
||||
expect(nodes.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should skip files not matching config', async () => {
|
||||
// Create a .js file which doesn't match **/*.ts
|
||||
fs.writeFileSync(
|
||||
path.join(testDir, 'src', 'ignored.js'),
|
||||
`function ignored() {}`
|
||||
);
|
||||
|
||||
const result = await cg.sync();
|
||||
|
||||
expect(result.filesAdded).toBe(0);
|
||||
expect(result.filesModified).toBe(0);
|
||||
});
|
||||
|
||||
it('should report no changes on clean working tree', async () => {
|
||||
const result = await cg.sync();
|
||||
|
||||
expect(result.filesAdded).toBe(0);
|
||||
expect(result.filesModified).toBe(0);
|
||||
expect(result.filesRemoved).toBe(0);
|
||||
expect(result.changedFilePaths).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user