mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-08 03:08:13 +00:00
- empty graph produces header-only CSVs (no data rows) - empty graph relCSV has only header - double quotes in node names are RFC 4180 escaped - file node without fileContents gets empty content (no crash) - community with empty keywords array produces valid CSV - unknown node labels are silently skipped Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
201 lines
7 KiB
TypeScript
201 lines
7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { generateAllCSVs } from '../../src/core/lbug/csv-generator';
|
|
import { createKnowledgeGraph } from '../../src/core/graph/graph';
|
|
import { NODE_TABLES } from '../../src/core/lbug/schema';
|
|
|
|
describe('generateAllCSVs', () => {
|
|
it('generates CSV for all NODE_TABLES present in the graph', () => {
|
|
const graph = createKnowledgeGraph();
|
|
// Add one node per multi-language table type
|
|
const testTables = ['Struct', 'Enum', 'Trait', 'Impl', 'Macro', 'TypeAlias'] as const;
|
|
for (const label of testTables) {
|
|
graph.addNode({
|
|
id: `${label}:test.rs:MyItem`,
|
|
label,
|
|
properties: { name: 'MyItem', filePath: 'test.rs', startLine: 1, endLine: 10 },
|
|
});
|
|
}
|
|
|
|
const csvData = generateAllCSVs(graph, new Map());
|
|
|
|
for (const label of testTables) {
|
|
const csv = csvData.nodes.get(label);
|
|
expect(csv, `CSV for ${label} should be generated`).toBeDefined();
|
|
expect(csv!.split('\n').length, `CSV for ${label} should have header + 1 row`).toBeGreaterThanOrEqual(2);
|
|
}
|
|
});
|
|
|
|
it('multi-language table CSVs have 6 columns (no isExported)', () => {
|
|
const graph = createKnowledgeGraph();
|
|
graph.addNode({
|
|
id: 'Struct:lib.rs:Point',
|
|
label: 'Struct',
|
|
properties: { name: 'Point', filePath: 'lib.rs', startLine: 5, endLine: 15 },
|
|
});
|
|
|
|
const csvData = generateAllCSVs(graph, new Map());
|
|
const csv = csvData.nodes.get('Struct')!;
|
|
const header = csv.split('\n')[0];
|
|
const columns = header.split(',').length;
|
|
expect(columns).toBe(6); // id, name, filePath, startLine, endLine, content
|
|
});
|
|
|
|
it('community keywords with commas are properly CSV-escaped', () => {
|
|
const graph = createKnowledgeGraph();
|
|
graph.addNode({
|
|
id: 'comm_0',
|
|
label: 'Community',
|
|
properties: {
|
|
name: 'TestCluster',
|
|
heuristicLabel: 'test',
|
|
keywords: ['auth, login', 'user management'],
|
|
description: 'test community',
|
|
cohesion: 0.8,
|
|
symbolCount: 5,
|
|
},
|
|
});
|
|
|
|
const csvData = generateAllCSVs(graph, new Map());
|
|
const csv = csvData.nodes.get('Community')!;
|
|
const rows = csv.split('\n');
|
|
expect(rows.length).toBeGreaterThanOrEqual(2);
|
|
// The keywords field should be quoted (RFC 4180) since it contains commas
|
|
const dataRow = rows[1];
|
|
expect(dataRow).toContain('auth');
|
|
expect(dataRow).toContain('login');
|
|
});
|
|
|
|
it('generates File CSV with content from fileContents map', () => {
|
|
const graph = createKnowledgeGraph();
|
|
graph.addNode({
|
|
id: 'File:src/index.ts',
|
|
label: 'File',
|
|
properties: { name: 'index.ts', filePath: 'src/index.ts' },
|
|
});
|
|
|
|
const fileContents = new Map([['src/index.ts', 'console.log("hello")']]);
|
|
const csvData = generateAllCSVs(graph, fileContents);
|
|
const csv = csvData.nodes.get('File')!;
|
|
expect(csv).toContain('hello');
|
|
});
|
|
|
|
it('returns a relCSV string', () => {
|
|
const graph = createKnowledgeGraph();
|
|
graph.addNode({
|
|
id: 'Function:a.ts:foo',
|
|
label: 'Function',
|
|
properties: { name: 'foo', filePath: 'a.ts', startLine: 1, endLine: 5 },
|
|
});
|
|
graph.addNode({
|
|
id: 'Function:a.ts:bar',
|
|
label: 'Function',
|
|
properties: { name: 'bar', filePath: 'a.ts', startLine: 10, endLine: 15 },
|
|
});
|
|
graph.addRelationship({
|
|
sourceId: 'Function:a.ts:foo',
|
|
targetId: 'Function:a.ts:bar',
|
|
type: 'CALLS',
|
|
properties: {},
|
|
});
|
|
|
|
const csvData = generateAllCSVs(graph, new Map());
|
|
expect(csvData.relCSV).toContain('CALLS');
|
|
expect(csvData.relCSV).toContain('Function:a.ts:foo');
|
|
});
|
|
|
|
it('handles all NODE_TABLES without crashing', () => {
|
|
const graph = createKnowledgeGraph();
|
|
for (const table of NODE_TABLES) {
|
|
if (table === 'File' || table === 'Folder' || table === 'Community' || table === 'Process') continue;
|
|
graph.addNode({
|
|
id: `${table}:test:item`,
|
|
label: table,
|
|
properties: { name: 'item', filePath: 'test', startLine: 1, endLine: 2 },
|
|
});
|
|
}
|
|
|
|
expect(() => generateAllCSVs(graph, new Map())).not.toThrow();
|
|
const csvData = generateAllCSVs(graph, new Map());
|
|
expect(csvData.nodes.size).toBeGreaterThan(0);
|
|
});
|
|
|
|
// ── Negative tests ──────────────────────────────────────────────
|
|
|
|
it('empty graph produces no node CSVs with data rows', () => {
|
|
const graph = createKnowledgeGraph();
|
|
const csvData = generateAllCSVs(graph, new Map());
|
|
// Nodes map may have header-only entries or be empty
|
|
for (const [, csv] of csvData.nodes.entries()) {
|
|
const rows = csv.split('\n').filter(r => r.trim());
|
|
expect(rows.length).toBeLessThanOrEqual(1); // header only, no data
|
|
}
|
|
});
|
|
|
|
it('empty graph produces relCSV with only a header', () => {
|
|
const graph = createKnowledgeGraph();
|
|
const csvData = generateAllCSVs(graph, new Map());
|
|
const lines = csvData.relCSV.split('\n').filter(r => r.trim());
|
|
expect(lines.length).toBeLessThanOrEqual(1);
|
|
});
|
|
|
|
it('node with double quotes in name is properly escaped', () => {
|
|
const graph = createKnowledgeGraph();
|
|
graph.addNode({
|
|
id: 'Function:a.ts:say"hello"',
|
|
label: 'Function',
|
|
properties: { name: 'say"hello"', filePath: 'a.ts', startLine: 1, endLine: 5 },
|
|
});
|
|
|
|
const csvData = generateAllCSVs(graph, new Map());
|
|
const csv = csvData.nodes.get('Function')!;
|
|
// RFC 4180: double quotes inside fields are doubled
|
|
expect(csv).toContain('""');
|
|
});
|
|
|
|
it('file node without matching fileContents gets empty content', () => {
|
|
const graph = createKnowledgeGraph();
|
|
graph.addNode({
|
|
id: 'File:missing.ts',
|
|
label: 'File',
|
|
properties: { name: 'missing.ts', filePath: 'missing.ts' },
|
|
});
|
|
|
|
const csvData = generateAllCSVs(graph, new Map()); // no file contents
|
|
const csv = csvData.nodes.get('File')!;
|
|
const rows = csv.split('\n');
|
|
expect(rows.length).toBeGreaterThanOrEqual(2);
|
|
// Content field should be empty (not undefined or crash)
|
|
});
|
|
|
|
it('community with empty keywords array produces valid CSV', () => {
|
|
const graph = createKnowledgeGraph();
|
|
graph.addNode({
|
|
id: 'comm_1',
|
|
label: 'Community',
|
|
properties: {
|
|
name: 'EmptyCluster',
|
|
heuristicLabel: 'empty',
|
|
keywords: [],
|
|
description: '',
|
|
cohesion: 0,
|
|
symbolCount: 0,
|
|
},
|
|
});
|
|
|
|
expect(() => generateAllCSVs(graph, new Map())).not.toThrow();
|
|
const csvData = generateAllCSVs(graph, new Map());
|
|
expect(csvData.nodes.get('Community')).toBeDefined();
|
|
});
|
|
|
|
it('node labels not in NODE_TABLES are silently skipped', () => {
|
|
const graph = createKnowledgeGraph();
|
|
graph.addNode({
|
|
id: 'FakeLabel:test:item',
|
|
label: 'FakeLabel' as any,
|
|
properties: { name: 'item', filePath: 'test' },
|
|
});
|
|
|
|
// Should not crash — unknown labels are just not in any CSV
|
|
expect(() => generateAllCSVs(graph, new Map())).not.toThrow();
|
|
});
|
|
});
|