Integration Tests #319
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Integration Tests | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| schedule: | |
| # Run integration tests nightly at 3 AM UTC to catch integration issues early | |
| - cron: "0 3 * * *" | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_PREFIX: ghcr.io/${{ github.repository_owner }}/nexuslims-test | |
| jobs: | |
| integration-test: | |
| name: Integration Tests (Python ${{ matrix.python-version }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.11", "3.12"] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Determine image tag | |
| id: image-tag | |
| run: | | |
| # Match the tag generation from build-test-images.yml | |
| # For branches: use branch name | |
| # For PRs: use pr-<number> | |
| # For other: use sha | |
| if [[ "${{ github.ref }}" == refs/heads/* ]]; then | |
| TAG="${{ github.ref_name }}" | |
| elif [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
| TAG="pr-${{ github.event.pull_request.number }}" | |
| else | |
| TAG="sha-${{ github.sha }}" | |
| fi | |
| # Replace / with - for valid Docker tags | |
| TAG="${TAG//\//-}" | |
| echo "tag=${TAG}" >> $GITHUB_OUTPUT | |
| echo "Using image tag: ${TAG}" | |
| - name: Pull pre-built test images | |
| continue-on-error: true | |
| run: | | |
| docker pull ${{ env.IMAGE_PREFIX }}-nemo:${{ steps.image-tag.outputs.tag }} || echo "Tagged NEMO image not found" | |
| docker pull ${{ env.IMAGE_PREFIX }}-cdcs:${{ steps.image-tag.outputs.tag }} || echo "Tagged CDCS image not found" | |
| docker pull ${{ env.IMAGE_PREFIX }}-elabftw:${{ steps.image-tag.outputs.tag }} || echo "Tagged eLabFTW image not found" | |
| - name: Tag images for local use | |
| continue-on-error: true | |
| run: | | |
| docker tag ${{ env.IMAGE_PREFIX }}-nemo:${{ steps.image-tag.outputs.tag }} nexuslims-test-nemo:latest 2>/dev/null || true | |
| docker tag ${{ env.IMAGE_PREFIX }}-cdcs:${{ steps.image-tag.outputs.tag }} nexuslims-test-cdcs:latest 2>/dev/null || true | |
| docker tag ${{ env.IMAGE_PREFIX }}-elabftw:${{ steps.image-tag.outputs.tag }} nexuslims-test-elabftw:latest 2>/dev/null || true | |
| - name: Start Docker services | |
| working-directory: tests/integration/docker | |
| env: | |
| REGISTRY: ${{ env.REGISTRY }} | |
| IMAGE_OWNER: ${{ github.repository_owner }} | |
| IMAGE_TAG: ${{ steps.image-tag.outputs.tag }} | |
| run: | | |
| # Try to use pre-built images with the PR/branch tag from build-test-images.yml | |
| if docker image inspect nexuslims-test-nemo:latest >/dev/null 2>&1 && \ | |
| docker image inspect nexuslims-test-cdcs:latest >/dev/null 2>&1 && \ | |
| docker image inspect nexuslims-test-elabftw:latest >/dev/null 2>&1; then | |
| echo "Using pre-built images from registry (tag: ${{ steps.image-tag.outputs.tag }})" | |
| docker compose -f docker-compose.ci.yml up -d | |
| else | |
| echo "Pre-built images not available - building locally" | |
| docker compose up -d | |
| fi | |
| - name: Wait for services to be healthy | |
| run: | | |
| echo "Waiting for NEMO to be ready..." | |
| timeout 180 bash -c 'until curl -f http://localhost:48000/ > /dev/null 2>&1; do echo -n "."; sleep 3; done' | |
| echo "✓ NEMO is ready" | |
| echo "Waiting for CDCS to be ready..." | |
| timeout 240 bash -c 'until curl -f http://localhost:48080/ > /dev/null 2>&1; do echo -n "."; sleep 5; done' | |
| echo "✓ CDCS is ready" | |
| echo "Waiting for eLabFTW to be ready..." | |
| timeout 240 bash -c 'until curl -fk https://localhost:48148/login.php > /dev/null 2>&1; do echo -n "."; sleep 5; done' | |
| echo "✓ eLabFTW is ready" | |
| echo "Waiting for MailPit to be ready..." | |
| timeout 60 bash -c 'until curl -f http://localhost:48025/ > /dev/null 2>&1; do echo -n "."; sleep 2; done' | |
| echo "✓ MailPit is ready" | |
| - name: Show Docker service status | |
| if: always() | |
| working-directory: tests/integration/docker | |
| run: docker compose ps | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y findutils | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@v4 | |
| with: | |
| enable-cache: true | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install dependencies | |
| run: uv sync | |
| - name: Run all tests (unit + integration) | |
| run: | | |
| uv run pytest tests/ \ | |
| -v \ | |
| --override-ini=addopts= \ | |
| --mpl \ | |
| --mpl-baseline-path=tests/unit/files/figs \ | |
| --cov=nexusLIMS \ | |
| --cov-report html:tests/coverage-combined \ | |
| --cov-report term-missing \ | |
| --cov-report=xml:coverage-combined.xml \ | |
| --tb=short \ | |
| --maxfail=5 | |
| env: | |
| # Integration test environment variables | |
| PYTEST_TIMEOUT: 600 | |
| - name: Upload combined test coverage | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-combined-${{ matrix.python-version }} | |
| path: | | |
| .coverage | |
| coverage-combined.xml | |
| include-hidden-files: true | |
| - name: Upload HTML coverage report | |
| if: matrix.python-version == '3.11' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-combined-html | |
| path: tests/coverage-combined | |
| - name: Collect Docker container logs | |
| if: always() | |
| working-directory: tests/integration/docker | |
| run: | | |
| mkdir -p ../../docker-logs | |
| # Debug: Show what containers exist | |
| echo "=== Running Containers ===" | |
| docker ps -a --filter "name=nexuslims-test" || true | |
| echo "" | |
| # Determine which compose file was used and collect logs | |
| if docker image inspect nexuslims-test-nemo:latest >/dev/null 2>&1 && \ | |
| docker image inspect nexuslims-test-cdcs:latest >/dev/null 2>&1 && \ | |
| docker image inspect nexuslims-test-elabftw:latest >/dev/null 2>&1; then | |
| COMPOSE_FILE="docker-compose.ci.yml" | |
| else | |
| COMPOSE_FILE="docker-compose.yml" | |
| fi | |
| echo "Collecting logs from compose stack (using $COMPOSE_FILE)..." | |
| # Collect full compose logs (try both approaches) | |
| docker compose -f "$COMPOSE_FILE" logs --no-color > ../../docker-logs/docker-compose.log 2>&1 || true | |
| # Also try collecting by container name directly as fallback | |
| for container in nexuslims-test-nemo nexuslims-test-cdcs nexuslims-test-cdcs-mongo nexuslims-test-cdcs-postgres nexuslims-test-cdcs-redis nexuslims-test-elabftw nexuslims-test-elabftw-mysql nexuslims-test-mailpit nexuslims-test-caddy-proxy; do | |
| docker logs "$container" > "../../docker-logs/${container##*-}.log" 2>&1 || true | |
| done | |
| # Display summary to action logs | |
| echo "=== Docker Logs Summary ===" | |
| du -sh ../../docker-logs/* 2>/dev/null || echo "No logs collected" | |
| echo "" | |
| echo "=== Log Files Created ===" | |
| ls -lh ../../docker-logs/ 2>/dev/null || echo "No log directory" | |
| - name: Display Docker logs on failure | |
| if: failure() | |
| run: | | |
| echo "=== Docker Compose Full Log ===" | |
| cat docker-logs/docker-compose.log 2>/dev/null || echo "No compose log available" | |
| echo "" | |
| echo "=== NEMO Log ===" | |
| cat docker-logs/nemo.log 2>/dev/null || echo "No NEMO log available" | |
| echo "" | |
| echo "=== CDCS Log ===" | |
| cat docker-logs/cdcs.log 2>/dev/null || echo "No CDCS log available" | |
| echo "" | |
| echo "=== MongoDB Log ===" | |
| cat docker-logs/mongo.log 2>/dev/null || echo "No MongoDB log available" | |
| echo "" | |
| echo "=== PostgreSQL Log ===" | |
| cat docker-logs/postgres.log 2>/dev/null || echo "No PostgreSQL log available" | |
| echo "" | |
| echo "=== Redis Log ===" | |
| cat docker-logs/redis.log 2>/dev/null || echo "No Redis log available" | |
| echo "" | |
| echo "=== eLabFTW Log ===" | |
| cat docker-logs/elabftw.log 2>/dev/null || echo "No eLabFTW log available" | |
| echo "" | |
| echo "=== eLabFTW MySQL Log ===" | |
| cat docker-logs/mysql.log 2>/dev/null || echo "No eLabFTW MySQL log available" | |
| echo "" | |
| echo "=== Mailpit Log ===" | |
| cat docker-logs/mailpit.log 2>/dev/null || echo "No Mailpit log available" | |
| echo "" | |
| echo "=== Caddy Proxy Log ===" | |
| cat docker-logs/proxy.log 2>/dev/null || echo "No Caddy Proxy log available" | |
| - name: Upload Docker logs artifact | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: docker-logs-${{ matrix.python-version }} | |
| path: tests/docker-logs/ | |
| if-no-files-found: ignore | |
| - name: Cleanup Docker services | |
| if: always() | |
| working-directory: tests/integration/docker | |
| run: | | |
| docker compose down -v --remove-orphans || true | |
| upload-coverage: | |
| name: Upload Combined Coverage to Codecov | |
| needs: integration-test | |
| runs-on: ubuntu-latest | |
| if: always() | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download all coverage artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: coverage-combined-* | |
| path: coverage-reports | |
| - name: Upload combined coverage to Codecov | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: ./coverage-reports/**/*.xml | |
| flags: combined | |
| fail_ci_if_error: false |