test: stabilize full test collection

This commit is contained in:
Vykos
2026-06-04 01:27:29 +02:00
committed by GitHub
parent 271489a10c
commit 5869106089
11 changed files with 125 additions and 7 deletions

View File

@@ -82,8 +82,7 @@ python -m uvicorn app:app --host 127.0.0.1 --port 7000
Requirements: Python 3.11+. Cookbook also needs `tmux` for background model Requirements: Python 3.11+. Cookbook also needs `tmux` for background model
downloads and serves. The app itself is lightweight; local model serving is the downloads and serves. The app itself is lightweight; local model serving is the
heavy part and depends on the model, runtime, GPU, and VRAM, so small hosts can heavy part and depends on the model, runtime, GPU, and VRAM, so small hosts can
connect to API or remote model servers instead. Use `--host 0.0.0.0` only when connect to API or remote model servers instead. Use `--host 0.0.0.0` only when you intentionally want LAN/reverse-proxy access.
you intentionally want LAN/reverse-proxy access.
### Apple Silicon ### Apple Silicon
Docker on macOS cannot use the Metal GPU. For GPU-accelerated Cookbook on an Docker on macOS cannot use the Metal GPU. For GPU-accelerated Cookbook on an

View File

@@ -6,6 +6,7 @@ calling do_manage_calendar with an rrule stores a single event carrying that RRU
""" """
import json import json
import sys
import tempfile import tempfile
import uuid import uuid
@@ -14,6 +15,21 @@ from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import NullPool from sqlalchemy.pool import NullPool
def _drop_fake_core_database():
parent = sys.modules.get("core")
attr = getattr(parent, "database", None) if parent is not None else None
mod = sys.modules.get("core.database") or attr
if mod is None or isinstance(getattr(mod, "__file__", None), str):
return
sys.modules.pop("core.database", None)
sys.modules.pop("src.database", None)
if parent is not None and attr is mod:
delattr(parent, "database")
_drop_fake_core_database()
import core.database as cdb import core.database as cdb
from core.database import CalendarEvent from core.database import CalendarEvent
@@ -32,6 +48,10 @@ def _bind_temp_db(monkeypatch):
# do_manage_calendar does `from core.database import SessionLocal` at call # do_manage_calendar does `from core.database import SessionLocal` at call
# time, so patch the module attribute to our temp DB — via monkeypatch so it # time, so patch the module attribute to our temp DB — via monkeypatch so it
# is RESTORED after each test and can't leak into later tests in the process. # is RESTORED after each test and can't leak into later tests in the process.
monkeypatch.setitem(sys.modules, "core.database", cdb)
parent = sys.modules.get("core")
if parent is not None:
monkeypatch.setattr(parent, "database", cdb, raising=False)
monkeypatch.setattr(cdb, "SessionLocal", _TS) monkeypatch.setattr(cdb, "SessionLocal", _TS)
yield yield

View File

@@ -1,6 +1,13 @@
import json import json
import sys
from types import SimpleNamespace from types import SimpleNamespace
_endpoint_resolver = sys.modules.get("src.endpoint_resolver")
if _endpoint_resolver is not None and not getattr(_endpoint_resolver, "__file__", None):
sys.modules.pop("src.endpoint_resolver", None)
sys.modules.pop("routes.model_routes", None)
sys.modules.pop("routes.chat_routes", None)
from routes import chat_routes from routes import chat_routes

View File

@@ -78,7 +78,12 @@ from core.middleware import require_admin # noqa: E402
# --- token minting: shown once, hashed at rest ----------------------------- # --- token minting: shown once, hashed at rest -----------------------------
def test_mint_token_returns_raw_once_and_stores_only_a_hash(): def test_mint_token_returns_raw_once_and_stores_only_a_hash(monkeypatch):
monkeypatch.setitem(sys.modules, "core.database", _db)
parent = sys.modules.get("core")
if parent is not None:
monkeypatch.setattr(parent, "database", _db, raising=False)
token_id, raw = P.mint_token("alice") token_id, raw = P.mint_token("alice")
assert raw.startswith("ody_") assert raw.startswith("ody_")
# The persisted row stores a bcrypt hash + prefix, never the plaintext. # The persisted row stores a bcrypt hash + prefix, never the plaintext.

View File

@@ -22,7 +22,8 @@ def test_background_status_poll_reconciles_into_local_tasks():
assert "const statusById = new Map(tasks.map(t => [t.session_id, t]));" in source assert "const statusById = new Map(tasks.map(t => [t.session_id, t]));" in source
assert "const nextStatus = live.status === 'completed'" in source assert "const nextStatus = live.status === 'completed'" in source
assert "? 'done'" in source assert "? 'done'" in source
assert "live.status === 'error'" in source assert ": (live.status === 'error'" in source
assert "? 'error'" in source
assert "_saveTasks(localTasks);" in source assert "_saveTasks(localTasks);" in source
assert "completedDeps.forEach(t => _refreshDepsAfterInstall(t));" in source assert "completedDeps.forEach(t => _refreshDepsAfterInstall(t));" in source

View File

