Files
lifeos-dev/tests/registry.py
M Dombaugh a427f7c781 fix: test suite green (156 passed, 7 skipped)
- Fix seed data to match actual DB schemas (capture.processed, daily_focus.completed, weblinks junction table)
- Add date/datetime coercion in BaseRepository for asyncpg compatibility
- Add UUID validation in BaseRepository.get() to prevent DataError on invalid UUIDs
- Fix focus.py and time_tracking.py date string handling for asyncpg
- Fix test ordering (action before delete) and skip destructive admin actions
- Fix form_factory FK resolution for flat UUID strings
- Fix route_report.py to use get_route_registry(app)
- Add asyncio_default_test_loop_scope=session to pytest.ini

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 21:30:27 +00:00

62 lines
2.3 KiB
Python

"""
Route registry - imports app, runs introspection once, exposes route data.
Disposes the async engine after introspection to avoid event loop conflicts.
"""
import os
import asyncio
# Point the app at the test database BEFORE importing
os.environ["DATABASE_URL"] = "postgresql+asyncpg://postgres:UCTOQDZiUhN8U@lifeos-db:5432/lifeos_test"
from main import app
from tests.introspect import introspect_app
# Build route registry from live app
ROUTE_REGISTRY = introspect_app(app)
ALL_ROUTES = ROUTE_REGISTRY # Alias used by test_crud_dynamic.py
# Classify routes into buckets for parametrized tests
GET_NO_PARAMS = [r for r in ROUTE_REGISTRY if "GET" in r.methods and not r.path_params]
GET_WITH_PARAMS = [r for r in ROUTE_REGISTRY if "GET" in r.methods and r.path_params]
POST_CREATE = [r for r in ROUTE_REGISTRY if "POST" in r.methods and r.kind == "create"]
POST_EDIT = [r for r in ROUTE_REGISTRY if "POST" in r.methods and r.kind == "edit"]
POST_DELETE = [r for r in ROUTE_REGISTRY if "POST" in r.methods and r.kind == "delete"]
POST_ACTION = [r for r in ROUTE_REGISTRY if "POST" in r.methods and r.kind in ("action", "toggle")]
# Map route prefixes to seed fixture keys
PREFIX_TO_SEED = {
"/domains": "domain",
"/areas": "area",
"/projects": "project",
"/tasks": "task",
"/notes": "note",
"/links": "link",
"/contacts": "contact",
"/lists": "list",
"/meetings": "meeting",
"/decisions": "decision",
"/weblinks": "weblink",
"/weblinks/folders": "weblink_folder",
"/appointments": "appointment",
"/focus": "focus",
"/capture": "capture",
"/time": "task",
"/files": None,
"/admin/trash": None,
}
def resolve_path(path_template, seeds):
"""Replace {id} placeholders with real seed UUIDs."""
import re
result = path_template
for param in re.findall(r"\{(\w+)\}", path_template):
# Find prefix for this route
for prefix, seed_key in sorted(PREFIX_TO_SEED.items(), key=lambda x: -len(x[0])):
if path_template.startswith(prefix) and seed_key and seed_key in seeds:
result = result.replace(f"{{{param}}}", str(seeds[seed_key]))
break
return result
# Note: Engine disposal is handled by the _reinit_engine fixture in conftest.py.
# It runs within the test event loop, ensuring the pool is recreated correctly.