更新
This commit is contained in:
@@ -278,8 +278,10 @@ def test_field_order_density_and_conditional_channel_row(
|
||||
application.processEvents()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("mode", ["video", "text"])
|
||||
def test_roster_slot_states_conflict_refresh_and_submit_contract(
|
||||
application: QApplication,
|
||||
mode: str,
|
||||
) -> None:
|
||||
repository = _VisualRepository(today_conflict=True)
|
||||
host, drawer = _show_drawer(
|
||||
@@ -318,6 +320,10 @@ def test_roster_slot_states_conflict_refresh_and_submit_contract(
|
||||
assert unavailable.status_label.isVisible()
|
||||
assert unavailable.accessibleName() == "10:00-10:30 已约"
|
||||
available.click()
|
||||
if mode == "text":
|
||||
drawer.text_appointment_type_radio.click()
|
||||
assert drawer.text_appointment_type_radio.isChecked()
|
||||
assert not drawer.appointment_type_radio.isChecked()
|
||||
drawer.channel_source.setCurrentIndex(drawer.channel_source.findData("online"))
|
||||
assert drawer.ok_button.isEnabled()
|
||||
|
||||
@@ -328,7 +334,7 @@ def test_roster_slot_states_conflict_refresh_and_submit_contract(
|
||||
"appointment_date": tomorrow,
|
||||
"appointment_time": "09:30-10:00",
|
||||
"period": "all",
|
||||
"appointment_type": "video",
|
||||
"appointment_type": mode,
|
||||
"remark": "",
|
||||
"channel_source": "online",
|
||||
"channel_source_detail": "",
|
||||
|
||||
@@ -742,7 +742,7 @@ def test_im_entry_does_not_require_a_live_video_hint(
|
||||
assert buttons_by_name["与已接通患者进行 IM 问诊"].isEnabled()
|
||||
waiting = buttons_by_name["与等待患者进行 IM 问诊"]
|
||||
assert waiting.isEnabled()
|
||||
assert waiting.toolTip() == "打开患者 IM,可发送消息并从会话中发起视频"
|
||||
assert waiting.toolTip() == "打开患者 IM,可发送消息;通话能力由本次挂号决定"
|
||||
page.close()
|
||||
application.processEvents()
|
||||
|
||||
|
||||
@@ -52,14 +52,14 @@ def test_display_fallback_requires_exact_current_appointment_id(application: QAp
|
||||
row = _row()
|
||||
before = copy.deepcopy(row)
|
||||
assert _blue_appointment_text(row, row["appointments"][0]) == (
|
||||
"陈医生(演示)", "2026-09-05 09:00-09:30",
|
||||
"陈医生(演示)", "2026-09-05 09:00-09:30 · 视频问诊",
|
||||
)
|
||||
assert _blue_appointment_text(row, row["appointments"][1]) == (
|
||||
"—", "2026-08-06 时间 —",
|
||||
"—", "2026-08-06 时间 — · 视频问诊",
|
||||
)
|
||||
nested = {"id": row["appointment_id"], "doctor_name": "原医生",
|
||||
"appointment_date": "2026-09-07", "time_text": "2026-09-07 11:00-11:30"}
|
||||
assert _blue_appointment_text(row, nested) == ("原医生", "2026-09-07 11:00-11:30")
|
||||
assert _blue_appointment_text(row, nested) == ("原医生", "2026-09-07 11:00-11:30 · 视频问诊")
|
||||
assert _blue_appointment_text(row, {}) == ("—", "时间 —")
|
||||
for missing_id in (0, "", None):
|
||||
assert _blue_appointment_text({**row, "appointment_id": missing_id}, {}) == ("—", "时间 —")
|
||||
@@ -70,13 +70,13 @@ def test_display_fallback_requires_exact_current_appointment_id(application: QAp
|
||||
host.set_rows([row])
|
||||
assert "陈医生" not in legacy.model.index(0, 4).data()
|
||||
assert blue.model.index(0, 4).data().splitlines() == [
|
||||
"陈医生(演示) · 2026-09-05 09:00-09:30", "— · 2026-08-06 时间 —",
|
||||
"陈医生(演示) · 2026-09-05 09:00-09:30 · 视频问诊", "— · 2026-08-06 时间 — · 视频问诊",
|
||||
]
|
||||
assert blue.model.index(0, 9).data() == "—"
|
||||
blue.set_rows([{**row, "appointments": [], "appointment_id": None}])
|
||||
assert blue.model.index(0, 4).data() == "— · 时间 —"
|
||||
blue.set_rows([{**row, "appointments": []}])
|
||||
assert blue.model.index(0, 4).data() == "陈医生(演示) · 2026-09-05 09:00-09:30"
|
||||
assert blue.model.index(0, 4).data() == "陈医生(演示) · 2026-09-05 09:00-09:30 · 视频问诊"
|
||||
legacy.close()
|
||||
blue.close()
|
||||
|
||||
|
||||
@@ -279,8 +279,10 @@ def test_workspace_workers_use_gui_thread_query_snapshots(
|
||||
application.processEvents()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("mode", ["video", "text"])
|
||||
def test_appointment_form_uses_rosters_slots_and_diagnosis_id_contract(
|
||||
application: QApplication,
|
||||
mode: str,
|
||||
immediate_async: None,
|
||||
) -> None:
|
||||
tomorrow = QDate.currentDate().addDays(1).toString("yyyy-MM-dd")
|
||||
@@ -317,6 +319,7 @@ def test_appointment_form_uses_rosters_slots_and_diagnosis_id_contract(
|
||||
"doctor_id": 77,
|
||||
}
|
||||
dialog = _AppointmentDialog(row, repository=Repository())
|
||||
dialog.appointment_type.setCurrentIndex(dialog.appointment_type.findData(mode))
|
||||
application.processEvents()
|
||||
dialog.channel_source.setCurrentIndex(dialog.channel_source.findData("online"))
|
||||
dialog.slot_combo.setCurrentIndex(dialog.slot_combo.findData("09:30-10:00"))
|
||||
@@ -339,7 +342,7 @@ def test_appointment_form_uses_rosters_slots_and_diagnosis_id_contract(
|
||||
"appointment_date": tomorrow,
|
||||
"appointment_time": "09:30-10:00",
|
||||
"period": "all",
|
||||
"appointment_type": "video",
|
||||
"appointment_type": mode,
|
||||
"remark": "复诊预约",
|
||||
"channel_source": "online",
|
||||
"channel_source_detail": "",
|
||||
|
||||
@@ -2565,11 +2565,14 @@ def test_reception_ai_analysis_discards_late_qwen_and_openai_results(
|
||||
application.processEvents()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("mode", [None, "text"])
|
||||
def test_video_payload_keeps_three_identifiers_distinct(
|
||||
application: QApplication,
|
||||
immediate_async: None,
|
||||
mode: str | None,
|
||||
) -> None:
|
||||
detail = _detail(41, name="视频患者")
|
||||
detail["appointment"]["appointment_type"] = mode
|
||||
|
||||
class Repository:
|
||||
def get_reception(self, appointment_id: int) -> dict[str, Any]:
|
||||
@@ -2586,6 +2589,7 @@ def test_video_payload_keeps_three_identifiers_distinct(
|
||||
{
|
||||
"source": "reception",
|
||||
"appointment_id": 41,
|
||||
"appointment_type": mode,
|
||||
"patient_id": 141,
|
||||
"diagnosis_id": 241,
|
||||
"patient_name": "视频患者",
|
||||
@@ -2593,6 +2597,7 @@ def test_video_payload_keeps_three_identifiers_distinct(
|
||||
"record": detail["appointment"],
|
||||
}
|
||||
]
|
||||
assert page.video_button.text() == ("图文沟通" if mode == "text" else "IM 问诊")
|
||||
page.close()
|
||||
application.processEvents()
|
||||
|
||||
|
||||
@@ -476,7 +476,7 @@ def test_remote_start_call_requires_and_normalizes_current_record_id() -> None:
|
||||
assert client.post_calls == [
|
||||
(
|
||||
"tcm.diagnosis/startCall",
|
||||
{"diagnosis_id": 501, "patient_id": 301, "call_type": 2},
|
||||
{"diagnosis_id": 501, "patient_id": 301, "call_type": 2, "appointment_id": 0},
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
"""Appointment policy must survive repository, launcher and lifecycle boundaries."""
|
||||
import logging
|
||||
from types import MethodType, SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from doctor_workstation.core.models import CallTicket
|
||||
from doctor_workstation.services.repository import RemoteDoctorRepository
|
||||
from doctor_workstation.video.launcher import normalize_backend_ticket
|
||||
from doctor_workstation.video.lifecycle import OrderedCallLifecycle
|
||||
|
||||
|
||||
def ticket(**policy):
|
||||
return CallTicket.from_dict({
|
||||
"sdkAppId": 1400123456, "userId": "doctor_1", "userSig": "test-ticket",
|
||||
"patientUserId": "patient_8", "diagnosis_id": 123, "patient_id": 8,
|
||||
"appointment_id": 456, "appointment_type": "text", **policy,
|
||||
})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", [None, False, 0, 1, "true", "1", []])
|
||||
def test_only_actual_server_true_authorizes_calls(value):
|
||||
request = normalize_backend_ticket(ticket(can_video_call=value, can_audio_call=value))
|
||||
assert request.appointment_id == 456
|
||||
assert request.appointment_type == "text"
|
||||
assert request.patient_id == 8
|
||||
assert request.to_web_config()["can_video_call"] is False
|
||||
assert request.to_web_config()["can_audio_call"] is False
|
||||
|
||||
|
||||
def test_raw_policy_preserved_and_missing_policy_denies():
|
||||
request = normalize_backend_ticket(ticket())
|
||||
assert not request.can_video_call and not request.can_audio_call
|
||||
request = normalize_backend_ticket(ticket(appointment_type="phone", can_audio_call=True))
|
||||
assert request.can_audio_call and not request.can_video_call
|
||||
|
||||
|
||||
def test_repository_forwards_exact_appointment_and_media_type():
|
||||
class Client:
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
|
||||
def post(self, endpoint, payload):
|
||||
self.calls.append((endpoint, payload))
|
||||
return {"call_record_id": 99} if endpoint.endswith("startCall") else ticket().raw
|
||||
|
||||
client = Client()
|
||||
repository = RemoteDoctorRepository(client)
|
||||
assert repository.get_call_ticket(8, 123, appointment_id=456).raw["appointment_type"] == "text"
|
||||
repository.start_call(123, 8, call_type=1, appointment_id=456)
|
||||
assert client.calls == [
|
||||
("tcm.diagnosis/getCallSignature", {"patient_id": 8, "diagnosis_id": 123, "appointment_id": 456}),
|
||||
("tcm.diagnosis/startCall", {"patient_id": 8, "diagnosis_id": 123, "appointment_id": 456, "call_type": 1}),
|
||||
]
|
||||
|
||||
|
||||
def test_lifecycle_refreshes_exact_identity_and_uses_actual_media():
|
||||
class Repository:
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
self.appointment_id = 456
|
||||
|
||||
def get_call_ticket(self, **payload):
|
||||
self.calls.append(payload)
|
||||
return ticket(appointment_id=self.appointment_id, can_audio_call=True)
|
||||
|
||||
def start_call(self, **payload):
|
||||
self.calls.append(payload)
|
||||
return {"call_record_id": 99}
|
||||
|
||||
def end_call(self, **payload):
|
||||
return {}
|
||||
|
||||
repository = Repository()
|
||||
lifecycle = OrderedCallLifecycle(normalize_backend_ticket(ticket()), repository, logging.getLogger(__name__))
|
||||
try:
|
||||
assert lifecycle.refresh_call_policy().result(timeout=2)["can_audio_call"] is True
|
||||
assert repository.calls[-1] == {"patient_id": 8, "diagnosis_id": 123, "appointment_id": 456}
|
||||
assert lifecycle.start(call_type=1).result(timeout=2)
|
||||
assert repository.calls[-1]["call_type"] == 1
|
||||
assert repository.calls[-1]["appointment_id"] == 456
|
||||
repository.appointment_id = 457
|
||||
with pytest.raises(ValueError, match="当前问诊已变更"):
|
||||
lifecycle.refresh_call_policy().result(timeout=2)
|
||||
finally:
|
||||
lifecycle.end("test").result(timeout=2)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def chat_controller(monkeypatch):
|
||||
"""Exercise the real controller callbacks without launching Qt or Tencent."""
|
||||
from doctor_workstation import app as app_module
|
||||
|
||||
queued = []
|
||||
launched = []
|
||||
previews = []
|
||||
diagnoses = []
|
||||
dialog = object()
|
||||
|
||||
def get_call_ticket(patient_id, diagnosis_id, *, appointment_id):
|
||||
return ticket(patient_id=patient_id, diagnosis_id=diagnosis_id, appointment_id=appointment_id)
|
||||
|
||||
def launch_video_call(_ticket, **kwargs):
|
||||
window = SimpleNamespace(
|
||||
show=lambda: None,
|
||||
raise_=lambda: None,
|
||||
activateWindow=lambda: None,
|
||||
destroyed=SimpleNamespace(connect=lambda _callback: None),
|
||||
)
|
||||
call = SimpleNamespace(open_im=kwargs["open_im"], qt_window=window, close=lambda: None)
|
||||
launched.append((call, kwargs))
|
||||
return call
|
||||
|
||||
def open_diagnosis_by_id(diagnosis_id, *, modeless):
|
||||
diagnoses.append((diagnosis_id, modeless))
|
||||
return dialog
|
||||
|
||||
monkeypatch.setattr(app_module, "run_async", lambda function, **callbacks: queued.append((function, callbacks)))
|
||||
monkeypatch.setattr(app_module, "launch_video_call", launch_video_call)
|
||||
monkeypatch.setattr(app_module, "show_toast", lambda *_args, **_kwargs: None)
|
||||
monkeypatch.setattr(app_module, "_build_video_patient_case", lambda *_args, **_kwargs: {})
|
||||
monkeypatch.setattr(app_module, "WEBENGINE_AVAILABLE", True)
|
||||
monkeypatch.setattr(app_module, "QTimer", SimpleNamespace(singleShot=lambda _delay, callback: callback()))
|
||||
controller = SimpleNamespace(
|
||||
shell_window=SimpleNamespace(open_diagnosis_by_id=open_diagnosis_by_id),
|
||||
current_repository=SimpleNamespace(get_call_ticket=get_call_ticket),
|
||||
current_demo_mode=False,
|
||||
video_calls={}, video_pending={}, demo_video_dialogs={}, _pending_im_request=None,
|
||||
config=SimpleNamespace(video_mode="embedded", video_web_url=""),
|
||||
_show_video_preview=lambda window, diagnosis_dialog: previews.append((window, diagnosis_dialog)),
|
||||
)
|
||||
for method in ("_request_video", "_launch_video", "_video_ticket_error", "_open_video_diagnosis"):
|
||||
setattr(controller, method, MethodType(getattr(app_module.ApplicationController, method), controller))
|
||||
|
||||
def request(appointment_id, diagnosis_id=123):
|
||||
controller._request_video({
|
||||
"patient_id": 8, "diagnosis_id": diagnosis_id,
|
||||
"appointment_id": appointment_id, "mode": "im",
|
||||
})
|
||||
|
||||
def complete(index):
|
||||
function, callbacks = queued[index]
|
||||
callbacks["on_success"](function())
|
||||
|
||||
return SimpleNamespace(
|
||||
controller=controller, request=request, complete=complete, queued=queued,
|
||||
launched=launched, previews=previews, diagnoses=diagnoses, dialog=dialog,
|
||||
)
|
||||
|
||||
|
||||
def test_diagnosis_callback_previews_exact_appointment_session(chat_controller):
|
||||
case = chat_controller
|
||||
case.request(456)
|
||||
case.complete(0)
|
||||
call, launch_args = case.launched[0]
|
||||
case.controller.video_calls["123:457"] = SimpleNamespace(qt_window=object())
|
||||
|
||||
launch_args["on_open_diagnosis"]()
|
||||
assert case.diagnoses == [(123, True)]
|
||||
assert case.previews == [(call.qt_window, case.dialog)]
|
||||
|
||||
case.controller.video_calls.pop("123:456")
|
||||
launch_args["on_open_diagnosis"]()
|
||||
assert len(case.previews) == 1 # A closed session cannot borrow another appointment's video.
|
||||
|
||||
|
||||
@pytest.mark.parametrize("second_diagnosis", [123, 124])
|
||||
@pytest.mark.parametrize("completion_order", [(0, 1), (1, 0)])
|
||||
def test_latest_im_selection_retires_pending_callbacks(chat_controller, second_diagnosis, completion_order):
|
||||
case = chat_controller
|
||||
case.request(456)
|
||||
case.request(457, diagnosis_id=second_diagnosis)
|
||||
assert list(case.controller.video_pending) == [f"{second_diagnosis}:457"]
|
||||
|
||||
for index in completion_order:
|
||||
case.complete(index)
|
||||
|
||||
assert list(case.controller.video_calls) == [f"{second_diagnosis}:457"]
|
||||
assert len(case.launched) == 1
|
||||
assert case.controller.video_pending == {}
|
||||
assert case.controller._pending_im_request is None
|
||||
|
||||
|
||||
def test_reselecting_open_im_also_retires_other_pending_selection(chat_controller):
|
||||
case = chat_controller
|
||||
case.request(456)
|
||||
case.complete(0)
|
||||
original_call = case.launched[0][0]
|
||||
case.request(457)
|
||||
# The previous page may still be finishing its native shutdown. Treat a
|
||||
# reactivated current page like any other current selection.
|
||||
case.controller.video_calls["123:456"] = original_call
|
||||
case.request(456)
|
||||
case.complete(1)
|
||||
assert list(case.controller.video_calls) == ["123:456"]
|
||||
assert case.controller.video_pending == {}
|
||||
assert len(case.launched) == 1
|
||||
|
||||
|
||||
def test_repeated_pending_im_selection_does_not_duplicate_request(chat_controller):
|
||||
case = chat_controller
|
||||
case.request(456)
|
||||
case.request(456)
|
||||
assert len(case.queued) == 1
|
||||
case.complete(0)
|
||||
assert list(case.controller.video_calls) == ["123:456"]
|
||||
|
||||
|
||||
def legacy_signature_repository(**changes):
|
||||
response = {
|
||||
"sdkAppId": 1400123456, "userId": "doctor_1", "userSig": "test-ticket",
|
||||
"patientUserId": "patient_8", "diagnosis_id": 123, "patient_id": 8,
|
||||
**changes,
|
||||
}
|
||||
repository = RemoteDoctorRepository(SimpleNamespace(post=lambda *_args: response))
|
||||
repository.patient_detail = lambda _diagnosis_id: {}
|
||||
return repository, response
|
||||
|
||||
|
||||
def test_legacy_signature_opens_im_with_requested_context_and_no_media(chat_controller):
|
||||
case = chat_controller
|
||||
repository, response = legacy_signature_repository()
|
||||
case.controller.current_repository = repository
|
||||
case.request(456)
|
||||
case.complete(0)
|
||||
assert list(case.controller.video_calls) == ["123:456"]
|
||||
assert case.launched[0][1]["open_im"] is True
|
||||
request = normalize_backend_ticket(repository.get_call_ticket(8, 123, appointment_id=456))
|
||||
assert request.appointment_id == 456
|
||||
assert request.target_user_id == "patient_8"
|
||||
assert request.can_video_call is False and request.can_audio_call is False
|
||||
assert request.call_disabled_reason
|
||||
assert "appointment_id" not in response # Never mutate the HTTP response/shared cache.
|
||||
lifecycle = OrderedCallLifecycle(request, repository, logging.getLogger(__name__))
|
||||
try:
|
||||
refreshed = lifecycle.refresh_call_policy().result(timeout=2)
|
||||
assert refreshed["appointmentId"] == 456
|
||||
assert refreshed["can_video_call"] is False and refreshed["can_audio_call"] is False
|
||||
finally:
|
||||
lifecycle.end("test").result(timeout=2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("changes", [
|
||||
{"diagnosis_id": 124}, {"patient_id": 9}, {"patientUserId": "patient_9"},
|
||||
{"diagnosis_id": None}, {"patient_id": None}, {"patientUserId": None},
|
||||
])
|
||||
def test_legacy_signature_requires_full_matching_identity(changes):
|
||||
repository, _ = legacy_signature_repository(**changes)
|
||||
with pytest.raises(ValueError, match="患者或诊单不匹配"):
|
||||
repository.get_call_ticket(8, 123, appointment_id=456)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("returned_id", [457, 0, None, "", True, -1, 456.9, 456.0, "bad"])
|
||||
def test_explicit_wrong_or_invalid_appointment_is_never_replaced(returned_id):
|
||||
repository, _ = legacy_signature_repository(appointment_id=returned_id)
|
||||
with pytest.raises(ValueError, match="本次挂号不匹配"):
|
||||
repository.get_call_ticket(8, 123, appointment_id=456)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("fragment", [
|
||||
{"can_video_call": True}, {"can_audio_call": True}, {"appointment_type": "text"},
|
||||
{"appointmentId": 457}, {"call_disabled_reason": ""},
|
||||
])
|
||||
def test_partial_policy_is_not_treated_as_legacy_signature(fragment):
|
||||
repository, _ = legacy_signature_repository(**fragment)
|
||||
with pytest.raises(ValueError, match="本次挂号不匹配"):
|
||||
repository.get_call_ticket(8, 123, appointment_id=456)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("returned_id", [456, "456"])
|
||||
def test_modern_exact_appointment_policy_is_preserved(returned_id):
|
||||
repository, _ = legacy_signature_repository(
|
||||
appointment_id=returned_id, appointment_type="video", can_video_call=True, can_audio_call=True,
|
||||
)
|
||||
request = normalize_backend_ticket(repository.get_call_ticket(8, 123, appointment_id=456))
|
||||
assert request.appointment_id == 456
|
||||
assert request.can_video_call is True and request.can_audio_call is True
|
||||
@@ -0,0 +1,103 @@
|
||||
"""Appointment medium stays attached to its own row and opens text chat without RTC."""
|
||||
import os
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
import pytest
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import QApplication, QLabel, QPushButton, QToolButton
|
||||
|
||||
from doctor_workstation.core import PermissionSet
|
||||
from doctor_workstation.core.appointment_modes import (
|
||||
appointment_type_description,
|
||||
appointment_type_value,
|
||||
can_appointment_video,
|
||||
)
|
||||
from doctor_workstation.services import DemoDoctorRepository
|
||||
from doctor_workstation.ui.diagnosis_index_widgets import _blue_appointment_text
|
||||
from doctor_workstation.ui.pages import appointments, consultations
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def application():
|
||||
return QApplication.instance() or QApplication([])
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value,label,video", [
|
||||
("text", "图文问诊", False), ("video", "视频问诊", True),
|
||||
(None, "视频问诊", True), (" ", "视频问诊", True),
|
||||
("phone", "电话问诊", False), ("unexpected", "未知", False),
|
||||
])
|
||||
def test_appointment_type_never_uses_diagnosis_visit_type(value, label, video):
|
||||
assert appointment_type_description(value) == label
|
||||
assert can_appointment_video(value) is video
|
||||
assert appointment_type_value({"appointment_type": value, "consultation_type": "复诊"}) == value or value == ""
|
||||
|
||||
|
||||
def test_nested_type_uses_current_appointment_id_not_another_latest():
|
||||
row = {"appointment_id": 12, "latest_appointment_type": "video", "appointments": [
|
||||
{"id": 11, "appointment_type": "video"}, {"id": 12, "appointment_type": "text"},
|
||||
]}
|
||||
assert appointment_type_value(row) == "text"
|
||||
assert "图文问诊" in _blue_appointment_text(row, row["appointments"][1])[1]
|
||||
assert "视频问诊" in _blue_appointment_text(row, row["appointments"][0])[1]
|
||||
assert appointment_type_value({"appointments": row["appointments"]}) == "unknown"
|
||||
|
||||
|
||||
def test_appointment_text_chat_carries_exact_ids_and_blocks_video_qr(application, monkeypatch):
|
||||
monkeypatch.setattr(appointments, "run_async", lambda *_a, **_kw: None)
|
||||
notices = []
|
||||
monkeypatch.setattr(appointments, "show_toast", lambda _parent, message, *_a: notices.append(message))
|
||||
page = appointments.AppointmentsPage(DemoDoctorRepository(), PermissionSet(["*"]))
|
||||
rows = [{"id": 71, "patient_id": 501, "diagnosis_id": 501, "source_patient_id": 901,
|
||||
"patient_name": "图文患者", "doctor_id": 7, "status": 1, "appointment_type": "text"},
|
||||
{"id": 72, "patient_id": 502, "diagnosis_id": 502, "source_patient_id": 902,
|
||||
"patient_name": "视频患者", "doctor_id": 8, "status": 1, "appointment_type": "video"}]
|
||||
try:
|
||||
page._loaded({"lists": rows, "count": 2}, page._generation, False)
|
||||
emitted = []
|
||||
page.video_requested.connect(emitted.append)
|
||||
page.table.sortItems(1, Qt.SortOrder.DescendingOrder)
|
||||
text_row = next(i for i in range(2) if page.table.item(i, 0).data(Qt.ItemDataRole.UserRole)["id"] == 71)
|
||||
button = page.table.cellWidget(text_row, 10).findChild(QPushButton, "AppointmentImConsultButton")
|
||||
assert button.text() == "图文沟通" and button.isEnabled()
|
||||
assert page.table.cellWidget(text_row, 4).findChild(QLabel, "AppointmentModeLabel").text() == "图文问诊"
|
||||
button.click()
|
||||
assert emitted[-1]["appointment_id"] == 71
|
||||
assert emitted[-1]["diagnosis_id"] == 501
|
||||
assert emitted[-1]["patient_id"] == 901
|
||||
assert emitted[-1]["appointment_type"] == "text"
|
||||
assert emitted[-1]["mode"] == "im"
|
||||
assert not page.toolbar_qr_button.isEnabled()
|
||||
page._request_video_qr()
|
||||
assert notices[-1] == "本次挂号不支持视频问诊二维码。"
|
||||
finally:
|
||||
page.close()
|
||||
page.deleteLater()
|
||||
|
||||
|
||||
def test_consultation_text_entry_does_not_wait_for_live_video(application, monkeypatch):
|
||||
monkeypatch.setattr(consultations, "run_async", lambda *_a, **_kw: None)
|
||||
row = {"id": 501, "patient_id": 901, "patient_name": "图文患者", "has_appointment": 1,
|
||||
"appointment_status": 1, "latest_appointment_id": 71,
|
||||
"appointments": [{"id": 71, "status": 1, "appointment_type": "text"}],
|
||||
"video_call_hint": {"state": "none"}}
|
||||
page = consultations.ConsultationsPage(DemoDoctorRepository(), PermissionSet(["*"]))
|
||||
try:
|
||||
page.table_host.set_rows([row])
|
||||
page.table.selectRow(0)
|
||||
page._selection_changed()
|
||||
buttons = page.table_host.findChildren(QToolButton)
|
||||
text_button = next(button for button in buttons if button.text() == "图文沟通")
|
||||
assert text_button.isEnabled()
|
||||
assert page.video_button.isEnabled()
|
||||
assert not page.video_qr_toolbar_button.isEnabled()
|
||||
emitted = []
|
||||
page.video_requested.connect(emitted.append)
|
||||
page._request_video()
|
||||
assert emitted[-1]["appointment_id"] == 71
|
||||
assert emitted[-1]["appointment_type"] == "text"
|
||||
assert emitted[-1]["mode"] == "im"
|
||||
finally:
|
||||
page.close()
|
||||
page.deleteLater()
|
||||
@@ -642,6 +642,13 @@ def test_normalizes_admin_ticket_aliases_to_companion_contract() -> None:
|
||||
"userSig": "short-lived-ticket",
|
||||
"targetUserId": "patient_8",
|
||||
"diagnosisId": 123,
|
||||
"patientId": 8,
|
||||
"appointmentId": 0,
|
||||
"appointment_type": None,
|
||||
"appointment_type_desc": "",
|
||||
"can_video_call": False,
|
||||
"can_audio_call": False,
|
||||
"call_disabled_reason": "",
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user