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.
188 lines (152 loc) • 7.93 kB
text/typescript
import { describe, it, expect, beforeEach, vi } from 'vitest';
import fs from 'fs-extra';
import path from 'path';
import { logger } from '../../src/utils/logger';
import { initServiceContext, generateServiceContext, updateServiceContext } from '../../src/commands/context';
vi.mock('fs-extra');
vi.mock('../../src/utils/logger', () => ({
logger: {
error: vi.fn(),
success: vi.fn(),
info: vi.fn(),
warning: vi.fn(),
log: vi.fn(),
}
}));
describe('Context Commands', () => {
const projectRoot = '/test/project';
const contextPath = path.join(projectRoot, 'src', 'context');
const contextFilePath = path.join(contextPath, 'ServiceContext.tsx');
const testFilePath = path.join(contextPath, 'ServiceContext.test.tsx');
const domainsPath = path.join(projectRoot, 'src', 'domains');
beforeEach(() => {
vi.clearAllMocks();
});
describe('initServiceContext', () => {
it('should create empty service context when none exists', async () => {
(fs.exists as any).mockResolvedValue(false);
(fs.ensureDir as any).mockResolvedValue(undefined);
(fs.writeFile as any).mockResolvedValue(undefined);
await initServiceContext(fs, logger, projectRoot);
expect(fs.exists).toHaveBeenCalledWith(contextFilePath);
expect(fs.ensureDir).toHaveBeenCalledWith(contextPath);
expect(fs.writeFile).toHaveBeenCalledTimes(2);
expect(logger.success).toHaveBeenCalledWith('Service context created successfully!');
expect(logger.info).toHaveBeenCalledWith(`Location: ${contextFilePath}`);
});
it('should not create context when it already exists', async () => {
(fs.exists as any).mockResolvedValue(true);
await initServiceContext(fs, logger, projectRoot);
expect(fs.exists).toHaveBeenCalledWith(contextFilePath);
expect(fs.ensureDir).not.toHaveBeenCalled();
expect(fs.writeFile).not.toHaveBeenCalled();
expect(logger.warning).toHaveBeenCalledWith('Service context already exists');
});
it('should handle errors during context creation', async () => {
(fs.exists as any).mockResolvedValue(false);
(fs.ensureDir as any).mockRejectedValue(new Error('Permission denied'));
await initServiceContext(fs, logger, projectRoot);
expect(logger.error).toHaveBeenCalledWith('Error initializing service context: Permission denied');
});
});
describe('generateServiceContext', () => {
it('should generate context with existing domains', async () => {
// Mock context doesn't exist
(fs.exists as any).mockImplementation((path: string) => {
if (path === contextFilePath) return Promise.resolve(false);
if (path === domainsPath) return Promise.resolve(true);
if (path.includes('domains/user')) return Promise.resolve(true);
if (path.includes('domains/product')) return Promise.resolve(true);
if (path.includes('UserService.ts')) return Promise.resolve(true);
if (path.includes('UserAdapter.ts')) return Promise.resolve(true);
if (path.includes('ProductService.ts')) return Promise.resolve(true);
if (path.includes('ProductAdapter.ts')) return Promise.resolve(true);
return Promise.resolve(false);
});
(fs.readdir as any).mockResolvedValue(['user', 'product']);
(fs.stat as any).mockResolvedValue({ isDirectory: () => true });
(fs.ensureDir as any).mockResolvedValue(undefined);
(fs.writeFile as any).mockResolvedValue(undefined);
await generateServiceContext(fs, logger, projectRoot);
expect(fs.writeFile).toHaveBeenCalledTimes(2);
expect(logger.success).toHaveBeenCalledWith('Service context created successfully!');
expect(logger.info).toHaveBeenCalledWith('Integrated 2 domain(s):');
});
it('should create empty context when no domains exist', async () => {
(fs.exists as any).mockImplementation((path: string) => {
if (path === contextFilePath) return Promise.resolve(false);
if (path === domainsPath) return Promise.resolve(false);
return Promise.resolve(false);
});
(fs.ensureDir as any).mockResolvedValue(undefined);
(fs.writeFile as any).mockResolvedValue(undefined);
await generateServiceContext(fs, logger, projectRoot);
expect(logger.warning).toHaveBeenCalledWith('No domains found. Creating empty service context.');
expect(fs.writeFile).toHaveBeenCalledTimes(2);
});
it('should not generate when context already exists', async () => {
(fs.exists as any).mockResolvedValue(true);
await generateServiceContext(fs, logger, projectRoot);
expect(logger.warning).toHaveBeenCalledWith('Service context already exists. Use "context update" to add new domains.');
expect(fs.writeFile).not.toHaveBeenCalled();
});
});
describe('updateServiceContext', () => {
it('should update context with missing domains', async () => {
// Mock context exists
(fs.exists as any).mockImplementation((path: string) => {
if (path === contextFilePath) return Promise.resolve(true);
if (path === domainsPath) return Promise.resolve(true);
if (path.includes('domains/user')) return Promise.resolve(true);
if (path.includes('domains/product')) return Promise.resolve(true);
if (path.includes('UserService.ts')) return Promise.resolve(true);
if (path.includes('UserAdapter.ts')) return Promise.resolve(true);
if (path.includes('ProductService.ts')) return Promise.resolve(true);
if (path.includes('ProductAdapter.ts')) return Promise.resolve(true);
return Promise.resolve(false);
});
// Mock existing context with only user domain
(fs.readFile as any).mockResolvedValue(`
interface Services {
userService: UserService;
}
`);
(fs.readdir as any).mockResolvedValue(['user', 'product']);
(fs.stat as any).mockResolvedValue({ isDirectory: () => true });
(fs.writeFile as any).mockResolvedValue(undefined);
await updateServiceContext(fs, logger, projectRoot, true);
// Now expects 3 calls: backup + context + test files
expect(fs.writeFile).toHaveBeenCalledTimes(3);
expect(logger.success).toHaveBeenCalledWith('Service context updated successfully!');
expect(logger.info).toHaveBeenCalledWith('Added 1 domain(s) to service context');
});
it('should not update when context is up to date', async () => {
// Mock context exists
(fs.exists as any).mockImplementation((path: string) => {
if (path === contextFilePath) return Promise.resolve(true);
if (path === domainsPath) return Promise.resolve(true);
if (path.includes('domains/user')) return Promise.resolve(true);
if (path.includes('UserService.ts')) return Promise.resolve(true);
if (path.includes('UserAdapter.ts')) return Promise.resolve(true);
return Promise.resolve(false);
});
// Mock existing context with user domain
(fs.readFile as any).mockResolvedValue(`
interface Services {
userService: UserService;
}
`);
(fs.readdir as any).mockResolvedValue(['user']);
(fs.stat as any).mockResolvedValue({ isDirectory: () => true });
await updateServiceContext(fs, logger, projectRoot, true);
expect(fs.writeFile).not.toHaveBeenCalled();
expect(logger.info).toHaveBeenCalledWith('Service context is already up to date. No missing domains found.');
});
it('should not update when context does not exist', async () => {
(fs.exists as any).mockResolvedValue(false);
await updateServiceContext(fs, logger, projectRoot, true);
expect(logger.error).toHaveBeenCalledWith('Service context does not exist. Use "context init" or "context generate" first.');
expect(fs.writeFile).not.toHaveBeenCalled();
});
});
});