ruch
Version:
Revolutionary React TypeScript CLI with hexagonal architecture & AI-powered development assistance. Create maintainable, scalable applications with domain-driven design and integrated AI tooling.
183 lines (143 loc) • 6.7 kB
text/typescript
import { describe, it, beforeEach, expect, vi } from 'vitest';
import { generateAiGuide } from '../../src/commands/guide-ai';
import type { Logger } from '../../src/services/console-logger';
// Mock fs-extra
vi.mock('fs-extra', () => ({
default: {
readdir: vi.fn().mockResolvedValue([]),
stat: vi.fn().mockResolvedValue({ isDirectory: () => true }),
writeFile: vi.fn().mockResolvedValue(undefined),
ensureDir: vi.fn().mockResolvedValue(undefined),
},
readdir: vi.fn().mockResolvedValue([]),
stat: vi.fn().mockResolvedValue({ isDirectory: () => true }),
writeFile: vi.fn().mockResolvedValue(undefined),
ensureDir: vi.fn().mockResolvedValue(undefined),
}));
// Mock console.warn
const mockConsoleWarn = vi.spyOn(console, 'warn').mockImplementation(() => {});
// Mock process.cwd
vi.spyOn(process, 'cwd').mockReturnValue('/test/project');
describe('guide-ai command', () => {
let mockLogger: Logger;
beforeEach(() => {
vi.clearAllMocks();
// Create fresh mock logger for each test
mockLogger = {
info: vi.fn(),
success: vi.fn(),
warning: vi.fn(),
error: vi.fn(),
log: vi.fn(),
};
});
describe('basic functionality', () => {
it('should generate guide files successfully', async () => {
await generateAiGuide(mockLogger, []);
expect(mockLogger.info).toHaveBeenCalledWith('🤖 Generating AI tools guide...');
expect(mockLogger.success).toHaveBeenCalledWith('🎉 AI tools guide generated successfully!');
});
it('should handle domain analysis', async () => {
const fs = await import('fs-extra');
vi.mocked(fs.readdir).mockResolvedValueOnce(['user', 'product'] as any);
await generateAiGuide(mockLogger, []);
// Vérifier que l'analyse de la structure a été loggée
expect(mockLogger.info).toHaveBeenCalledWith('📊 Analyzing project structure...');
});
it('should handle missing domains folder gracefully', async () => {
const fs = await import('fs-extra');
vi.mocked(fs.readdir).mockRejectedValueOnce(new Error('ENOENT'));
await generateAiGuide(mockLogger, []);
// Vérifier que le processus continue malgré l'erreur
expect(mockLogger.success).toHaveBeenCalledWith('🎉 AI tools guide generated successfully!');
});
});
describe('tool configurations', () => {
it('should generate Cursor configuration', async () => {
await generateAiGuide(mockLogger, ['cursor']);
expect(mockLogger.info).toHaveBeenCalledWith(
'🔧 Generating configurations for AI tools: cursor...'
);
expect(mockLogger.success).toHaveBeenCalledWith(
'✅ Tool configurations generated for: cursor'
);
});
it('should generate multiple tool configurations', async () => {
await generateAiGuide(mockLogger, ['cursor', 'copilot']);
expect(mockLogger.info).toHaveBeenCalledWith(
'🔧 Generating configurations for AI tools: cursor, copilot...'
);
expect(mockLogger.success).toHaveBeenCalledWith(
'✅ Tool configurations generated for: cursor, copilot'
);
});
it('should warn about unknown tools', async () => {
await generateAiGuide(mockLogger, ['unknown-tool']);
expect(mockConsoleWarn).toHaveBeenCalledWith(
expect.stringContaining('Warning: Unknown tool "unknown-tool"')
);
});
it('should accept all supported tools', async () => {
await generateAiGuide(mockLogger, ['cursor', 'copilot', 'windsurf', 'juni']);
expect(mockLogger.success).toHaveBeenCalledWith(
'✅ Tool configurations generated for: cursor, copilot, windsurf, juni'
);
});
});
describe('domain guides generation', () => {
it('should complete successfully even with domain analysis', async () => {
const fs = await import('fs-extra');
vi.mocked(fs.readdir).mockResolvedValueOnce(['user', 'product'] as any);
await generateAiGuide(mockLogger, []);
// Vérifier que le processus s'est terminé avec succès
expect(mockLogger.success).toHaveBeenCalledWith('🎉 AI tools guide generated successfully!');
});
it('should skip domain guides when no domains exist', async () => {
const fs = await import('fs-extra');
vi.mocked(fs.readdir).mockResolvedValueOnce([] as any);
await generateAiGuide(mockLogger, []);
// Pas de guides de domaines générés car aucun domaine détecté
expect(mockLogger.success).toHaveBeenCalledWith('🎉 AI tools guide generated successfully!');
});
});
describe('file writing', () => {
it('should write configuration files', async () => {
await generateAiGuide(mockLogger, []);
// Vérifier que le processus de génération s'est bien déroulé
expect(mockLogger.info).toHaveBeenCalledWith('📝 Generating configuration file...');
expect(mockLogger.success).toHaveBeenCalledWith('✅ ruch-guide.json generated');
});
it('should write global guide file', async () => {
await generateAiGuide(mockLogger, []);
// Vérifier que le guide global a été généré
expect(mockLogger.info).toHaveBeenCalledWith('📚 Generating global guide...');
expect(mockLogger.success).toHaveBeenCalledWith('✅ GUIDE.md generated');
});
it('should handle tool configurations', async () => {
await generateAiGuide(mockLogger, ['copilot', 'juni']);
// Vérifier que les configurations d'outils ont été traitées
expect(mockLogger.info).toHaveBeenCalledWith(
expect.stringContaining('Generating configurations for AI tools')
);
expect(mockLogger.success).toHaveBeenCalledWith(
expect.stringContaining('Tool configurations generated for')
);
});
});
describe('error handling', () => {
it('should handle errors gracefully in most cases', async () => {
const fs = await import('fs-extra');
vi.mocked(fs.readdir).mockRejectedValueOnce(new Error('ENOENT'));
await generateAiGuide(mockLogger, []);
// Le processus doit continuer malgré l'erreur de lecture des domaines
expect(mockLogger.success).toHaveBeenCalledWith('🎉 AI tools guide generated successfully!');
});
it('should handle domain analysis errors gracefully', async () => {
const fs = await import('fs-extra');
vi.mocked(fs.readdir).mockResolvedValueOnce(['user'] as any);
vi.mocked(fs.stat).mockRejectedValueOnce(new Error('Stat error'));
await generateAiGuide(mockLogger, []);
expect(mockLogger.success).toHaveBeenCalledWith('🎉 AI tools guide generated successfully!');
});
});
});