/** * Deployment & Infrastructure Tests * Tests for Docker, CI/CD, and deployment configuration */ import fs from 'fs' import path from 'path' describe('Deployment Configuration', () => { describe('Dockerfile', () => { it('should have valid Dockerfile', () => { const dockerfilePath = path.join(process.cwd(), 'Dockerfile') expect(fs.existsSync(dockerfilePath)).toBe(true) }) it('should use Alpine base image', () => { const dockerfilePath = path.join(process.cwd(), 'Dockerfile') const content = fs.readFileSync(dockerfilePath, 'utf-8') expect(content).toContain('alpine') }) it('should have health check configured', () => { const dockerfilePath = path.join(process.cwd(), 'Dockerfile') const content = fs.readFileSync(dockerfilePath, 'utf-8') expect(content).toContain('HEALTHCHECK') }) it('should run as non-root user', () => { const dockerfilePath = path.join(process.cwd(), 'Dockerfile') const content = fs.readFileSync(dockerfilePath, 'utf-8') expect(content).toContain('USER nextjs') expect(content).toContain('addgroup') expect(content).toContain('adduser') }) it('should expose port 3000', () => { const dockerfilePath = path.join(process.cwd(), 'Dockerfile') const content = fs.readFileSync(dockerfilePath, 'utf-8') expect(content).toContain('EXPOSE 3000') }) it('should use multi-stage build', () => { const dockerfilePath = path.join(process.cwd(), 'Dockerfile') const content = fs.readFileSync(dockerfilePath, 'utf-8') expect(content).toContain('FROM node:20-alpine AS builder') expect(content).toContain('FROM node:20-alpine') expect(content.match(/FROM node:20-alpine/g)!.length).toBe(2) }) }) describe('Environment Configuration', () => { it('should have .env.production file', () => { const envPath = path.join(process.cwd(), '.env.production') expect(fs.existsSync(envPath)).toBe(true) }) it('should have .env.test file', () => { const envPath = path.join(process.cwd(), '.env.test') expect(fs.existsSync(envPath)).toBe(true) }) it('should have production API URL configured', () => { const envPath = path.join(process.cwd(), '.env.production') const content = fs.readFileSync(envPath, 'utf-8') expect(content).toContain('NEXT_PUBLIC_API_URL') expect(content).toContain('NODE_ENV=production') }) it('should not expose sensitive data in .env files', () => { const envPath = path.join(process.cwd(), '.env.production') const content = fs.readFileSync(envPath, 'utf-8') expect(content).not.toContain('SECRET') expect(content).not.toContain('PRIVATE_KEY') expect(content).not.toContain('PASSWORD') }) }) describe('CI/CD Workflows', () => { it('should have test and validate workflow', () => { const workflowPath = path.join(process.cwd(), '.gitea/workflows/test-and-validate.yml') expect(fs.existsSync(workflowPath)).toBe(true) }) it('should have production deployment workflow', () => { const workflowPath = path.join(process.cwd(), '.gitea/workflows/deploy-prod.yml') expect(fs.existsSync(workflowPath)).toBe(true) }) it('test workflow should run tests', () => { const workflowPath = path.join(process.cwd(), '.gitea/workflows/test-and-validate.yml') const content = fs.readFileSync(workflowPath, 'utf-8') expect(content).toContain('npm run test:ci') expect(content).toContain('npm run build') }) it('test workflow should check coverage', () => { const workflowPath = path.join(process.cwd(), '.gitea/workflows/test-and-validate.yml') const content = fs.readFileSync(workflowPath, 'utf-8') expect(content).toContain('coverage') expect(content).toContain('90') }) it('production workflow should deploy with kubectl', () => { const workflowPath = path.join(process.cwd(), '.gitea/workflows/deploy-prod.yml') const content = fs.readFileSync(workflowPath, 'utf-8') expect(content).toContain('kubectl') expect(content).toContain('Deploy to Production') }) }) describe('Build Configuration', () => { it('should have next.config.js', () => { const nextConfigPath = path.join(process.cwd(), 'next.config.js') expect(fs.existsSync(nextConfigPath)).toBe(true) }) it('should have tsconfig.json', () => { const tsconfigPath = path.join(process.cwd(), 'tsconfig.json') expect(fs.existsSync(tsconfigPath)).toBe(true) }) it('should have jest.config.js', () => { const jestConfigPath = path.join(process.cwd(), 'jest.config.js') expect(fs.existsSync(jestConfigPath)).toBe(true) }) it('should have package.json with build script', () => { const packageJsonPath = path.join(process.cwd(), 'package.json') const content = fs.readFileSync(packageJsonPath, 'utf-8') const packageJson = JSON.parse(content) expect(packageJson.scripts).toHaveProperty('build') expect(packageJson.scripts.build).toContain('next build') }) }) describe('Documentation', () => { it('should have deployment guide', () => { const docPath = path.join(process.cwd(), 'docs/DEPLOYMENT.md') expect(fs.existsSync(docPath)).toBe(true) }) it('should have README.md', () => { const readmePath = path.join(process.cwd(), 'README.md') expect(fs.existsSync(readmePath)).toBe(true) }) it('deployment guide should include troubleshooting', () => { const docPath = path.join(process.cwd(), 'docs/DEPLOYMENT.md') const content = fs.readFileSync(docPath, 'utf-8') expect(content).toContain('Troubleshooting') expect(content).toContain('Health Check') }) }) describe('Security Configuration', () => { it('should have .dockerignore file', () => { const dockerignorePath = path.join(process.cwd(), '.dockerignore') expect(fs.existsSync(dockerignorePath)).toBe(true) }) it('.dockerignore should exclude node_modules', () => { const dockerignorePath = path.join(process.cwd(), '.dockerignore') const content = fs.readFileSync(dockerignorePath, 'utf-8') expect(content).toContain('node_modules') }) it('should have .gitignore file', () => { const gitignorePath = path.join(process.cwd(), '.gitignore') expect(fs.existsSync(gitignorePath)).toBe(true) }) it('.gitignore should exclude .env files', () => { const gitignorePath = path.join(process.cwd(), '.gitignore') const content = fs.readFileSync(gitignorePath, 'utf-8') expect(content).toContain('.env') }) }) describe('Build Optimization', () => { it('package.json should have build optimization scripts', () => { const packageJsonPath = path.join(process.cwd(), 'package.json') const content = fs.readFileSync(packageJsonPath, 'utf-8') const packageJson = JSON.parse(content) expect(packageJson.scripts).toHaveProperty('build') expect(packageJson.scripts).toHaveProperty('start') expect(packageJson.scripts).toHaveProperty('dev') }) it('should have proper npm dependencies', () => { const packageJsonPath = path.join(process.cwd(), 'package.json') const content = fs.readFileSync(packageJsonPath, 'utf-8') const packageJson = JSON.parse(content) // Production dependencies expect(packageJson.dependencies).toHaveProperty('next') expect(packageJson.dependencies).toHaveProperty('react') expect(packageJson.dependencies).toHaveProperty('react-dom') // Dev dependencies expect(packageJson.devDependencies).toHaveProperty('typescript') expect(packageJson.devDependencies).toHaveProperty('jest') }) }) describe('Kubernetes Configuration', () => { it('should have k3s deployment manifests', () => { const k3sDir = path.join(process.cwd(), 'deploy/k3s/prod') expect(fs.existsSync(k3sDir)).toBe(true) }) it('should have kustomization.yaml', () => { const kustomizePath = path.join(process.cwd(), 'deploy/k3s/prod/kustomization.yaml') expect(fs.existsSync(kustomizePath)).toBe(true) }) }) })