"""End-to-end coverage of the core data model through the API: tenancy, the privacy seam, and the pre-auth actor shim.""" async def _create_user(client, email: str) -> str: resp = await client.post( "/api/v1/users", json={"email": email, "display_name": email} ) assert resp.status_code == 201, resp.text return resp.json()["id"] async def test_tree_and_person_flow(client): user_id = await _create_user(client, "keeper@example.com") headers = {"X-User-Id": user_id} resp = await client.post( "/api/v1/trees", json={"name": "Smith Family", "visibility": "private"}, headers=headers ) assert resp.status_code == 201, resp.text tree = resp.json() assert tree["visibility"] == "private" assert tree["owner_id"] == user_id tree_id = tree["id"] resp = await client.get("/api/v1/trees", headers=headers) assert resp.status_code == 200 assert len(resp.json()) == 1 resp = await client.post( f"/api/v1/trees/{tree_id}/persons", json={"given": "John", "surname": "Smith"}, headers=headers, ) assert resp.status_code == 201, resp.text person = resp.json() assert person["primary_name"] == "John Smith" assert person["tree_id"] == tree_id resp = await client.get(f"/api/v1/trees/{tree_id}/persons", headers=headers) assert resp.status_code == 200 assert len(resp.json()) == 1 async def test_private_tree_isolated_from_other_users(client): owner = await _create_user(client, "owner@example.com") other = await _create_user(client, "stranger@example.com") resp = await client.post( "/api/v1/trees", json={"name": "Private", "visibility": "private"}, headers={"X-User-Id": owner}, ) tree_id = resp.json()["id"] # A non-member cannot view a private tree, nor list its people. resp = await client.get(f"/api/v1/trees/{tree_id}", headers={"X-User-Id": other}) assert resp.status_code == 403 resp = await client.get(f"/api/v1/trees/{tree_id}/persons", headers={"X-User-Id": other}) assert resp.status_code == 403 async def test_public_tree_viewable_but_not_editable_by_non_member(client): owner = await _create_user(client, "owner2@example.com") viewer = await _create_user(client, "viewer2@example.com") resp = await client.post( "/api/v1/trees", json={"name": "Public", "visibility": "public"}, headers={"X-User-Id": owner}, ) tree_id = resp.json()["id"] # Visible to a non-member... resp = await client.get(f"/api/v1/trees/{tree_id}", headers={"X-User-Id": viewer}) assert resp.status_code == 200 # ...but not writable (not an editor). resp = await client.post( f"/api/v1/trees/{tree_id}/persons", json={"given": "Nope"}, headers={"X-User-Id": viewer}, ) assert resp.status_code == 403 async def test_duplicate_email_conflicts(client): await _create_user(client, "dupe@example.com") resp = await client.post("/api/v1/users", json={"email": "dupe@example.com"}) assert resp.status_code == 409 async def test_auth_required_without_header(client): resp = await client.get("/api/v1/trees") assert resp.status_code == 401