mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-08-28 05:25:25 +00:00
- 59 test files covering unit and integration tests - vitest config with coverage thresholds and fork pooling - Test fixtures (mini-repo + multi-language sample code) - Add vitest + coverage-v8 to devDependencies - Add test scripts (test, test:integration, test:all, test:watch, test:coverage) - Move typescript to devDependencies where it belongs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
178 lines
6 KiB
TypeScript
178 lines
6 KiB
TypeScript
/**
|
|
* P1 Integration Tests: CSV Pipeline
|
|
*
|
|
* Tests: streamAllCSVsToDisk with real graph data.
|
|
* Covers hardening fixes: LRU cache (#24), BufferedCSVWriter flush
|
|
*/
|
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
import fs from 'fs/promises';
|
|
import path from 'path';
|
|
import { createTempDir, type TestDBHandle } from '../helpers/test-db.js';
|
|
import { buildTestGraph } from '../helpers/test-graph.js';
|
|
import { streamAllCSVsToDisk } from '../../src/core/kuzu/csv-generator.js';
|
|
|
|
let tmpHandle: TestDBHandle;
|
|
let csvDir: string;
|
|
let repoDir: string;
|
|
|
|
beforeAll(async () => {
|
|
tmpHandle = await createTempDir('csv-pipeline-test-');
|
|
csvDir = path.join(tmpHandle.dbPath, 'csv');
|
|
repoDir = path.join(tmpHandle.dbPath, 'repo');
|
|
|
|
// Create a fake repo directory with source files
|
|
await fs.mkdir(path.join(repoDir, 'src'), { recursive: true });
|
|
await fs.writeFile(
|
|
path.join(repoDir, 'src', 'index.ts'),
|
|
'export function main() {\n console.log("hello");\n helper();\n}\n\nexport class App {\n run() {}\n}\n',
|
|
);
|
|
await fs.writeFile(
|
|
path.join(repoDir, 'src', 'utils.ts'),
|
|
'export function helper() {\n return 42;\n}\n',
|
|
);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
try { await tmpHandle.cleanup(); } catch { /* best-effort */ }
|
|
});
|
|
|
|
describe('streamAllCSVsToDisk', () => {
|
|
it('generates CSV files for all node types in the graph', async () => {
|
|
const graph = buildTestGraph(
|
|
[
|
|
{ id: 'file:src/index.ts', label: 'File', name: 'index.ts', filePath: 'src/index.ts' },
|
|
{ id: 'file:src/utils.ts', label: 'File', name: 'utils.ts', filePath: 'src/utils.ts' },
|
|
{ id: 'func:main', label: 'Function', name: 'main', filePath: 'src/index.ts', startLine: 1, endLine: 4, isExported: true },
|
|
{ id: 'func:helper', label: 'Function', name: 'helper', filePath: 'src/utils.ts', startLine: 1, endLine: 3, isExported: true },
|
|
{ id: 'class:App', label: 'Class', name: 'App', filePath: 'src/index.ts', startLine: 6, endLine: 8, isExported: true },
|
|
{ id: 'folder:src', label: 'Folder', name: 'src', filePath: 'src' },
|
|
],
|
|
[
|
|
{ sourceId: 'func:main', targetId: 'func:helper', type: 'CALLS' },
|
|
{ sourceId: 'file:src/index.ts', targetId: 'func:main', type: 'CONTAINS' },
|
|
{ sourceId: 'file:src/utils.ts', targetId: 'func:helper', type: 'CONTAINS' },
|
|
],
|
|
);
|
|
|
|
const result = await streamAllCSVsToDisk(graph, repoDir, csvDir);
|
|
|
|
// Check that CSV files were created
|
|
expect(result.nodeFiles.size).toBeGreaterThan(0);
|
|
expect(result.relRows).toBe(3);
|
|
|
|
// Verify File CSV
|
|
const fileCsv = result.nodeFiles.get('File');
|
|
expect(fileCsv).toBeDefined();
|
|
expect(fileCsv!.rows).toBe(2);
|
|
|
|
// Verify Function CSV
|
|
const funcCsv = result.nodeFiles.get('Function');
|
|
expect(funcCsv).toBeDefined();
|
|
expect(funcCsv!.rows).toBe(2);
|
|
|
|
// Verify Class CSV
|
|
const classCsv = result.nodeFiles.get('Class');
|
|
expect(classCsv).toBeDefined();
|
|
expect(classCsv!.rows).toBe(1);
|
|
|
|
// Verify Folder CSV
|
|
const folderCsv = result.nodeFiles.get('Folder');
|
|
expect(folderCsv).toBeDefined();
|
|
expect(folderCsv!.rows).toBe(1);
|
|
|
|
// Verify relations CSV exists
|
|
const relContent = await fs.readFile(result.relCsvPath, 'utf-8');
|
|
const relLines = relContent.trim().split('\n');
|
|
expect(relLines.length).toBe(4); // header + 3 relationships
|
|
});
|
|
|
|
it('CSV content is properly escaped', async () => {
|
|
const graph = buildTestGraph([
|
|
{
|
|
id: 'file:src/index.ts',
|
|
label: 'File',
|
|
name: 'index.ts',
|
|
filePath: 'src/index.ts',
|
|
},
|
|
]);
|
|
|
|
const result = await streamAllCSVsToDisk(graph, repoDir, csvDir);
|
|
const fileCsv = result.nodeFiles.get('File');
|
|
expect(fileCsv).toBeDefined();
|
|
|
|
const content = await fs.readFile(fileCsv!.csvPath, 'utf-8');
|
|
// Content should be properly quoted
|
|
expect(content).toContain('"file:src/index.ts"');
|
|
expect(content).toContain('"index.ts"');
|
|
});
|
|
|
|
it('handles community nodes with keywords', async () => {
|
|
const graph = buildTestGraph([
|
|
{
|
|
id: 'comm:auth',
|
|
label: 'Community' as any,
|
|
name: 'Auth',
|
|
filePath: '',
|
|
extra: {
|
|
heuristicLabel: 'Authentication',
|
|
keywords: ['auth', 'login', 'pass,word'],
|
|
description: 'Auth module',
|
|
enrichedBy: 'heuristic',
|
|
cohesion: 0.85,
|
|
symbolCount: 5,
|
|
},
|
|
},
|
|
]);
|
|
|
|
const result = await streamAllCSVsToDisk(graph, repoDir, csvDir);
|
|
const commCsv = result.nodeFiles.get('Community');
|
|
expect(commCsv).toBeDefined();
|
|
expect(commCsv!.rows).toBe(1);
|
|
|
|
const content = await fs.readFile(commCsv!.csvPath, 'utf-8');
|
|
// Keywords with commas should be escaped with \,
|
|
expect(content).toContain('pass\\,word');
|
|
});
|
|
|
|
it('handles process nodes', async () => {
|
|
const graph = buildTestGraph([
|
|
{
|
|
id: 'proc:flow',
|
|
label: 'Process' as any,
|
|
name: 'LoginFlow',
|
|
filePath: '',
|
|
extra: {
|
|
heuristicLabel: 'User Login',
|
|
processType: 'intra_community',
|
|
stepCount: 3,
|
|
communities: ['auth'],
|
|
entryPointId: 'func:login',
|
|
terminalId: 'func:validate',
|
|
},
|
|
},
|
|
]);
|
|
|
|
const result = await streamAllCSVsToDisk(graph, repoDir, csvDir);
|
|
const procCsv = result.nodeFiles.get('Process');
|
|
expect(procCsv).toBeDefined();
|
|
expect(procCsv!.rows).toBe(1);
|
|
});
|
|
|
|
it('deduplicates File nodes', async () => {
|
|
const graph = buildTestGraph([
|
|
{ id: 'file:src/index.ts', label: 'File', name: 'index.ts', filePath: 'src/index.ts' },
|
|
// Duplicate (same id) — should not appear twice
|
|
]);
|
|
// Add the same node again manually
|
|
graph.addNode({
|
|
id: 'file:src/index.ts',
|
|
label: 'File',
|
|
properties: { name: 'index.ts', filePath: 'src/index.ts' },
|
|
});
|
|
|
|
const result = await streamAllCSVsToDisk(graph, repoDir, csvDir);
|
|
const fileCsv = result.nodeFiles.get('File');
|
|
expect(fileCsv).toBeDefined();
|
|
expect(fileCsv!.rows).toBe(1);
|
|
});
|
|
});
|