Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/tests/integration/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Integration test package for the MEP backend."""
33 changes: 33 additions & 0 deletions backend/tests/integration/test_smoke.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Integration smoke test — verifies the API boots and answers on /api/health.

This is the minimum bar for any deploy: if this test fails, nothing else
matters. It uses the shared conftest fixtures (SQLite-backed TestClient), so
it runs in CI without a PostgreSQL service, yet exercises the full FastAPI
stack: routing, dependency injection, and the health service.
"""
import pytest


@pytest.mark.integration
def test_health_endpoint_returns_200(client):
"""GET /api/health must return HTTP 200 OK."""
response = client.get("/api/health")
assert response.status_code == 200


@pytest.mark.integration
def test_health_endpoint_reports_expected_shape(client):
"""The health payload must expose status / version / environment / database."""
body = client.get("/api/health").json()
assert body["status"] == "healthy"
assert "version" in body
assert "environment" in body
assert body["database"] in ("connected", "disconnected")


@pytest.mark.integration
def test_root_endpoint_returns_200(client):
"""GET / must return HTTP 200 OK and identify the API."""
response = client.get("/")
assert response.status_code == 200
assert response.json()["message"] == "MEP API is running"
19 changes: 18 additions & 1 deletion docs/ci/quality-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ on:
pull_request:

jobs:
# Guard rail: fail fast if any workflow step references a file or directory
# that no longer exists (e.g. after a repository restructure).
validation:
name: validation (workflow path references)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Verify workflow path references
run: python3 tools/verify_workflow_paths.py --verbose

# Fast, deterministic checks — no database, no network beyond pip.
free-tier:
name: free-tier (unit, lint, health, evals-p)
Expand Down Expand Up @@ -89,7 +106,7 @@ jobs:
EOF

