"""Tests for the `intake` work-record entity (CUST-WP-0061-T01, work-record stage 3). Real PostgreSQL test database, no mocking — matches tests/test_routers_core.py conventions. """ from __future__ import annotations import pytest async def _create_domain(client, slug="testdomain", name="Test Domain"): r = await client.post("/domains/", json={"slug": slug, "name": name}) assert r.status_code == 201, r.text return r.json() async def _create_topic(client, domain_slug="testdomain", slug="testtopic", title="Test Topic"): r = await client.post("/topics/", json={ "slug": slug, "title": title, "domain": domain_slug, }) assert r.status_code == 201, r.text return r.json() async def _create_intake(client, topic_id=None, workplan_id=None, repo_id=None, title="Qonto MCP mailing", lane="green", **extra): payload = {"title": title, "lane": lane, **extra} if topic_id is not None: payload["topic_id"] = topic_id if workplan_id is not None: payload["workplan_id"] = workplan_id if repo_id is not None: payload["repo_id"] = repo_id r = await client.post("/intakes/", json=payload) assert r.status_code == 201, r.text return r.json() class TestIntakeCreateAndRead: async def test_create_requires_a_scope(self, client): r = await client.post("/intakes/", json={"title": "orphan intake", "lane": "green"}) assert r.status_code == 422 async def test_create_with_topic_scope(self, client): await _create_domain(client) topic = await _create_topic(client) body = await _create_intake(client, topic_id=topic["id"]) assert body["status"] == "open" assert body["lane"] == "green" assert body["outcome"] is None async def test_create_preserves_explicit_authoritative_id(self, client): await _create_domain(client) topic = await _create_topic(client) expected = "11111111-1111-4111-8111-111111111111" r = await client.post( "/intakes/", json={"id": expected, "title": "file intake", "topic_id": topic["id"]}, ) assert r.status_code == 201 assert r.json()["id"] == expected async def test_get_unknown_intake_404s(self, client): r = await client.get("/intakes/00000000-0000-0000-0000-000000000000") assert r.status_code == 404 async def test_list_filters_by_topic(self, client): await _create_domain(client) topic = await _create_topic(client) other_topic = await _create_topic(client, slug="other-topic", title="Other") await _create_intake(client, topic_id=topic["id"], title="in scope") await _create_intake(client, topic_id=other_topic["id"], title="out of scope") r = await client.get("/intakes/", params={"topic_id": topic["id"]}) assert r.status_code == 200 titles = [i["title"] for i in r.json()] assert titles == ["in scope"] class TestIntakeLifecycle: async def test_route_moves_open_to_routed(self, client): await _create_domain(client) topic = await _create_topic(client) intake = await _create_intake(client, topic_id=topic["id"]) r = await client.post(f"/intakes/{intake['id']}/route", json={"routed_note": "green lane, ready"}) assert r.status_code == 200 assert r.json()["status"] == "routed" assert r.json()["routed_note"] == "green lane, ready" async def test_route_from_closed_is_rejected(self, client): await _create_domain(client) topic = await _create_topic(client) intake = await _create_intake(client, topic_id=topic["id"]) await client.post(f"/intakes/{intake['id']}/close", json={"outcome": "declined"}) r = await client.post(f"/intakes/{intake['id']}/route", json={}) assert r.status_code == 409 async def test_close_declined_does_not_require_promoted_to(self, client): await _create_domain(client) topic = await _create_topic(client) intake = await _create_intake(client, topic_id=topic["id"]) r = await client.post(f"/intakes/{intake['id']}/close", json={"outcome": "declined"}) assert r.status_code == 200 body = r.json() assert body["status"] == "closed" assert body["outcome"] == "declined" assert body["closed_at"] is not None async def test_close_promoted_without_promoted_to_is_rejected(self, client): await _create_domain(client) topic = await _create_topic(client) intake = await _create_intake(client, topic_id=topic["id"]) r = await client.post(f"/intakes/{intake['id']}/close", json={"outcome": "promoted"}) assert r.status_code == 422 async def test_close_promoted_with_promoted_to_succeeds(self, client): await _create_domain(client) topic = await _create_topic(client) intake = await _create_intake(client, topic_id=topic["id"], title="AWQ-010") r = await client.post( f"/intakes/{intake['id']}/close", json={"outcome": "promoted", "promoted_to": "BINKY-WP-0005"}, ) assert r.status_code == 200 body = r.json() assert body["outcome"] == "promoted" assert body["promoted_to"] == "BINKY-WP-0005" async def test_close_already_closed_is_rejected(self, client): await _create_domain(client) topic = await _create_topic(client) intake = await _create_intake(client, topic_id=topic["id"]) await client.post(f"/intakes/{intake['id']}/close", json={"outcome": "declined"}) r = await client.post(f"/intakes/{intake['id']}/close", json={"outcome": "absorbed"}) assert r.status_code == 409 class TestIntakeNotes: async def test_add_note_appears_on_intake(self, client): await _create_domain(client) topic = await _create_topic(client) intake = await _create_intake(client, topic_id=topic["id"]) r = await client.post( f"/intakes/{intake['id']}/notes", json={"content": "vetted, looks real", "author": "agt-rhythm-bridge"}, ) assert r.status_code == 201 notes = r.json()["notes"] assert len(notes) == 1 assert notes[0]["content"] == "vetted, looks real" assert notes[0]["author"] == "agt-rhythm-bridge" class TestIntakeUUIDv7: async def test_id_is_uuidv7(self, client): await _create_domain(client) topic = await _create_topic(client) intake = await _create_intake(client, topic_id=topic["id"]) import uuid u = uuid.UUID(intake["id"]) assert u.version == 7