Ergaenze die Kansho-analoge Test-Suite gegen buyingagent_test.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
e1800e7b0f
commit
c9c0ff96fc
|
|
@ -9,6 +9,52 @@ on:
|
|||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
pytest-backend:
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: pytest against Dev Postgres (buyingagent_test)
|
||||
run: |
|
||||
set -e
|
||||
APP_DIR="$HOME/docker/buyingagent-dev"
|
||||
COMPOSE_FILE="docker-compose.dev-env.yml"
|
||||
echo "pytest uses Dev Postgres (${APP_DIR}); event=${{ github.event_name }}"
|
||||
cd "$APP_DIR"
|
||||
for i in $(seq 1 60); do
|
||||
if [ -f "$APP_DIR/pyproject.toml" ] && docker compose -f "$COMPOSE_FILE" exec -T api true 2>/dev/null; then
|
||||
echo "api and pyproject.toml ready (attempt $i)"
|
||||
break
|
||||
fi
|
||||
if [ "$i" -eq 60 ]; then
|
||||
echo "timeout waiting for Dev checkout/api"
|
||||
docker compose -f "$COMPOSE_FILE" ps || true
|
||||
ls -la "$APP_DIR" || true
|
||||
exit 1
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
PGUSER="$(docker compose -f "$COMPOSE_FILE" exec -T postgres printenv POSTGRES_USER | tr -d '\r')"
|
||||
echo "test database buyingagent_test on the existing Dev Postgres (owner ${PGUSER})"
|
||||
docker compose -f "$COMPOSE_FILE" exec -T postgres \
|
||||
psql -U "$PGUSER" -d postgres -v ON_ERROR_STOP=1 \
|
||||
-c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'buyingagent_test' AND pid <> pg_backend_pid();"
|
||||
docker compose -f "$COMPOSE_FILE" exec -T postgres \
|
||||
psql -U "$PGUSER" -d postgres -v ON_ERROR_STOP=1 \
|
||||
-c "DROP DATABASE IF EXISTS buyingagent_test;"
|
||||
docker compose -f "$COMPOSE_FILE" exec -T postgres \
|
||||
psql -U "$PGUSER" -d postgres -v ON_ERROR_STOP=1 \
|
||||
-c "CREATE DATABASE buyingagent_test OWNER ${PGUSER};"
|
||||
docker compose -f "$COMPOSE_FILE" --profile test build --pull=false api-test
|
||||
docker compose -f "$COMPOSE_FILE" --profile test run --rm --no-deps \
|
||||
-v "${APP_DIR}:/src:ro" \
|
||||
-w /src \
|
||||
-e PYTHONPATH=/src \
|
||||
-e DB_NAME=buyingagent_test \
|
||||
-e ENVIRONMENT=test \
|
||||
-e ENGINE_BUILD=test \
|
||||
api-test \
|
||||
sh -lc 'set -e; alembic upgrade head; pytest tests -m "not slow" -ra --tb=short'
|
||||
|
||||
smoke-dev:
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -16,7 +62,7 @@ jobs:
|
|||
- name: Non-destructive smoke against the running Dev API
|
||||
run: |
|
||||
set -e
|
||||
echo "wait for live Dev API (buyingagent-dev)"
|
||||
echo "live Dev API (buyingagent-dev), read-only health"
|
||||
for i in $(seq 1 60); do
|
||||
if curl -sf http://localhost:8095/health/live >/dev/null; then
|
||||
echo "DEV /health/live ready (attempt $i)"
|
||||
|
|
|
|||
|
|
@ -37,4 +37,4 @@ RUN uv sync --frozen --extra test \
|
|||
&& chown -R app:app /app
|
||||
|
||||
USER app
|
||||
CMD ["pytest", "tests", "-ra"]
|
||||
CMD ["pytest", "tests", "-m", "not slow", "-ra", "--tb=short"]
|
||||
|
|
|
|||
|
|
@ -26,11 +26,13 @@ Ready: `GET /health/ready` (PostgreSQL erreichbar und Alembic auf Head)
|
|||
|
||||
## Tests
|
||||
|
||||
Lokal, isolierter Stack (tmpfs, kein Dev-/Prod-Volume):
|
||||
|
||||
```sh
|
||||
docker compose -f docker-compose.test.yml up --build --abort-on-container-exit --exit-code-from api
|
||||
```
|
||||
|
||||
Die Testdatenbank liegt auf tmpfs und leert kein normales Datenvolume.
|
||||
Auf dem Server nach Dev-Deploy (Gitea `test.yml`, analog Kanshō): pytest gegen `buyingagent_test` auf dem Dev-Postgres plus read-only Smoke gegen `http://192.168.2.49:8095/health/ready`.
|
||||
|
||||
## Server
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,26 @@ services:
|
|||
networks:
|
||||
- dev-buyingagent-network
|
||||
|
||||
api-test:
|
||||
profiles: ["test"]
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
target: test
|
||||
environment:
|
||||
DB_HOST: postgres
|
||||
DB_PORT: "5432"
|
||||
DB_NAME: "${DB_NAME:-buyingagent_dev}"
|
||||
DB_USER: "${DB_USER:-buyingagent_dev}"
|
||||
DB_PASSWORD: "${DB_PASSWORD:-dev_password_change_me}"
|
||||
ENVIRONMENT: test
|
||||
ENGINE_BUILD: test
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- dev-buyingagent-network
|
||||
|
||||
volumes:
|
||||
dev-buyingagent-db-data:
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,12 @@ services:
|
|||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
command: ["pytest", "tests", "-ra"]
|
||||
command:
|
||||
[
|
||||
"sh",
|
||||
"-lc",
|
||||
"alembic upgrade head && pytest tests -m 'not slow' -ra --tb=short",
|
||||
]
|
||||
networks:
|
||||
- buyingagent-test-network
|
||||
|
||||
|
|
|
|||
|
|
@ -64,10 +64,12 @@ Watchtower bleibt aus.
|
|||
|----------|---------|--------|
|
||||
| `deploy-dev.yml` | Push `develop` | Deploy nach `~/docker/buyingagent-dev`, Health `localhost:8095` |
|
||||
| `deploy-prod.yml` | Push `main` | Deploy nach `~/docker/buyingagent`, Health `localhost:8006` |
|
||||
| `test.yml` | nach Dev-Deploy bzw. Push `develop` | nicht-schreibender Smoke gegen `/health/live` und `/health/ready` |
|
||||
| `test.yml` | nach Dev-Deploy bzw. Push `develop` | pytest auf `buyingagent_test` (Dev-Postgres, nicht Live-DB) und read-only Smoke gegen `/health/live` und `/health/ready` |
|
||||
|
||||
Der API-Build nutzt `ghcr.io` (nicht Docker Hub). `docker compose build --pull=false` verwendet vorhandene Basis-Images und vermeidet IPv6-Timeouts gegen `auth.docker.io`.
|
||||
|
||||
`test.yml` folgt Kanshō: pytest läuft in einem Einweg-Container (`api-test`, Compose-Profil `test`) gegen die wegwerfbare Datenbank `buyingagent_test` auf dem bereits laufenden Dev-Postgres. Die Live-Datenbank bleibt unangetastet. Es gibt kein Frontend, daher keinen Frontend-Build-Job.
|
||||
|
||||
---
|
||||
|
||||
## Manuelles Deploy
|
||||
|
|
|
|||
|
|
@ -30,3 +30,7 @@ include = ["app*"]
|
|||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
addopts = "-ra"
|
||||
markers = [
|
||||
"slow: long-running tests skipped in the default CI suite",
|
||||
"integration: needs PostgreSQL 17",
|
||||
]
|
||||
|
|
|
|||
40
tests/integration/test_ready.py
Normal file
40
tests/integration/test_ready.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import text
|
||||
|
||||
from alembic.config import Config
|
||||
from alembic.runtime.migration import MigrationContext
|
||||
from alembic.script import ScriptDirectory
|
||||
|
||||
from app.main import app
|
||||
from app.persistence.db import engine
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def require_postgres() -> None:
|
||||
try:
|
||||
with engine.connect() as connection:
|
||||
connection.execute(text("SELECT 1"))
|
||||
except Exception as exc:
|
||||
pytest.skip(f"PostgreSQL not available: {exc}")
|
||||
|
||||
|
||||
def test_health_ready_when_migrated() -> None:
|
||||
response = client.get("/health/ready")
|
||||
assert response.status_code == 200
|
||||
body = response.json()
|
||||
assert body["status"] == "ready"
|
||||
assert "environment" in body
|
||||
assert "engine_build" in body
|
||||
|
||||
|
||||
def test_alembic_is_at_head() -> None:
|
||||
config = Config("alembic.ini")
|
||||
script = ScriptDirectory.from_config(config)
|
||||
with engine.connect() as connection:
|
||||
context = MigrationContext.configure(connection)
|
||||
assert set(context.get_current_heads()) == set(script.get_heads())
|
||||
14
tests/unit/test_settings.py
Normal file
14
tests/unit/test_settings.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
from app.settings import Settings
|
||||
|
||||
|
||||
def test_database_url_uses_psycopg_and_explicit_fields() -> None:
|
||||
settings = Settings(
|
||||
db_host="postgres",
|
||||
db_port=5432,
|
||||
db_name="buyingagent_test",
|
||||
db_user="buyingagent",
|
||||
db_password="secret",
|
||||
)
|
||||
assert settings.database_url == (
|
||||
"postgresql+psycopg://buyingagent:secret@postgres:5432/buyingagent_test"
|
||||
)
|
||||
Loading…
Reference in New Issue
Block a user