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:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [develop, main]
|
|
||||||
pull_request:
|
|
||||||
branches: [develop]
|
branches: [develop]
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["Deploy Development"]
|
||||||
|
types: [completed]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
backend-postgres:
|
pytest-backend:
|
||||||
|
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Backend tests against Dev Postgres (kansho_test)
|
- name: pytest against Dev Postgres (kansho_test)
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
APP_DIR="/home/lars/docker/kansho-dev"
|
APP_DIR="/home/lars/docker/kansho-dev"
|
||||||
COMPOSE_FILE="docker-compose.dev-env.yml"
|
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"
|
cd "$APP_DIR"
|
||||||
for i in $(seq 1 60); do
|
for i in $(seq 1 60); do
|
||||||
if docker compose -f "$COMPOSE_FILE" exec -T backend true 2>/dev/null; then
|
if [ -f "$APP_DIR/backend/pytest.ini" ] && docker compose -f "$COMPOSE_FILE" exec -T backend true 2>/dev/null; then
|
||||||
echo "backend ready (attempt $i)"
|
echo "backend and pytest.ini ready (attempt $i)"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
if [ "$i" -eq 60 ]; then
|
if [ "$i" -eq 60 ]; then
|
||||||
echo "timeout waiting for backend"
|
echo "timeout waiting for Dev checkout/backend"
|
||||||
docker compose -f "$COMPOSE_FILE" ps || true
|
docker compose -f "$COMPOSE_FILE" ps || true
|
||||||
|
ls -la "$APP_DIR/backend" || true
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
@ -51,24 +55,32 @@ jobs:
|
||||||
backend sh -lc '
|
backend sh -lc '
|
||||||
set -e
|
set -e
|
||||||
unset KANSHO_DB_PATH
|
unset KANSHO_DB_PATH
|
||||||
for test in tests/test_*.py; do
|
pip install -q -r requirements-dev.txt
|
||||||
echo "=== $test ==="
|
python -m pytest tests -m "not slow" -ra -vv --tb=short
|
||||||
python "$test"
|
|
||||||
done
|
|
||||||
'
|
'
|
||||||
|
|
||||||
frontend-build:
|
smoke-dev:
|
||||||
|
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Frontend production build on deploy checkout
|
- name: Non-destructive smoke against the running Dev app
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
EVENT_REF="${{ github.ref_name }}"
|
echo "live Dev API (kansho_dev), read-only health"
|
||||||
BASE_REF="${{ github.base_ref }}"
|
curl -sf http://localhost:8096/api/health
|
||||||
APP_DIR="/home/lars/docker/kansho"
|
echo
|
||||||
if [ "$EVENT_REF" = "develop" ] || [ "$BASE_REF" = "develop" ]; then
|
curl -sf http://localhost:3096/api/health
|
||||||
APP_DIR="/home/lars/docker/kansho-dev"
|
echo
|
||||||
fi
|
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
|
||||||
|
APP_DIR="/home/lars/docker/kansho-dev"
|
||||||
cd "$APP_DIR/frontend"
|
cd "$APP_DIR/frontend"
|
||||||
npm ci
|
npm ci
|
||||||
npm run build
|
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()
|
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:
|
def _require_test_database() -> None:
|
||||||
|
if is_postgres_test_database():
|
||||||
|
return
|
||||||
backend = _backend()
|
backend = _backend()
|
||||||
if backend not in {"postgres", "postgresql", "pg"}:
|
if backend not in {"postgres", "postgresql", "pg"}:
|
||||||
raise SystemExit(
|
raise SystemExit(
|
||||||
|
|
@ -44,11 +61,10 @@ def _require_test_database() -> None:
|
||||||
"DB_NAME fehlt. Tests nutzen auf der Dev-Postgres die Datenbank kansho_test, "
|
"DB_NAME fehlt. Tests nutzen auf der Dev-Postgres die Datenbank kansho_test, "
|
||||||
"nicht kansho_dev."
|
"nicht kansho_dev."
|
||||||
)
|
)
|
||||||
if name in LIVE_DB_NAMES or not name.endswith("_test"):
|
raise SystemExit(
|
||||||
raise SystemExit(
|
f"DB_NAME={_db_name()!r} ist für Tests gesperrt. "
|
||||||
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."
|
||||||
"Die Suite setzt das Schema zurück und darf daher nicht kansho oder kansho_dev treffen."
|
)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def reset_postgres_schema() -> None:
|
def reset_postgres_schema() -> None:
|
||||||
|
|
@ -71,10 +87,8 @@ def reset_postgres_schema() -> None:
|
||||||
|
|
||||||
|
|
||||||
def configure_test_engine() -> None:
|
def configure_test_engine() -> None:
|
||||||
"""Call after sys.path includes backend/, before importing main/db."""
|
"""Reset kansho_test. pytest uses the fixture in tests/conftest.py instead."""
|
||||||
os.environ.setdefault("KANSHO_PROVIDER_KEY", "")
|
prepare_test_env()
|
||||||
os.environ.setdefault("KANSHO_DETECT_PROVIDER_KEY", "")
|
|
||||||
os.environ.pop("KANSHO_DB_PATH", None)
|
|
||||||
_require_test_database()
|
_require_test_database()
|
||||||
try:
|
try:
|
||||||
reset_postgres_schema()
|
reset_postgres_schema()
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,6 @@ from pathlib import Path
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,6 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||||
REPO = ROOT.parent
|
REPO = ROOT.parent
|
||||||
sys.path.insert(0, str(ROOT))
|
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_MEDIA_ROOT"] = str(Path(tempfile.gettempdir()) / "kansho-debug-persist-media")
|
||||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,6 @@ from unittest.mock import patch
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,6 @@ from pathlib import Path
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
sys.path.insert(0, str(ROOT))
|
||||||
|
|
||||||
from tests.harness import configure_test_engine
|
|
||||||
|
|
||||||
configure_test_engine()
|
|
||||||
|
|
||||||
|
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from main import app
|
from main import app
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,6 @@ from pathlib import Path
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
sys.path.insert(0, str(ROOT))
|
||||||
|
|
||||||
from tests.harness import configure_test_engine
|
|
||||||
|
|
||||||
configure_test_engine()
|
|
||||||
|
|
||||||
os.environ["KANSHO_PROVIDER_KEY"] = ""
|
os.environ["KANSHO_PROVIDER_KEY"] = ""
|
||||||
os.environ["KANSHO_DETECT_PROVIDER_KEY"] = ""
|
os.environ["KANSHO_DETECT_PROVIDER_KEY"] = ""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,6 @@ from unittest.mock import patch
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,6 @@ from unittest.mock import patch
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,6 @@ from unittest.mock import patch
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,6 @@ from pathlib import Path
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,6 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||||
FRONTEND = ROOT.parent / "frontend"
|
FRONTEND = ROOT.parent / "frontend"
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,6 @@ from unittest.mock import patch
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,6 @@ from datetime import date
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_MEDIA_ROOT"] = str(Path(tempfile.gettempdir()) / "kansho-opening-media")
|
||||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,6 @@ from pathlib import Path
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,6 @@ from pathlib import Path
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,6 @@ from pathlib import Path
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_MEDIA_ROOT"] = str(Path(tempfile.gettempdir()) / "kansho-trash-media")
|
||||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,6 @@ from pathlib import Path
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_MEDIA_ROOT"] = str(Path(tempfile.gettempdir()) / "kansho-mvp-journal-media")
|
||||||
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
os.environ["KANSHO_FAKE_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,6 @@ from unittest.mock import patch
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,6 @@ from unittest.mock import patch
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,6 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||||
REPO = ROOT.parent
|
REPO = ROOT.parent
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,6 @@ from pathlib import Path
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,6 @@ from pathlib import Path
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
os.environ["KANSHO_FAKE_DETECT"] = "1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,6 @@ import httpx
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
sys.path.insert(0, str(ROOT))
|
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_PROVIDER"] = "1"
|
||||||
os.environ["KANSHO_FAKE_DETECT"] = "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 |
|
| **Backend-Port** | 8005 | 8096 |
|
||||||
| **PostgreSQL** | nur Compose-Netz, DB `kansho` | nur Compose-Netz, DB `kansho_dev` |
|
| **PostgreSQL** | nur Compose-Netz, DB `kansho` | nur Compose-Netz, DB `kansho_dev` |
|
||||||
| **Domain** | kansho.jinkendo.de | dev.kansho.jinkendo.de |
|
| **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` |
|
| **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.
|
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
|
# 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`).
|
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-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` |
|
| `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.
|
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:
|
Additiv, ohne das Fachschema vorzuziehen:
|
||||||
|
|
||||||
- Windows-Checkout: SQLite unter `backend/data/` (gitignoriert) für die lokale App ohne Docker.
|
- 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`.
|
- 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).
|
- Identity-Mappings bleiben Klasse A in der Local Trusted Zone (jetzt: Pi-Postgres, nicht Provider).
|
||||||
- Kein Shared-Schema mit Mitai.
|
- 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 (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:
|
Empfohlene Reihenfolge in Welle 2:
|
||||||
|
|
||||||
1. Offene Host-Fragen beantworten und in `runtime_and_deploy.md` §1/§6 eintragen.
|
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`.
|
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`.
|
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` |
|
| 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` |
|
| 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` |
|
| 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.
|
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`.
|
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.
|
- 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`.
|
- 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.
|
- 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
|
```text
|
||||||
push develop → deploy-dev.yml → compose build/up → Healthcheck
|
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
|
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`.
|
**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.
|
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
|
## 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 |
|
| 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 |
|
| Gitea Workflows | `.gitea/workflows/` analog Kairo | entschieden 2026-09-07 |
|
||||||
| Auto-Rollback | nein | verworfen |
|
| 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 |
|
| 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 |
|
| 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")
|
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 "Python: $py"
|
||||||
Write-Host "Isolation: $($iso.Base)"
|
Write-Host "Isolation: $($iso.Base)"
|
||||||
Write-Host "No live provider/detect calls. Production DB/media untouched."
|
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
|
$runBackend = Test-PostgresSuite
|
||||||
if ($runBackend) {
|
if ($runBackend) {
|
||||||
Write-Host "Backend tests: Postgres $($env:DB_NAME)"
|
Write-Host "Backend pytest: Postgres $($env:DB_NAME)"
|
||||||
} else {
|
} 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 {
|
try {
|
||||||
$backendTests = Get-ChildItem -Path (Join-Path $root "backend\tests") -Filter "test_*.py" | Sort-Object Name
|
Invoke-Step -Name "backend pytest" -Action {
|
||||||
if (-not $backendTests) {
|
Push-Location (Join-Path $root "backend")
|
||||||
throw "no backend/tests/test_*.py files found"
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($test in $backendTests) {
|
|
||||||
$needsPg = $false
|
|
||||||
try {
|
try {
|
||||||
$needsPg = [bool](Select-String -Path $test.FullName -Pattern "configure_test_engine" -Quiet)
|
if ($runBackend) {
|
||||||
} catch {
|
& $py -m pytest tests -m "not slow" -ra --tb=short
|
||||||
$needsPg = $true
|
} else {
|
||||||
}
|
& $py -m pytest tests -m "unit and not slow" -ra --tb=short
|
||||||
if ($needsPg -and -not $runBackend) {
|
}
|
||||||
$script:ran++
|
} finally {
|
||||||
$script:passed++
|
Pop-Location
|
||||||
$script:results += [pscustomobject]@{ Name = "backend $($test.Name)"; Status = "SKIP" }
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
Invoke-Step -Name "backend $($test.Name)" -Action {
|
|
||||||
& $py $test.FullName
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,5 +151,5 @@ Write-Host ("{0} von {1} Schritten erfolgreich." -f $passed, $ran)
|
||||||
if ($failed) {
|
if ($failed) {
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
Write-Host "MVP local acceptance: OK"
|
Write-Host "local tests: OK"
|
||||||
exit 0
|
exit 0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user