"""Tests for the Phase 4 task-to-goal integration endpoints.""" import uuid from datetime import datetime, timedelta, timezone API_KEY = {"X-API-Key": "test-api-key"} async def _seed_profile() -> str: from app.auth import API_KEY_ADMIN_ID, ensure_api_key_admin from app.database import AsyncSessionLocal from app.models import DiscoveryConversation, DiscoveryProfile async with AsyncSessionLocal() as db: await ensure_api_key_admin(db) conv = DiscoveryConversation( id=str(uuid.uuid4()), user_id=API_KEY_ADMIN_ID, started_at=datetime.now(timezone.utc), ) db.add(conv) await db.commit() profile = DiscoveryProfile( id=str(uuid.uuid4()), user_id=API_KEY_ADMIN_ID, conversation_id=conv.id, generated_at=datetime.now(timezone.utc), triad="gut", short_term_goals="run a pilot welding cohort", long_term_goals="a statewide trades outfit", ) db.add(profile) await db.commit() return profile.id async def test_foundations_lists_six_with_profile_text(app_client): await _seed_profile() r = await app_client.get( "/discovery/integration/foundations", headers=API_KEY ) assert r.status_code == 200 items = r.json() assert len(items) == 6 keys = {i["key"] for i in items} assert keys == {"love", "strength", "mission", "vocation", "short_term", "long_term"} short = next(i for i in items if i["key"] == "short_term") assert short["text"] == "run a pilot welding cohort" async def test_foundations_without_profile_404(app_client): r = await app_client.get( "/discovery/integration/foundations", headers=API_KEY ) assert r.status_code == 404 async def test_create_task_mapping(app_client): await _seed_profile() r = await app_client.post( "/discovery/integration/task-mappings", headers=API_KEY, json={ "external_task_id": "task-123", "foundation": "short_term", "minutes": 90, "task_label": "weld practice", }, ) assert r.status_code == 200 body = r.json() assert body["foundation"] == "short_term" assert body["minutes"] == 90 assert body["external_task_id"] == "task-123" async def test_invalid_foundation_rejected(app_client): await _seed_profile() r = await app_client.post( "/discovery/integration/task-mappings", headers=API_KEY, json={"external_task_id": "t", "foundation": "vibes", "minutes": 10}, ) assert r.status_code == 400 async def test_negative_minutes_rejected(app_client): await _seed_profile() r = await app_client.post( "/discovery/integration/task-mappings", headers=API_KEY, json={"external_task_id": "t", "foundation": "love", "minutes": -5}, ) assert r.status_code == 400 async def _post_mapping(app_client, foundation, minutes, occurred_at=None): payload = { "external_task_id": str(uuid.uuid4()), "foundation": foundation, "minutes": minutes, } if occurred_at: payload["occurred_at"] = occurred_at return await app_client.post( "/discovery/integration/task-mappings", headers=API_KEY, json=payload ) async def test_work_patterns_aggregate(app_client): await _seed_profile() await _post_mapping(app_client, "short_term", 120) await _post_mapping(app_client, "short_term", 60) await _post_mapping(app_client, "vocation", 60) r = await app_client.get( "/discovery/integration/work-patterns?days=30", headers=API_KEY ) assert r.status_code == 200 data = r.json() assert data["total_minutes"] == 240 top = data["by_foundation"][0] assert top["foundation"] == "short_term" assert top["minutes"] == 180 assert "long_term" in data["neglected"] assert "mission" in data["neglected"] async def test_work_patterns_window_excludes_old(app_client): await _seed_profile() old = (datetime.now(timezone.utc) - timedelta(days=40)).isoformat() await _post_mapping(app_client, "love", 100, occurred_at=old) await _post_mapping(app_client, "strength", 50) # recent data = (await app_client.get( "/discovery/integration/work-patterns?days=7", headers=API_KEY )).json() assert data["total_minutes"] == 50 # only the recent one assert "love" in data["neglected"] async def test_task_mappings_requires_auth(app_client): r = await app_client.get("/discovery/integration/work-patterns") assert r.status_code == 401