UNPKG

@ordojs/cli

Version:

Command-line interface for OrdoJS framework

273 lines (221 loc) 11 kB
/** * @fileoverview Tests for the infrastructure command */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { CLIError } from '../utils/index.js'; import { infrastructureCommand } from './infrastructure.js'; // Mock the generators vi.mock('../utils/deployment/docker-generator.js', () => ({ DockerGenerator: vi.fn().mockImplementation(() => ({ generateDockerfile: vi.fn().mockReturnValue('FROM node:18-alpine'), generateDockerCompose: vi.fn().mockReturnValue('version: \'3.8\''), generateDockerignore: vi.fn().mockReturnValue('node_modules'), generateKubernetesManifests: vi.fn().mockReturnValue({ deployment: 'apiVersion: apps/v1', service: 'apiVersion: v1', ingress: 'apiVersion: networking.k8s.io/v1', configMap: 'apiVersion: v1', secret: '' }) })) })); vi.mock('../utils/deployment/cicd-generator.js', () => ({ CICDGenerator: vi.fn().mockImplementation(() => ({ generatePipeline: vi.fn().mockReturnValue('name: OrdoJS CI/CD Pipeline') })) })); vi.mock('../utils/deployment/monitoring-generator.js', () => ({ MonitoringGenerator: vi.fn().mockImplementation(() => ({ generateMonitoringConfig: vi.fn().mockReturnValue({ logger: 'import winston', metrics: 'import prometheus', tracing: 'import opentelemetry', healthCheck: 'import healthCheck', errorHandler: 'import errorHandler', dockerCompose: 'version: \'3.8\'', kubernetes: 'apiVersion: v1' }) })) })); // Mock file system operations vi.mock('../utils/fs.js', () => ({ writeFile: vi.fn().mockResolvedValue(undefined), mkdir: vi.fn().mockResolvedValue(undefined) })); describe('infrastructureCommand', () => { const mockOptions = { type: 'all', platform: 'github', output: '.', nodeVersion: '18', port: 3000, multiStage: true, healthCheck: true, autoDeploy: false, monitoring: true, env: { NODE_ENV: 'production', PORT: '3000' } }; beforeEach(() => { vi.clearAllMocks(); }); afterEach(() => { vi.restoreAllMocks(); }); describe('Docker infrastructure generation', () => { it('should generate Docker infrastructure when type is docker', async () => { const options = { ...mockOptions, type: 'docker' }; await infrastructureCommand(options); const { writeFile } = await import('../utils/fs.js'); expect(writeFile).toHaveBeenCalledWith('./Dockerfile', 'FROM node:18-alpine'); expect(writeFile).toHaveBeenCalledWith('./docker-compose.yml', 'version: \'3.8\''); expect(writeFile).toHaveBeenCalledWith('./.dockerignore', 'node_modules'); }); it('should generate Kubernetes manifests', async () => { const options = { ...mockOptions, type: 'docker' }; await infrastructureCommand(options); const { writeFile, mkdir } = await import('../utils/fs.js'); expect(mkdir).toHaveBeenCalledWith('./k8s', { recursive: true }); expect(writeFile).toHaveBeenCalledWith('./k8s/deployment.yaml', 'apiVersion: apps/v1'); expect(writeFile).toHaveBeenCalledWith('./k8s/service.yaml', 'apiVersion: v1'); expect(writeFile).toHaveBeenCalledWith('./k8s/ingress.yaml', 'apiVersion: networking.k8s.io/v1'); expect(writeFile).toHaveBeenCalledWith('./k8s/configmap.yaml', 'apiVersion: v1'); }); it('should use custom Node.js version', async () => { const options = { ...mockOptions, type: 'docker', nodeVersion: '20' }; await infrastructureCommand(options); const { DockerGenerator } = await import('../utils/deployment/docker-generator.js'); const mockGenerator = DockerGenerator.mock.instances[0]; expect(mockGenerator.generateDockerfile).toHaveBeenCalledWith( expect.any(Object), expect.objectContaining({ nodeVersion: '20' }) ); }); it('should use custom port', async () => { const options = { ...mockOptions, type: 'docker', port: 8080 }; await infrastructureCommand(options); const { DockerGenerator } = await import('../utils/deployment/docker-generator.js'); const mockGenerator = DockerGenerator.mock.instances[0]; expect(mockGenerator.generateDockerfile).toHaveBeenCalledWith( expect.any(Object), expect.objectContaining({ port: 8080 }) ); }); }); describe('CI/CD infrastructure generation', () => { it('should generate GitHub Actions when platform is github', async () => { const options = { ...mockOptions, type: 'cicd', platform: 'github' }; await infrastructureCommand(options); const { writeFile, mkdir } = await import('../utils/fs.js'); expect(mkdir).toHaveBeenCalledWith('./.github/workflows', { recursive: true }); expect(writeFile).toHaveBeenCalledWith('./.github/workflows/ci-cd.yml', 'name: OrdoJS CI/CD Pipeline'); }); it('should generate GitLab CI when platform is gitlab', async () => { const options = { ...mockOptions, type: 'cicd', platform: 'gitlab' }; await infrastructureCommand(options); const { writeFile } = await import('../utils/fs.js'); expect(writeFile).toHaveBeenCalledWith('./.gitlab-ci.yml', 'name: OrdoJS CI/CD Pipeline'); }); it('should generate Jenkins pipeline when platform is jenkins', async () => { const options = { ...mockOptions, type: 'cicd', platform: 'jenkins' }; await infrastructureCommand(options); const { writeFile } = await import('../utils/fs.js'); expect(writeFile).toHaveBeenCalledWith('./Jenkinsfile', 'name: OrdoJS CI/CD Pipeline'); }); it('should throw error for unsupported platform', async () => { const options = { ...mockOptions, type: 'cicd', platform: 'unsupported' }; await expect(infrastructureCommand(options)).rejects.toThrow(CLIError); }); }); describe('Monitoring infrastructure generation', () => { it('should generate monitoring infrastructure when monitoring is enabled', async () => { const options = { ...mockOptions, type: 'monitoring', monitoring: true }; await infrastructureCommand(options); const { writeFile, mkdir } = await import('../utils/fs.js'); expect(mkdir).toHaveBeenCalledWith('./monitoring', { recursive: true }); expect(mkdir).toHaveBeenCalledWith('./src/monitoring', { recursive: true }); expect(writeFile).toHaveBeenCalledWith('./src/monitoring/logger.js', 'import winston'); expect(writeFile).toHaveBeenCalledWith('./src/monitoring/metrics.js', 'import prometheus'); expect(writeFile).toHaveBeenCalledWith('./src/monitoring/tracing.js', 'import opentelemetry'); expect(writeFile).toHaveBeenCalledWith('./src/monitoring/health-check.js', 'import healthCheck'); expect(writeFile).toHaveBeenCalledWith('./src/monitoring/error-handler.js', 'import errorHandler'); expect(writeFile).toHaveBeenCalledWith('./docker-compose.monitoring.yml', 'version: \'3.8\''); expect(writeFile).toHaveBeenCalledWith('./k8s/monitoring/monitoring.yml', 'apiVersion: v1'); }); it('should not generate monitoring infrastructure when monitoring is disabled', async () => { const options = { ...mockOptions, type: 'monitoring', monitoring: false }; await infrastructureCommand(options); const { writeFile } = await import('../utils/fs.js'); // Should not call writeFile for monitoring files expect(writeFile).not.toHaveBeenCalledWith('./src/monitoring/logger.js', expect.any(String)); }); }); describe('Complete infrastructure generation', () => { it('should generate all infrastructure components when type is all', async () => { const options = { ...mockOptions, type: 'all' }; await infrastructureCommand(options); const { writeFile, mkdir } = await import('../utils/fs.js'); // Check Docker files expect(writeFile).toHaveBeenCalledWith('./Dockerfile', expect.any(String)); expect(writeFile).toHaveBeenCalledWith('./docker-compose.yml', expect.any(String)); expect(writeFile).toHaveBeenCalledWith('./.dockerignore', expect.any(String)); // Check Kubernetes files expect(mkdir).toHaveBeenCalledWith('./k8s', { recursive: true }); expect(writeFile).toHaveBeenCalledWith('./k8s/deployment.yaml', expect.any(String)); expect(writeFile).toHaveBeenCalledWith('./k8s/service.yaml', expect.any(String)); // Check CI/CD files expect(mkdir).toHaveBeenCalledWith('./.github/workflows', { recursive: true }); expect(writeFile).toHaveBeenCalledWith('./.github/workflows/ci-cd.yml', expect.any(String)); // Check monitoring files expect(mkdir).toHaveBeenCalledWith('./monitoring', { recursive: true }); expect(mkdir).toHaveBeenCalledWith('./src/monitoring', { recursive: true }); expect(writeFile).toHaveBeenCalledWith('./src/monitoring/logger.js', expect.any(String)); expect(writeFile).toHaveBeenCalledWith('./src/monitoring/metrics.js', expect.any(String)); expect(writeFile).toHaveBeenCalledWith('./src/monitoring/tracing.js', expect.any(String)); expect(writeFile).toHaveBeenCalledWith('./src/monitoring/health-check.js', expect.any(String)); expect(writeFile).toHaveBeenCalledWith('./src/monitoring/error-handler.js', expect.any(String)); expect(writeFile).toHaveBeenCalledWith('./docker-compose.monitoring.yml', expect.any(String)); expect(writeFile).toHaveBeenCalledWith('./k8s/monitoring/monitoring.yml', expect.any(String)); }); }); describe('Environment variables', () => { it('should pass environment variables to generators', async () => { const options = { ...mockOptions, type: 'docker', env: { NODE_ENV: 'production', API_KEY: 'secret', DATABASE_URL: 'postgres://localhost:5432/mydb' } }; await infrastructureCommand(options); const { DockerGenerator } = await import('../utils/deployment/docker-generator.js'); const mockGenerator = DockerGenerator.mock.instances[0]; expect(mockGenerator.generateDockerfile).toHaveBeenCalledWith( expect.objectContaining({ env: { NODE_ENV: 'production', API_KEY: 'secret', DATABASE_URL: 'postgres://localhost:5432/mydb' } }), expect.any(Object) ); }); }); describe('Error handling', () => { it('should throw CLIError when file writing fails', async () => { const { writeFile } = await import('../utils/fs.js'); vi.mocked(writeFile).mockRejectedValueOnce(new Error('Write failed')); await expect(infrastructureCommand(mockOptions)).rejects.toThrow(CLIError); }); it('should throw CLIError when directory creation fails', async () => { const { mkdir } = await import('../utils/fs.js'); vi.mocked(mkdir).mockRejectedValueOnce(new Error('Mkdir failed')); await expect(infrastructureCommand(mockOptions)).rejects.toThrow(CLIError); }); }); });