@@ -13,6 +13,7 @@ while completing reliably everywhere.
""" """
import tempfile import tempfile
import sys
import uuid import uuid
from types import SimpleNamespace from types import SimpleNamespace
@@ -21,6 +22,21 @@ from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import NullPool from sqlalchemy.pool import NullPool
from unittest.mock import MagicMock from unittest.mock import MagicMock
def _drop_fake_core_database():
parent = sys.modules.get("core")
attr = getattr(parent, "database", None) if parent is not None else None
mod = sys.modules.get("core.database") or attr
if mod is None or isinstance(getattr(mod, "__file__", None), str):
return
sys.modules.pop("core.database", None)
sys.modules.pop("src.database", None)
if parent is not None and attr is mod:
delattr(parent, "database")
_drop_fake_core_database()
import core.database as cdb import core.database as cdb
import routes.document_routes as droutes import routes.document_routes as droutes
from core.database import Document from core.database import Document

View File

@@ -141,4 +141,3 @@ def test_build_anthropic_payload_alternating_roles():

View File

@@ -1,6 +1,23 @@
import pytest import pytest
import sys
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
def _drop_fake_core_database():
parent = sys.modules.get("core")
attr = getattr(parent, "database", None) if parent is not None else None
mod = sys.modules.get("core.database") or attr
if mod is None or isinstance(getattr(mod, "__file__", None), str):
return
sys.modules.pop("core.database", None)
sys.modules.pop("src.database", None)
if parent is not None and attr is mod:
delattr(parent, "database")
_drop_fake_core_database()
from core.database import Base, Session, ChatMessage from core.database import Base, Session, ChatMessage
from datetime import datetime from datetime import datetime
@@ -35,4 +52,3 @@ def test_sqlite_foreign_keys_cascade():
assert db.query(ChatMessage).count() == 0 assert db.query(ChatMessage).count() == 0
db.close() db.close()

View File

@@ -1,5 +1,6 @@
"""Regression tests for task-result delivery into chat sessions (issue #326).""" """Regression tests for task-result delivery into chat sessions (issue #326)."""
import asyncio import asyncio
import sys
import types as _types import types as _types
import pytest import pytest
@@ -11,6 +12,22 @@ if not isinstance(sqlalchemy, _types.ModuleType):
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
def _drop_fake_core_database():
parent = sys.modules.get("core")
attr = getattr(parent, "database", None) if parent is not None else None
mod = sys.modules.get("core.database") or attr
if mod is None or isinstance(getattr(mod, "__file__", None), str):
return
sys.modules.pop("core.database", None)
sys.modules.pop("src.database", None)
if parent is not None and attr is mod:
delattr(parent, "database")
_drop_fake_core_database()
import core.database as cdb
from core.database import Base, Session as DbSession from core.database import Base, Session as DbSession
from src.task_scheduler import TaskScheduler from src.task_scheduler import TaskScheduler
@@ -46,10 +63,15 @@ def _make_task():
) )
def test_session_delivery_survives_empty_database(): def test_session_delivery_survives_empty_database(monkeypatch):
"""On a fresh/wiped database there is no session to inherit endpoint/model """On a fresh/wiped database there is no session to inherit endpoint/model
from, so _resolve_defaults returns None. The delivery must still persist a from, so _resolve_defaults returns None. The delivery must still persist a
session instead of crashing on the NOT NULL constraint (issue #326).""" session instead of crashing on the NOT NULL constraint (issue #326)."""
monkeypatch.setitem(sys.modules, "core.database", cdb)
parent = sys.modules.get("core")
if parent is not None:
monkeypatch.setattr(parent, "database", cdb, raising=False)
db = _make_db() db = _make_db()
scheduler = TaskScheduler.__new__(TaskScheduler) scheduler = TaskScheduler.__new__(TaskScheduler)
scheduler._session_manager = None scheduler._session_manager = None

View File

@@ -1,8 +1,25 @@
"""Tests for topic keyword matching (src/topic_analyzer.py).""" """Tests for topic keyword matching (src/topic_analyzer.py)."""
import sys
from types import SimpleNamespace from types import SimpleNamespace
import pytest import pytest
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
def _drop_fake_core_database():
parent = sys.modules.get("core")
attr = getattr(parent, "database", None) if parent is not None else None
mod = sys.modules.get("core.database") or attr
if mod is None or isinstance(getattr(mod, "__file__", None), str):
return
sys.modules.pop("core.database", None)
sys.modules.pop("src.database", None)
if parent is not None and attr is mod:
delattr(parent, "database")
_drop_fake_core_database()
from core.database import Base, Session as DbSession, ChatMessage as DbChatMessage from core.database import Base, Session as DbSession, ChatMessage as DbChatMessage
from core.session_manager import SessionManager from core.session_manager import SessionManager
from src.topic_analyzer import analyze_topics from src.topic_analyzer import analyze_topics

View File

@@ -6,6 +6,22 @@ from datetime import datetime
# from it, so drop the stub here to load the real module under test. # from it, so drop the stub here to load the real module under test.
if "src.database" in sys.modules: if "src.database" in sys.modules:
del sys.modules["src.database"] del sys.modules["src.database"]
_core_database = sys.modules.get("core.database")
_core_database_all = getattr(_core_database, "__all__", None) if _core_database is not None else None
if (
_core_database is not None
and (
not getattr(_core_database, "__file__", None)
or (
_core_database_all is not None
and (
not isinstance(_core_database_all, (list, tuple, set))
or not all(isinstance(name, str) for name in _core_database_all)
)
)
)
):
del sys.modules["core.database"]
import pytest import pytest
from src.webhook_manager import validate_webhook_url from src.webhook_manager import validate_webhook_url