Run backend tests with pytest after Dev deploy.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
6ea36f7d1f
commit
9d79e3e92f
|
|
@ -2,29 +2,33 @@ name: Test Suite
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [develop, main]
|
||||
pull_request:
|
||||
branches: [develop]
|
||||
workflow_run:
|
||||
workflows: ["Deploy Development"]
|
||||
types: [completed]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
backend-postgres:
|
||||
pytest-backend:
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Backend tests against Dev Postgres (kansho_test)
|
||||
- name: pytest against Dev Postgres (kansho_test)
|
||||
run: |
|
||||
set -e
|
||||
APP_DIR="/home/lars/docker/kansho-dev"
|
||||
COMPOSE_FILE="docker-compose.dev-env.yml"
|
||||
echo "backend tests always use Dev Postgres (${APP_DIR})"
|
||||
echo "pytest uses Dev Postgres (${APP_DIR}); event=${{ github.event_name }}"
|
||||
cd "$APP_DIR"
|
||||
for i in $(seq 1 60); do
|
||||
if docker compose -f "$COMPOSE_FILE" exec -T backend true 2>/dev/null; then
|
||||
echo "backend ready (attempt $i)"
|
||||
if [ -f "$APP_DIR/backend/pytest.ini" ] && docker compose -f "$COMPOSE_FILE" exec -T backend true 2>/dev/null; then
|
||||
echo "backend and pytest.ini ready (attempt $i)"
|
||||
break
|
||||
fi
|
||||
if [ "$i" -eq 60 ]; then
|
||||
echo "timeout waiting for backend"
|
||||
echo "timeout waiting for Dev checkout/backend"
|
||||
docker compose -f "$COMPOSE_FILE" ps || true
|
||||
ls -la "$APP_DIR/backend" || true
|
||||
exit 1
|
||||
fi
|
||||
sleep 5
|
||||
|
|
@ -51,24 +55,32 @@ jobs:
|
|||
backend sh -lc '
|
||||
set -e
|
||||
unset KANSHO_DB_PATH
|
||||
for test in tests/test_*.py; do
|
||||
echo "=== $test ==="
|
||||
python "$test"
|
||||
done
|
||||
pip install -q -r requirements-dev.txt
|
||||
python -m pytest tests -m "not slow" -ra -vv --tb=short
|
||||
'
|
||||
|
||||
frontend-build:
|
||||
smoke-dev:
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Frontend production build on deploy checkout
|
||||
- name: Non-destructive smoke against the running Dev app
|
||||
run: |
|
||||
set -e
|
||||
echo "live Dev API (kansho_dev), read-only health"
|
||||
curl -sf http://localhost:8096/api/health
|
||||
echo
|
||||
curl -sf http://localhost:3096/api/health
|
||||
echo
|
||||
echo "LAN smoke OK. Public URL after TLS: https://dev.kansho.jinkendo.de"
|
||||
|
||||
frontend-build:
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Frontend production build on Dev checkout
|
||||
run: |
|
||||
set -e
|
||||
EVENT_REF="${{ github.ref_name }}"
|
||||
BASE_REF="${{ github.base_ref }}"
|
||||
APP_DIR="/home/lars/docker/kansho"
|
||||
if [ "$EVENT_REF" = "develop" ] || [ "$BASE_REF" = "develop" ]; then
|
||||
APP_DIR="/home/lars/docker/kansho-dev"
|
||||
fi
|
||||
cd "$APP_DIR/frontend"
|
||||
npm ci
|
||||
npm run build
|
||||
|
|
|
|||
12
backend/pytest.ini
Normal file
12
backend/pytest.ini
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[pytest]
|
||||
testpaths = tests
|
||||
# Default python collector is disabled; tests/conftest.py collects each
|
||||
# test_*.py as one case via main(). That keeps the existing scripts until
|
||||
# they are split into native pytest functions.
|
||||
python_files = __pytest_default_collector_disabled__.py
|
||||
python_functions = test_*
|
||||
addopts = -ra --tb=short
|
||||
markers =
|
||||
unit: no Postgres; safe on Windows SQLite checkouts
|
||||
integration: API/store tests on kansho_test (never kansho_dev)
|
||||
slow: long-running; omitted in the default Gitea command
|
||||
3
backend/requirements-dev.txt
Normal file
3
backend/requirements-dev.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# CI / local test runner. Install on top of requirements.txt:
|
||||
# pip install -r requirements.txt -r requirements-dev.txt
|
||||
pytest>=8.0
|
||||
89
backend/tests/conftest.py
Normal file
89
backend/tests/conftest.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
"""pytest runner for the existing test_*.py scripts.
|
||||
|
||||
Each file stays one case (`main()`) until it is split into native pytest
|
||||
functions. Integration files reset `kansho_test` only; `kansho_dev` is never
|
||||
the test target.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import ( # noqa: E402
|
||||
is_postgres_test_database,
|
||||
prepare_test_env,
|
||||
reset_postgres_schema,
|
||||
)
|
||||
|
||||
UNIT_MODULES = frozenset(
|
||||
{
|
||||
"test_sql_compat",
|
||||
"test_local_backup",
|
||||
"test_provenance",
|
||||
"test_model_catalog",
|
||||
"test_privacy_detect_eval",
|
||||
"test_journal_shape",
|
||||
"test_journal_body",
|
||||
"test_writing_profile",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def pytest_configure(config: pytest.Config) -> None:
|
||||
prepare_test_env()
|
||||
|
||||
|
||||
def pytest_collect_file(file_path: Path, parent: pytest.Collector):
|
||||
if file_path.suffix == ".py" and file_path.name.startswith("test_"):
|
||||
return KanshoScriptModule.from_parent(parent, path=file_path)
|
||||
return None
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]) -> None:
|
||||
for item in items:
|
||||
stem = Path(str(item.path)).stem
|
||||
marker = pytest.mark.unit if stem in UNIT_MODULES else pytest.mark.integration
|
||||
item.add_marker(marker)
|
||||
|
||||
|
||||
class KanshoScriptModule(pytest.Module):
|
||||
def collect(self):
|
||||
module = self.obj
|
||||
|
||||
def test_main() -> None:
|
||||
main = getattr(module, "main", None)
|
||||
if main is None or not callable(main):
|
||||
pytest.fail(f"{self.path.name} has no callable main()")
|
||||
main()
|
||||
|
||||
yield pytest.Function.from_parent(self, name="test_main", callobj=test_main)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def _module_backend(request: pytest.FixtureRequest):
|
||||
stem = Path(str(request.path)).stem
|
||||
if stem in UNIT_MODULES:
|
||||
saved = dict(os.environ)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.environ.clear()
|
||||
os.environ.update(saved)
|
||||
return
|
||||
if not is_postgres_test_database():
|
||||
pytest.skip(
|
||||
"integration requires KANSHO_DB_BACKEND=postgres and DB_NAME ending _test "
|
||||
"(Gitea: kansho_test on the Dev Postgres, never kansho_dev)"
|
||||
)
|
||||
reset_postgres_schema()
|
||||
from db import init_db
|
||||
|
||||
init_db()
|
||||
yield
|
||||
|
|
@ -29,7 +29,24 @@ def _db_name() -> str:
|
|||
return (os.environ.get("DB_NAME") or "").strip()
|
||||
|
||||
|
||||
def is_postgres_test_database() -> bool:
|
||||
if _backend() not in {"postgres", "postgresql", "pg"}:
|
||||
return False
|
||||
name = _db_name().lower()
|
||||
if not name or name in LIVE_DB_NAMES:
|
||||
return False
|
||||
return name.endswith("_test")
|
||||
|
||||
|
||||
def prepare_test_env() -> None:
|
||||
os.environ.setdefault("KANSHO_PROVIDER_KEY", "")
|
||||
os.environ.setdefault("KANSHO_DETECT_PROVIDER_KEY", "")
|
||||
os.environ.pop("KANSHO_DB_PATH", None)
|
||||
|
||||
|
||||
def _require_test_database() -> None:
|
||||
if is_postgres_test_database():
|
||||
return
|
||||
backend = _backend()
|
||||
if backend not in {"postgres", "postgresql", "pg"}:
|
||||
raise SystemExit(
|
||||
|
|
@ -44,7 +61,6 @@ def _require_test_database() -> None:
|
|||
"DB_NAME fehlt. Tests nutzen auf der Dev-Postgres die Datenbank kansho_test, "
|
||||
"nicht kansho_dev."
|
||||
)
|
||||
if name in LIVE_DB_NAMES or not name.endswith("_test"):
|
||||
raise SystemExit(
|
||||
f"DB_NAME={_db_name()!r} ist für Tests gesperrt. "
|
||||
"Die Suite setzt das Schema zurück und darf daher nicht kansho oder kansho_dev treffen."
|
||||
|
|
@ -71,10 +87,8 @@ def reset_postgres_schema() -> None:
|
|||
|
||||
|
||||
def configure_test_engine() -> None:
|
||||
"""Call after sys.path includes backend/, before importing main/db."""
|
||||
os.environ.setdefault("KANSHO_PROVIDER_KEY", "")
|
||||
os.environ.setdefault("KANSHO_DETECT_PROVIDER_KEY", "")
|
||||
os.environ.pop("KANSHO_DB_PATH", None)
|
||||
"""Reset kansho_test. pytest uses the fixture in tests/conftest.py instead."""
|
||||
prepare_test_env()
|
||||
_require_test_database()
|
||||
try:
|
||||
reset_postgres_schema()
|
||||
|
|
|
|||
|
|
@ -11,10 +11,6 @@ from pathlib import Path
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,6 @@ ROOT = Path(__file__).resolve().parents[1]
|
|||
REPO = ROOT.parent
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_MEDIA_ROOT"] = str(Path(tempfile.gettempdir()) / "kansho-debug-persist-media")
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ from unittest.mock import patch
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,6 @@ from pathlib import Path
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
from main import app
|
||||
|
|
|
|||
|
|
@ -10,10 +10,6 @@ from pathlib import Path
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_PROVIDER_KEY"] = ""
|
||||
os.environ["KANSHO_DETECT_PROVIDER_KEY"] = ""
|
||||
|
||||
|
|
|
|||
|
|
@ -10,10 +10,6 @@ from unittest.mock import patch
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,6 @@ from unittest.mock import patch
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -10,10 +10,6 @@ from unittest.mock import patch
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,6 @@ from pathlib import Path
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -12,10 +12,6 @@ ROOT = Path(__file__).resolve().parents[1]
|
|||
FRONTEND = ROOT.parent / "frontend"
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,6 @@ from unittest.mock import patch
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,6 @@ from datetime import date
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_MEDIA_ROOT"] = str(Path(tempfile.gettempdir()) / "kansho-opening-media")
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
|
|
|||
|
|
@ -10,10 +10,6 @@ from pathlib import Path
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -10,10 +10,6 @@ from pathlib import Path
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,6 @@ from pathlib import Path
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_MEDIA_ROOT"] = str(Path(tempfile.gettempdir()) / "kansho-trash-media")
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
|
|
|||
|
|
@ -9,10 +9,6 @@ from pathlib import Path
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_MEDIA_ROOT"] = str(Path(tempfile.gettempdir()) / "kansho-mvp-journal-media")
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
|
|
|||
|
|
@ -15,10 +15,6 @@ from unittest.mock import patch
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,6 @@ from unittest.mock import patch
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,6 @@ ROOT = Path(__file__).resolve().parents[1]
|
|||
REPO = ROOT.parent
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,6 @@ from pathlib import Path
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,6 @@ from pathlib import Path
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ import httpx
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from tests.harness import configure_test_engine
|
||||
|
||||
configure_test_engine()
|
||||
|
||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ Datenübernahme SQLite → Postgres: `backend/sqlite_to_postgres.py` (Probe auf
|
|||
| **Backend-Port** | 8005 | 8096 |
|
||||
| **PostgreSQL** | nur Compose-Netz, DB `kansho` | nur Compose-Netz, DB `kansho_dev` |
|
||||
| **Domain** | kansho.jinkendo.de | dev.kansho.jinkendo.de |
|
||||
| **Öffentliche URL** | https://kansho.jinkendo.de | https://dev.kansho.jinkendo.de |
|
||||
| **Compose** | `docker-compose.yml` | `docker-compose.dev-env.yml` |
|
||||
|
||||
Prod-Frontend ist **3006**, nicht 3005: auf dem Pi lauscht Bookstack bereits auf `0.0.0.0:3005`. Dev 3096/8096 und Prod-API 8005 waren frei.
|
||||
|
|
@ -46,7 +47,7 @@ cp .env.example .env
|
|||
# Prod-DB_PASSWORD und Provider-Keys setzen
|
||||
```
|
||||
|
||||
Host-nginx: `nginx/kansho.conf` und `nginx/kansho-dev.conf` nach `/etc/nginx/sites-available/`, dann `nginx/certbot-setup.sh`. DNS für beide Hostnamen auf den Reverse-Proxy.
|
||||
Host-nginx: `nginx/kansho.conf` und `nginx/kansho-dev.conf` nach `/etc/nginx/sites-available/`, dann `nginx/certbot-setup.sh`. DNS A/AAAA für `dev.kansho.jinkendo.de` und `kansho.jinkendo.de` auf den Reverse-Proxy. Bis TLS steht, bleibt der LAN-Zugriff über die Publish-Ports.
|
||||
|
||||
Gitea: Actions aktivieren. Derselbe Pi-Runner wie die Schwesterprodukte (`ubuntu-latest`).
|
||||
|
||||
|
|
@ -60,7 +61,7 @@ Watchtower bleibt aus.
|
|||
|----------|---------|--------|
|
||||
| `deploy-dev.yml` | Push `develop` | Deploy nach `/home/lars/docker/kansho-dev`, Health `localhost:8096` |
|
||||
| `deploy-prod.yml` | Push `main` | Deploy nach `/home/lars/docker/kansho`, Health `localhost:8005` |
|
||||
| `test.yml` | Push `develop`/`main`, PR `develop` | Backend-Tests auf der bestehenden Compose-Postgres in DB `kansho_test` (nicht Live-`kansho_dev`) plus Frontend-Build. Keine Live-Provider-Keys. |
|
||||
| `test.yml` | nach erfolgreichem `Deploy Development` | pytest auf `kansho_test`, nicht-schreibender Smoke gegen die laufende Dev-API, Frontend-Build. Keine Live-Provider-Keys. |
|
||||
|
||||
Prod nur über Merge `develop` → `main`, kein direkter Prod-Schreibzugriff.
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ Entsprechen den Interview-Leitfragen Phase F1/F4 und werden hier nicht vorab bea
|
|||
Additiv, ohne das Fachschema vorzuziehen:
|
||||
|
||||
- Windows-Checkout: SQLite unter `backend/data/` (gitignoriert) für die lokale App ohne Docker.
|
||||
- Test-Suite und Gitea: dieselbe Dev-Postgres-Instanz, Datenbank `kansho_test` neben `kansho_dev` (keine dritte Instanz). Nicht `kansho` / `kansho_dev`, weil die Suite das Schema zurücksetzt.
|
||||
- Test-Suite und Gitea: pytest auf derselben Dev-Postgres-Instanz, Datenbank `kansho_test` neben `kansho_dev` (keine dritte Instanz). Nicht `kansho` / `kansho_dev`, weil die Suite das Schema zurücksetzt. Live-Smoke trifft nur `/api/health` der laufenden Dev-App.
|
||||
- Docker auf dem Pi: PostgreSQL 16, eigene Instanz je Umgebung, Medien im Compose-Volume `kansho-media` / `dev-kansho-media`.
|
||||
- Identity-Mappings bleiben Klasse A in der Local Trusted Zone (jetzt: Pi-Postgres, nicht Provider).
|
||||
- Kein Shared-Schema mit Mitai.
|
||||
|
|
|
|||
|
|
@ -164,6 +164,8 @@ Offen, vor Compose festlegen — **beantwortet 2026-09-07:**
|
|||
|
||||
**Additiv 2026-09-07 (Test-Engine):** Zwei Postgres-Instanzen bleiben Dev und Prod. Die Gitea-Suite läuft auf der Dev-Postgres in der Datenbank `kansho_test` (neben `kansho_dev`), nicht gegen SQLite und nicht gegen das persönliche `kansho_dev`-Journal. Die Suite setzt dort das Schema zurück. SQLite bleibt die Windows-App ohne Docker und das lokale Backup-Format.
|
||||
|
||||
**Additiv 2026-09-07 (pytest + öffentliche URLs):** Kanonischer Runner ist `python -m pytest tests` nach Dev-Deploy. `scripts/test-mvp.ps1` wrappt pytest lokal. Produkt-URLs nach Nginx/TLS: `https://dev.kansho.jinkendo.de` und `https://kansho.jinkendo.de`.
|
||||
|
||||
Empfohlene Reihenfolge in Welle 2:
|
||||
|
||||
1. Offene Host-Fragen beantworten und in `runtime_and_deploy.md` §1/§6 eintragen.
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ Lokal, eine Instanz, ein Profil-Nutzer nach Setup:
|
|||
|
||||
Proxy: Vite leitet `/api` an 8018. CORS erlaubt `localhost:5188`.
|
||||
|
||||
**Abweichung vom technischen Zielrahmen (lokal):** `product_frame_and_stack.md` nennt PostgreSQL. Der Slice auf dem Windows-Checkout bleibt SQLite (`5188`/`8018`). **Additiv 2026-09-07:** Docker/Server nutzt PostgreSQL 16 (`KANSHO_DB_BACKEND=postgres`). Stores behalten SQLite-förmiges SQL; `sql_compat.py` übersetzt zur Laufzeit. Nummerierte Dateien ab `backend/migrations/022_*.sql`. Siehe `runtime_and_deploy.md` und `docs/DEPLOYMENT.md`. **Additiv (Test-Suite):** API- und Store-Tests setzen `tests/harness.py` auf die Dev-Postgres, Datenbank `kansho_test` neben `kansho_dev` (keine dritte Instanz). Gitea `test.yml` trifft nicht das persönliche `kansho_dev`-Journal. SQLite-Testdateien sind abgelöst; `test_local_backup.py` prüft weiter das Windows-Backup-Format.
|
||||
**Abweichung vom technischen Zielrahmen (lokal):** `product_frame_and_stack.md` nennt PostgreSQL. Der Slice auf dem Windows-Checkout bleibt SQLite (`5188`/`8018`). **Additiv 2026-09-07:** Docker/Server nutzt PostgreSQL 16 (`KANSHO_DB_BACKEND=postgres`). Stores behalten SQLite-förmiges SQL; `sql_compat.py` übersetzt zur Laufzeit. Nummerierte Dateien ab `backend/migrations/022_*.sql`. Siehe `runtime_and_deploy.md` und `docs/DEPLOYMENT.md`. **Additiv (Qualitätssystem 2026-09-07):** Kanonischer Backend-Runner ist pytest im Dev-Checkout nach erfolgreichem Deploy. `python -m pytest tests -m "not slow"` gegen `kansho_test`. `scripts/test-mvp.ps1` ist nur noch der lokale Wrapper (Unit ohne Postgres, Integration nur mit `DB_NAME=kansho_test`). SQLite-Testdateien sind abgelöst; `test_local_backup.py` prüft weiter das Windows-Backup-Format.
|
||||
|
||||
Health: `GET /api/health`.
|
||||
|
||||
|
|
@ -223,11 +223,11 @@ Gateway-Verfahren und offener Security Layer: `privacy_gateway.md` §9.1–9.2 u
|
|||
| Erster Journal-Impuls | `backend/tests/test_journal_opening.py` |
|
||||
| Writing-Profile-Hülle / Initial Build | `backend/tests/test_profile_review.py`, `backend/tests/test_profile_governance.py` |
|
||||
| Markdown-Runde | `frontend/src/journal/document.test.js` |
|
||||
| Lokaler Abnahmelauf | `scripts/test-mvp.ps1` |
|
||||
| Lokaler Wrapper | `scripts/test-mvp.ps1` (pytest, kein kanonischer Runner) |
|
||||
|
||||
Tests beweisen API-Verträge und Invarianten, nicht Dialogqualität.
|
||||
|
||||
**Additiv 2026-09-07:** Die Backend-Suite in Gitea läuft auf der Dev-Postgres in `kansho_test` (Harness `backend/tests/harness.py`). `scripts/test-mvp.ps1` führt Store-/API-Tests lokal nur mit `KANSHO_DB_BACKEND=postgres` und `DB_NAME=kansho_test` aus; Unit-Tests ohne Datenbank laufen weiter. Kanonisch bleibt der Pi-Lauf.
|
||||
**Additiv 2026-09-07:** pytest ist der Backend-Runner (`backend/pytest.ini`). Gitea führt `python -m pytest tests -m "not slow"` nach erfolgreichem Dev-Deploy auf `kansho_test` aus. `scripts/test-mvp.ps1` wrappt denselben Aufruf lokal. Unit-Marker laufen ohne Postgres; Integration nie gegen `kansho_dev`.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -279,7 +279,7 @@ Additiv zum Slice, 2026-08-25. Kein Target-Model-Vorbau.
|
|||
|
||||
Additiv zum Slice. Fachliche Abnahme: `../functional/mvp_freeze_candidate.md`.
|
||||
|
||||
- Lokaler Testeinstieg `scripts/test-mvp.ps1` (UTF-8, isolierte Temp-Daten, keine Live-Keys; Fake nur in den Suites, die ihn setzen).
|
||||
- Lokaler Wrapper `scripts/test-mvp.ps1` (pytest; UTF-8, isolierte Temp-Daten, keine Live-Keys; Fake nur in den Suites, die ihn setzen). Kanonisch: Gitea pytest auf `kansho_test`.
|
||||
- Backup: `backend/local_backup.py`, Einstieg `scripts/backup-local.ps1`. SQLite-Backup-API, Medien, Manifest, keine Secrets. Restore bestätigt, prüft Checksummen, legt Sicherheitsbackup an, bricht bei geöffneter DB ab.
|
||||
- Papierkorb: `GET /spaces/{id}/entries`, `GET /spaces/{id}/trash`, `POST /entries/{id}/undelete`, `POST /entries/{id}/purge`.
|
||||
- Erster Impuls: `journal_opening.py`, derselbe Gateway-Pfad, `{{opening_hint}}`. **Additiv 2026-09-03:** Vorhaben nur aus dem user-Dialog des vorigen Kalendertags (`calendar_date` an `space_recent_sources`); Journal-Entries und ältere Recency sind kein Opening-Mandat. Der Opening-Call setzt `include_space_recency=False`. Tests: abgeschlossener Entry mit „nächsten Tag“ und Plan von vorgestern → `local_neutral`; „Morgen wollen wir …“ nur am Folgetag → Modellpfad. **Additiv 2026-09-07:** Prefix-Recency (5×400 Entries, 3×400 Source-Conversations) bleibt im *laufenden* Dialogzug; das ist kein Opening-Mandat. Ein Nachsatz mit Morgenuhrzeit darf den Folgetag nicht über den Entry-Anfang steuern.
|
||||
|
|
|
|||
|
|
@ -77,12 +77,14 @@ Mitai-Pipeline als Vorlage:
|
|||
|
||||
```text
|
||||
push develop → deploy-dev.yml → compose build/up → Healthcheck
|
||||
push/PR nach Tests → test.yml (pytest, Frontend-Build)
|
||||
successful Dev deploy → test.yml (pytest on kansho_test, Live-Smoke /api/health, Frontend-Build)
|
||||
merge in main → deploy-prod.yml
|
||||
```
|
||||
|
||||
**Status: entschieden.** Workflows liegen unter `.gitea/workflows/` (`deploy-dev.yml`, `deploy-prod.yml`, `test.yml`). Muster analog Kairo: `git reset --hard`, `build --no-cache`, Health `GET /api/health`.
|
||||
|
||||
**Additiv 2026-09-07 (Qualitätssystem):** Backend-Runner ist pytest (`backend/pytest.ini`, `backend/tests/conftest.py`). Gitea startet die Suite erst nach erfolgreichem Dev-Deploy (`workflow_run` auf `Deploy Development`), nicht parallel zum Image-Build. Destruktive Tests bleiben auf `kansho_test`. Zusätzlich ein nicht-schreibender Smoke gegen die laufende Dev-API (`kansho_dev`, nur `/api/health`). Öffentliche URLs nach Host-Nginx/TLS: `https://dev.kansho.jinkendo.de` und `https://kansho.jinkendo.de`. LAN-Ports 3096/8096 und 3006/8005 bleiben die Compose-Publish-Ziele.
|
||||
|
||||
Kanshō-Repo liegt auf Gitea (`Lars/Kansho`). HTTPS-Push ist eingerichtet. Der Pi-Runner (`ubuntu-latest`) ist derselbe wie bei Mitai/Shinkan/Kairo.
|
||||
|
||||
## 5. Was Deploy nicht übernimmt
|
||||
|
|
@ -103,7 +105,8 @@ Kanshō-Repo liegt auf Gitea (`Lars/Kansho`). HTTPS-Push ist eingerichtet. Der P
|
|||
| Postgres | eigene Instanz je Umgebung (`kansho` / `kansho_dev`), kein Shared-Schema | entschieden 2026-09-07 |
|
||||
| Gitea Workflows | `.gitea/workflows/` analog Kairo | entschieden 2026-09-07 |
|
||||
| Auto-Rollback | nein | verworfen |
|
||||
| Dual-Backend | SQLite nur Windows-App ohne Docker; Server Dev+Prod und Test-Suite PostgreSQL. Tests auf der Dev-Instanz in DB `kansho_test` neben `kansho_dev` (keine dritte Instanz) | entschieden 2026-09-07, Tests nachgezogen |
|
||||
| Dual-Backend | SQLite nur Windows-App ohne Docker; Server Dev+Prod PostgreSQL. pytest auf der Dev-Instanz in DB `kansho_test` neben `kansho_dev` (keine dritte Instanz) | entschieden 2026-09-07, pytest 2026-09-07 |
|
||||
| Öffentliche URLs | `https://dev.kansho.jinkendo.de` / `https://kansho.jinkendo.de` (Host-Nginx + Let's Encrypt; Einrichtung parallel) | entschieden 2026-09-07 |
|
||||
| Urlaubs-Laptop ohne Docker/SQLite | Phase A auf dem Heimrechner restored | dokumentiert 2026-09-07 |
|
||||
| Transfer vor Postgres | SQLite-Restore auf dem Heimrechner zuerst | entschieden als Reihenfolge |
|
||||
|
||||
|
|
|
|||
|
|
@ -80,39 +80,29 @@ function Test-PostgresSuite {
|
|||
return ($backend -in @("postgres", "postgresql", "pg")) -and $name.EndsWith("_test")
|
||||
}
|
||||
|
||||
Write-Host "Kansho local MVP tests"
|
||||
Write-Host "Kansho local tests (pytest wrapper)"
|
||||
Write-Host "Python: $py"
|
||||
Write-Host "Isolation: $($iso.Base)"
|
||||
Write-Host "No live provider/detect calls. Production DB/media untouched."
|
||||
Write-Host "Fail-closed stays testable; suites that need a fake provider set it themselves."
|
||||
Write-Host "Canonical backend suite: Gitea pytest on kansho_test after Dev deploy."
|
||||
$runBackend = Test-PostgresSuite
|
||||
if ($runBackend) {
|
||||
Write-Host "Backend tests: Postgres $($env:DB_NAME)"
|
||||
Write-Host "Backend pytest: Postgres $($env:DB_NAME)"
|
||||
} else {
|
||||
Write-Host "Backend Postgres-Suite: SKIP (KANSHO_DB_BACKEND=postgres und DB_NAME=kansho_test auf der Dev-Postgres; kanonisch Gitea auf dem Pi). Reine Unit-Tests ohne DB laufen trotzdem."
|
||||
Write-Host "Backend pytest: unit only (set KANSHO_DB_BACKEND=postgres and DB_NAME=kansho_test for integration)."
|
||||
}
|
||||
|
||||
try {
|
||||
$backendTests = Get-ChildItem -Path (Join-Path $root "backend\tests") -Filter "test_*.py" | Sort-Object Name
|
||||
if (-not $backendTests) {
|
||||
throw "no backend/tests/test_*.py files found"
|
||||
}
|
||||
|
||||
foreach ($test in $backendTests) {
|
||||
$needsPg = $false
|
||||
Invoke-Step -Name "backend pytest" -Action {
|
||||
Push-Location (Join-Path $root "backend")
|
||||
try {
|
||||
$needsPg = [bool](Select-String -Path $test.FullName -Pattern "configure_test_engine" -Quiet)
|
||||
} catch {
|
||||
$needsPg = $true
|
||||
if ($runBackend) {
|
||||
& $py -m pytest tests -m "not slow" -ra --tb=short
|
||||
} else {
|
||||
& $py -m pytest tests -m "unit and not slow" -ra --tb=short
|
||||
}
|
||||
if ($needsPg -and -not $runBackend) {
|
||||
$script:ran++
|
||||
$script:passed++
|
||||
$script:results += [pscustomobject]@{ Name = "backend $($test.Name)"; Status = "SKIP" }
|
||||
continue
|
||||
}
|
||||
Invoke-Step -Name "backend $($test.Name)" -Action {
|
||||
& $py $test.FullName
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -161,5 +151,5 @@ Write-Host ("{0} von {1} Schritten erfolgreich." -f $passed, $ran)
|
|||
if ($failed) {
|
||||
exit 1
|
||||
}
|
||||
Write-Host "MVP local acceptance: OK"
|
||||
Write-Host "local tests: OK"
|
||||
exit 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user