## Changes
- Updated jest.config.js to exclude utility test files from test execution
- Enhanced test-helpers with flexible auth context mocking
- Support for authenticated and unauthenticated test states
- Fixed landing page tests to use unauthenticated state
- Fixed navbar tests to handle multiple identical elements
- Fixed portfolio dashboard tests for status indicators
- Improved .gitignore with .env file exclusions
## Test Status
- Passing: 310/338 tests (92%)
- Failing: 28 tests (8%)
- Main issues: Multiple element matching, async validation
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
217 lines
8.1 KiB
TypeScript
217 lines
8.1 KiB
TypeScript
/**
|
|
* 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.yml', () => {
|
|
const kustomizePath = path.join(process.cwd(), 'deploy/k3s/prod/kustomization.yml')
|
|
expect(fs.existsSync(kustomizePath)).toBe(true)
|
|
})
|
|
})
|
|
})
|