diff --git a/tests/test_activities.py b/tests/test_activities.py new file mode 100644 index 0000000..0c58f32 --- /dev/null +++ b/tests/test_activities.py @@ -0,0 +1,63 @@ +"""Tests for FR-5 (Activity entity, polymorphic parent).""" + +from __future__ import annotations + +from datetime import UTC, datetime, timedelta + +import pytest +from httpx import AsyncClient + + +@pytest.mark.asyncio +async def test_create_activity_with_polymorphic_fk( + client: AsyncClient, auth_headers: dict[str, str], seed_data: dict +) -> None: + deal_id = seed_data["deal_ids"][0] + resp = await client.post( + "/api/v1/activities/", + json={"type": "task", "subject": "Follow up", "deal_id": deal_id}, + headers=auth_headers, + ) + assert resp.status_code == 201, resp.text + body = resp.json() + assert body["deal_id"] == deal_id + + +@pytest.mark.asyncio +async def test_list_activities_filter_overdue(client: AsyncClient, seed_data: dict) -> None: + # seed_data inserts 4 overdue activities + resp = await client.get("/api/v1/activities/?overdue=true", headers=seed_data["headers"]) + assert resp.status_code == 200 + body = resp.json() + now = datetime.now() # naive: SQLite returns naive datetimes + for a in body: + if a.get("due_date") and a.get("completed_at") is None: + due = a["due_date"] + if due.endswith("Z"): + due = due.replace("Z", "+00:00") + assert datetime.fromisoformat(due) < now + + +@pytest.mark.asyncio +async def test_complete_activity(client: AsyncClient, seed_data: dict) -> None: + a_id = seed_data["activity_ids"][0] + resp = await client.patch( + f"/api/v1/activities/{a_id}/complete", + json={"outcome": "Resolved via email"}, + headers=seed_data["headers"], + ) + assert resp.status_code == 200, resp.text + body = resp.json() + assert body["completed_at"] is not None + + +@pytest.mark.asyncio +async def test_activity_requires_at_least_one_parent( + client: AsyncClient, auth_headers: dict[str, str] +) -> None: + resp = await client.post( + "/api/v1/activities/", + json={"type": "task", "subject": "Orphan task"}, # no account/contact/deal + headers=auth_headers, + ) + assert resp.status_code == 422