Commit prior to Claude Code implementation on VM
This commit is contained in:
69
tests/registry.py
Normal file
69
tests/registry.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
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)
|
||||
|
||||
# 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
|
||||
|
||||
# CRITICAL: Dispose the async engine created at import time.
|
||||
# It was bound to whatever event loop existed during collection.
|
||||
# When tests run, pytest-asyncio creates a NEW event loop.
|
||||
# The engine will lazily recreate its connection pool on that new loop.
|
||||
try:
|
||||
from core.database import engine
|
||||
loop = asyncio.new_event_loop()
|
||||
loop.run_until_complete(engine.dispose())
|
||||
loop.close()
|
||||
except Exception:
|
||||
pass # If disposal fails, tests will still try to proceed
|
||||
Reference in New Issue
Block a user