Files
zyt/app/tests/test_diagnosis_detail_contract.py
T
2026-08-11 17:39:41 +08:00

187 lines
6.7 KiB
Python

"""Exact endpoint and fail-closed tests for diagnosis-detail mutations."""
from __future__ import annotations
import time
from datetime import date
from pathlib import Path
from typing import Any
import pytest
from doctor_workstation.services.mock_repository import DemoDoctorRepository
from doctor_workstation.services.repository import RemoteDoctorRepository
class RecordingClient:
"""Minimal no-network client that preserves exact endpoint DTOs."""
def __init__(self) -> None:
self.get_calls: list[tuple[str, dict[str, Any]]] = []
self.post_calls: list[tuple[str, dict[str, Any]]] = []
def get(self, endpoint: str, params: dict[str, Any] | None = None) -> Any:
body = dict(params or {})
self.get_calls.append((endpoint, body))
if endpoint == "tcm.diagnosis/getImChatMessages":
return {"lists": [], "patient_im_id": "patient_301"}
return {"lists": [], "count": 0}
def post(self, endpoint: str, payload: dict[str, Any] | None = None) -> Any:
body = dict(payload or {})
self.post_calls.append((endpoint, body))
if endpoint == "tcm.diagnosis/createManualCallRecord":
return {"id": 88}
if endpoint == "tcm.diagnosis/generateMiniProgramQrcode":
return {"qrcode_url": "https://example.invalid/mini.png"}
if endpoint == "tcm.diagnosis/generateOrderQrcode":
return {"qrcode_url": "https://example.invalid/order.png"}
if endpoint == "order.order/create":
return {"id": 99, "order_no": "ORDER99"}
return {"ok": True}
def test_remote_detail_actions_use_exact_confirmed_endpoints() -> None:
client = RecordingClient()
repository = RemoteDoctorRepository(client) # type: ignore[arg-type]
today = date.today().isoformat()
repository.set_revisit_slot_start_offset(501, 4)
repository.add_blood_record(
diagnosis_id=501,
patient_id=301,
record_date=today,
record_time="09:30",
fasting_blood_sugar=6.2,
)
repository.add_diet_record(
diagnosis_id=501,
patient_id=301,
record_date=today,
breakfast_foods="燕麦",
breakfast_images=[],
lunch_images=[],
dinner_images=[],
)
repository.add_exercise_record(
diagnosis_id=501,
patient_id=301,
record_date=today,
exercise_type="步行",
duration=30,
intensity=2,
images=[],
)
repository.add_tracking_note(501, "继续观察")
repository.add_diagnosis_todo(501, "复测餐后血糖", int(time.time()) + 120)
repository.cancel_diagnosis_todo(77)
repository.list_call_records(501)
repository.create_manual_call_record(501)
repository.attach_local_call_recording(
501, "https://media.example.invalid/replay.mp4", call_record_id=88
)
assert repository.list_im_chat_messages(501)["lists"] == []
repository.sync_im_chat_messages(501)
repository.list_appointment_logs(501)
repository.generate_video_qrcode(1001, 301, 1001)
repository.generate_diagnosis_qrcode(501, 1001, 301, 1001)
repository.create_diagnosis_order(301, 2, 88.6, remark="检查费")
repository.generate_order_qrcode("ORDER99")
repository.cancel_diagnosis_appointment(101)
assert ("tcm.diagnosis/getCallRecords", {"diagnosis_id": 501}) in client.get_calls
assert (
"tcm.diagnosis/getImChatMessages",
{"diagnosis_id": 501, "only_archived": 1},
) in client.get_calls
assert ("tcm.diagnosis/guahaoLogList", {"id": 501}) in client.get_calls
assert (
"doctor.appointment/cancel",
{"id": 101},
) in client.post_calls
assert (
"tcm.diagnosis/setRevisitSlotStartOffset",
{"id": 501, "revisit_slot_start_offset": 4},
) in client.post_calls
assert (
"order.order/create",
{"patient_id": 301, "order_type": 2, "amount": 88.6, "remark": "检查费"},
) in client.post_calls
qr_payloads = [
body
for endpoint, body in client.post_calls
if endpoint == "tcm.diagnosis/generateMiniProgramQrcode"
]
assert qr_payloads[0] == {
"doctor_id": 1001,
"patient_id": 301,
"share_user_id": 1001,
"mini_program_path": "pages/login/login",
}
assert qr_payloads[1] == {
"diagnosis_id": 501,
"doctor_id": 1001,
"patient_id": 301,
"share_user_id": 1001,
}
@pytest.mark.parametrize(
("call", "message"),
[
(lambda repo: repo.set_revisit_slot_start_offset(501, 21), "between 0 and 20"),
(lambda repo: repo.add_tracking_note(501, ""), "1 to 1000"),
(
lambda repo: repo.add_diagnosis_todo(501, "稍后", int(time.time()) + 5),
"30 seconds",
),
(lambda repo: repo.cancel_diagnosis_todo(0), "positive"),
(lambda repo: repo.create_diagnosis_order(301, 9, 1), "between 1 and 8"),
(lambda repo: repo.cancel_diagnosis_appointment(0), "positive"),
],
)
def test_remote_detail_validation_fails_before_transport(call: Any, message: str) -> None:
client = RecordingClient()
repository = RemoteDoctorRepository(client) # type: ignore[arg-type]
with pytest.raises(ValueError, match=message):
call(repository)
assert client.get_calls == []
assert client.post_calls == []
def test_demo_detail_mutations_round_trip(tmp_path: Path) -> None:
repository = DemoDoctorRepository(today=date(2026, 8, 10))
blood = repository.add_blood_record(
diagnosis_id=501,
patient_id=301,
record_date="2026-08-10",
fasting_blood_sugar=5.8,
)
assert blood["id"] > 0
assert (
repository.get_tracking_window(501, start_date="2026-08-10", end_date="2026-08-10")[
"blood_records"
][-1]["fasting_blood_sugar"]
== 5.8
)
todo = repository.add_diagnosis_todo(501, "今晚回访", int(time.time()) + 120)
cancelled = repository.cancel_diagnosis_todo(todo["id"])
assert cancelled["status_text"] == "已取消"
replay = tmp_path / "replay.mp4"
replay.write_bytes(b"demo-video")
uploaded = repository.upload_call_recording(replay, 501)
assert uploaded["file_url"].startswith("/demo/uploads/video/")
assert uploaded["file_url"] in repository.list_call_records(501)[0]["recording_urls_list"]
archive = repository.list_im_chat_messages(501, only_archived=True)
assert archive["only_archived"] is True
assert {row["msg_type"] for row in archive["lists"]} >= {"text", "image", "file"}
assert repository.sync_im_chat_messages(501)["queued"] is True
repository.set_revisit_slot_start_offset(501, 7)
assert repository.get_diagnosis_detail(501)["diagnosis"]["revisit_slot_start_offset"] == 7
assert repository.cancel_diagnosis_appointment(101)["status"] == 2