更新
This commit is contained in:
@@ -29,6 +29,19 @@ def application() -> QApplication:
|
||||
return QApplication.instance() or QApplication([])
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def offline_thumbnails(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""离线传输:缩略图停在 loading 状态,工作站用例永不真正联网。"""
|
||||
|
||||
def hold(self: Any, source: str) -> None:
|
||||
self._source = str(source).strip()
|
||||
self._invalidate_request()
|
||||
self.setToolTip(self._source)
|
||||
self._show_loading()
|
||||
|
||||
monkeypatch.setattr(ai_consult_module._RemoteImageButton, "load_url", hold)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def immediate_async(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def run_immediately(
|
||||
@@ -468,17 +481,33 @@ def test_patient_report_response_owner_must_match_exactly(
|
||||
dialog.close()
|
||||
|
||||
|
||||
def test_exam_tab_filters_foreign_attachments_and_blocks_file_urls(
|
||||
def test_exam_tab_previews_images_in_app_and_blocks_file_urls(
|
||||
application: QApplication,
|
||||
immediate_async: None,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
opened: list[str] = []
|
||||
previews: list[tuple[tuple[str, ...], int]] = []
|
||||
monkeypatch.setattr(
|
||||
ai_consult_module,
|
||||
"open_safe_http_url",
|
||||
lambda target: opened.append(target) or True,
|
||||
)
|
||||
|
||||
class RecordingPreviewDialog(QWidget):
|
||||
def __init__(
|
||||
self,
|
||||
sources: Any,
|
||||
*,
|
||||
index: int = 0,
|
||||
names: Any = None,
|
||||
title: str = "",
|
||||
parent: QWidget | None = None,
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
previews.append((tuple(sources), int(index)))
|
||||
|
||||
monkeypatch.setattr(ai_consult_module, "ImagePreviewDialog", RecordingPreviewDialog)
|
||||
dialog = _open_dialog(application, WorkspaceRepository())
|
||||
pane = dialog.records["检查检验"]
|
||||
dialog.tabs.setCurrentIndex(2)
|
||||
@@ -498,20 +527,187 @@ def test_exam_tab_filters_foreign_attachments_and_blocks_file_urls(
|
||||
assert len(thumbnails) == 1
|
||||
assert thumbnails[0].isEnabled()
|
||||
assert thumbnails[0].accessibleName() == "舌苔图片点击查看"
|
||||
assert thumbnails[0].property("loadState") == "blocked"
|
||||
assert thumbnails[0].property("loadState") == "loading"
|
||||
assert pane.state_label.property("state") == "warning" # type: ignore[attr-defined]
|
||||
assert pane.retry_button.isHidden() # type: ignore[attr-defined]
|
||||
image_button = next(button for button in buttons if "甲舌苔照片.jpg" in button.text())
|
||||
assert image_button.text().endswith("· 预览")
|
||||
report_button = next(button for button in buttons if "甲血糖报告.pdf" in button.text())
|
||||
assert report_button.text().endswith("· 打开")
|
||||
unsafe = next(button for button in buttons if "本地危险附件" in button.text())
|
||||
assert not unsafe.isEnabled()
|
||||
|
||||
for button in buttons:
|
||||
button.click()
|
||||
assert len(opened) == 2
|
||||
assert all(target.startswith(("http://", "https://")) for target in opened)
|
||||
assert all(not target.startswith("file:") for target in opened)
|
||||
thumbnails[0].click()
|
||||
# 图片留在工作站内预览,只有非图片附件才交给系统打开。
|
||||
assert opened == ["https://media.example.invalid/甲/report.pdf"]
|
||||
assert previews == [(("https://media.example.invalid/甲/tongue.jpg",), 0)] * 2
|
||||
dialog.close()
|
||||
|
||||
|
||||
def test_tongue_thumbnail_auto_get_requires_configured_https_origin(
|
||||
class ProductionShapeRepository(WorkspaceRepository):
|
||||
"""按线上 readonlyDetail 的真实返回构造:既有 code,也有后端补的 *_text。"""
|
||||
|
||||
def get_diagnosis_detail(
|
||||
self,
|
||||
diagnosis_id: int,
|
||||
*,
|
||||
readonly: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
detail = dict(super().get_diagnosis_detail(diagnosis_id, readonly=readonly))
|
||||
diagnosis = dict(detail["diagnosis"])
|
||||
diagnosis.update(
|
||||
{
|
||||
"gender": 1,
|
||||
"gender_text": "男",
|
||||
"diagnosis_type": "follow_up",
|
||||
"diagnosis_type_text": "复诊",
|
||||
"eye_condition": "blurred,dry",
|
||||
"eye_condition_text": "模糊、干涩",
|
||||
"skin_condition": "dry,itching",
|
||||
"skin_condition_text": "干燥、瘙痒",
|
||||
"urine_condition": "yellow_urine",
|
||||
"urine_condition_text": "尿黄",
|
||||
"fatty_liver_degree": "mild",
|
||||
"fatty_liver_degree_text": "轻度",
|
||||
"past_history": "hypertension",
|
||||
"past_history_text": "高血压",
|
||||
"trauma_history": 0,
|
||||
"trauma_history_text": "无",
|
||||
# 诊单表里的技术列:医生页面不应出现这些英文列名。
|
||||
"status": 1,
|
||||
"show_card": 1,
|
||||
"revisit_slot_start_offset": 0,
|
||||
"delete_time": None,
|
||||
"assistant_id": 131,
|
||||
"assign_read_at": 1787882294,
|
||||
"shipped_non_er_assistant_cleared_at": 0,
|
||||
"external_userid": "",
|
||||
"is_view": 0,
|
||||
"create_time": 1787882294,
|
||||
"update_time": 1787882303,
|
||||
"tongue_images": [
|
||||
"https://media.example.invalid/11702/a.jpg",
|
||||
"https://media.example.invalid/11702/b.jpg",
|
||||
],
|
||||
}
|
||||
)
|
||||
detail["diagnosis"] = diagnosis
|
||||
return detail
|
||||
|
||||
|
||||
def test_case_tab_hides_raw_columns_and_never_shows_text_mirror_fields(
|
||||
application: QApplication,
|
||||
immediate_async: None,
|
||||
) -> None:
|
||||
dialog = _open_dialog(application, ProductionShapeRepository())
|
||||
dialog.tabs.setCurrentIndex(1)
|
||||
application.processEvents()
|
||||
text = _pane_text(dialog.records["病历资料"])
|
||||
|
||||
# 后端补的 *_text 只用来取值,不再作为独立英文字段列出来。
|
||||
for mirror in (
|
||||
"diagnosis type text",
|
||||
"eye condition text",
|
||||
"gender text",
|
||||
"past history text",
|
||||
):
|
||||
assert mirror not in text
|
||||
# 技术列不再泄漏英文列名。
|
||||
for internal in (
|
||||
"assign read at",
|
||||
"shipped non er assistant cleared at",
|
||||
"external userid",
|
||||
"is view",
|
||||
"assistant id",
|
||||
"show card",
|
||||
"revisit slot start offset",
|
||||
"delete time",
|
||||
):
|
||||
assert internal not in text
|
||||
# 有中文名的字段照常显示,取的是后端翻译过的值。
|
||||
assert "复诊" in text
|
||||
assert "模糊、干涩" in text
|
||||
assert "1787882294" not in text
|
||||
assert "https://media.example.invalid/11702/a.jpg" not in text
|
||||
assert len(dialog.records["病历资料"].findChildren(QPushButton, "AiConsultTongueThumb")) == 2
|
||||
dialog.close()
|
||||
|
||||
|
||||
class RawCodeRepository(WorkspaceRepository):
|
||||
"""只读接口偶尔缺少 `*_text`(历史数据 / 快照),此时必须自己翻译字典 code。"""
|
||||
|
||||
def get_diagnosis_detail(
|
||||
self,
|
||||
diagnosis_id: int,
|
||||
*,
|
||||
readonly: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
detail = dict(super().get_diagnosis_detail(diagnosis_id, readonly=readonly))
|
||||
diagnosis = dict(detail["diagnosis"])
|
||||
diagnosis.update(
|
||||
{
|
||||
"gender": 1,
|
||||
"diagnosis_type": "follow_up",
|
||||
"appetite": "dry,bitter",
|
||||
"weight_change": "lose_10_jin",
|
||||
"fatty_liver_degree": "mild",
|
||||
"allergy_history": 0,
|
||||
"status": 1,
|
||||
"show_card": 1,
|
||||
"revisit_slot_start_offset": 0,
|
||||
"delete_time": None,
|
||||
"create_source": "admin",
|
||||
"create_time": 1783838927,
|
||||
"tongue_images": ["https://media.example.invalid/501/tongue-raw.jpg"],
|
||||
}
|
||||
)
|
||||
detail["diagnosis"] = diagnosis
|
||||
patient = dict(detail.get("patient") or {})
|
||||
patient.update({"gender": 1, "marital_status": 1})
|
||||
detail["patient"] = patient
|
||||
return detail
|
||||
|
||||
|
||||
def test_case_and_health_tabs_translate_codes_and_hide_internal_columns(
|
||||
application: QApplication,
|
||||
immediate_async: None,
|
||||
) -> None:
|
||||
dialog = _open_dialog(application, RawCodeRepository())
|
||||
dialog.tabs.setCurrentIndex(1)
|
||||
application.processEvents()
|
||||
case_text = _pane_text(dialog.records["病历资料"])
|
||||
|
||||
# 字典 code、性别与是否类枚举、时间戳都按后台只读页的口径显示。
|
||||
assert "干、苦" in case_text
|
||||
assert "瘦10斤" in case_text
|
||||
assert "轻度" in case_text
|
||||
assert "复诊" in case_text
|
||||
assert "后台创建" in case_text
|
||||
assert "dry,bitter" not in case_text
|
||||
assert "lose_10_jin" not in case_text
|
||||
assert "follow_up" not in case_text
|
||||
assert "1783838927" not in case_text
|
||||
# 纯内部列不再泄漏给医生。
|
||||
for internal in ("show card", "revisit slot start offset", "delete time"):
|
||||
assert internal not in case_text
|
||||
# 舌象附件渲染成缩略图,不再是一长串 URL 文本。
|
||||
assert "https://media.example.invalid/501/tongue-raw.jpg" not in case_text
|
||||
thumbnails = dialog.records["病历资料"].findChildren(QPushButton, "AiConsultTongueThumb")
|
||||
assert len(thumbnails) == 1
|
||||
|
||||
dialog.tabs.setCurrentIndex(4)
|
||||
application.processEvents()
|
||||
health_text = _pane_text(dialog.records["健康档案"])
|
||||
assert "性别\n男" in health_text
|
||||
assert "过敏史\n无" in health_text
|
||||
assert "162 cm" in health_text
|
||||
assert dialog.records["健康档案"].findChildren(QPushButton, "AiConsultTongueThumb")
|
||||
dialog.close()
|
||||
|
||||
|
||||
def test_tongue_thumbnails_load_safe_http_sources_and_skip_unsafe_schemes(
|
||||
application: QApplication,
|
||||
immediate_async: None,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
@@ -533,23 +729,22 @@ def test_tongue_thumbnail_auto_get_requires_configured_https_origin(
|
||||
RecordingRemoteImageButton,
|
||||
)
|
||||
|
||||
untrusted = _open_dialog(application, WorkspaceRepository())
|
||||
assert requested == []
|
||||
untrusted.close()
|
||||
|
||||
trusted_repository = WorkspaceRepository()
|
||||
trusted_repository.trusted_media_domains = ["media.example.invalid"]
|
||||
assert not ai_consult_module._trusted_thumbnail_url(
|
||||
trusted_repository,
|
||||
"http://media.example.invalid/甲/tongue.jpg",
|
||||
)
|
||||
assert not ai_consult_module._trusted_thumbnail_url(
|
||||
trusted_repository,
|
||||
"https://sub.media.example.invalid/甲/tongue.jpg",
|
||||
)
|
||||
trusted = _open_dialog(application, trusted_repository)
|
||||
dialog = _open_dialog(application, WorkspaceRepository())
|
||||
# 舌象照片按字段直接渲染,PDF 报告与 file:// 附件不会发起任何图片请求。
|
||||
assert requested == ["https://media.example.invalid/甲/tongue.jpg"]
|
||||
trusted.close()
|
||||
assert not ai_consult_module._previewable_attachment(
|
||||
"tongue_images",
|
||||
"file:///C:/private/tongue.jpg",
|
||||
)
|
||||
assert not ai_consult_module._previewable_attachment(
|
||||
"report_files",
|
||||
"https://media.example.invalid/甲/report.pdf",
|
||||
)
|
||||
assert ai_consult_module._previewable_attachment(
|
||||
"report_files",
|
||||
"https://media.example.invalid/甲/report.PNG",
|
||||
)
|
||||
dialog.close()
|
||||
|
||||
|
||||
def test_three_prescription_cards_open_exact_details_and_reject_wrong_or_late_ids(
|
||||
|
||||
Reference in New Issue
Block a user