From 11070fbf257fb78518b474c305283cf42eaa4d45 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Mon, 9 Feb 2026 10:13:32 -0500 Subject: [PATCH] Improve test infrastructure and organization - Add test-runner stage to Dockerfile for isolated test execution - Refactor Makefile test targets: separate unit and integration tests - Unit tests now run on host (fast, no containers required) - Integration tests run in containers matching production environment - Add dedicated 'tests' service to docker-compose.yml - Update test-integration target to use containerized test runner - Improve service health checks and wait conditions - Add test environment variables for consistent testing This change separates unit tests (fast, local) from integration tests (full environment, containerized) for better developer experience and more reliable CI/CD pipelines. --- Dockerfile | 23 +++++++++++++++++++ Makefile | 55 ++++++++++++++++------------------------------ docker-compose.yml | 42 +++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 36 deletions(-) diff --git a/Dockerfile b/Dockerfile index c588c97..8cc3509 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,6 +38,29 @@ RUN npm run build:ts # Build the application RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main ./cmd/server +# Test runner stage - includes Go runtime and test dependencies +# This stage is ONLY used for running tests, never deployed to production +FROM golang:1.25-alpine AS test-runner + +RUN apk --no-cache add ca-certificates curl + +WORKDIR /app + +# Copy everything from builder (includes compiled binary, source code, and dependencies) +COPY --from=builder /app ./ + +# Install kepubify for conversion tests +RUN wget -O /usr/bin/kepubify https://github.com/pgaskin/kepubify/releases/latest/download/kepubify-linux-64bit \ + && chmod +x /usr/bin/kepubify + +# Set test environment variables +ENV TEST_MODE=true +ENV RATE_LIMIT_ENABLED=false +ENV REQUESTS_PER_MINUTE=1000 + +# Default command for running tests +CMD ["go", "test", "./cmd/server/tests", "-v", "-timeout", "5m"] + # Final stage FROM alpine:latest diff --git a/Makefile b/Makefile index 7f747b6..1345b22 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help test test-integration test-all test-stop build build-force clean rebuild logs test-env-up test-env-down +.PHONY: help test test-integration test-all build build-force clean rebuild logs test-env-up test-env-down verify-guidelines verify-quick # Include .env file for environment variables (single source of truth) # Ignore if .env doesn't exist yet @@ -10,10 +10,9 @@ endif # Default target help: @echo "Available targets:" - @echo " make test - Run unit tests" - @echo " make test-integration - Build containers, start services, run integration tests" - @echo " make test-all - Run all tests" - @echo " make test-stop - Stop containers after testing" + @echo " make test - Run unit tests (from host, fast)" + @echo " make test-integration - Run integration tests (in containers, matches prod)" + @echo " make test-all - Run all tests (unit + integration)" @echo " make build - Build and start containers" @echo " make build-force - Force rebuild and start containers" @echo " make clean - Stop and remove containers" @@ -22,45 +21,33 @@ help: @echo " make test-env-up - Start containers with test mode enabled" @echo " make test-env-down - Stop test environment" -# Run unit tests +# Run unit tests locally (fast, no containers) test: - go test ./... -v -short + go test ./internal/... -v -short -# Run integration tests with test mode enabled (avoids rate limiting) +# Run integration tests in containers (matches production environment) test-integration: - @echo "Building containers..." - podman compose build + @echo "Building test containers..." + podman compose --profile tests build @echo "Starting application containers..." - podman compose up -d + podman compose up -d db app @echo "Waiting for services to be healthy..." - @echo "Waiting for database..." @until podman exec bookhoard_db pg_isready -U postgres > /dev/null 2>&1; do \ echo " Database not ready yet..."; \ sleep 2; \ done; \ echo " ✓ Database is ready" - @echo "Waiting for application..." - @sleep 3; \ - until curl -sf http://localhost:8765/health > /dev/null 2>&1; do \ + @until podman exec bookhoard curl -sf http://localhost:8765/health > /dev/null 2>&1; do \ echo " Application not ready yet..."; \ sleep 2; \ done; \ echo " ✓ Application is ready" @echo "" - @echo "Running integration tests from host against containerized database..." - DATABASE_HOST=localhost \ - DATABASE_PORT=5432 \ - DATABASE_USER=postgres \ - DATABASE_PASSWORD=$(DBPASS) \ - DATABASE_NAME=bookhoard \ - JWT_SECRET=$(JWT_SECRET) \ - TEST_MODE=true \ - RATE_LIMIT_ENABLED=false \ - TEST_UPLOAD_PATH=./uploads \ - go test ./cmd/server/tests -v -timeout 5m + @echo "Running integration tests in container..." + podman compose --profile tests run --rm tests @echo "" @echo "✅ Integration tests completed!" - @echo "📝 Containers are still running. Use 'podman compose logs' to view logs or 'podman compose down' to stop." + @echo "📝 Containers are still running. Use 'make logs' to view logs or 'make clean' to stop." # Run all tests test-all: test test-integration @@ -85,24 +72,20 @@ rebuild: clean build logs: podman compose logs -f -# Start containers with test mode enabled for integration testing +# Start containers with test mode enabled for manual testing test-env-up: @echo "Starting containers with test mode enabled..." TEST_MODE=true RATE_LIMIT_ENABLED=false REQUESTS_PER_MINUTE=1000 podman compose up --build -d @echo "Waiting for services to be ready..." - sleep 10 - @echo "Test environment is ready!" + @until podman exec bookhoard_db pg_isready -U postgres > /dev/null 2>&1; do sleep 1; done + @until podman exec bookhoard curl -sf http://localhost:8765/health > /dev/null 2>&1; do sleep 1; done + @echo "✓ Test environment is ready!" + @echo "Application available at http://localhost:8765" # Stop test environment test-env-down: podman compose down -v -# Stop containers after testing -test-stop: - @echo "Stopping containers..." - podman compose down - @echo "✓ Containers stopped" - # Verify project guidelines compliance verify-guidelines: @echo "Running comprehensive project guidelines verification..." diff --git a/docker-compose.yml b/docker-compose.yml index bb64464..7e4dbe0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -65,6 +65,48 @@ services: retries: 3 start_period: 10s + # Integration Tests - runs against containerized app and db + tests: + build: + context: . + dockerfile: ./Dockerfile + target: test-runner + container_name: bookhoard_tests + environment: + # Database Configuration + DATABASE_HOST: db + DATABASE_PORT: 5432 + DATABASE_USER: postgres + DATABASE_PASSWORD: ${DBPASS} + DATABASE_NAME: bookhoard + + # Application Configuration + JWT_SECRET: ${JWT_SECRET} + SERVER_PORT: 8765 + + # Test Configuration + TEST_MODE: "true" + RATE_LIMIT_ENABLED: "false" + REQUESTS_PER_MINUTE: 1000 + + # Conversion Service Configuration + BOOKHOARD_CONVERSION_CACHE_DIR: /app/cache/kepub + BOOKHOARD_CONVERSION_TOOL: /usr/bin/kepubify + BOOKHOARD_CONVERSION_CACHE_TTL: 24h + + # Test upload path (inside container) + TEST_UPLOAD_PATH: /app/uploads + depends_on: + db: + condition: service_healthy + app: + condition: service_healthy + volumes: + - ./uploads:/app/uploads + - bookhoard_conversion_cache:/app/cache/kepub + profiles: + - tests + # Named Volumes volumes: postgres_data: