Spaces:
Running
Running
File size: 6,616 Bytes
fd8cdf5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | import { describe, it, expect, afterEach } from 'vitest';
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as os from 'node:os';
import { analyze } from './index';
/**
* Creates a temporary directory with TypeScript files for testing.
*/
function createTempProject(files) {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'analyze-test-'));
for (const [filePath, content] of Object.entries(files)) {
const fullPath = path.join(tmpDir, filePath);
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
fs.writeFileSync(fullPath, content, 'utf-8');
}
return tmpDir;
}
/**
* Recursively removes a directory.
*/
function removeTempDir(dir) {
fs.rmSync(dir, { recursive: true, force: true });
}
describe('analyze', () => {
let tmpDir;
afterEach(() => {
if (tmpDir) {
removeTempDir(tmpDir);
}
});
it('should analyze a directory with TypeScript files and return correct structure', async () => {
tmpDir = createTempProject({
'package.json': JSON.stringify({ name: 'test-project', description: 'A test' }),
'src/index.ts': `
import { helper } from './utils';
export function main() {
return helper();
}
`,
'src/utils.ts': `
export function helper() {
return 'hello';
}
export function anotherHelper() {
return 'world';
}
`,
});
const result = await analyze(tmpDir, { full: true });
// Should have a valid dashboard structure
expect(result.dashboard.version).toBe('1.0.0');
expect(result.dashboard.project.name).toBe('test-project');
expect(result.dashboard.nodes.length).toBeGreaterThan(0);
expect(result.dashboard.edges.length).toBeGreaterThan(0);
expect(result.dashboard.layers).toBeDefined();
expect(result.dashboard.tour).toBeDefined();
// Should have stats — scanner includes package.json as a data file
expect(result.stats.filesAnalyzed).toBeGreaterThanOrEqual(2);
expect(result.stats.edgesCreated).toBeGreaterThan(0);
});
it('should compute correct stats with nodesByType counts', async () => {
tmpDir = createTempProject({
'src/app.ts': `
export class AppService {
run() { return true; }
}
export function bootstrap() {
return new AppService();
}
`,
});
const result = await analyze(tmpDir, { full: true });
// Should have file nodes and function/class nodes
expect(result.stats.nodesByType['file']).toBe(1);
expect(result.stats.filesAnalyzed).toBe(1);
// Should have function and/or class nodes
const totalNodes = Object.values(result.stats.nodesByType).reduce((a, b) => a + b, 0);
expect(totalNodes).toBeGreaterThan(1); // At least file + function/class
});
it('should return empty dashboard for empty directory', async () => {
tmpDir = createTempProject({});
const result = await analyze(tmpDir, { full: true });
expect(result.dashboard.nodes).toEqual([]);
expect(result.dashboard.edges).toEqual([]);
expect(result.stats.filesAnalyzed).toBe(0);
expect(result.stats.nodesByType).toEqual({});
expect(result.stats.edgesCreated).toBe(0);
expect(result.stats.layersIdentified).toBe(0);
});
it('should detect incremental mode when meta.json exists', async () => {
tmpDir = createTempProject({
'src/index.ts': `export function main() { return 1; }`,
'.understand-anything/meta.json': JSON.stringify({
lastAnalyzedAt: '2024-01-01T00:00:00.000Z',
gitCommitHash: 'abc123nonexistent',
version: '1.0.0',
analyzedFiles: 1,
}),
});
// With full=false and a meta.json present, it should attempt incremental
// but fall back to full since git diff will fail (not a git repo)
const result = await analyze(tmpDir, { full: false });
// Should still produce a valid result (falls back to full)
expect(result.dashboard.version).toBe('1.0.0');
expect(result.stats.filesAnalyzed).toBe(1);
});
it('should force full rebuild when --full flag is set', async () => {
tmpDir = createTempProject({
'src/index.ts': `export function main() { return 1; }`,
'.understand-anything/meta.json': JSON.stringify({
lastAnalyzedAt: '2024-01-01T00:00:00.000Z',
gitCommitHash: 'abc123',
version: '1.0.0',
analyzedFiles: 1,
}),
'.understand-anything/knowledge-graph.json': JSON.stringify({
version: '1.0.0',
project: { name: 'old' },
nodes: [],
edges: [],
}),
});
// With full=true, should ignore meta.json and do full rebuild
const result = await analyze(tmpDir, { full: true });
expect(result.dashboard.version).toBe('1.0.0');
expect(result.stats.filesAnalyzed).toBe(1);
// Should have nodes from the actual file, not the empty existing graph
expect(result.dashboard.nodes.length).toBeGreaterThan(0);
});
it('should handle files that fail to parse gracefully', async () => {
tmpDir = createTempProject({
'src/good.ts': `export function hello() { return 'hi'; }`,
// Binary-like content that might cause parse issues — but our parser
// is regex-based so it won't throw. Instead test with a file that
// doesn't exist on disk (scanner won't include it).
'src/another.ts': `export const x = 42;`,
});
const result = await analyze(tmpDir, { full: true });
// Should still produce results even if some files are problematic
expect(result.stats.filesAnalyzed).toBeGreaterThan(0);
expect(result.dashboard.nodes.length).toBeGreaterThan(0);
});
it('should include layers in the output', async () => {
tmpDir = createTempProject({
'src/components/Button.tsx': `
export function Button() {
return '<button>Click</button>';
}
`,
'src/utils/format.ts': `
export function formatDate(d: Date) {
return d.toISOString();
}
`,
});
const result = await analyze(tmpDir, { full: true });
expect(result.dashboard.layers).toBeDefined();
expect(result.dashboard.layers.length).toBeGreaterThan(0);
expect(result.stats.layersIdentified).toBeGreaterThan(0);
});
});
|