Load the test schema once and truncate data between files.
All checks were successful
Deploy Development / deploy (push) Successful in 56s
Test Suite / pytest-backend (push) Successful in 2m43s
Test Suite / smoke-dev (push) Successful in 0s
Test Suite / frontend-build (push) Successful in 15s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Lars 2026-09-07 15:07:33 +02:00
parent 3c0213ae74
commit 79e4fd0d4b
8 changed files with 78 additions and 25 deletions

View File

@ -59,7 +59,7 @@ jobs:
export KANSHO_PROVIDER_KEY= export KANSHO_PROVIDER_KEY=
export KANSHO_DETECT_PROVIDER_KEY= export KANSHO_DETECT_PROVIDER_KEY=
pip install -q -r requirements-dev.txt pip install -q -r requirements-dev.txt
python -m pytest tests -m "not slow" -ra -vv --tb=short python -m pytest tests -m "not slow" -ra --tb=short
' '
smoke-dev: smoke-dev:

View File

@ -555,16 +555,21 @@ def _seed_runtime(conn: Any) -> None:
seed_generation_instructions(conn) seed_generation_instructions(conn)
def init_db() -> None: def refresh_runtime_seed() -> None:
if use_postgres(): """Re-seed catalog rows after a data truncate. Schema must already exist."""
from db_init import ensure_postgres_ready
ensure_postgres_ready()
with get_db() as conn: with get_db() as conn:
_seed_runtime(conn) _seed_runtime(conn)
from writing_profile_store import bootstrap_from_existing from writing_profile_store import bootstrap_from_existing
bootstrap_from_existing() bootstrap_from_existing()
def init_db() -> None:
if use_postgres():
from db_init import ensure_postgres_ready
ensure_postgres_ready()
refresh_runtime_seed()
return return
schema = SCHEMA_PATH.read_text(encoding="utf-8") schema = SCHEMA_PATH.read_text(encoding="utf-8")

View File

@ -36,6 +36,13 @@ SQLITE_HISTORY_MIGRATIONS = (
"021_voice_legacy_immutable", "021_voice_legacy_immutable",
) )
_LEADING_DIGITS = re.compile(r"^(\d{3})_.*\.sql$") _LEADING_DIGITS = re.compile(r"^(\d{3})_.*\.sql$")
_schema_ready = False
def mark_schema_dirty() -> None:
"""Call after DROP SCHEMA so the next init_db reloads schema.sql."""
global _schema_ready
_schema_ready = False
def _connect_raw(): def _connect_raw():
@ -137,13 +144,17 @@ def apply_schema_and_migrations() -> None:
def ensure_postgres_ready() -> None: def ensure_postgres_ready() -> None:
global _schema_ready
if not use_postgres(): if not use_postgres():
return return
if _schema_ready:
return
wait_for_postgres() wait_for_postgres()
try: try:
apply_schema_and_migrations() apply_schema_and_migrations()
except Exception: except Exception:
sys.exit(1) sys.exit(1)
_schema_ready = True
def main() -> None: def main() -> None:

View File

