fix(api): provide pseudonymous patients access to doctor care
This commit is contained in:
@@ -12,6 +12,7 @@ from app.db import identity_repo, repo, session
|
||||
from app.db.bootstrap import init_db
|
||||
from app.db.clinical import Dose, Prescription
|
||||
from app.main import app
|
||||
from app.services.anonymous_care import doctor_id as anonymous_doctor
|
||||
|
||||
MEM = "sqlite+aiosqlite:///:memory:"
|
||||
TOKEN = "b" * 32
|
||||
@@ -48,17 +49,17 @@ async def test_anonymous_tokens_are_fresh_and_store_no_personal_data(client: Asy
|
||||
async with session.get_identity_session() as s:
|
||||
assert await identity_repo.get_patient(s, first["token"]) is None
|
||||
async with session.get_clinical_session() as s:
|
||||
# Only a doctor-less pseudonymous profile: the dialog API serves known profiles only.
|
||||
# The care team sees a pseudonymous profile without an identity record.
|
||||
profile = await repo.get_profile(s, first["token"])
|
||||
assert profile.doctor_id == "" and profile.modules == []
|
||||
assert profile.doctor_id == anonymous_doctor() and profile.modules == []
|
||||
|
||||
|
||||
async def test_anonymous_focus_picks_the_module_without_a_doctor(client: AsyncClient) -> None:
|
||||
async def test_anonymous_focus_does_not_remove_access_to_doctor(client: AsyncClient) -> None:
|
||||
token = (await client.post("/api/v1/auth/anonymous", json={"focus": "ptsd"})).json()["token"]
|
||||
async with session.get_clinical_session() as s:
|
||||
profile = await repo.get_profile(s, token)
|
||||
assert profile.modules == ["veteran"] and not profile.doctor_id
|
||||
assert (await client.get(f"/api/v1/me/{token}/today")).json()["has_doctor"] is False
|
||||
assert profile.modules == ["veteran"] and profile.doctor_id == anonymous_doctor()
|
||||
assert (await client.get(f"/api/v1/me/{token}/today")).json()["has_doctor"] is True
|
||||
assert (await client.post("/api/v1/auth/anonymous", json={"focus": "flu"})).status_code == 422
|
||||
|
||||
|
||||
@@ -122,7 +123,7 @@ async def test_today_plan_and_week_marks(client: AsyncClient) -> None:
|
||||
async def test_today_for_anonymous_is_empty(client: AsyncClient) -> None:
|
||||
token = (await client.post("/api/v1/auth/anonymous")).json()["token"]
|
||||
body = (await client.get(f"/api/v1/me/{token}/today")).json()
|
||||
assert body["has_doctor"] is False and body["doses"] == [] and body["prescriptions"] == []
|
||||
assert body["has_doctor"] is True and body["doses"] == [] and body["prescriptions"] == []
|
||||
|
||||
|
||||
async def test_crisis_resources_come_from_config(client: AsyncClient) -> None:
|
||||
@@ -220,3 +221,67 @@ async def test_stage_one_starts_without_modules(client: AsyncClient, monkeypatch
|
||||
async def test_patient_app_has_its_own_demo_personas(client: AsyncClient) -> None:
|
||||
body = (await client.get("/api/v1/patient/demo-personas")).json()
|
||||
assert [p["token"] for p in body] == [p.token for p in fixtures.DEMO_PERSONAS]
|
||||
|
||||
|
||||
async def test_anonymous_is_visible_only_to_assigned_doctor(client: AsyncClient) -> None:
|
||||
first = (await client.post("/api/v1/auth/anonymous")).json()["token"]
|
||||
second = (await client.post("/api/v1/auth/anonymous")).json()["token"]
|
||||
items = (await client.get(f"/api/v1/doctors/{anonymous_doctor()}/patients")).json()
|
||||
names = {p["token"]: p["display_name"] for p in items}
|
||||
assert names[first].startswith("Пациент ") and names[first] != names[second]
|
||||
assert first not in names[first]
|
||||
own = await client.get(f"/api/v1/patients/{first}", params={"doctor_id": anonymous_doctor()})
|
||||
assert own.status_code == 200 and own.json()["patient"]["display_name"] == names[first]
|
||||
denied = await client.get(f"/api/v1/patients/{first}", params={"doctor_id": "another-doctor"})
|
||||
assert denied.status_code == 403
|
||||
assert (await client.get("/api/v1/doctors/another-doctor/patients")).json() == []
|
||||
|
||||
|
||||
async def test_old_anonymous_profile_requires_explicit_care_opt_in(client: AsyncClient) -> None:
|
||||
async with session.get_clinical_session() as s:
|
||||
await repo.upsert_profile(s, token=TOKEN, doctor_id="", modules=["veteran"])
|
||||
# Merely opening Today must not reveal an old private profile to a doctor.
|
||||
assert not (await client.get(f"/api/v1/me/{TOKEN}/today")).json()["has_doctor"]
|
||||
assert (await client.get(f"/api/v1/doctors/{anonymous_doctor()}/patients")).json() == []
|
||||
for _ in range(2):
|
||||
assert (await client.post(f"/api/v1/me/{TOKEN}/doctor/connect")).json() == {"has_doctor": True}
|
||||
async with session.get_clinical_session() as s:
|
||||
p = await repo.get_profile(s, TOKEN)
|
||||
assert p.doctor_id == anonymous_doctor() and p.modules == ["veteran"]
|
||||
async with session.get_identity_session() as s:
|
||||
assert await identity_repo.get_patient(s, TOKEN) is None
|
||||
|
||||
|
||||
async def test_connect_care_does_not_reassign_or_create_patients(client: AsyncClient) -> None:
|
||||
assert (await client.post(f"/api/v1/me/{TOKEN}/doctor/connect")).status_code == 404
|
||||
async with session.get_clinical_session() as s:
|
||||
await repo.upsert_profile(s, token=TOKEN, doctor_id="own-doctor")
|
||||
assert (await client.post(f"/api/v1/me/{TOKEN}/doctor/connect")).status_code == 200
|
||||
async with session.get_clinical_session() as s:
|
||||
assert (await repo.get_profile(s, TOKEN)).doctor_id == "own-doctor"
|
||||
|
||||
|
||||
async def test_screening_capability_respects_release_gate(client: AsyncClient, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("INTAKE_DEMO", "0")
|
||||
assert (await client.get("/api/v1/patient/capabilities")).json() == {"primary_screening": False}
|
||||
monkeypatch.setenv("INTAKE_DEMO", "1")
|
||||
assert (await client.get("/api/v1/patient/capabilities")).json() == {"primary_screening": True}
|
||||
|
||||
|
||||
async def test_anonymous_contact_request_reaches_doctor(client: AsyncClient, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("INTAKE_DEMO", "0")
|
||||
token = (await client.post("/api/v1/auth/anonymous")).json()["token"]
|
||||
for event in [
|
||||
{"type": "start"},
|
||||
{"type": "button", "button": "consent:yes"},
|
||||
{"type": "button", "button": "age:adult"},
|
||||
{"type": "button", "button": "menu:contact_doctor"},
|
||||
]:
|
||||
response = await client.post(f"/api/v1/dialog/{token}/events", json=event)
|
||||
assert response.status_code == 200
|
||||
items = (await client.get(f"/api/v1/doctors/{anonymous_doctor()}/patients")).json()
|
||||
assert items[0]["last_event"] == "Пациент просит врача связаться"
|
||||
card = (await client.get(f"/api/v1/patients/{token}", params={"doctor_id": anonymous_doctor()})).json()
|
||||
assert card["patient"]["last_event"] == items[0]["last_event"]
|
||||
async with session.get_identity_session() as s:
|
||||
assert await identity_repo.get_patient(s, token) is None
|
||||
|
||||
Reference in New Issue
Block a user