Spaces:
Running
Running
| import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; | |
| import fs from 'node:fs'; | |
| import path from 'node:path'; | |
| import os from 'node:os'; | |
| // Mock the analyzer module | |
| vi.mock('./analyzer/index.js', () => ({ | |
| analyze: vi.fn(), | |
| })); | |
| // Mock the logger | |
| vi.mock('./logger.js', () => ({ | |
| log: vi.fn(), | |
| })); | |
| describe('runAnalyze output files', () => { | |
| let tmpDir; | |
| beforeEach(() => { | |
| tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'analyze-test-')); | |
| }); | |
| afterEach(() => { | |
| fs.rmSync(tmpDir, { recursive: true, force: true }); | |
| }); | |
| it('should write knowledge-graph.json with full DashboardJSON schema', async () => { | |
| const { analyze } = await import('./analyzer/index.js'); | |
| const mockedAnalyze = vi.mocked(analyze); | |
| const mockDashboard = { | |
| version: '1.0.0', | |
| project: { | |
| name: 'test-project', | |
| description: 'A test project', | |
| languages: ['typescript'], | |
| frameworks: ['express'], | |
| analyzedAt: '2024-01-01T00:00:00.000Z', | |
| gitCommitHash: 'abc123def456', | |
| }, | |
| nodes: [ | |
| { id: 'file::src/index.ts', type: 'file', name: 'index.ts', summary: 'Entry point', tags: ['entry'] }, | |
| ], | |
| edges: [ | |
| { source: 'file::src/index.ts', target: 'file::src/utils.ts', type: 'imports' }, | |
| ], | |
| layers: [ | |
| { id: 'layer::core', name: 'Core', description: 'Core layer', nodeIds: ['file::src/index.ts'] }, | |
| ], | |
| tour: [ | |
| { order: 1, title: 'Start here', description: 'Entry point', nodeIds: ['file::src/index.ts'] }, | |
| ], | |
| }; | |
| mockedAnalyze.mockResolvedValue({ | |
| dashboard: mockDashboard, | |
| stats: { | |
| filesAnalyzed: 5, | |
| nodesByType: { file: 3, function: 2 }, | |
| edgesCreated: 4, | |
| layersIdentified: 1, | |
| }, | |
| files: [], | |
| }); | |
| // Simulate what runAnalyze does (without process.exit) | |
| const targetPath = tmpDir; | |
| const { dashboard, stats } = await analyze(targetPath, { full: false }); | |
| const outputDir = path.join(targetPath, '.understand-anything'); | |
| if (!fs.existsSync(outputDir)) { | |
| fs.mkdirSync(outputDir, { recursive: true }); | |
| } | |
| const knowledgeGraphPath = path.join(outputDir, 'knowledge-graph.json'); | |
| fs.writeFileSync(knowledgeGraphPath, JSON.stringify(dashboard, null, 2)); | |
| const meta = { | |
| lastAnalyzedAt: new Date().toISOString(), | |
| gitCommitHash: dashboard.project.gitCommitHash || '', | |
| version: '1.0.0', | |
| analyzedFiles: stats.filesAnalyzed, | |
| }; | |
| const metaPath = path.join(outputDir, 'meta.json'); | |
| fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2)); | |
| // Verify knowledge-graph.json | |
| const kgContent = JSON.parse(fs.readFileSync(knowledgeGraphPath, 'utf-8')); | |
| expect(kgContent.version).toBe('1.0.0'); | |
| expect(kgContent.project.name).toBe('test-project'); | |
| expect(kgContent.project.gitCommitHash).toBe('abc123def456'); | |
| expect(kgContent.nodes).toHaveLength(1); | |
| expect(kgContent.edges).toHaveLength(1); | |
| expect(kgContent.layers).toHaveLength(1); | |
| expect(kgContent.tour).toHaveLength(1); | |
| // Verify meta.json | |
| const metaContent = JSON.parse(fs.readFileSync(metaPath, 'utf-8')); | |
| expect(metaContent.lastAnalyzedAt).toBeDefined(); | |
| expect(metaContent.gitCommitHash).toBe('abc123def456'); | |
| expect(metaContent.version).toBe('1.0.0'); | |
| expect(metaContent.analyzedFiles).toBe(5); | |
| }); | |
| it('should create .understand-anything directory if it does not exist', async () => { | |
| const { analyze } = await import('./analyzer/index.js'); | |
| const mockedAnalyze = vi.mocked(analyze); | |
| mockedAnalyze.mockResolvedValue({ | |
| dashboard: { | |
| version: '1.0.0', | |
| project: { | |
| name: 'test', | |
| description: '', | |
| languages: [], | |
| frameworks: [], | |
| analyzedAt: '2024-01-01T00:00:00.000Z', | |
| gitCommitHash: '', | |
| }, | |
| nodes: [], | |
| edges: [], | |
| layers: [], | |
| tour: [], | |
| }, | |
| stats: { | |
| filesAnalyzed: 0, | |
| nodesByType: {}, | |
| edgesCreated: 0, | |
| layersIdentified: 0, | |
| }, | |
| files: [], | |
| }); | |
| const targetPath = tmpDir; | |
| const outputDir = path.join(targetPath, '.understand-anything'); | |
| expect(fs.existsSync(outputDir)).toBe(false); | |
| const { dashboard, stats } = await analyze(targetPath, { full: false }); | |
| if (!fs.existsSync(outputDir)) { | |
| fs.mkdirSync(outputDir, { recursive: true }); | |
| } | |
| fs.writeFileSync(path.join(outputDir, 'knowledge-graph.json'), JSON.stringify(dashboard, null, 2)); | |
| fs.writeFileSync(path.join(outputDir, 'meta.json'), JSON.stringify({ | |
| lastAnalyzedAt: new Date().toISOString(), | |
| gitCommitHash: dashboard.project.gitCommitHash || '', | |
| version: '1.0.0', | |
| analyzedFiles: stats.filesAnalyzed, | |
| }, null, 2)); | |
| expect(fs.existsSync(outputDir)).toBe(true); | |
| expect(fs.existsSync(path.join(outputDir, 'knowledge-graph.json'))).toBe(true); | |
| expect(fs.existsSync(path.join(outputDir, 'meta.json'))).toBe(true); | |
| }); | |
| it('should produce output compatible with --preview command', async () => { | |
| const { analyze } = await import('./analyzer/index.js'); | |
| const mockedAnalyze = vi.mocked(analyze); | |
| mockedAnalyze.mockResolvedValue({ | |
| dashboard: { | |
| version: '1.0.0', | |
| project: { | |
| name: 'my-project', | |
| description: 'Test', | |
| languages: ['typescript'], | |
| frameworks: [], | |
| analyzedAt: '2024-06-01T12:00:00.000Z', | |
| gitCommitHash: 'deadbeef', | |
| }, | |
| nodes: [ | |
| { id: 'file::app.ts', type: 'file', name: 'app.ts', summary: 'App', tags: [] }, | |
| ], | |
| edges: [], | |
| layers: [], | |
| tour: [], | |
| }, | |
| stats: { | |
| filesAnalyzed: 1, | |
| nodesByType: { file: 1 }, | |
| edgesCreated: 0, | |
| layersIdentified: 0, | |
| }, | |
| files: [], | |
| }); | |
| const targetPath = tmpDir; | |
| const { dashboard, stats } = await analyze(targetPath, { full: true }); | |
| const outputDir = path.join(targetPath, '.understand-anything'); | |
| fs.mkdirSync(outputDir, { recursive: true }); | |
| fs.writeFileSync(path.join(outputDir, 'knowledge-graph.json'), JSON.stringify(dashboard, null, 2)); | |
| fs.writeFileSync(path.join(outputDir, 'meta.json'), JSON.stringify({ | |
| lastAnalyzedAt: new Date().toISOString(), | |
| gitCommitHash: dashboard.project.gitCommitHash || '', | |
| version: '1.0.0', | |
| analyzedFiles: stats.filesAnalyzed, | |
| }, null, 2)); | |
| // The output should be compatible with --preview: | |
| // knowledge-graph.json must have nodes and edges (DashboardJSON schema) | |
| const kg = JSON.parse(fs.readFileSync(path.join(outputDir, 'knowledge-graph.json'), 'utf-8')); | |
| expect(kg).toHaveProperty('version'); | |
| expect(kg).toHaveProperty('project'); | |
| expect(kg).toHaveProperty('nodes'); | |
| expect(kg).toHaveProperty('edges'); | |
| expect(Array.isArray(kg.nodes)).toBe(true); | |
| expect(Array.isArray(kg.edges)).toBe(true); | |
| // meta.json must have the expected fields | |
| const meta = JSON.parse(fs.readFileSync(path.join(outputDir, 'meta.json'), 'utf-8')); | |
| expect(meta).toHaveProperty('lastAnalyzedAt'); | |
| expect(meta).toHaveProperty('gitCommitHash'); | |
| expect(meta).toHaveProperty('version'); | |
| expect(meta).toHaveProperty('analyzedFiles'); | |
| expect(typeof meta.analyzedFiles).toBe('number'); | |
| }); | |
| }); | |