@ -1,9 +1,9 @@
"""pytest runner for the existing test_*.py scripts. """pytest runner for the existing test_*.py scripts.
Each file stays one case (`main()`) until it is split into native pytest Each file stays one case (`main()`) until it is split into native pytest
functions. Integration files reset `kansho_test` only; `kansho_dev` is never functions. Schema is loaded once per session; each integration file only
the test target. Provider keys and fake flags are restored per file so the truncates data so kansho_dev is never the target and CI does not reload
shared pytest process matches the old one-script-per-process isolation. schema.sql twenty times.
""" """
from __future__ import annotations from __future__ import annotations
@ -21,6 +21,7 @@ from tests.harness import ( # noqa: E402
apply_module_env, apply_module_env,
is_postgres_test_database, is_postgres_test_database,
prepare_test_env, prepare_test_env,
reset_postgres_data,
reset_postgres_schema, reset_postgres_schema,
) )
@ -64,16 +65,8 @@ class KanshoScriptModule(pytest.Module):
yield pytest.Function.from_parent(self, name="test_main", callobj=main) yield pytest.Function.from_parent(self, name="test_main", callobj=main)
@pytest.fixture(scope="module", autouse=True) @pytest.fixture(scope="session")
def _module_backend(request: pytest.FixtureRequest): def _session_postgres():
source = Path(str(request.path))
stem = source.stem
saved = dict(os.environ)
try:
apply_module_env(source)
if stem in UNIT_MODULES:
yield
return
if not is_postgres_test_database(): if not is_postgres_test_database():
pytest.skip( pytest.skip(
"integration requires KANSHO_DB_BACKEND=postgres and DB_NAME ending _test " "integration requires KANSHO_DB_BACKEND=postgres and DB_NAME ending _test "
@ -84,6 +77,21 @@ def _module_backend(request: pytest.FixtureRequest):
init_db() init_db()
yield yield
@pytest.fixture(scope="module", autouse=True)
def _module_backend(request: pytest.FixtureRequest):
source = Path(str(request.path))
stem = source.stem
saved = dict(os.environ)
try:
apply_module_env(source)
if stem in UNIT_MODULES:
yield
return
request.getfixturevalue("_session_postgres")
reset_postgres_data()
yield
finally: finally:
os.environ.clear() os.environ.clear()
os.environ.update(saved) os.environ.update(saved)

View File

@ -89,6 +89,7 @@ def _require_test_database() -> None:
def reset_postgres_schema() -> None: def reset_postgres_schema() -> None:
import psycopg import psycopg
from db_init import mark_schema_dirty
from sql_compat import postgres_connect_kwargs from sql_compat import postgres_connect_kwargs
kwargs = postgres_connect_kwargs() kwargs = postgres_connect_kwargs()
@ -103,6 +104,36 @@ def reset_postgres_schema() -> None:
conn.execute("GRANT ALL ON SCHEMA public TO CURRENT_USER") conn.execute("GRANT ALL ON SCHEMA public TO CURRENT_USER")
finally: finally:
conn.close() conn.close()
mark_schema_dirty()
def reset_postgres_data() -> None:
"""Clear rows on kansho_test without reloading schema.sql."""
import psycopg
from sql_compat import postgres_connect_kwargs
kwargs = postgres_connect_kwargs()
if "conninfo" in kwargs:
conn = psycopg.connect(kwargs["conninfo"], autocommit=True)
else:
conn = psycopg.connect(autocommit=True, **kwargs)
try:
rows = conn.execute(
"""
SELECT tablename FROM pg_tables
WHERE schemaname = 'public' AND tablename <> 'schema_migrations'
"""
).fetchall()
names = [row[0] for row in rows]
if names:
joined = ", ".join('"' + name.replace('"', '""') + '"' for name in names)
conn.execute(f"TRUNCATE TABLE {joined} RESTART IDENTITY CASCADE")
finally:
conn.close()
from db import refresh_runtime_seed
refresh_runtime_seed()
def configure_test_engine() -> None: def configure_test_engine() -> None:

View File

@ -209,10 +209,9 @@ def main() -> None:
test_compile_flags() test_compile_flags()
test_budget_omission_in_effective_trace() test_budget_omission_in_effective_trace()
from tests.harness import reset_postgres_schema from tests.harness import reset_postgres_data
reset_postgres_schema() reset_postgres_data()
init_db()
reset_debug() reset_debug()
with TestClient(app) as client: with TestClient(app) as client:
setup = client.post( setup = client.post(

View File

@ -268,10 +268,9 @@ def test_later_semantic_seed_creates_successor() -> None:
def test_legacy_generate_trace_and_prompt() -> None: def test_legacy_generate_trace_and_prompt() -> None:
from tests.harness import reset_postgres_schema from tests.harness import reset_postgres_data
reset_postgres_schema() reset_postgres_data()
init_db()
client = TestClient(app) client = TestClient(app)
setup = client.post( setup = client.post(
"/api/auth/setup", "/api/auth/setup",

View File

@ -227,7 +227,7 @@ Gateway-Verfahren und offener Security Layer: `privacy_gateway.md` §9.19.2 u
Tests beweisen API-Verträge und Invarianten, nicht Dialogqualität. Tests beweisen API-Verträge und Invarianten, nicht Dialogqualität.
**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`. **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`. Schema einmal pro Lauf, dazwischen nur Daten-Truncate — nicht 22× `schema.sql`.
--- ---