Commit prior to Claude Code implementation on VM
This commit is contained in:
202
tests/conftest.py
Normal file
202
tests/conftest.py
Normal file
@@ -0,0 +1,202 @@
|
||||
"""
|
||||
Test fixtures - uses psycopg2 (sync) for seed data to avoid event loop conflicts.
|
||||
Seeds are session-scoped and committed so the app can see them via its own engine.
|
||||
"""
|
||||
import asyncio
|
||||
import uuid
|
||||
import pytest
|
||||
import psycopg2
|
||||
from httpx import AsyncClient, ASGITransport
|
||||
|
||||
from tests.registry import app
|
||||
|
||||
TEST_DB_DSN = "host=lifeos-db port=5432 dbname=lifeos_test user=postgres password=UCTOQDZiUhN8U"
|
||||
|
||||
# ── Fixed seed UUIDs (stable across test runs) ──────────────
|
||||
SEED_IDS = {
|
||||
"domain": "a0000000-0000-0000-0000-000000000001",
|
||||
"area": "a0000000-0000-0000-0000-000000000002",
|
||||
"project": "a0000000-0000-0000-0000-000000000003",
|
||||
"task": "a0000000-0000-0000-0000-000000000004",
|
||||
"contact": "a0000000-0000-0000-0000-000000000005",
|
||||
"note": "a0000000-0000-0000-0000-000000000006",
|
||||
"meeting": "a0000000-0000-0000-0000-000000000007",
|
||||
"decision": "a0000000-0000-0000-0000-000000000008",
|
||||
"appointment": "a0000000-0000-0000-0000-000000000009",
|
||||
"weblink_folder": "a0000000-0000-0000-0000-00000000000a",
|
||||
"list": "a0000000-0000-0000-0000-00000000000b",
|
||||
"link": "a0000000-0000-0000-0000-00000000000c",
|
||||
"weblink": "a0000000-0000-0000-0000-00000000000d",
|
||||
"capture": "a0000000-0000-0000-0000-00000000000e",
|
||||
"focus": "a0000000-0000-0000-0000-00000000000f",
|
||||
}
|
||||
|
||||
|
||||
# ── Session-scoped event loop ───────────────────────────────
|
||||
# All async tests share one loop so the app's engine pool stays valid.
|
||||
@pytest.fixture(scope="session")
|
||||
def event_loop():
|
||||
loop = asyncio.new_event_loop()
|
||||
yield loop
|
||||
loop.close()
|
||||
|
||||
|
||||
# ── Sync DB connection for seed management ──────────────────
|
||||
@pytest.fixture(scope="session")
|
||||
def sync_conn():
|
||||
conn = psycopg2.connect(TEST_DB_DSN)
|
||||
conn.autocommit = False
|
||||
yield conn
|
||||
conn.close()
|
||||
|
||||
|
||||
# ── Seed data (session-scoped, committed) ───────────────────
|
||||
@pytest.fixture(scope="session")
|
||||
def all_seeds(sync_conn):
|
||||
"""Insert all seed data once. Committed so the app's engine can see it."""
|
||||
cur = sync_conn.cursor()
|
||||
d = SEED_IDS
|
||||
|
||||
try:
|
||||
# Domain
|
||||
cur.execute("""
|
||||
INSERT INTO domains (id, name, color, description, sort_order, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test Domain', '#FF5733', 'Auto test domain', 0, false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["domain"],))
|
||||
|
||||
# Area
|
||||
cur.execute("""
|
||||
INSERT INTO areas (id, name, domain_id, description, status, sort_order, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test Area', %s, 'Auto test area', 'active', 0, false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["area"], d["domain"]))
|
||||
|
||||
# Project
|
||||
cur.execute("""
|
||||
INSERT INTO projects (id, name, domain_id, area_id, description, status, priority, sort_order, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test Project', %s, %s, 'Auto test project', 'active', 2, 0, false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["project"], d["domain"], d["area"]))
|
||||
|
||||
# Task
|
||||
cur.execute("""
|
||||
INSERT INTO tasks (id, title, domain_id, project_id, description, priority, status, sort_order, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test Task', %s, %s, 'Auto test task', 2, 'todo', 0, false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["task"], d["domain"], d["project"]))
|
||||
|
||||
# Contact
|
||||
cur.execute("""
|
||||
INSERT INTO contacts (id, first_name, last_name, company, email, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test', 'Contact', 'TestCorp', 'test@example.com', false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["contact"],))
|
||||
|
||||
# Note
|
||||
cur.execute("""
|
||||
INSERT INTO notes (id, title, domain_id, body, content_format, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test Note', %s, 'Test body content', 'markdown', false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["note"], d["domain"]))
|
||||
|
||||
# Meeting
|
||||
cur.execute("""
|
||||
INSERT INTO meetings (id, title, meeting_date, status, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test Meeting', '2025-06-15', 'scheduled', false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["meeting"],))
|
||||
|
||||
# Decision
|
||||
cur.execute("""
|
||||
INSERT INTO decisions (id, title, status, impact, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test Decision', 'decided', 'high', false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["decision"],))
|
||||
|
||||
# Appointment
|
||||
cur.execute("""
|
||||
INSERT INTO appointments (id, title, start_at, end_at, all_day, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test Appointment', '2025-06-15 10:00:00', '2025-06-15 11:00:00', false, false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["appointment"],))
|
||||
|
||||
# Weblink folder
|
||||
cur.execute("""
|
||||
INSERT INTO weblink_folders (id, name, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test Folder', false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["weblink_folder"],))
|
||||
|
||||
# List
|
||||
cur.execute("""
|
||||
INSERT INTO lists (id, name, domain_id, project_id, list_type, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test List', %s, %s, 'checklist', false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["list"], d["domain"], d["project"]))
|
||||
|
||||
# Link
|
||||
cur.execute("""
|
||||
INSERT INTO links (id, label, url, domain_id, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test Link', 'https://example.com', %s, false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["link"], d["domain"]))
|
||||
|
||||
# Weblink
|
||||
cur.execute("""
|
||||
INSERT INTO weblinks (id, label, url, folder_id, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test Weblink', 'https://example.com/wl', %s, false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["weblink"], d["weblink_folder"]))
|
||||
|
||||
# Capture
|
||||
cur.execute("""
|
||||
INSERT INTO capture (id, raw_text, status, is_deleted, created_at, updated_at)
|
||||
VALUES (%s, 'Test capture item', 'pending', false, now(), now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["capture"],))
|
||||
|
||||
# Daily focus
|
||||
cur.execute("""
|
||||
INSERT INTO daily_focus (id, task_id, focus_date, is_completed, created_at)
|
||||
VALUES (%s, %s, CURRENT_DATE, false, now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (d["focus"], d["task"]))
|
||||
|
||||
sync_conn.commit()
|
||||
except Exception as e:
|
||||
sync_conn.rollback()
|
||||
raise RuntimeError(f"Seed data insertion failed: {e}") from e
|
||||
|
||||
yield d
|
||||
|
||||
# Cleanup: delete all seed data (reverse dependency order)
|
||||
try:
|
||||
cur.execute("DELETE FROM daily_focus WHERE id = %s", (d["focus"],))
|
||||
cur.execute("DELETE FROM capture WHERE id = %s", (d["capture"],))
|
||||
cur.execute("DELETE FROM weblinks WHERE id = %s", (d["weblink"],))
|
||||
cur.execute("DELETE FROM links WHERE id = %s", (d["link"],))
|
||||
cur.execute("DELETE FROM lists WHERE id = %s", (d["list"],))
|
||||
cur.execute("DELETE FROM weblink_folders WHERE id = %s", (d["weblink_folder"],))
|
||||
cur.execute("DELETE FROM appointments WHERE id = %s", (d["appointment"],))
|
||||
cur.execute("DELETE FROM decisions WHERE id = %s", (d["decision"],))
|
||||
cur.execute("DELETE FROM meetings WHERE id = %s", (d["meeting"],))
|
||||
cur.execute("DELETE FROM notes WHERE id = %s", (d["note"],))
|
||||
cur.execute("DELETE FROM contacts WHERE id = %s", (d["contact"],))
|
||||
cur.execute("DELETE FROM tasks WHERE id = %s", (d["task"],))
|
||||
cur.execute("DELETE FROM projects WHERE id = %s", (d["project"],))
|
||||
cur.execute("DELETE FROM areas WHERE id = %s", (d["area"],))
|
||||
cur.execute("DELETE FROM domains WHERE id = %s", (d["domain"],))
|
||||
sync_conn.commit()
|
||||
except Exception:
|
||||
sync_conn.rollback()
|
||||
finally:
|
||||
cur.close()
|
||||
|
||||
|
||||
# ── HTTP client (function-scoped) ───────────────────────────
|
||||
@pytest.fixture
|
||||
async def client():
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as c:
|
||||
yield c
|
||||
Reference in New Issue
Block a user