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.
153 lines (125 loc) • 6.13 kB
text/typescript
import { describe, it, expect, beforeEach, vi } from 'vitest';
import fs from 'fs-extra';
import { logger } from '../../src/utils/logger';
import { listDomains } from '../../src/commands/list';
import path from 'path';
import * as paths from '../../src/utils/paths';
import type { FileSystem } from '../../src/utils/file-operations';
import type { Logger } from '../../src/utils/logging';
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(),
}
}));
vi.mock('../../src/utils/paths', () => ({
getDomainsSourceDirectoryPath: vi.fn(),
}));
describe('listDomains', () => {
const domainsSourcePath = path.join(process.cwd(), 'src', 'domains');
const mockFileSystem: FileSystem = fs;
const mockLogger: Logger = logger;
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(paths.getDomainsSourceDirectoryPath).mockReturnValue(domainsSourcePath);
});
it('should list all domains when domains exist', async () => {
const domains = ['domain1', 'domain2', 'domain3'];
const existsMock = vi.fn().mockResolvedValue(true);
const readdirMock = vi.fn().mockResolvedValue(domains);
const statMock = vi.fn().mockResolvedValue({
isDirectory: () => true
} as fs.Stats);
vi.mocked(fs.exists).mockImplementation(existsMock);
vi.mocked(fs.readdir).mockImplementation(readdirMock);
vi.mocked(fs.stat).mockImplementation(statMock);
await listDomains(mockFileSystem, mockLogger);
expect(fs.exists).toHaveBeenCalledWith(domainsSourcePath);
expect(fs.readdir).toHaveBeenCalledWith(domainsSourcePath);
expect(logger.success).toHaveBeenCalledWith(`${domains.length} domain(s) found:`);
expect(logger.info).toHaveBeenCalledWith(`Total: ${domains.length} domain(s)`);
expect(logger.info).toHaveBeenCalledWith(`Location: ${domainsSourcePath}`);
domains.forEach(domain => {
expect(logger.log).toHaveBeenCalledWith(expect.stringContaining(domain));
});
});
it('should show message when no domains exist', async () => {
const existsMock = vi.fn().mockResolvedValue(true);
const readdirMock = vi.fn().mockResolvedValue([]);
vi.mocked(fs.exists).mockImplementation(existsMock);
vi.mocked(fs.readdir).mockImplementation(readdirMock);
await listDomains(mockFileSystem, mockLogger);
expect(fs.exists).toHaveBeenCalledWith(domainsSourcePath);
expect(fs.readdir).toHaveBeenCalledWith(domainsSourcePath);
expect(logger.info).toHaveBeenCalledWith('No domains found in src/domains.');
expect(logger.info).toHaveBeenCalledWith('💡 Use "ruch create <domain-name>" to create your first domain.');
});
it('should handle errors when reading directory', async () => {
const error = new Error('Failed to read directory');
const existsMock = vi.fn().mockResolvedValue(true);
const readdirMock = vi.fn().mockRejectedValue(error);
vi.mocked(fs.exists).mockImplementation(existsMock);
vi.mocked(fs.readdir).mockImplementation(readdirMock);
await listDomains(mockFileSystem, mockLogger);
expect(fs.exists).toHaveBeenCalledWith(domainsSourcePath);
expect(logger.error).toHaveBeenCalledWith(`Error reading domains: ${error.message}`);
});
it('should only list directories', async () => {
const files = ['domain1', 'domain2', 'file.txt', 'README.md'];
const existsMock = vi.fn().mockResolvedValue(true);
const readdirMock = vi.fn().mockResolvedValue(files);
const statMock = vi.fn().mockImplementation(async (p) => ({
isDirectory: () => typeof p === 'string' && p.includes('domain')
} as fs.Stats));
vi.mocked(fs.exists).mockImplementation(existsMock);
vi.mocked(fs.readdir).mockImplementation(readdirMock);
vi.mocked(fs.stat).mockImplementation(statMock);
await listDomains(mockFileSystem, mockLogger);
expect(fs.exists).toHaveBeenCalledWith(domainsSourcePath);
expect(logger.success).toHaveBeenCalledWith('4 domain(s) found:');
expect(logger.info).toHaveBeenCalledWith('Total: 4 domain(s)');
expect(logger.info).toHaveBeenCalledWith(`Location: ${domainsSourcePath}`);
expect(logger.log).toHaveBeenCalledWith(expect.stringContaining('domain1'));
expect(logger.log).toHaveBeenCalledWith(expect.stringContaining('domain2'));
});
it('should show message when domains folder does not exist', async () => {
const existsMock = vi.fn().mockResolvedValue(false);
vi.mocked(fs.exists).mockImplementation(existsMock);
await listDomains(mockFileSystem, mockLogger);
expect(fs.exists).toHaveBeenCalledWith(domainsSourcePath);
expect(logger.info).toHaveBeenCalledWith('No domains found in src/domains.');
expect(logger.info).toHaveBeenCalledWith('💡 Use "ruch create <domain-name>" to create your first domain.');
});
it('should sort folders in logical order', async () => {
const domain = 'test-domain';
const folders = ['hooks', 'ui', 'services', 'adapters', 'entities', 'ports'];
const existsMock = vi.fn().mockResolvedValue(true);
const readdirMock = vi.fn().mockResolvedValue([domain]);
const statMock = vi.fn().mockResolvedValue({
isDirectory: () => true
} as fs.Stats);
const domainReaddirMock = vi.fn().mockResolvedValue(folders);
const domainStatMock = vi.fn().mockResolvedValue({
isDirectory: () => true
} as fs.Stats);
vi.mocked(fs.exists).mockImplementation(existsMock);
vi.mocked(fs.readdir)
.mockImplementationOnce(readdirMock)
.mockImplementationOnce(domainReaddirMock);
vi.mocked(fs.stat)
.mockImplementationOnce(statMock)
.mockImplementation(domainStatMock);
await listDomains(mockFileSystem, mockLogger);
const expectedOrder = ['entities', 'ports', 'services', 'adapters', 'hooks', 'ui'];
const logCalls = vi.mocked(logger.log).mock.calls;
const folderLogs = logCalls
.map(call => call?.[0] ?? '')
.filter(log => log.includes('📂'))
.map(log => log.split('📂')[1]?.trim() ?? '');
expect(folderLogs).toEqual(expectedOrder);
});
});