Initial commit
This commit is contained in:
223
skills/cli-testing-patterns/templates/jest-integration-test.ts
Normal file
223
skills/cli-testing-patterns/templates/jest-integration-test.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user