add optinal apps
This commit is contained in:
@@ -1,5 +1,298 @@
|
||||
import { AppRuntime } from '../common/enums';
|
||||
|
||||
/**
|
||||
* Tests for build service — Dockerfile generation for all runtimes
|
||||
*/
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Go Dockerfile tests
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
describe('Go Dockerfile generation', () => {
|
||||
function goDockerfile(app: { runtimeVersion?: string; port?: number }): string {
|
||||
const goVersion = app.runtimeVersion || '1.22';
|
||||
const port = app.port || 8080;
|
||||
return `FROM golang:${goVersion}-alpine AS builder
|
||||
WORKDIR /app
|
||||
RUN apk add --no-cache git
|
||||
COPY go.mod go.sum* ./
|
||||
RUN go mod download || true
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags="-w -s" -o main .
|
||||
|
||||
FROM alpine:3.19
|
||||
WORKDIR /app
|
||||
RUN apk --no-cache add ca-certificates tzdata
|
||||
RUN addgroup -g 1001 -S appgroup && adduser -S appuser -u 1001 -G appgroup
|
||||
COPY --from=builder /app/main .
|
||||
RUN mkdir -p /app/data && chown -R appuser:appgroup /app
|
||||
USER appuser
|
||||
ENV PORT=${port}
|
||||
EXPOSE ${port}
|
||||
HEALTHCHECK --interval=30s --timeout=3s CMD wget --no-verbose --tries=1 --spider http://localhost:${port}/health || exit 1
|
||||
CMD ["./main"]
|
||||
`;
|
||||
}
|
||||
|
||||
it('should use correct Go version', () => {
|
||||
const df = goDockerfile({ runtimeVersion: '1.21' });
|
||||
expect(df).toContain('FROM golang:1.21-alpine');
|
||||
});
|
||||
|
||||
it('should default to Go 1.22', () => {
|
||||
const df = goDockerfile({});
|
||||
expect(df).toContain('FROM golang:1.22-alpine');
|
||||
});
|
||||
|
||||
it('should build static binary with CGO_ENABLED=0', () => {
|
||||
const df = goDockerfile({});
|
||||
expect(df).toContain('CGO_ENABLED=0');
|
||||
});
|
||||
|
||||
it('should use multi-stage build for smaller image', () => {
|
||||
const df = goDockerfile({});
|
||||
expect(df).toContain('AS builder');
|
||||
expect(df).toContain('FROM alpine:3.19');
|
||||
});
|
||||
|
||||
it('should include health check', () => {
|
||||
const df = goDockerfile({ port: 8080 });
|
||||
expect(df).toContain('HEALTHCHECK');
|
||||
expect(df).toContain('http://localhost:8080/health');
|
||||
});
|
||||
|
||||
it('should create data directory for persistent storage', () => {
|
||||
const df = goDockerfile({});
|
||||
expect(df).toContain('mkdir -p /app/data');
|
||||
});
|
||||
|
||||
it('should run as non-root user', () => {
|
||||
const df = goDockerfile({});
|
||||
expect(df).toContain('USER appuser');
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Python Dockerfile tests
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
describe('Python Dockerfile generation', () => {
|
||||
function pythonDockerfile(app: { runtimeVersion?: string; port?: number }): string {
|
||||
const pythonVersion = app.runtimeVersion || '3.12';
|
||||
const port = app.port || 8000;
|
||||
return `FROM python:${pythonVersion}-slim AS builder
|
||||
WORKDIR /app
|
||||
RUN apt-get update && apt-get install -y build-essential libpq-dev
|
||||
COPY requirements.txt* ./
|
||||
RUN pip install --no-cache-dir --user -r requirements.txt 2>/dev/null || pip install --no-cache-dir --user flask gunicorn
|
||||
|
||||
FROM python:${pythonVersion}-slim
|
||||
WORKDIR /app
|
||||
RUN groupadd -g 1001 appgroup && useradd -r -u 1001 -g appgroup appuser
|
||||
COPY --from=builder /root/.local /home/appuser/.local
|
||||
COPY . .
|
||||
RUN mkdir -p /app/data && chown -R appuser:appgroup /app
|
||||
USER appuser
|
||||
ENV PATH=/home/appuser/.local/bin:$PATH
|
||||
ENV PORT=${port}
|
||||
EXPOSE ${port}
|
||||
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost:${port}/health || exit 1
|
||||
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:${port}", "app:app"]
|
||||
`;
|
||||
}
|
||||
|
||||
it('should use correct Python version', () => {
|
||||
const df = pythonDockerfile({ runtimeVersion: '3.11' });
|
||||
expect(df).toContain('FROM python:3.11-slim');
|
||||
});
|
||||
|
||||
it('should default to Python 3.12', () => {
|
||||
const df = pythonDockerfile({});
|
||||
expect(df).toContain('FROM python:3.12-slim');
|
||||
});
|
||||
|
||||
it('should use multi-stage build', () => {
|
||||
const df = pythonDockerfile({});
|
||||
expect(df).toContain('AS builder');
|
||||
});
|
||||
|
||||
it('should install from requirements.txt', () => {
|
||||
const df = pythonDockerfile({});
|
||||
expect(df).toContain('requirements.txt');
|
||||
});
|
||||
|
||||
it('should include health check', () => {
|
||||
const df = pythonDockerfile({ port: 8000 });
|
||||
expect(df).toContain('HEALTHCHECK');
|
||||
expect(df).toContain('http://localhost:8000/health');
|
||||
});
|
||||
|
||||
it('should run as non-root user', () => {
|
||||
const df = pythonDockerfile({});
|
||||
expect(df).toContain('USER appuser');
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Django Dockerfile tests
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
describe('Django Dockerfile generation', () => {
|
||||
function djangoDockerfile(app: { runtimeVersion?: string; port?: number }): string {
|
||||
const pythonVersion = app.runtimeVersion || '3.12';
|
||||
const port = app.port || 8000;
|
||||
return `FROM python:${pythonVersion}-slim AS builder
|
||||
WORKDIR /app
|
||||
RUN apt-get update && apt-get install -y build-essential libpq-dev
|
||||
COPY requirements.txt* ./
|
||||
RUN pip install --no-cache-dir --user -r requirements.txt 2>/dev/null || pip install --no-cache-dir --user django gunicorn
|
||||
|
||||
FROM python:${pythonVersion}-slim
|
||||
WORKDIR /app
|
||||
COPY --from=builder /root/.local /home/appuser/.local
|
||||
COPY . .
|
||||
RUN mkdir -p /app/staticfiles /app/media /app/data
|
||||
USER appuser
|
||||
ENV PORT=${port}
|
||||
ENV DJANGO_SETTINGS_MODULE=config.settings
|
||||
EXPOSE ${port}
|
||||
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost:${port}/health/ || exit 1
|
||||
CMD ["sh", "-c", "python manage.py migrate --noinput && gunicorn config.wsgi:application --bind 0.0.0.0:${port}"]
|
||||
`;
|
||||
}
|
||||
|
||||
it('should use correct Python version', () => {
|
||||
const df = djangoDockerfile({ runtimeVersion: '3.10' });
|
||||
expect(df).toContain('FROM python:3.10-slim');
|
||||
});
|
||||
|
||||
it('should set DJANGO_SETTINGS_MODULE', () => {
|
||||
const df = djangoDockerfile({});
|
||||
expect(df).toContain('DJANGO_SETTINGS_MODULE');
|
||||
});
|
||||
|
||||
it('should create staticfiles and media directories', () => {
|
||||
const df = djangoDockerfile({});
|
||||
expect(df).toContain('/app/staticfiles');
|
||||
expect(df).toContain('/app/media');
|
||||
});
|
||||
|
||||
it('should run migrations on startup', () => {
|
||||
const df = djangoDockerfile({});
|
||||
expect(df).toContain('migrate');
|
||||
});
|
||||
|
||||
it('should use gunicorn for production', () => {
|
||||
const df = djangoDockerfile({});
|
||||
expect(df).toContain('gunicorn');
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// .NET Dockerfile tests
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
describe('.NET Dockerfile generation', () => {
|
||||
function dotnetDockerfile(app: { runtimeVersion?: string; port?: number }): string {
|
||||
const dotnetVersion = app.runtimeVersion || '8.0';
|
||||
const port = app.port || 5000;
|
||||
return `FROM mcr.microsoft.com/dotnet/sdk:${dotnetVersion} AS build
|
||||
WORKDIR /src
|
||||
COPY *.csproj ./
|
||||
RUN dotnet restore || true
|
||||
COPY . .
|
||||
RUN dotnet publish -c Release -o /app/publish
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:${dotnetVersion}
|
||||
WORKDIR /app
|
||||
COPY --from=build /app/publish .
|
||||
RUN mkdir -p /app/data
|
||||
USER appuser
|
||||
ENV ASPNETCORE_URLS=http://+:${port}
|
||||
ENV ASPNETCORE_ENVIRONMENT=Production
|
||||
EXPOSE ${port}
|
||||
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost:${port}/health || exit 1
|
||||
CMD ["dotnet", "app.dll"]
|
||||
`;
|
||||
}
|
||||
|
||||
it('should use correct .NET version', () => {
|
||||
const df = dotnetDockerfile({ runtimeVersion: '7.0' });
|
||||
expect(df).toContain('dotnet/sdk:7.0');
|
||||
expect(df).toContain('dotnet/aspnet:7.0');
|
||||
});
|
||||
|
||||
it('should default to .NET 8.0', () => {
|
||||
const df = dotnetDockerfile({});
|
||||
expect(df).toContain('dotnet/sdk:8.0');
|
||||
});
|
||||
|
||||
it('should use multi-stage build', () => {
|
||||
const df = dotnetDockerfile({});
|
||||
expect(df).toContain('AS build');
|
||||
expect(df).toContain('dotnet/aspnet');
|
||||
});
|
||||
|
||||
it('should publish in Release mode', () => {
|
||||
const df = dotnetDockerfile({});
|
||||
expect(df).toContain('-c Release');
|
||||
});
|
||||
|
||||
it('should set ASPNETCORE_ENVIRONMENT to Production', () => {
|
||||
const df = dotnetDockerfile({});
|
||||
expect(df).toContain('ASPNETCORE_ENVIRONMENT=Production');
|
||||
});
|
||||
|
||||
it('should configure ASPNETCORE_URLS for correct port', () => {
|
||||
const df = dotnetDockerfile({ port: 8080 });
|
||||
expect(df).toContain('ASPNETCORE_URLS=http://+:8080');
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// PHP Dockerfile tests
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
describe('PHP Dockerfile generation', () => {
|
||||
function phpDockerfile(app: { phpVersion?: string; port?: number }): string {
|
||||
const phpVersion = app.phpVersion || '8.3';
|
||||
const port = app.port || 80;
|
||||
return `FROM php:${phpVersion}-fpm-alpine
|
||||
RUN apk add --no-cache nginx supervisor curl
|
||||
RUN docker-php-ext-install pdo pdo_mysql opcache
|
||||
WORKDIR /var/www/html
|
||||
COPY . .
|
||||
RUN mkdir -p /var/www/html/uploads /var/www/html/data
|
||||
RUN chown -R www-data:www-data /var/www/html
|
||||
EXPOSE ${port}
|
||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
|
||||
`;
|
||||
}
|
||||
|
||||
it('should use correct PHP version', () => {
|
||||
const df = phpDockerfile({ phpVersion: '8.2' });
|
||||
expect(df).toContain('FROM php:8.2-fpm-alpine');
|
||||
});
|
||||
|
||||
it('should default to PHP 8.3', () => {
|
||||
const df = phpDockerfile({});
|
||||
expect(df).toContain('FROM php:8.3-fpm-alpine');
|
||||
});
|
||||
|
||||
it('should use FPM with nginx via supervisord', () => {
|
||||
const df = phpDockerfile({});
|
||||
expect(df).toContain('supervisord');
|
||||
expect(df).toContain('nginx');
|
||||
});
|
||||
|
||||
it('should install common PHP extensions', () => {
|
||||
const df = phpDockerfile({});
|
||||
expect(df).toContain('pdo');
|
||||
expect(df).toContain('opcache');
|
||||
});
|
||||
|
||||
it('should create upload and data directories', () => {
|
||||
const df = phpDockerfile({});
|
||||
expect(df).toContain('/var/www/html/uploads');
|
||||
expect(df).toContain('/var/www/html/data');
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests for the WordPress build flow — specifically:
|
||||
* 1. Helper pod PVC race condition (must wait for termination)
|
||||
|
||||
Reference in New Issue
Block a user