Files
Profa cb27ee303f feat(v2.5.0): G4 BinaryCache + G6 OpenRouter hot-swap + ruvLLM integration
## Major Features

### G4: Unified Memory Architecture - BinaryCache Integration
- Integrate BinaryMetadataCache with UnifiedMemoryCoordinator
- Add TRM pattern caching with 6x faster pattern loading
- New methods: cacheTRMPattern(), getCachedTRMPattern(), persistBinaryCache()

### G6: OpenRouter Provider with Model Hot-Swap
- Full ILLMProvider implementation for OpenRouter API (300+ models)
- Model hot-swapping at runtime without restart
- Smart environment detection for auto provider selection

### ruvLLM Integration (Issue #131)
- SONA Learning Strategy with MicroLoRA/BaseLoRA/EWC++
- TRM Learning Strategy for trajectory tracking
- ReasoningBank integration for pattern storage
- 104 new integration tests

Closes #131

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 18:09:30 +00:00

582 lines
18 KiB
TypeScript

/**
* Init Command Tests
*
* Tests for CLI init command with environment configuration generation
* Coverage target: 95%+
*
* Test scenarios:
* 1. generateEnvironmentConfigs with various inputs
* 2. Full init command execution flow
* 3. Environment config structure validation
* 4. Error handling for invalid inputs
* 5. File system operations (config writing)
*/
import { describe, it, expect, jest, beforeEach, afterEach } from '@jest/globals';
import * as fs from 'fs/promises';
import * as path from 'path';
// Mock file system operations
jest.mock('fs/promises');
// TDD RED Phase: These tests define the API for future implementation.
// The functions (generateEnvironmentConfigs, initCommand, migrateConfig) are not yet implemented.
// See src/cli/commands/init.ts - currently only exports InitCommand class.
describe.skip('Init Command (TODO: implement generateEnvironmentConfigs, initCommand, migrateConfig)', () => {
let mockFs: jest.Mocked<typeof fs>;
beforeEach(() => {
mockFs = fs as jest.Mocked<typeof fs>;
jest.clearAllMocks();
});
afterEach(() => {
jest.resetModules();
});
describe('generateEnvironmentConfigs', () => {
it('should generate default environment configs', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
environments: ['development', 'production']
};
// Act
const result = await generateEnvironmentConfigs(options);
// Assert
expect(result).toBeDefined();
expect(result).toHaveProperty('development');
expect(result).toHaveProperty('production');
});
it('should generate configs with custom environments', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
environments: ['development', 'staging', 'production', 'qa']
};
// Act
const result = await generateEnvironmentConfigs(options);
// Assert
expect(Object.keys(result)).toHaveLength(4);
expect(result).toHaveProperty('development');
expect(result).toHaveProperty('staging');
expect(result).toHaveProperty('production');
expect(result).toHaveProperty('qa');
});
it('should include all required config fields', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
environments: ['development']
};
// Act
const result = await generateEnvironmentConfigs(options);
const devConfig = result.development;
// Assert
expect(devConfig).toHaveProperty('apiKey');
expect(devConfig).toHaveProperty('endpoint');
expect(devConfig).toHaveProperty('timeout');
expect(devConfig).toHaveProperty('retryAttempts');
expect(devConfig).toHaveProperty('logLevel');
});
it('should generate different configs for different environments', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
environments: ['development', 'production']
};
// Act
const result = await generateEnvironmentConfigs(options);
// Assert
expect(result.development.logLevel).toBe('debug');
expect(result.production.logLevel).toBe('error');
expect(result.development.timeout).toBeLessThan(result.production.timeout);
});
it('should handle empty environment list', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
environments: []
};
// Act
const result = await generateEnvironmentConfigs(options);
// Assert
expect(result).toBeDefined();
expect(Object.keys(result)).toHaveLength(0);
});
it('should validate environment names', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
environments: ['dev-123', 'prod@2024', 'test_env']
};
// Act & Assert
await expect(generateEnvironmentConfigs(options)).rejects.toThrow(/invalid environment name/i);
});
it('should handle special characters in project name', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project-123_v2',
environments: ['development']
};
// Act
const result = await generateEnvironmentConfigs(options);
// Assert
expect(result).toBeDefined();
expect(result.development).toBeDefined();
});
it('should merge custom config overrides', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
environments: ['development'],
configOverrides: {
development: {
timeout: 10000,
customField: 'custom-value'
}
}
};
// Act
const result = await generateEnvironmentConfigs(options);
// Assert
expect(result.development.timeout).toBe(10000);
expect(result.development).toHaveProperty('customField', 'custom-value');
});
});
describe('Full Init Command Execution', () => {
beforeEach(() => {
mockFs.mkdir.mockResolvedValue(undefined);
mockFs.writeFile.mockResolvedValue(undefined);
mockFs.access.mockRejectedValue(new Error('Directory does not exist'));
});
it('should execute init command successfully', async () => {
// Arrange
const { initCommand } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
outputDir: '/tmp/test-output'
};
// Act
await initCommand(options);
// Assert
expect(mockFs.mkdir).toHaveBeenCalled();
expect(mockFs.writeFile).toHaveBeenCalled();
});
it('should create config directory structure', async () => {
// Arrange
const { initCommand } = await import('../../../src/cli/commands/init');
const outputDir = '/tmp/test-output';
const options = {
projectName: 'test-project',
outputDir
};
// Act
await initCommand(options);
// Assert
expect(mockFs.mkdir).toHaveBeenCalledWith(
expect.stringContaining('.agentic-qe'),
expect.objectContaining({ recursive: true })
);
expect(mockFs.mkdir).toHaveBeenCalledWith(
expect.stringContaining('config'),
expect.objectContaining({ recursive: true })
);
});
it('should write environment config files', async () => {
// Arrange
const { initCommand } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
outputDir: '/tmp/test-output',
environments: ['development', 'production']
};
// Act
await initCommand(options);
// Assert
expect(mockFs.writeFile).toHaveBeenCalledTimes(2);
expect(mockFs.writeFile).toHaveBeenCalledWith(
expect.stringContaining('development.json'),
expect.any(String),
'utf-8'
);
expect(mockFs.writeFile).toHaveBeenCalledWith(
expect.stringContaining('production.json'),
expect.any(String),
'utf-8'
);
});
it('should handle existing directory gracefully', async () => {
// Arrange
mockFs.access.mockResolvedValue(undefined); // Directory exists
const { initCommand } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
outputDir: '/tmp/existing-dir',
force: false
};
// Act & Assert
await expect(initCommand(options)).rejects.toThrow(/directory already exists/i);
});
it('should overwrite with force flag', async () => {
// Arrange
mockFs.access.mockResolvedValue(undefined); // Directory exists
const { initCommand } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
outputDir: '/tmp/existing-dir',
force: true
};
// Act
await initCommand(options);
// Assert
expect(mockFs.writeFile).toHaveBeenCalled();
});
it('should create fleet.json config', async () => {
// Arrange
const { initCommand } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
outputDir: '/tmp/test-output'
};
// Act
await initCommand(options);
// Assert
expect(mockFs.writeFile).toHaveBeenCalledWith(
expect.stringContaining('fleet.json'),
expect.stringContaining('"topology"'),
'utf-8'
);
});
it('should create routing.json config', async () => {
// Arrange
const { initCommand } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
outputDir: '/tmp/test-output'
};
// Act
await initCommand(options);
// Assert
expect(mockFs.writeFile).toHaveBeenCalledWith(
expect.stringContaining('routing.json'),
expect.stringContaining('"multiModelRouter"'),
'utf-8'
);
});
it('should create aqe-hooks.json config', async () => {
// Arrange
const { initCommand } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
outputDir: '/tmp/test-output'
};
// Act
await initCommand(options);
// Assert
expect(mockFs.writeFile).toHaveBeenCalledWith(
expect.stringContaining('aqe-hooks.json'),
expect.stringContaining('"hooks"'),
'utf-8'
);
});
it('should handle file write errors', async () => {
// Arrange
mockFs.writeFile.mockRejectedValue(new Error('Permission denied'));
const { initCommand } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
outputDir: '/tmp/test-output'
};
// Act & Assert
await expect(initCommand(options)).rejects.toThrow(/permission denied/i);
});
it('should validate project name format', async () => {
// Arrange
const { initCommand } = await import('../../../src/cli/commands/init');
const options = {
projectName: '',
outputDir: '/tmp/test-output'
};
// Act & Assert
await expect(initCommand(options)).rejects.toThrow(/invalid project name/i);
});
it('should use current directory if no outputDir specified', async () => {
// Arrange
const { initCommand } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project'
};
// Act
await initCommand(options);
// Assert
expect(mockFs.mkdir).toHaveBeenCalledWith(
expect.stringContaining(process.cwd()),
expect.any(Object)
);
});
});
describe('Environment Config Structure', () => {
it('should have valid JSON structure', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
environments: ['development']
};
// Act
const result = await generateEnvironmentConfigs(options);
const jsonString = JSON.stringify(result.development);
const parsed = JSON.parse(jsonString);
// Assert
expect(parsed).toEqual(result.development);
});
it('should include metadata', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
environments: ['development']
};
// Act
const result = await generateEnvironmentConfigs(options);
// Assert
expect(result.development).toHaveProperty('metadata');
expect(result.development.metadata).toHaveProperty('created');
expect(result.development.metadata).toHaveProperty('version');
});
it('should validate config schema', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
environments: ['development']
};
// Act
const result = await generateEnvironmentConfigs(options);
const config = result.development;
// Assert - Check data types
expect(typeof config.apiKey).toBe('string');
expect(typeof config.endpoint).toBe('string');
expect(typeof config.timeout).toBe('number');
expect(typeof config.retryAttempts).toBe('number');
expect(typeof config.logLevel).toBe('string');
});
it('should have valid timeout values', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
environments: ['development', 'production']
};
// Act
const result = await generateEnvironmentConfigs(options);
// Assert
expect(result.development.timeout).toBeGreaterThan(0);
expect(result.development.timeout).toBeLessThanOrEqual(300000); // Max 5 minutes
expect(result.production.timeout).toBeGreaterThan(0);
expect(result.production.timeout).toBeLessThanOrEqual(300000);
});
it('should have valid retry attempts', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
environments: ['development']
};
// Act
const result = await generateEnvironmentConfigs(options);
// Assert
expect(result.development.retryAttempts).toBeGreaterThanOrEqual(0);
expect(result.development.retryAttempts).toBeLessThanOrEqual(10);
});
it('should have valid log levels', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const validLogLevels = ['debug', 'info', 'warn', 'error'];
const options = {
projectName: 'test-project',
environments: ['development', 'production']
};
// Act
const result = await generateEnvironmentConfigs(options);
// Assert
expect(validLogLevels).toContain(result.development.logLevel);
expect(validLogLevels).toContain(result.production.logLevel);
});
});
describe('Error Handling', () => {
it('should handle invalid output directory', async () => {
// Arrange
mockFs.mkdir.mockRejectedValue(new Error('Invalid path'));
const { initCommand } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
outputDir: '/invalid/\0/path'
};
// Act & Assert
await expect(initCommand(options)).rejects.toThrow();
});
it('should handle filesystem errors gracefully', async () => {
// Arrange
mockFs.mkdir.mockRejectedValue(new Error('Disk full'));
const { initCommand } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
outputDir: '/tmp/test-output'
};
// Act & Assert
await expect(initCommand(options)).rejects.toThrow(/disk full/i);
});
it('should rollback on partial failure', async () => {
// Arrange
let callCount = 0;
mockFs.writeFile.mockImplementation(async () => {
callCount++;
if (callCount > 2) {
throw new Error('Write failed');
}
});
const { initCommand } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
outputDir: '/tmp/test-output',
environments: ['development', 'staging', 'production']
};
// Act
try {
await initCommand(options);
} catch (error) {
// Expected error
}
// Assert - verify cleanup was attempted
// (In real implementation, this would check cleanup calls)
expect(callCount).toBeGreaterThan(0);
});
});
describe('Backward Compatibility', () => {
it('should support legacy config format', async () => {
// Arrange
const { generateEnvironmentConfigs } = await import('../../../src/cli/commands/init');
const options = {
projectName: 'test-project',
environments: ['development'],
legacyFormat: true
};
// Act
const result = await generateEnvironmentConfigs(options);
// Assert
expect(result).toBeDefined();
expect(result.development).toBeDefined();
});
it('should migrate from v1 config format', async () => {
// Arrange
const { migrateConfig } = await import('../../../src/cli/commands/init');
const v1Config = {
apiKey: 'test-key',
endpoint: 'http://localhost:3000'
};
// Act
const result = await migrateConfig(v1Config);
// Assert
expect(result).toHaveProperty('apiKey', 'test-key');
expect(result).toHaveProperty('endpoint', 'http://localhost:3000');
expect(result).toHaveProperty('timeout');
expect(result).toHaveProperty('retryAttempts');
});
});
});