457 lines
13 KiB
Python
457 lines
13 KiB
Python
import uuid
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Medications
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_create_medication_minimal(client, medication_data):
|
|
r = client.post("/health/medications", json=medication_data)
|
|
assert r.status_code == 201
|
|
data = r.json()
|
|
assert "record_id" in data
|
|
assert data["kind"] == "prescription"
|
|
assert data["product_name"] == "Epiduo"
|
|
|
|
|
|
def test_create_medication_invalid_kind(client):
|
|
r = client.post(
|
|
"/health/medications", json={"kind": "invalid_kind", "product_name": "X"}
|
|
)
|
|
assert r.status_code == 422
|
|
|
|
|
|
def test_list_medications_empty(client):
|
|
r = client.get("/health/medications")
|
|
assert r.status_code == 200
|
|
assert r.json() == []
|
|
|
|
|
|
def test_list_filter_kind(client):
|
|
client.post(
|
|
"/health/medications", json={"kind": "prescription", "product_name": "A"}
|
|
)
|
|
client.post("/health/medications", json={"kind": "supplement", "product_name": "B"})
|
|
r = client.get("/health/medications?kind=supplement")
|
|
assert r.status_code == 200
|
|
data = r.json()
|
|
assert len(data) == 1
|
|
assert data[0]["kind"] == "supplement"
|
|
|
|
|
|
def test_list_filter_product_name(client):
|
|
client.post(
|
|
"/health/medications", json={"kind": "otc", "product_name": "Epiduo Forte"}
|
|
)
|
|
client.post("/health/medications", json={"kind": "otc", "product_name": "Panoxyl"})
|
|
r = client.get("/health/medications?product_name=epid")
|
|
assert r.status_code == 200
|
|
data = r.json()
|
|
assert len(data) == 1
|
|
assert "Epiduo" in data[0]["product_name"]
|
|
|
|
|
|
def test_get_medication(client, created_medication):
|
|
mid = created_medication["record_id"]
|
|
r = client.get(f"/health/medications/{mid}")
|
|
assert r.status_code == 200
|
|
assert r.json()["record_id"] == mid
|
|
|
|
|
|
def test_get_medication_not_found(client):
|
|
r = client.get(f"/health/medications/{uuid.uuid4()}")
|
|
assert r.status_code == 404
|
|
|
|
|
|
def test_update_medication(client, created_medication):
|
|
mid = created_medication["record_id"]
|
|
r = client.patch(
|
|
f"/health/medications/{mid}", json={"notes": "Take in the evening"}
|
|
)
|
|
assert r.status_code == 200
|
|
assert r.json()["notes"] == "Take in the evening"
|
|
|
|
|
|
def test_update_medication_not_found(client):
|
|
r = client.patch(f"/health/medications/{uuid.uuid4()}", json={"notes": "x"})
|
|
assert r.status_code == 404
|
|
|
|
|
|
def test_delete_medication_no_usages(client, created_medication):
|
|
mid = created_medication["record_id"]
|
|
r = client.delete(f"/health/medications/{mid}")
|
|
assert r.status_code == 204
|
|
r2 = client.get(f"/health/medications/{mid}")
|
|
assert r2.status_code == 404
|
|
|
|
|
|
def test_delete_medication_with_usages(client, created_medication):
|
|
mid = created_medication["record_id"]
|
|
# Create a usage
|
|
client.post(
|
|
f"/health/medications/{mid}/usages",
|
|
json={"valid_from": "2026-01-01T00:00:00"},
|
|
)
|
|
# Delete medication (should also delete usages)
|
|
r = client.delete(f"/health/medications/{mid}")
|
|
assert r.status_code == 204
|
|
r2 = client.get(f"/health/medications/{mid}")
|
|
assert r2.status_code == 404
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Usages
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_create_usage(client, created_medication):
|
|
mid = created_medication["record_id"]
|
|
r = client.post(
|
|
f"/health/medications/{mid}/usages",
|
|
json={
|
|
"valid_from": "2026-01-01T08:00:00",
|
|
"dose_value": 1.0,
|
|
"dose_unit": "pea",
|
|
},
|
|
)
|
|
assert r.status_code == 201
|
|
data = r.json()
|
|
assert "record_id" in data
|
|
assert data["medication_record_id"] == mid
|
|
assert data["dose_unit"] == "pea"
|
|
|
|
|
|
def test_create_usage_medication_not_found(client):
|
|
r = client.post(
|
|
f"/health/medications/{uuid.uuid4()}/usages",
|
|
json={"valid_from": "2026-01-01T00:00:00"},
|
|
)
|
|
assert r.status_code == 404
|
|
|
|
|
|
def test_list_usages_empty(client, created_medication):
|
|
mid = created_medication["record_id"]
|
|
r = client.get(f"/health/medications/{mid}/usages")
|
|
assert r.status_code == 200
|
|
assert r.json() == []
|
|
|
|
|
|
def test_list_usages_returns_entries(client, created_medication):
|
|
mid = created_medication["record_id"]
|
|
client.post(
|
|
f"/health/medications/{mid}/usages",
|
|
json={"valid_from": "2026-01-01T00:00:00"},
|
|
)
|
|
r = client.get(f"/health/medications/{mid}/usages")
|
|
assert r.status_code == 200
|
|
assert len(r.json()) == 1
|
|
|
|
|
|
def test_update_usage(client, created_medication):
|
|
mid = created_medication["record_id"]
|
|
r = client.post(
|
|
f"/health/medications/{mid}/usages",
|
|
json={"valid_from": "2026-01-01T00:00:00"},
|
|
)
|
|
uid = r.json()["record_id"]
|
|
|
|
r2 = client.patch(
|
|
f"/health/usages/{uid}", json={"dose_value": 2.5, "dose_unit": "mg"}
|
|
)
|
|
assert r2.status_code == 200
|
|
assert r2.json()["dose_value"] == 2.5
|
|
assert r2.json()["dose_unit"] == "mg"
|
|
|
|
|
|
def test_update_usage_not_found(client):
|
|
r = client.patch(f"/health/usages/{uuid.uuid4()}", json={"dose_value": 1.0})
|
|
assert r.status_code == 404
|
|
|
|
|
|
def test_delete_usage(client, created_medication):
|
|
mid = created_medication["record_id"]
|
|
r = client.post(
|
|
f"/health/medications/{mid}/usages",
|
|
json={"valid_from": "2026-01-01T00:00:00"},
|
|
)
|
|
uid = r.json()["record_id"]
|
|
|
|
r2 = client.delete(f"/health/usages/{uid}")
|
|
assert r2.status_code == 204
|
|
|
|
|
|
def test_delete_usage_not_found(client):
|
|
r = client.delete(f"/health/usages/{uuid.uuid4()}")
|
|
assert r.status_code == 404
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Lab results
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
LAB_RESULT_DATA = {
|
|
"collected_at": "2026-01-15T09:00:00",
|
|
"test_code": "718-7",
|
|
"test_name_original": "Hemoglobin",
|
|
"value_num": 14.5,
|
|
"unit_original": "g/dL",
|
|
"flag": "N",
|
|
}
|
|
|
|
|
|
def test_create_lab_result(client):
|
|
r = client.post("/health/lab-results", json=LAB_RESULT_DATA)
|
|
assert r.status_code == 201
|
|
data = r.json()
|
|
assert "record_id" in data
|
|
assert data["test_code"] == "718-7"
|
|
assert data["flag"] == "N"
|
|
|
|
|
|
def test_create_lab_result_invalid_code(client):
|
|
bad = {**LAB_RESULT_DATA, "test_code": "not-a-loinc-code"}
|
|
r = client.post("/health/lab-results", json=bad)
|
|
assert r.status_code == 422
|
|
|
|
|
|
def test_create_lab_result_invalid_flag(client):
|
|
bad = {**LAB_RESULT_DATA, "flag": "INVALID"}
|
|
r = client.post("/health/lab-results", json=bad)
|
|
assert r.status_code == 422
|
|
|
|
|
|
def test_list_lab_results_empty(client):
|
|
r = client.get("/health/lab-results")
|
|
assert r.status_code == 200
|
|
data = r.json()
|
|
assert data["items"] == []
|
|
assert data["total"] == 0
|
|
assert data["limit"] == 50
|
|
assert data["offset"] == 0
|
|
|
|
|
|
def test_list_filter_test_code(client):
|
|
client.post("/health/lab-results", json=LAB_RESULT_DATA)
|
|
client.post("/health/lab-results", json={**LAB_RESULT_DATA, "test_code": "2951-2"})
|
|
r = client.get("/health/lab-results?test_code=718-7")
|
|
assert r.status_code == 200
|
|
items = r.json()["items"]
|
|
assert len(items) == 1
|
|
assert items[0]["test_code"] == "718-7"
|
|
|
|
|
|
def test_list_filter_flag(client):
|
|
client.post("/health/lab-results", json={**LAB_RESULT_DATA, "flag": "N"})
|
|
client.post("/health/lab-results", json={**LAB_RESULT_DATA, "flag": "H"})
|
|
r = client.get("/health/lab-results?flag=H")
|
|
assert r.status_code == 200
|
|
items = r.json()["items"]
|
|
assert len(items) == 1
|
|
assert items[0]["flag"] == "H"
|
|
|
|
|
|
def test_list_filter_date_range(client):
|
|
client.post(
|
|
"/health/lab-results",
|
|
json={**LAB_RESULT_DATA, "collected_at": "2026-01-01T00:00:00"},
|
|
)
|
|
client.post(
|
|
"/health/lab-results",
|
|
json={**LAB_RESULT_DATA, "collected_at": "2026-06-01T00:00:00"},
|
|
)
|
|
r = client.get("/health/lab-results?from_date=2026-05-01T00:00:00")
|
|
assert r.status_code == 200
|
|
assert len(r.json()["items"]) == 1
|
|
|
|
|
|
def test_list_lab_results_search_and_pagination(client):
|
|
client.post(
|
|
"/health/lab-results",
|
|
json={
|
|
**LAB_RESULT_DATA,
|
|
"test_code": "718-7",
|
|
"test_name_original": "Hemoglobin",
|
|
},
|
|
)
|
|
client.post(
|
|
"/health/lab-results",
|
|
json={
|
|
**LAB_RESULT_DATA,
|
|
"test_code": "4548-4",
|
|
"test_name_original": "Hemoglobin A1c",
|
|
},
|
|
)
|
|
client.post(
|
|
"/health/lab-results",
|
|
json={**LAB_RESULT_DATA, "test_code": "2951-2", "test_name_original": "Sodium"},
|
|
)
|
|
|
|
r = client.get("/health/lab-results?q=hemo&limit=1&offset=1")
|
|
assert r.status_code == 200
|
|
data = r.json()
|
|
assert data["total"] == 2
|
|
assert data["limit"] == 1
|
|
assert data["offset"] == 1
|
|
assert len(data["items"]) == 1
|
|
assert "Hemoglobin" in data["items"][0]["test_name_original"]
|
|
|
|
|
|
def test_list_lab_results_sorted_newest_first(client):
|
|
client.post(
|
|
"/health/lab-results",
|
|
json={
|
|
**LAB_RESULT_DATA,
|
|
"collected_at": "2026-01-01T00:00:00",
|
|
"test_code": "1111-1",
|
|
},
|
|
)
|
|
client.post(
|
|
"/health/lab-results",
|
|
json={
|
|
**LAB_RESULT_DATA,
|
|
"collected_at": "2026-06-01T00:00:00",
|
|
"test_code": "2222-2",
|
|
},
|
|
)
|
|
r = client.get("/health/lab-results")
|
|
assert r.status_code == 200
|
|
items = r.json()["items"]
|
|
assert items[0]["collected_at"] == "2026-06-01T00:00:00"
|
|
|
|
|
|
def test_list_lab_results_test_code_sorted_numerically_for_same_date(client):
|
|
client.post(
|
|
"/health/lab-results",
|
|
json={
|
|
**LAB_RESULT_DATA,
|
|
"collected_at": "2026-06-01T00:00:00",
|
|
"test_code": "1111-1",
|
|
},
|
|
)
|
|
client.post(
|
|
"/health/lab-results",
|
|
json={
|
|
**LAB_RESULT_DATA,
|
|
"collected_at": "2026-06-01T00:00:00",
|
|
"test_code": "99-9",
|
|
},
|
|
)
|
|
client.post(
|
|
"/health/lab-results",
|
|
json={
|
|
**LAB_RESULT_DATA,
|
|
"collected_at": "2026-06-01T00:00:00",
|
|
"test_code": "718-7",
|
|
},
|
|
)
|
|
|
|
r = client.get("/health/lab-results")
|
|
assert r.status_code == 200
|
|
items = r.json()["items"]
|
|
assert [items[0]["test_code"], items[1]["test_code"], items[2]["test_code"]] == [
|
|
"99-9",
|
|
"718-7",
|
|
"1111-1",
|
|
]
|
|
|
|
|
|
def test_list_lab_results_latest_only_returns_one_per_test_code(client):
|
|
client.post(
|
|
"/health/lab-results",
|
|
json={
|
|
**LAB_RESULT_DATA,
|
|
"test_code": "1742-6",
|
|
"test_name_original": "ALT",
|
|
"collected_at": "2026-01-01T00:00:00",
|
|
"value_num": 30,
|
|
},
|
|
)
|
|
client.post(
|
|
"/health/lab-results",
|
|
json={
|
|
**LAB_RESULT_DATA,
|
|
"test_code": "1742-6",
|
|
"test_name_original": "ALT",
|
|
"collected_at": "2026-02-01T00:00:00",
|
|
"value_num": 60,
|
|
},
|
|
)
|
|
client.post(
|
|
"/health/lab-results",
|
|
json={
|
|
**LAB_RESULT_DATA,
|
|
"test_code": "2093-3",
|
|
"test_name_original": "Cholesterol total",
|
|
"collected_at": "2026-01-10T00:00:00",
|
|
"value_num": 200,
|
|
},
|
|
)
|
|
|
|
r = client.get("/health/lab-results?latest_only=true")
|
|
assert r.status_code == 200
|
|
data = r.json()
|
|
assert data["total"] == 2
|
|
assert len(data["items"]) == 2
|
|
alt = next(item for item in data["items"] if item["test_code"] == "1742-6")
|
|
assert alt["collected_at"] == "2026-02-01T00:00:00"
|
|
|
|
|
|
def test_get_lab_result(client):
|
|
r = client.post("/health/lab-results", json=LAB_RESULT_DATA)
|
|
rid = r.json()["record_id"]
|
|
r2 = client.get(f"/health/lab-results/{rid}")
|
|
assert r2.status_code == 200
|
|
assert r2.json()["record_id"] == rid
|
|
|
|
|
|
def test_get_lab_result_not_found(client):
|
|
r = client.get(f"/health/lab-results/{uuid.uuid4()}")
|
|
assert r.status_code == 404
|
|
|
|
|
|
def test_update_lab_result(client):
|
|
r = client.post("/health/lab-results", json=LAB_RESULT_DATA)
|
|
rid = r.json()["record_id"]
|
|
r2 = client.patch(
|
|
f"/health/lab-results/{rid}", json={"notes": "Recheck in 3 months"}
|
|
)
|
|
assert r2.status_code == 200
|
|
assert r2.json()["notes"] == "Recheck in 3 months"
|
|
|
|
|
|
def test_update_lab_result_can_clear_and_switch_value_type(client):
|
|
r = client.post(
|
|
"/health/lab-results",
|
|
json={
|
|
**LAB_RESULT_DATA,
|
|
"value_num": 12.5,
|
|
"unit_original": "mg/dl",
|
|
"flag": "H",
|
|
},
|
|
)
|
|
rid = r.json()["record_id"]
|
|
r2 = client.patch(
|
|
f"/health/lab-results/{rid}",
|
|
json={
|
|
"value_num": None,
|
|
"value_text": "not detected",
|
|
"flag": None,
|
|
"unit_original": None,
|
|
},
|
|
)
|
|
assert r2.status_code == 200
|
|
data = r2.json()
|
|
assert data["value_num"] is None
|
|
assert data["value_text"] == "not detected"
|
|
assert data["flag"] is None
|
|
assert data["unit_original"] is None
|
|
|
|
|
|
def test_delete_lab_result(client):
|
|
r = client.post("/health/lab-results", json=LAB_RESULT_DATA)
|
|
rid = r.json()["record_id"]
|
|
r2 = client.delete(f"/health/lab-results/{rid}")
|
|
assert r2.status_code == 204
|
|
r3 = client.get(f"/health/lab-results/{rid}")
|
|
assert r3.status_code == 404
|