@ordojs/cli
Version:
Command-line interface for OrdoJS framework
257 lines (215 loc) • 9.73 kB
text/typescript
/**
* @fileoverview Tests for the Docker generator
*/
import { beforeEach, describe, expect, it } from 'vitest';
import type { DeploymentConfig } from './adapter-interface.js';
import { DockerGenerator } from './docker-generator.js';
describe('DockerGenerator', () => {
let dockerGenerator: DockerGenerator;
let mockDeploymentConfig: DeploymentConfig;
beforeEach(() => {
dockerGenerator = new DockerGenerator();
mockDeploymentConfig = {
outputDir: 'dist',
isStatic: false,
includeServerFunctions: true,
env: {
NODE_ENV: 'production',
PORT: '3000'
}
};
});
describe('generateDockerfile', () => {
it('should generate multi-stage Dockerfile by default', () => {
const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig);
expect(dockerfile).toContain('FROM node:18-alpine AS base');
expect(dockerfile).toContain('FROM base AS builder');
expect(dockerfile).toContain('FROM node:18-alpine AS production');
expect(dockerfile).toContain('RUN pnpm install --frozen-lockfile');
expect(dockerfile).toContain('RUN pnpm run build');
expect(dockerfile).toContain('EXPOSE 3000');
expect(dockerfile).toContain('CMD ["node", "dist/server.js"]');
});
it('should generate simple Dockerfile when multiStage is false', () => {
const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig, {
multiStage: false
});
expect(dockerfile).toContain('FROM node:18-alpine');
expect(dockerfile).not.toContain('AS base');
expect(dockerfile).not.toContain('AS builder');
expect(dockerfile).not.toContain('AS production');
expect(dockerfile).toContain('RUN pnpm install --frozen-lockfile');
expect(dockerfile).toContain('RUN pnpm run build');
});
it('should include environment variables', () => {
const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig, {
environment: {
NODE_ENV: 'production',
API_KEY: 'secret'
}
});
expect(dockerfile).toContain('ENV NODE_ENV=production');
expect(dockerfile).toContain('ENV API_KEY=secret');
});
it('should include health check when enabled', () => {
const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig, {
healthCheck: true,
port: 3000
});
expect(dockerfile).toContain('HEALTHCHECK');
expect(dockerfile).toContain('curl -f http://localhost:3000/health');
});
it('should not include health check when disabled', () => {
const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig, {
healthCheck: false
});
expect(dockerfile).not.toContain('HEALTHCHECK');
});
it('should use custom Node.js version', () => {
const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig, {
nodeVersion: '20'
});
expect(dockerfile).toContain('FROM node:20-alpine');
});
it('should use custom port', () => {
const dockerfile = dockerGenerator.generateDockerfile(mockDeploymentConfig, {
port: 8080
});
expect(dockerfile).toContain('EXPOSE 8080');
});
});
describe('generateDockerCompose', () => {
it('should generate docker-compose.yml with basic configuration', () => {
const dockerCompose = dockerGenerator.generateDockerCompose(mockDeploymentConfig);
expect(dockerCompose).toContain('version: \'3.8\'');
expect(dockerCompose).toContain('ordojs-app:');
expect(dockerCompose).toContain('build:');
expect(dockerCompose).toContain('context: .');
expect(dockerCompose).toContain('dockerfile: Dockerfile');
expect(dockerCompose).toContain('ports:');
expect(dockerCompose).toContain('- "3000:3000"');
expect(dockerCompose).toContain('restart: unless-stopped');
});
it('should include environment variables', () => {
const dockerCompose = dockerGenerator.generateDockerCompose(mockDeploymentConfig, {
environment: {
NODE_ENV: 'production',
DATABASE_URL: 'postgres://localhost:5432/mydb'
}
});
expect(dockerCompose).toContain('environment:');
expect(dockerCompose).toContain('- NODE_ENV=production');
expect(dockerCompose).toContain('- DATABASE_URL=postgres://localhost:5432/mydb');
});
it('should include volumes when specified', () => {
const dockerCompose = dockerGenerator.generateDockerCompose(mockDeploymentConfig, {
volumes: ['./logs:/app/logs', './data:/app/data']
});
expect(dockerCompose).toContain('volumes:');
expect(dockerCompose).toContain('- ./logs:/app/logs');
expect(dockerCompose).toContain('- ./data:/app/data');
});
it('should include health check configuration', () => {
const dockerCompose = dockerGenerator.generateDockerCompose(mockDeploymentConfig, {
port: 3000
});
expect(dockerCompose).toContain('healthcheck:');
expect(dockerCompose).toContain('test: ["CMD", "curl", "-f", "http://localhost:3000/health"]');
expect(dockerCompose).toContain('interval: 30s');
expect(dockerCompose).toContain('timeout: 10s');
expect(dockerCompose).toContain('retries: 3');
});
});
describe('generateDockerignore', () => {
it('should generate comprehensive .dockerignore file', () => {
const dockerignore = dockerGenerator.generateDockerignore();
expect(dockerignore).toContain('node_modules');
expect(dockerignore).toContain('.git/');
expect(dockerignore).toContain('.env');
expect(dockerignore).toContain('dist/');
expect(dockerignore).toContain('*.log');
expect(dockerignore).toContain('coverage');
expect(dockerignore).toContain('.vscode/');
expect(dockerignore).toContain('*.test.js');
expect(dockerignore).toContain('*.test.ts');
});
});
describe('generateKubernetesManifests', () => {
it('should generate Kubernetes deployment manifest', () => {
const manifests = dockerGenerator.generateKubernetesManifests(mockDeploymentConfig);
expect(manifests.deployment).toContain('apiVersion: apps/v1');
expect(manifests.deployment).toContain('kind: Deployment');
expect(manifests.deployment).toContain('name: ordojs-app');
expect(manifests.deployment).toContain('replicas: 3');
expect(manifests.deployment).toContain('containerPort: 3000');
expect(manifests.deployment).toContain('livenessProbe:');
expect(manifests.deployment).toContain('readinessProbe:');
});
it('should generate Kubernetes service manifest', () => {
const manifests = dockerGenerator.generateKubernetesManifests(mockDeploymentConfig);
expect(manifests.service).toContain('apiVersion: v1');
expect(manifests.service).toContain('kind: Service');
expect(manifests.service).toContain('name: ordojs-app-service');
expect(manifests.service).toContain('type: ClusterIP');
expect(manifests.service).toContain('port: 80');
expect(manifests.service).toContain('targetPort: 3000');
});
it('should generate Kubernetes ingress manifest', () => {
const manifests = dockerGenerator.generateKubernetesManifests(mockDeploymentConfig);
expect(manifests.ingress).toContain('apiVersion: networking.k8s.io/v1');
expect(manifests.ingress).toContain('kind: Ingress');
expect(manifests.ingress).toContain('name: ordojs-app-ingress');
expect(manifests.ingress).toContain('host: ordojs-app.local');
});
it('should generate ConfigMap for non-secret environment variables', () => {
const manifests = dockerGenerator.generateKubernetesManifests(mockDeploymentConfig, {
environment: {
NODE_ENV: 'production',
API_URL: 'https://api.example.com',
DB_PASSWORD: 'secret'
}
});
expect(manifests.configMap).toContain('apiVersion: v1');
expect(manifests.configMap).toContain('kind: ConfigMap');
expect(manifests.configMap).toContain('NODE_ENV: "production"');
expect(manifests.configMap).toContain('API_URL: "https://api.example.com"');
expect(manifests.configMap).not.toContain('DB_PASSWORD');
});
it('should generate Secret for sensitive environment variables', () => {
const manifests = dockerGenerator.generateKubernetesManifests(mockDeploymentConfig, {
environment: {
NODE_ENV: 'production',
DB_PASSWORD: 'secret',
API_SECRET: 'very-secret'
}
});
expect(manifests.secret).toContain('apiVersion: v1');
expect(manifests.secret).toContain('kind: Secret');
expect(manifests.secret).toContain('type: Opaque');
expect(manifests.secret).toContain('DB_PASSWORD:');
expect(manifests.secret).toContain('API_SECRET:');
expect(manifests.secret).not.toContain('NODE_ENV');
});
it('should not generate Secret when no sensitive variables', () => {
const manifests = dockerGenerator.generateKubernetesManifests(mockDeploymentConfig, {
environment: {
NODE_ENV: 'production',
API_URL: 'https://api.example.com'
}
});
expect(manifests.secret).toBe('');
});
});
describe('error handling', () => {
it('should throw CLIError when Dockerfile generation fails', () => {
// Mock a scenario that would cause generation to fail
const invalidConfig = {
...mockDeploymentConfig,
outputDir: null as any
};
expect(() => {
dockerGenerator.generateDockerfile(invalidConfig);
}).toThrow();
});
});
});