Files
2025-11-30 09:04:14 +08:00

224 lines
6.6 KiB
TypeScript

/**
* Jest Integration Test Template
*
* Test complete CLI workflows with multiple commands and state persistence
*/
import { execSync } from 'child_process';
import fs from 'fs';
import path from 'path';
import os from 'os';
describe('CLI Integration Tests', () => {
const CLI_PATH = path.join(__dirname, '../bin/mycli');
const TEST_WORKSPACE = path.join(os.tmpdir(), 'cli-integration-test');
function runCLI(args: string, cwd: string = TEST_WORKSPACE): {
stdout: string;
stderr: string;
code: number;
} {
try {
const stdout = execSync(`${CLI_PATH} ${args}`, {
encoding: 'utf8',
stdio: 'pipe',
cwd,
});
return { stdout, stderr: '', code: 0 };
} catch (error: any) {
return {
stdout: error.stdout || '',
stderr: error.stderr || '',
code: error.status || 1,
};
}
}
beforeEach(() => {
// Create clean test workspace
if (fs.existsSync(TEST_WORKSPACE)) {
fs.rmSync(TEST_WORKSPACE, { recursive: true, force: true });
}
fs.mkdirSync(TEST_WORKSPACE, { recursive: true });
});
afterEach(() => {
// Clean up test workspace
if (fs.existsSync(TEST_WORKSPACE)) {
fs.rmSync(TEST_WORKSPACE, { recursive: true, force: true });
}
});
describe('complete deployment workflow', () => {
test('should initialize, configure, and deploy', () => {
// Step 1: Initialize project
const init = runCLI('init my-project');
expect(init.code).toBe(0);
expect(init.stdout).toContain('Project initialized');
// Step 2: Configure deployment
const config = runCLI('config set api_key test_key_123');
expect(config.code).toBe(0);
// Step 3: Build project
const build = runCLI('build --production');
expect(build.code).toBe(0);
expect(build.stdout).toContain('Build successful');
// Step 4: Deploy
const deploy = runCLI('deploy production');
expect(deploy.code).toBe(0);
expect(deploy.stdout).toContain('Deployed successfully');
// Verify deployment artifacts
const deployFile = path.join(TEST_WORKSPACE, '.deploy');
expect(fs.existsSync(deployFile)).toBe(true);
});
test('should fail deployment without configuration', () => {
runCLI('init my-project');
// Try to deploy without configuring API key
const { stderr, code } = runCLI('deploy production');
expect(code).toBe(1);
expect(stderr).toContain('API key not configured');
});
});
describe('multi-environment workflow', () => {
test('should manage multiple environments', () => {
// Initialize project
runCLI('init my-project');
// Configure development environment
runCLI('config set api_key dev_key_123 --env development');
runCLI('config set base_url https://dev.example.com --env development');
// Configure production environment
runCLI('config set api_key prod_key_123 --env production');
runCLI('config set base_url https://api.example.com --env production');
// Deploy to development
const devDeploy = runCLI('deploy development');
expect(devDeploy.code).toBe(0);
expect(devDeploy.stdout).toContain('dev.example.com');
// Deploy to production
const prodDeploy = runCLI('deploy production');
expect(prodDeploy.code).toBe(0);
expect(prodDeploy.stdout).toContain('api.example.com');
});
});
describe('state persistence workflow', () => {
test('should persist and restore state', () => {
// Create initial state
runCLI('state set counter 0');
// Increment counter multiple times
runCLI('increment');
runCLI('increment');
runCLI('increment');
// Verify final state
const { stdout } = runCLI('state get counter');
expect(stdout).toContain('3');
});
test('should handle state file corruption', () => {
runCLI('state set key value');
// Corrupt state file
const stateFile = path.join(TEST_WORKSPACE, '.state');
fs.writeFileSync(stateFile, 'invalid json {[}');
// Should recover gracefully
const { stderr, code } = runCLI('state get key');
expect(code).toBe(1);
expect(stderr).toContain('Corrupted state file');
});
});
describe('plugin workflow', () => {
test('should install and use plugins', () => {
// Initialize project
runCLI('init my-project');
// Install plugin
const install = runCLI('plugin install my-plugin');
expect(install.code).toBe(0);
// Verify plugin is listed
const list = runCLI('plugin list');
expect(list.stdout).toContain('my-plugin');
// Use plugin command
const usePlugin = runCLI('my-plugin:command');
expect(usePlugin.code).toBe(0);
// Uninstall plugin
const uninstall = runCLI('plugin uninstall my-plugin');
expect(uninstall.code).toBe(0);
// Verify plugin is removed
const listAfter = runCLI('plugin list');
expect(listAfter.stdout).not.toContain('my-plugin');
});
});
describe('error recovery workflow', () => {
test('should recover from partial failure', () => {
runCLI('init my-project');
// Simulate partial deployment failure
runCLI('deploy staging --force');
// Should be able to rollback
const rollback = runCLI('rollback');
expect(rollback.code).toBe(0);
expect(rollback.stdout).toContain('Rollback successful');
// Should be able to retry
const retry = runCLI('deploy staging --retry');
expect(retry.code).toBe(0);
});
});
describe('concurrent operations', () => {
test('should handle file locking', async () => {
runCLI('init my-project');
// Start long-running operation
const longOp = execSync(`${CLI_PATH} long-running-task &`, {
cwd: TEST_WORKSPACE,
});
// Try to run another operation that needs lock
const { stderr, code } = runCLI('another-task');
expect(code).toBe(1);
expect(stderr).toContain('Another operation in progress');
});
});
describe('data migration workflow', () => {
test('should migrate data between versions', () => {
// Create old version data
const oldData = { version: 1, data: 'legacy format' };
fs.writeFileSync(
path.join(TEST_WORKSPACE, 'data.json'),
JSON.stringify(oldData)
);
// Run migration
const migrate = runCLI('migrate --to 2.0');
expect(migrate.code).toBe(0);
// Verify new format
const newData = JSON.parse(
fs.readFileSync(path.join(TEST_WORKSPACE, 'data.json'), 'utf8')
);
expect(newData.version).toBe(2);
});
});
});