Spaces:
Runtime error
Runtime error
feat: Story Mode v2 — cluster boundaries toggle, Path Finder, fraud pattern highlighting, and UI toolbar fixes
bb050ce | """ | |
| Automated unit/integration tests to verify Case Access Control. | |
| Ensures that an investigator can only access and edit their own cases (or guest sessions). | |
| """ | |
| from fastapi.testclient import TestClient | |
| from server import app | |
| # We use headers with "Bearer guest:<user_id>" to simulate active investigator sessions | |
| ALICE_HEADERS = {"Authorization": "Bearer guest:user_alice"} | |
| BOB_HEADERS = {"Authorization": "Bearer guest:user_bob"} | |
| def test_row_level_access_control(): | |
| client = TestClient(app) | |
| # 1. Create a case as Alice | |
| create_resp = client.post( | |
| "/api/v1/cases", | |
| json={ | |
| "title": "Alice's Investigation", | |
| "description": "Cyclic round tripping suspected.", | |
| "priority": "HIGH", | |
| "primary_subject_account_id": "ACC_11111", | |
| "subject_account_ids": ["ACC_11111"], | |
| "tags": ["suspicious", "ml_flagged"] | |
| }, | |
| headers=ALICE_HEADERS | |
| ) | |
| assert create_resp.status_code == 201, create_resp.text | |
| case = create_resp.json() | |
| case_id = case["id"] | |
| assert case["investigator_id"] == "user_alice" | |
| # 2. Alice can read her own case details | |
| get_resp = client.get(f"/api/v1/cases/{case_id}", headers=ALICE_HEADERS) | |
| assert get_resp.status_code == 200 | |
| assert get_resp.json()["id"] == case_id | |
| # 3. Bob attempts to read Alice's case -> 403 Forbidden | |
| get_resp_bob = client.get(f"/api/v1/cases/{case_id}", headers=BOB_HEADERS) | |
| assert get_resp_bob.status_code == 403, get_resp_bob.text | |
| assert "access" in get_resp_bob.json()["detail"].lower() | |
| # 4. Bob attempts to update Alice's case -> 403 Forbidden | |
| patch_resp_bob = client.patch( | |
| f"/api/v1/cases/{case_id}", | |
| json={"title": "Bob's Malicious Edit"}, | |
| headers=BOB_HEADERS | |
| ) | |
| assert patch_resp_bob.status_code == 403 | |
| # 5. Bob attempts to list cases -> Alice's case is NOT returned in Bob's list | |
| list_resp_bob = client.get("/api/v1/cases", headers=BOB_HEADERS) | |
| assert list_resp_bob.status_code == 200 | |
| items = list_resp_bob.json()["items"] | |
| for item in items: | |
| assert item["id"] != case_id | |
| # 6. Alice can successfully update her case | |
| patch_resp_alice = client.patch( | |
| f"/api/v1/cases/{case_id}", | |
| json={"title": "Alice's Authorized Edit"}, | |
| headers=ALICE_HEADERS | |
| ) | |
| assert patch_resp_alice.status_code == 200 | |
| assert patch_resp_alice.json()["title"] == "Alice's Authorized Edit" | |
| # 7. Alice can delete/soft-delete her case | |
| del_resp = client.delete(f"/api/v1/cases/{case_id}", headers=ALICE_HEADERS) | |
| assert del_resp.status_code == 204 | |