# env_*.sql scripts do not create their databases — deploy_all.sh does that
# before invoking them (see build/te_core_schema.sql:41). Mirror it here.
# before invoking them (see backend/migration/build/te_core_schema.sql:41). Mirror it here.
- name: Create environment databases
run: |
for db in te_mgmt_dev te_mgmt_test; do
Expand Down
12 changes: 6 additions & 6 deletions preflight.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,16 @@ Test-Check 'psql on PATH' {
Write-Host ""
Write-Host "Project files" -ForegroundColor Cyan

Test-Check 'in project root (build/, tests/, evals/ exist)' {
(Test-Path build) -and (Test-Path tests) -and (Test-Path evals)
Test-Check 'in project root (backend/migration present)' {
(Test-Path backend/migration/build) -and (Test-Path backend/migration/tests) -and (Test-Path backend/migration/evals)
} -FixHint 'cd into the PostgreDataMigrationApp folder before running this script'

Test-Check 'evals/runner.py exists' {
Test-Path 'evals\runner.py'
Test-Check 'backend/migration/evals/runner.py exists' {
Test-Path 'backend\migration\evals\runner.py'
}

Test-Check 'build/deploy_all.sh exists' {
Test-Path 'build\deploy_all.sh'
Test-Check 'backend/migration/build/deploy_all.sh exists' {
Test-Path 'backend\migration\build\deploy_all.sh'
}

# ----- Git state -----
Expand Down
8 changes: 4 additions & 4 deletions preflight.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ check 'psql on PATH' opt 'Add PG client to PATH. brew install pos
# ----- Project files -----
echo
echo "${C}Project files${X}"
check 'in project root (build/ tests/ evals/)' req 'cd into the PostgreDataMigrationApp folder first' \
bash -c '[ -d build ] && [ -d tests ] && [ -d evals ]'
check 'evals/runner.py exists' req 'Project files missing/corrupt' test -f evals/runner.py
check 'build/deploy_all.sh exists' req 'Project files missing/corrupt' test -f build/deploy_all.sh
check 'in project root (backend/migration present)' req 'cd into the PostgreDataMigrationApp folder first' \
bash -c '[ -d backend/migration/build ] && [ -d backend/migration/tests ] && [ -d backend/migration/evals ]'
check 'backend/migration/evals/runner.py exists' req 'Project files missing/corrupt' test -f backend/migration/evals/runner.py
check 'backend/migration/build/deploy_all.sh exists' req 'Project files missing/corrupt' test -f backend/migration/build/deploy_all.sh

# ----- Git state -----
echo
Expand Down
2 changes: 1 addition & 1 deletion scripts/eval_compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
REPORTS_DIR = ROOT / "evals" / "reports"
REPORTS_DIR = ROOT / "backend" / "migration" / "evals" / "reports"

GREEN = "\033[0;32m"
RED = "\033[0;31m"
Expand Down
6 changes: 3 additions & 3 deletions scripts/eval_list.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""List all past eval runs stored in evals/reports/.
"""List all past eval runs stored in backend/migration/evals/reports/.

Mirrors `bun run eval:list` in gstack.
"""
Expand All @@ -10,7 +10,7 @@
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
REPORTS_DIR = ROOT / "evals" / "reports"
REPORTS_DIR = ROOT / "backend" / "migration" / "evals" / "reports"

GREEN = "\033[0;32m"
RED = "\033[0;31m"
Expand All @@ -22,7 +22,7 @@

def main() -> None:
if not REPORTS_DIR.exists():
print(f"{YELLOW}No eval reports found — run `python3 evals/runner.py` first.{NC}")
print(f"{YELLOW}No eval reports found — run `python3 backend/migration/evals/runner.py` first.{NC}")
return

runs = sorted(
Expand Down
4 changes: 2 additions & 2 deletions scripts/eval_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
REPORTS_DIR = ROOT / "evals" / "reports"
REPORTS_DIR = ROOT / "backend" / "migration" / "evals" / "reports"

GREEN = "\033[0;32m"
RED = "\033[0;31m"
Expand All @@ -27,7 +27,7 @@ def main() -> None:

runs = sorted(p for p in REPORTS_DIR.iterdir() if p.is_dir())
if not runs:
print(f"{YELLOW}No eval runs found — run `python3 evals/runner.py` first.{NC}")
print(f"{YELLOW}No eval runs found — run `python3 backend/migration/evals/runner.py` first.{NC}")
return

total_runs = 0
Expand Down
24 changes: 12 additions & 12 deletions scripts/run_qa.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,29 @@ $ErrorActionPreference = "Stop"

switch ($Target) {
"test-free" {
pytest -m "unit or regression or security or snapshot" tests/ -v
pytest -m "unit or regression or security or snapshot" backend/migration/tests/ -v
}
"test-gate" {
pytest -m "unit or regression or security or snapshot" tests/ `
--cov=build/csv --cov=evals --cov-report=term-missing
pytest -m "unit or regression or security or snapshot" backend/migration/tests/ `
--cov=backend/migration/build/csv --cov=backend/migration/evals --cov-report=term-missing
}
"test-evals" {
python evals/runner.py --tiers p,i,s --verbose
python backend/migration/evals/runner.py --tiers p,i,s --verbose
}
"test-e2e" {
pytest -m "e2e or integration or parity" tests/ -v
pytest -m "e2e or integration or parity" backend/migration/tests/ -v
}
"test-all" {
pytest tests/ --cov=build/csv --cov=evals --cov-report=term-missing
python evals/runner.py --tiers p,i,s --verbose
pytest backend/migration/tests/ --cov=backend/migration/build/csv --cov=backend/migration/evals --cov-report=term-missing
python backend/migration/evals/runner.py --tiers p,i,s --verbose
}
"lint" {
$pyFiles = @(
"build/csv/validator.py",
"evals/runner.py",
"evals/gap_report.py",
"tests/test_csv_validator.py",
"tests/test_evals_runner.py"
"backend/migration/build/csv/validator.py",
"backend/migration/evals/runner.py",
"backend/migration/evals/gap_report.py",
"backend/migration/tests/test_csv_validator.py",
"backend/migration/tests/test_evals_runner.py"
)
$scriptPy = Get-ChildItem scripts/*.py -ErrorAction SilentlyContinue |
ForEach-Object { $_.FullName }
Expand Down
10 changes: 5 additions & 5 deletions scripts/test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Layers:
# 1. pytest -m unit (Python unit tests, no DB needed)
# 2. SQL test suite (5 suites against deployed env, needs PG)
# 3. evals/runner.py (Tier P offline + Tier I/S need PG)
# 3. backend/migration/evals/runner.py (Tier P offline + Tier I/S need PG)
#
# Usage:
# .\scripts\test.ps1 # all three layers
Expand Down Expand Up @@ -72,12 +72,12 @@ try {
} else {
$TestRunner = Join-Path $ProjectRoot 'tests\run_tests.sh'
if (Test-Path $TestRunner) {
& bash tests/run_tests.sh $Env
& bash backend/migration/tests/run_tests.sh $Env
$pass = $LASTEXITCODE -eq 0
Record -Layer 'sql suite' -Pass $pass -Detail "exit=$LASTEXITCODE"
Write-Host "[layer 2] $(if ($pass) {'PASS'} else {'FAIL'})" -ForegroundColor $(if ($pass) {'Green'} else {'Red'})
} else {
Write-Host "[layer 2] SKIP: tests/run_tests.sh not found" -ForegroundColor Yellow
Write-Host "[layer 2] SKIP: backend/migration/tests/run_tests.sh not found" -ForegroundColor Yellow
Record -Layer 'sql suite' -Pass $true -Detail 'skipped: runner missing'
}
}
Expand All @@ -86,7 +86,7 @@ try {
# --- Layer 3: evals ---
if (-not ($SkipEvals -or $OnlyPython)) {
Write-Host ""
Write-Host "[layer 3] evals/runner.py" -ForegroundColor Yellow
Write-Host "[layer 3] backend/migration/evals/runner.py" -ForegroundColor Yellow
$Runner = Join-Path $ProjectRoot 'evals\runner.py'
if (Test-Path $Runner) {
$tiers = if ($env:PGHOST -and $env:PGPASSWORD) { 'p,i,s' } else { 'p' }
Expand All @@ -95,7 +95,7 @@ try {
Record -Layer "evals ($tiers)" -Pass $pass -Detail "exit=$LASTEXITCODE"
Write-Host "[layer 3] $(if ($pass) {'PASS'} else {'FAIL'})" -ForegroundColor $(if ($pass) {'Green'} else {'Red'})
} else {
Write-Host "[layer 3] SKIP: evals/runner.py not found" -ForegroundColor Yellow
Write-Host "[layer 3] SKIP: backend/migration/evals/runner.py not found" -ForegroundColor Yellow
Record -Layer 'evals' -Pass $true -Detail 'skipped: runner missing'
}
}
Expand Down
16 changes: 8 additions & 8 deletions scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Layers:
# 1. pytest -m unit (Python unit tests)
# 2. SQL test suite (5 suites, needs PG)
# 3. evals/runner.py (Tier P offline + Tier I/S need PG)
# 3. backend/migration/evals/runner.py (Tier P offline + Tier I/S need PG)
#
# Usage:
# ./scripts/test.sh # all three
Expand Down Expand Up @@ -82,11 +82,11 @@ if [ "$SKIP_SQL" -eq 0 ] && [ "$ONLY_PYTHON" -eq 0 ]; then
elif [ -z "${PGPASSWORD:-}" ]; then
echo "${Y}[layer 2] SKIP: PGPASSWORD not set${X}"
record "sql suite" PASS "skipped: env vars"
elif [ ! -f tests/run_tests.sh ]; then
echo "${Y}[layer 2] SKIP: tests/run_tests.sh missing${X}"
elif [ ! -f backend/migration/tests/run_tests.sh ]; then
echo "${Y}[layer 2] SKIP: backend/migration/tests/run_tests.sh missing${X}"
record "sql suite" PASS "skipped: runner missing"
else
if bash tests/run_tests.sh "$ENV_TARGET"; then
if bash backend/migration/tests/run_tests.sh "$ENV_TARGET"; then
echo "${G}[layer 2] PASS${X}"
record "sql suite" PASS "exit=0"
else
Expand All @@ -99,16 +99,16 @@ fi
# --- Layer 3: evals ---
if [ "$SKIP_EVALS" -eq 0 ] && [ "$ONLY_PYTHON" -eq 0 ]; then
echo
echo "${Y}[layer 3] evals/runner.py${X}"
if [ ! -f evals/runner.py ]; then
echo "${Y}[layer 3] SKIP: evals/runner.py missing${X}"
echo "${Y}[layer 3] backend/migration/evals/runner.py${X}"
if [ ! -f backend/migration/evals/runner.py ]; then
echo "${Y}[layer 3] SKIP: backend/migration/evals/runner.py missing${X}"
record "evals" PASS "skipped: runner missing"
else
TIERS="p"
if [ -n "${PGHOST:-}" ] && [ -n "${PGPASSWORD:-}" ]; then
TIERS="p,i,s"
fi
if python3 evals/runner.py --tiers "$TIERS"; then
if python3 backend/migration/evals/runner.py --tiers "$TIERS"; then
echo "${G}[layer 3] PASS${X}"
record "evals ($TIERS)" PASS "exit=0"
else
Expand Down
Loading
Loading