UNPKG

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.

373 lines (307 loc) 12.5 kB
import { describe, it, expect, vi, beforeEach } from 'vitest'; import path from 'path'; import { capitalizeFirstLetter, createDomainInfo, buildDomainsPath, buildContextPath, buildServiceContextFilePath, buildServiceContextTestFilePath, validateDomainStructure, filterValidDomains, isDirectoryEntry, getExistingDomains, parseExistingServiceContext, findMissingDomains } from '../src/utils/domain-analyzer'; import type { FileSystem } from '../src/utils/file-operations'; // Mock FileSystem pour les tests const createMockFileSystem = ( files: Record<string, string> = {}, directories: string[] = [] ): FileSystem => ({ writeFile: vi.fn() as any, readFile: vi.fn().mockImplementation(async (filePath: string) => { if (files[filePath]) { return files[filePath]; } throw new Error(`File not found: ${filePath}`); }) as any, readdir: vi.fn().mockImplementation(async (dirPath: string) => { const dirEntries = Object.keys(files) .filter(file => path.dirname(file) === dirPath) .map(file => path.basename(file)); const subDirs = directories .filter(dir => path.dirname(dir) === dirPath && dir !== dirPath) .map(dir => path.basename(dir)); return [...new Set([...dirEntries, ...subDirs])]; }) as any, stat: vi.fn().mockImplementation(async (filePath: string) => ({ isDirectory: () => directories.includes(filePath), isFile: () => files[filePath] !== undefined })) as any, remove: vi.fn() as any, mkdir: vi.fn() as any, exists: vi.fn().mockImplementation(async (filePath: string) => { return files[filePath] !== undefined || directories.includes(filePath); }), pathExists: vi.fn() as any, ensureDir: vi.fn() as any }); describe('Domain Analyzer', () => { describe('Pure functions', () => { describe('capitalizeFirstLetter', () => { it('should capitalize first letter of lowercase string', () => { expect(capitalizeFirstLetter('user')).toBe('User'); expect(capitalizeFirstLetter('product')).toBe('Product'); }); it('should handle already capitalized string', () => { expect(capitalizeFirstLetter('User')).toBe('User'); }); it('should handle empty string', () => { expect(capitalizeFirstLetter('')).toBe(''); }); it('should handle single character', () => { expect(capitalizeFirstLetter('a')).toBe('A'); expect(capitalizeFirstLetter('A')).toBe('A'); }); }); describe('createDomainInfo', () => { it('should create correct domain info object', () => { const result = createDomainInfo('user'); expect(result).toEqual({ name: 'user', capitalizedName: 'User' }); }); it('should handle complex domain names', () => { const result = createDomainInfo('userAccount'); expect(result).toEqual({ name: 'userAccount', capitalizedName: 'UserAccount' }); }); }); describe('Path builders', () => { const projectRoot = '/test-project'; it('should build domains path correctly', () => { const result = buildDomainsPath(projectRoot); expect(result).toBe('/test-project/src/domains'); }); it('should build context path correctly', () => { const result = buildContextPath(projectRoot); expect(result).toBe('/test-project/src/context'); }); it('should build service context file path correctly', () => { const result = buildServiceContextFilePath(projectRoot); expect(result).toBe('/test-project/src/context/ServiceContext.tsx'); }); it('should build service context test file path correctly', () => { const result = buildServiceContextTestFilePath(projectRoot); expect(result).toBe('/test-project/src/context/ServiceContext.test.tsx'); }); it('should use process.cwd() as default project root', () => { const result = buildDomainsPath(); expect(result).toBe(path.join(process.cwd(), 'src', 'domains')); }); }); }); describe('File system operations', () => { describe('isDirectoryEntry', () => { it('should return true for directories', async () => { const directories = ['/test/path']; const fileSystem = createMockFileSystem({}, directories); const result = await isDirectoryEntry(fileSystem, '/test/path'); expect(result).toBe(true); }); it('should return false for files', async () => { const files = { '/test/file.ts': 'content' }; const fileSystem = createMockFileSystem(files); const result = await isDirectoryEntry(fileSystem, '/test/file.ts'); expect(result).toBe(false); }); it('should return false for non-existent paths', async () => { const fileSystem = createMockFileSystem(); const result = await isDirectoryEntry(fileSystem, '/non/existent'); expect(result).toBe(false); }); }); describe('validateDomainStructure', () => { it('should return true for valid domain structure', async () => { const files = { '/project/src/domains/user/services/UserService.ts': 'export class UserService {}', '/project/src/domains/user/adapters/UserAdapter.ts': 'export class UserAdapter {}' }; const fileSystem = createMockFileSystem(files); const result = await validateDomainStructure( fileSystem, '/project/src/domains/user', 'user' ); expect(result).toBe(true); }); it('should return false for incomplete domain structure', async () => { const files = { '/project/src/domains/user/services/UserService.ts': 'export class UserService {}' // Missing UserAdapter.ts }; const fileSystem = createMockFileSystem(files); const result = await validateDomainStructure( fileSystem, '/project/src/domains/user', 'user' ); expect(result).toBe(false); }); }); describe('filterValidDomains', () => { it('should filter and return only valid domains', async () => { const files = { // Valid domain '/project/src/domains/user/services/UserService.ts': 'content', '/project/src/domains/user/adapters/UserAdapter.ts': 'content', // Invalid domain (missing adapter) '/project/src/domains/product/services/ProductService.ts': 'content', // File instead of directory - should be filtered out '/project/src/domains/somefile.txt': 'content' }; const directories = [ '/project/src/domains', '/project/src/domains/user', '/project/src/domains/user/services', '/project/src/domains/user/adapters', '/project/src/domains/product', '/project/src/domains/product/services' ]; const fileSystem = createMockFileSystem(files, directories); const result = await filterValidDomains( fileSystem, '/project/src/domains', ['user', 'product', 'somefile.txt'] ); expect(result).toEqual(['user']); }); it('should return empty array when no valid domains exist', async () => { const directories = ['/project/src/domains']; const fileSystem = createMockFileSystem({}, directories); const result = await filterValidDomains( fileSystem, '/project/src/domains', ['invalidDomain'] ); expect(result).toEqual([]); }); }); }); describe('Domain discovery', () => { describe('getExistingDomains', () => { it('should return all valid domains', async () => { const files = { '/project/src/domains/user/services/UserService.ts': 'content', '/project/src/domains/user/adapters/UserAdapter.ts': 'content', '/project/src/domains/product/services/ProductService.ts': 'content', '/project/src/domains/product/adapters/ProductAdapter.ts': 'content' }; const directories = [ '/project/src/domains', '/project/src/domains/user', '/project/src/domains/user/services', '/project/src/domains/user/adapters', '/project/src/domains/product', '/project/src/domains/product/services', '/project/src/domains/product/adapters' ]; const fileSystem = createMockFileSystem(files, directories); const result = await getExistingDomains(fileSystem, '/project'); expect(result).toEqual([ { name: 'user', capitalizedName: 'User' }, { name: 'product', capitalizedName: 'Product' } ]); }); it('should return empty array when domains directory does not exist', async () => { const fileSystem = createMockFileSystem(); const result = await getExistingDomains(fileSystem, '/project'); expect(result).toEqual([]); }); }); }); describe('Service context parsing', () => { describe('parseExistingServiceContext', () => { it('should parse domains from existing service context', async () => { const serviceContextContent = ` interface Services { userService: UserService; productService: ProductService; } `; const files = { '/project/src/context/ServiceContext.tsx': serviceContextContent }; const fileSystem = createMockFileSystem(files); const result = await parseExistingServiceContext(fileSystem, '/project'); expect(result).toEqual([ { name: 'user', capitalizedName: 'User' }, { name: 'product', capitalizedName: 'Product' } ]); }); it('should return empty array when service context does not exist', async () => { const fileSystem = createMockFileSystem(); const result = await parseExistingServiceContext(fileSystem, '/project'); expect(result).toEqual([]); }); }); }); describe('Missing domains detection', () => { describe('findMissingDomains', () => { it('should find domains missing from service context', async () => { // Existing domains in filesystem const files = { '/project/src/domains/user/services/UserService.ts': 'content', '/project/src/domains/user/adapters/UserAdapter.ts': 'content', '/project/src/domains/order/services/OrderService.ts': 'content', '/project/src/domains/order/adapters/OrderAdapter.ts': 'content', // Service context only includes user '/project/src/context/ServiceContext.tsx': ` interface Services { userService: UserService; } ` }; const directories = [ '/project/src/domains', '/project/src/domains/user', '/project/src/domains/user/services', '/project/src/domains/user/adapters', '/project/src/domains/order', '/project/src/domains/order/services', '/project/src/domains/order/adapters' ]; const fileSystem = createMockFileSystem(files, directories); const result = await findMissingDomains(fileSystem, '/project'); expect(result).toEqual([ { name: 'order', capitalizedName: 'Order' } ]); }); it('should return empty array when all domains are in service context', async () => { const files = { '/project/src/domains/user/services/UserService.ts': 'content', '/project/src/domains/user/adapters/UserAdapter.ts': 'content', '/project/src/context/ServiceContext.tsx': ` interface Services { userService: UserService; } ` }; const directories = [ '/project/src/domains', '/project/src/domains/user', '/project/src/domains/user/services', '/project/src/domains/user/adapters' ]; const fileSystem = createMockFileSystem(files, directories); const result = await findMissingDomains(fileSystem, '/project'); expect(result).toEqual([]); }); }); }); });