719 lines
31 KiB
Python
719 lines
31 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Automatisierte Tests für Doku-Prompt-System."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
|
|
class TestDokuPromptSystem(unittest.TestCase):
|
|
def setUp(self):
|
|
self._tmpdir = tempfile.mkdtemp()
|
|
self._orig_path_fn = None
|
|
|
|
def tearDown(self):
|
|
import shutil
|
|
shutil.rmtree(self._tmpdir, ignore_errors=True)
|
|
|
|
def _patch_templates_path(self):
|
|
import aza_doku_vorlagen as dv
|
|
path = os.path.join(self._tmpdir, "aza_document_templates.json")
|
|
dv._templates_json_path = lambda: path # type: ignore
|
|
return path, dv
|
|
|
|
def test_doc_types_order(self):
|
|
from aza_doku_vorlagen import DOC_TYPES
|
|
labels = [l for _, l in DOC_TYPES]
|
|
self.assertEqual(labels[0], "Krankengeschichte")
|
|
self.assertEqual(labels[1], "Verlauf")
|
|
self.assertEqual(labels[-1], "Eigenes Dokument")
|
|
self.assertEqual(len(DOC_TYPES), 8)
|
|
|
|
def test_verlauf_default_present(self):
|
|
from aza_doku_vorlagen import AZA_DEFAULT_TEMPLATES, VERLAUF_DEFAULT_PROMPT
|
|
self.assertIn("verlauf", AZA_DEFAULT_TEMPLATES)
|
|
self.assertIn("Verlaufsbericht", VERLAUF_DEFAULT_PROMPT)
|
|
|
|
def test_migration_v1_to_v2(self):
|
|
path, dv = self._patch_templates_path()
|
|
old = {
|
|
"schema_version": 1,
|
|
"active": {"kg": "aza_default", "brief": "aza_default"},
|
|
"templates": {
|
|
"kg": [{"id": "aza_default", "name": "AzA-Grundvorlage", "is_system": True,
|
|
"content": "KG alt", "created_at": "t", "updated_at": "t"}],
|
|
"brief": [{"id": "aza_default", "name": "AzA-Grundvorlage", "is_system": True,
|
|
"content": "Brief alt", "created_at": "t", "updated_at": "t"}],
|
|
},
|
|
}
|
|
with open(path, "w", encoding="utf-8") as f:
|
|
json.dump(old, f)
|
|
data = dv._load()
|
|
self.assertEqual(data["schema_version"], 2)
|
|
self.assertEqual(data["templates"]["kg"][0]["content"], "KG alt")
|
|
self.assertIn("verlauf", data["templates"])
|
|
self.assertIn("eigenes_dokument", data["templates"])
|
|
|
|
def test_sanitize_strips_script(self):
|
|
from aza_doku_vorlagen import sanitize_prompt_text
|
|
s = sanitize_prompt_text("<script>alert(1)</script>Verlauf")
|
|
self.assertNotIn("script", s.lower())
|
|
self.assertIn("Verlauf", s)
|
|
|
|
def test_dynamic_button_labels(self):
|
|
from aza_doku_vorlagen import DOC_TYPE_CREATE_LABELS, DOC_TYPE_COPY_LABELS
|
|
self.assertEqual(DOC_TYPE_CREATE_LABELS["verlauf"], "Verlauf erstellen")
|
|
self.assertEqual(DOC_TYPE_COPY_LABELS["brief"], "Brief kopieren")
|
|
|
|
def test_get_active_no_override_for_default(self):
|
|
path, dv = self._patch_templates_path()
|
|
dv._save(dv._default_structure())
|
|
from aza_text_windows_mixin import _get_doku_vorlage_for
|
|
self.assertEqual(_get_doku_vorlage_for("kg"), "")
|
|
self.assertEqual(_get_doku_vorlage_for("verlauf"), "")
|
|
|
|
def test_get_active_user_template(self):
|
|
path, dv = self._patch_templates_path()
|
|
data = dv._default_structure()
|
|
data["templates"]["verlauf"].append({
|
|
"id": "user_test", "name": "Mein Verlauf", "is_system": False,
|
|
"content": "Custom Verlauf", "created_at": "t", "updated_at": "t",
|
|
"revision": 1,
|
|
})
|
|
data["active"]["verlauf"] = "user_test"
|
|
dv._save(data)
|
|
from aza_text_windows_mixin import _get_doku_vorlage_for
|
|
self.assertEqual(_get_doku_vorlage_for("verlauf"), "Custom Verlauf")
|
|
|
|
def test_copy_public_template(self):
|
|
path, dv = self._patch_templates_path()
|
|
dv._save(dv._default_structure())
|
|
new_id = dv.copy_public_template_as_own(
|
|
"brief", content="Pub", title="Pub Brief", source_author="Dr. X", source_server_id="s1",
|
|
)
|
|
self.assertTrue(new_id)
|
|
data = dv._load()
|
|
tpl = next(t for t in data["templates"]["brief"] if t["id"] == new_id)
|
|
self.assertEqual(tpl["content"], "Pub")
|
|
self.assertEqual(tpl["source_author"], "Dr. X")
|
|
|
|
def test_atomic_save(self):
|
|
path, dv = self._patch_templates_path()
|
|
dv._save(dv._default_structure())
|
|
self.assertTrue(os.path.isfile(path))
|
|
self.assertFalse(os.path.isfile(path + ".tmp"))
|
|
|
|
def test_merge_sync_newest_revision(self):
|
|
from aza_doku_prompt_sync import merge_server_doku_items_local
|
|
data = {
|
|
"schema_version": 2,
|
|
"active": {"kg": "aza_default"},
|
|
"templates": {"kg": []},
|
|
"sync_meta": {"conflicts": []},
|
|
}
|
|
server_items = [{
|
|
"id": "user_1_kg", "item_type": "doku_prompt", "trigger": "kg",
|
|
"content": json.dumps({
|
|
"doc_type": "kg", "name": "Sync KG", "content": "Synced",
|
|
"revision": 2, "updated_at": "2026-01-02T00:00:00+00:00",
|
|
}),
|
|
}]
|
|
data, changed = merge_server_doku_items_local(data, server_items)
|
|
self.assertTrue(changed)
|
|
self.assertEqual(data["templates"]["kg"][0]["content"], "Synced")
|
|
|
|
def test_sync_serialization_private_only(self):
|
|
from aza_doku_prompt_sync import local_private_templates_to_sync_items
|
|
path, dv = self._patch_templates_path()
|
|
data = dv._default_structure()
|
|
data["templates"]["verlauf"].append({
|
|
"id": "user_v1", "name": "Privat", "is_system": False,
|
|
"content": "Verlauf privat", "created_at": "t", "updated_at": "t", "revision": 3,
|
|
})
|
|
dv._save(data)
|
|
items = local_private_templates_to_sync_items(dv._load())
|
|
self.assertEqual(len(items), 1)
|
|
self.assertEqual(items[0]["item_type"], "doku_prompt")
|
|
self.assertEqual(items[0]["trigger"], "verlauf")
|
|
payload = json.loads(items[0]["content"])
|
|
self.assertEqual(payload["content"], "Verlauf privat")
|
|
self.assertEqual(payload["revision"], 3)
|
|
|
|
def test_merge_conflict_keeps_local_when_newer(self):
|
|
from aza_doku_prompt_sync import merge_server_doku_items_local
|
|
data = {
|
|
"schema_version": 2,
|
|
"active": {"kg": "user_1_kg"},
|
|
"templates": {"kg": [{
|
|
"id": "user_1_kg", "name": "Lokal", "is_system": False,
|
|
"content": "Lokal neu", "revision": 5,
|
|
"updated_at": "2026-06-10T12:00:00+00:00",
|
|
"created_at": "t",
|
|
}]},
|
|
"sync_meta": {"conflicts": []},
|
|
}
|
|
server_items = [{
|
|
"id": "user_1_kg", "item_type": "doku_prompt", "trigger": "kg",
|
|
"content": json.dumps({
|
|
"doc_type": "kg", "name": "Server alt", "content": "Server alt",
|
|
"revision": 2, "updated_at": "2026-01-01T00:00:00+00:00",
|
|
}),
|
|
}]
|
|
merged, changed = merge_server_doku_items_local(data, server_items)
|
|
self.assertEqual(merged["templates"]["kg"][0]["content"], "Lokal neu")
|
|
self.assertTrue(len(merged["sync_meta"]["conflicts"]) >= 1)
|
|
|
|
def test_publish_payload_fields(self):
|
|
from aza_doku_prompt_sync import PublishDokuPromptIn
|
|
p = PublishDokuPromptIn(
|
|
doc_type="verlauf", title="Test", content="Prompt ohne Patientendaten",
|
|
description="Demo", author_display="Dr. Test",
|
|
)
|
|
self.assertEqual(p.doc_type, "verlauf")
|
|
d = p.model_dump()
|
|
self.assertNotIn("patient_name", d)
|
|
self.assertNotIn("transcript", d)
|
|
|
|
def test_load_all_templates_alias(self):
|
|
path, dv = self._patch_templates_path()
|
|
dv._save(dv._default_structure())
|
|
self.assertEqual(dv._load_all_templates()["schema_version"], 2)
|
|
|
|
def test_active_per_doc_type(self):
|
|
path, dv = self._patch_templates_path()
|
|
data = dv._default_structure()
|
|
for dt, _ in dv.DOC_TYPES:
|
|
self.assertIn(dt, data["active"])
|
|
self.assertIn(dt, data["templates"])
|
|
|
|
def test_no_patient_fields_in_template_schema(self):
|
|
path, dv = self._patch_templates_path()
|
|
data = dv._default_structure()
|
|
blob = json.dumps(data).lower()
|
|
for forbidden in ("patient_name", "transcript", "geburtsdatum", "sozialversicherung"):
|
|
self.assertNotIn(forbidden, blob)
|
|
|
|
|
|
def test_office_shell_kg_section_markers(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_office_shell_v1.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
self.assertIn('"Doku-Prompt"', src)
|
|
self.assertIn("DocTypePicker", src)
|
|
self.assertIn("app._doc_type_picker", src)
|
|
self.assertNotIn("app._doc_type_combo", src)
|
|
self.assertNotIn('("Doku-Prompt", "_open_doku_vorlage_fenster")', src)
|
|
self.assertNotIn('actions, "Kommentare"', src)
|
|
self.assertIn('"autotext"', src)
|
|
self.assertIn('"folder"', src)
|
|
self.assertIn('"library"', src)
|
|
|
|
def test_short_doc_type_labels(self):
|
|
from aza_doku_vorlagen import (
|
|
DOC_TYPE_CREATE_LABELS_SHORT,
|
|
DOC_TYPE_COPY_LABELS_SHORT,
|
|
DOC_TYPE_KEYS,
|
|
)
|
|
# Für jeden Dokumenttyp existiert ein Kurzlabel.
|
|
for key in DOC_TYPE_KEYS:
|
|
self.assertIn(key, DOC_TYPE_CREATE_LABELS_SHORT)
|
|
self.assertIn(key, DOC_TYPE_COPY_LABELS_SHORT)
|
|
self.assertEqual(DOC_TYPE_CREATE_LABELS_SHORT["verlauf"], "Verl. erst.")
|
|
self.assertEqual(DOC_TYPE_COPY_LABELS_SHORT["verlauf"], "Verl. kop.")
|
|
self.assertEqual(DOC_TYPE_CREATE_LABELS_SHORT["kg"], "KG erstellen")
|
|
self.assertEqual(DOC_TYPE_CREATE_LABELS_SHORT["eigenes_dokument"], "Eig. Dok. erst.")
|
|
# Kurzlabels sind nicht länger als die Langform.
|
|
from aza_doku_vorlagen import DOC_TYPE_CREATE_LABELS
|
|
self.assertLessEqual(
|
|
len(DOC_TYPE_CREATE_LABELS_SHORT["verlauf"]),
|
|
len(DOC_TYPE_CREATE_LABELS["verlauf"]),
|
|
)
|
|
|
|
def test_office_shell_uses_short_labels(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_office_shell_v1.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
self.assertIn("DOC_TYPE_CREATE_LABELS_SHORT", src)
|
|
self.assertIn("DOC_TYPE_COPY_LABELS_SHORT", src)
|
|
|
|
def test_doku_window_button_texts(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_doku_vorlagen.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
self.assertIn('"Veröffentlichen"', src)
|
|
self.assertNotIn('"Für Ärzte veröffentlichen"', src)
|
|
self.assertIn('"Öffentliche Vorlagen"', src)
|
|
self.assertIn("btn_public_left", src)
|
|
self.assertIn("open_public_doku_vorlagen_window", src)
|
|
self.assertNotIn("Öffentliche Prompt-Bibliothek", src)
|
|
self.assertNotIn('text="Als eigene Vorlage\\nübernehmen"', src)
|
|
self.assertNotIn('text="Aktualisieren"', src)
|
|
|
|
def test_publish_dialog_replaces_simpledialog(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_doku_vorlagen.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
self.assertIn("def open_doku_publish_dialog", src)
|
|
self.assertIn("dlg.title(window_title)", src)
|
|
self.assertIn('window_title: str = "Vorlage veröffentlichen"', src)
|
|
self.assertIn("⏺ Diktieren", src)
|
|
self.assertIn("open_doku_publish_dialog(", src)
|
|
publish_block = src.split("def _do_publish", 1)[-1].split("\n def ", 1)[0]
|
|
self.assertNotIn("simpledialog.askstring", publish_block)
|
|
self.assertNotIn("messagebox.askyesno", publish_block)
|
|
|
|
def test_public_vorlagen_window_buttons(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_doku_vorlagen.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
pub_block = src.split("def open_public_doku_vorlagen_window", 1)[-1].split("\ndef ", 1)[0]
|
|
self.assertIn("In eigene Vorlage übernehmen", pub_block)
|
|
self.assertIn("Bearbeiten", pub_block)
|
|
self.assertIn("Löschen", pub_block)
|
|
self.assertIn("Schliessen", pub_block)
|
|
self.assertIn("create_tool_action_button", pub_block)
|
|
self.assertIn("set_tool_action_button_enabled", pub_block)
|
|
self.assertIn("action_bar.pack(side=\"bottom\"", pub_block)
|
|
self.assertIn("load_local_prepared_public_items", pub_block)
|
|
self.assertIn("detail_prompt", pub_block)
|
|
self.assertIn("Eigene Veröffentlichung", pub_block)
|
|
|
|
def test_tool_action_button_disabled_readable(self):
|
|
from aza_ui_helpers import (
|
|
TOOL_ACTION_BTN_STYLES,
|
|
create_tool_action_button,
|
|
set_tool_action_button_enabled,
|
|
)
|
|
|
|
for kind in ("primary", "secondary", "danger"):
|
|
dis = TOOL_ACTION_BTN_STYLES[kind]["disabled"]
|
|
fg = dis["fg"].upper()
|
|
self.assertNotIn(fg, ("#FFFFFF", "#FFF"))
|
|
self.assertIn("disabledforeground", dis)
|
|
|
|
import tkinter as tk
|
|
root = tk.Tk()
|
|
root.withdraw()
|
|
try:
|
|
btn = create_tool_action_button(root, "Löschen", lambda: None, kind="danger")
|
|
set_tool_action_button_enabled(btn, False)
|
|
self.assertEqual(str(btn.cget("state")), "disabled")
|
|
self.assertNotEqual(str(btn.cget("disabledforeground")).upper(), "#FFFFFF")
|
|
finally:
|
|
root.destroy()
|
|
|
|
def test_load_local_prepared_public_items_helper(self):
|
|
import tempfile
|
|
import aza_doku_vorlagen as dv
|
|
import aza_config
|
|
with tempfile.TemporaryDirectory() as td:
|
|
qpath = os.path.join(td, "doku_prompt_publish_queue.json")
|
|
with open(qpath, "w", encoding="utf-8") as fh:
|
|
json.dump([{
|
|
"document_type_key": "verlauf",
|
|
"title": "Test",
|
|
"description": "Beschreibung",
|
|
"prompt_text": "Inhalt",
|
|
"author_display_name": "Dr. X",
|
|
}], fh)
|
|
with patch.object(aza_config, "get_writable_data_dir", return_value=td):
|
|
items = dv.load_local_prepared_public_items()
|
|
self.assertEqual(len(items), 1)
|
|
self.assertEqual(items[0]["doc_type"], "verlauf")
|
|
self.assertEqual(items[0]["title"], "Test")
|
|
|
|
def test_doku_window_larger(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_doku_vorlagen.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
self.assertIn("_DOKU_WIN_W, _DOKU_WIN_H = 1080, 820", src)
|
|
self.assertIn("_DOKU_WIN_MIN_W, _DOKU_WIN_MIN_H = 980, 700", src)
|
|
self.assertIn("_PUBLIC_WIN_W, _PUBLIC_WIN_H = 1180, 860", src)
|
|
self.assertIn("_PUBLISH_DLG_W, _PUBLISH_DLG_H = 720, 860", src)
|
|
|
|
def test_public_vorlagen_window_structure(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_doku_vorlagen.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
self.assertIn('win.title("Öffentliche Vorlagen")', src)
|
|
self.assertIn("_PUBLIC_LIB_PLACEHOLDER", src)
|
|
self.assertIn("Live-Bibliothek", src)
|
|
|
|
def test_publish_user_message_text(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_doku_vorlagen.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
self.assertIn("_PUBLISH_ROUTE_MSG", src)
|
|
done_block = src.split("def _publish_done", 1)[-1]
|
|
self.assertIn("_PUBLISH_ROUTE_MSG", done_block)
|
|
|
|
def test_publish_route_missing_translation(self):
|
|
# publish_doku_prompt liefert __ROUTE_MISSING__ bei fehlender Route (404),
|
|
# damit die UI kein rohes "Not Found" zeigt.
|
|
import aza_doku_prompt_sync as sync
|
|
|
|
class _FakeApp:
|
|
_user_profile = {"display_name": "Dr. Test"}
|
|
def get_backend_url(self):
|
|
return "https://example.invalid"
|
|
def _empfang_self_user_id(self):
|
|
return "u1"
|
|
|
|
with patch.object(sync, "_sync_headers", return_value={"X-Api-Key": "x"}), \
|
|
patch.object(sync, "_http_post", return_value=(404, {"detail": "Not Found"})):
|
|
ok, msg = sync.publish_doku_prompt(_FakeApp(), "verlauf", {"content": "x"}, "Titel")
|
|
self.assertFalse(ok)
|
|
self.assertEqual(msg, "__ROUTE_MISSING__")
|
|
|
|
def test_publish_conn_error_translation(self):
|
|
import aza_doku_prompt_sync as sync
|
|
|
|
class _FakeApp:
|
|
_user_profile = {"display_name": "Dr. Test"}
|
|
def get_backend_url(self):
|
|
return "https://example.invalid"
|
|
def _empfang_self_user_id(self):
|
|
return "u1"
|
|
|
|
def _raise(*a, **k):
|
|
raise OSError("timeout")
|
|
|
|
with patch.object(sync, "_sync_headers", return_value={"X-Api-Key": "x"}), \
|
|
patch.object(sync, "_http_post", side_effect=_raise):
|
|
ok, msg = sync.publish_doku_prompt(_FakeApp(), "verlauf", {"content": "x"}, "Titel")
|
|
self.assertFalse(ok)
|
|
self.assertEqual(msg, "__CONN_ERROR__")
|
|
|
|
def test_publish_payload_includes_author_and_type(self):
|
|
import aza_doku_prompt_sync as sync
|
|
captured = {}
|
|
|
|
class _FakeApp:
|
|
_user_profile = {"display_name": "Dr. Test"}
|
|
def get_backend_url(self):
|
|
return "https://example.invalid"
|
|
def _empfang_self_user_id(self):
|
|
return "u1"
|
|
|
|
def _capture(url, headers, body):
|
|
captured.update(body)
|
|
return (200, {"ok": True})
|
|
|
|
with patch.object(sync, "_sync_headers", return_value={"X-Api-Key": "x"}), \
|
|
patch.object(sync, "_http_post", side_effect=_capture):
|
|
ok, _ = sync.publish_doku_prompt(_FakeApp(), "verlauf", {"content": "P"}, "Titel", "Beschr")
|
|
self.assertTrue(ok)
|
|
self.assertEqual(captured.get("doc_type"), "verlauf")
|
|
self.assertEqual(captured.get("author_display"), "Dr. Test")
|
|
self.assertEqual(captured.get("title"), "Titel")
|
|
|
|
def test_local_publish_payload_helper(self):
|
|
import tempfile
|
|
import aza_doku_vorlagen as dv
|
|
import aza_config
|
|
with tempfile.TemporaryDirectory() as td:
|
|
with patch.object(aza_config, "get_writable_data_dir", return_value=td):
|
|
# dv ruft aza_config.get_writable_data_dir intern über import auf
|
|
dv.save_local_publish_payload({"document_type_key": "verlauf", "title": "T"})
|
|
p = os.path.join(td, "doku_prompt_publish_queue.json")
|
|
self.assertTrue(os.path.isfile(p))
|
|
with open(p, encoding="utf-8") as fh:
|
|
data = json.load(fh)
|
|
self.assertEqual(data[0]["document_type_key"], "verlauf")
|
|
|
|
def test_publish_payload_metadata_fields(self):
|
|
import tempfile
|
|
import aza_doku_vorlagen as dv
|
|
import aza_config
|
|
with tempfile.TemporaryDirectory() as td:
|
|
with patch.object(aza_config, "get_writable_data_dir", return_value=td):
|
|
dv.save_local_publish_payload({
|
|
"document_type_key": "verlauf",
|
|
"document_type_label": "Verlauf",
|
|
"title": "Mein Verlauf",
|
|
"author_display_name": "Dr. Test",
|
|
"author_initials": "D.T.",
|
|
"region": "Winterthur",
|
|
"specialty": "Dermatologie",
|
|
"description": "Beschreibungstext",
|
|
"prompt_text": "Prompt",
|
|
})
|
|
items = dv.load_local_prepared_public_items()
|
|
self.assertEqual(len(items), 1)
|
|
item = items[0]
|
|
self.assertEqual(item["doc_type"], "verlauf")
|
|
self.assertEqual(item["title"], "Mein Verlauf")
|
|
self.assertEqual(item["author_display"], "Dr. Test")
|
|
self.assertEqual(item["location"], "Winterthur")
|
|
self.assertEqual(item["specialty"], "Dermatologie")
|
|
self.assertEqual(item["description"], "Beschreibungstext")
|
|
|
|
def test_adopt_verlauf_appears_in_dropdown_data(self):
|
|
path, dv = self._patch_templates_path()
|
|
dv._save(dv._default_structure())
|
|
|
|
class _FakeApp:
|
|
_user_profile = {"display_name": "Dr. Test"}
|
|
def _empfang_self_user_id(self):
|
|
return "u1"
|
|
def get_practice_id(self):
|
|
return "p1"
|
|
|
|
item = {
|
|
"doc_type": "verlauf",
|
|
"content": "Verlauf-Prompt",
|
|
"title": "Öffentlicher Verlauf",
|
|
"description": "Beschreibung",
|
|
"author_display": "Dr. X",
|
|
}
|
|
new_id = dv.adopt_public_template_to_own(_FakeApp(), item, activate=True)
|
|
self.assertIsNotNone(new_id)
|
|
data = dv._load()
|
|
verlauf_names = [t["name"] for t in data["templates"]["verlauf"] if not t.get("is_system")]
|
|
kg_names = [t["name"] for t in data["templates"]["kg"] if not t.get("is_system")]
|
|
self.assertIn("Öffentlicher Verlauf", verlauf_names)
|
|
self.assertNotIn("Öffentlicher Verlauf", kg_names)
|
|
self.assertEqual(data["active"]["verlauf"], new_id)
|
|
|
|
def test_adopted_template_deletable(self):
|
|
path, dv = self._patch_templates_path()
|
|
dv._save(dv._default_structure())
|
|
|
|
class _FakeApp:
|
|
def _empfang_self_user_id(self):
|
|
return "u1"
|
|
def get_practice_id(self):
|
|
return "p1"
|
|
|
|
item = {"doc_type": "verlauf", "content": "X", "title": "Löschbar"}
|
|
new_id = dv.adopt_public_template_to_own(_FakeApp(), item, activate=True)
|
|
data = dv._load()
|
|
data["templates"]["verlauf"] = [
|
|
t for t in data["templates"]["verlauf"]
|
|
if not (isinstance(t, dict) and t.get("id") == new_id)
|
|
]
|
|
data["active"]["verlauf"] = "aza_default"
|
|
dv._save(data)
|
|
data2 = dv._load()
|
|
names = [t["name"] for t in data2["templates"]["verlauf"] if not t.get("is_system")]
|
|
self.assertNotIn("Löschbar", names)
|
|
self.assertEqual(data2["active"]["verlauf"], "aza_default")
|
|
|
|
def test_is_own_public_item_recognition(self):
|
|
import aza_doku_vorlagen as dv
|
|
|
|
class _FakeApp:
|
|
_user_profile = {"display_name": "André M. Surovy"}
|
|
def _empfang_self_user_id(self):
|
|
return "user-andre"
|
|
def get_practice_id(self):
|
|
return "p1"
|
|
|
|
own = {"author_user_id": "user-andre", "author_display": "André M. Surovy"}
|
|
foreign = {"author_user_id": "other-user", "author_display": "Dr. Fremd"}
|
|
self.assertTrue(dv.is_own_public_item(_FakeApp(), own))
|
|
self.assertFalse(dv.is_own_public_item(_FakeApp(), foreign))
|
|
|
|
def test_update_local_publish_queue_item(self):
|
|
import tempfile
|
|
import aza_doku_vorlagen as dv
|
|
import aza_config
|
|
with tempfile.TemporaryDirectory() as td:
|
|
qpath = os.path.join(td, "doku_prompt_publish_queue.json")
|
|
with open(qpath, "w", encoding="utf-8") as fh:
|
|
json.dump([{
|
|
"document_type_key": "brief",
|
|
"title": "Alt",
|
|
"author_user_id": "u1",
|
|
}], fh)
|
|
with patch.object(aza_config, "get_writable_data_dir", return_value=td):
|
|
ok = dv.update_local_publish_queue_item(0, {
|
|
"title": "Neu",
|
|
"document_type_key": "verlauf",
|
|
"document_type_label": "Verlauf",
|
|
})
|
|
items = dv.load_local_prepared_public_items()
|
|
self.assertTrue(ok)
|
|
self.assertEqual(items[0]["title"], "Neu")
|
|
self.assertEqual(items[0]["doc_type"], "verlauf")
|
|
|
|
def test_publish_dialog_has_doc_type_dropdown(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_doku_vorlagen.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
dlg_block = src.split("def open_doku_publish_dialog", 1)[-1].split("\ndef ", 1)[0]
|
|
self.assertIn("Dokumenttyp:", dlg_block)
|
|
self.assertIn("doc_combo", dlg_block)
|
|
self.assertIn('"doc_type": _get_selected_doc_type()', dlg_block)
|
|
|
|
def test_doku_refresh_callback_registered(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_doku_vorlagen.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
self.assertIn("_doku_vorlage_refresh_cb", src)
|
|
self.assertIn("adopt_public_template_to_own", src)
|
|
self.assertIn("refresh_doku_prompt_window", src)
|
|
|
|
def test_metadata_dialog_height_and_action_bar(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_doku_vorlagen.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
dlg_block = src.split("def open_doku_publish_dialog", 1)[-1].split("\ndef ", 1)[0]
|
|
self.assertIn("btn_row.pack(side=\"bottom\"", dlg_block)
|
|
self.assertIn("_PUBLISH_DLG_MIN_W, _PUBLISH_DLG_MIN_H = 650, 760", src)
|
|
self.assertIn("⏺ Diktieren", dlg_block)
|
|
self.assertIn("Beschreibung:", dlg_block)
|
|
|
|
def test_remove_local_publish_queue_item(self):
|
|
import tempfile
|
|
import aza_doku_vorlagen as dv
|
|
import aza_config
|
|
with tempfile.TemporaryDirectory() as td:
|
|
with patch.object(aza_config, "get_writable_data_dir", return_value=td):
|
|
dv.save_local_publish_payload({"document_type_key": "verlauf", "title": "A"})
|
|
dv.save_local_publish_payload({"document_type_key": "brief", "title": "B"})
|
|
self.assertTrue(dv.remove_local_publish_queue_item(0))
|
|
items = dv.load_local_prepared_public_items()
|
|
self.assertEqual(len(items), 1)
|
|
self.assertEqual(items[0]["title"], "B")
|
|
|
|
def test_filter_public_items_search(self):
|
|
import aza_doku_vorlagen as dv
|
|
items = [
|
|
{"doc_type": "verlauf", "title": "Alpha", "specialty": "Dermatologie",
|
|
"content": "Verlaufstext", "description": ""},
|
|
{"doc_type": "brief", "title": "Beta", "specialty": "Allgemein",
|
|
"content": "Brieftext", "description": ""},
|
|
]
|
|
by_name = dv.filter_public_items(items, "alpha")
|
|
self.assertEqual(len(by_name), 1)
|
|
self.assertEqual(by_name[0]["title"], "Alpha")
|
|
by_spec = dv.filter_public_items(items, "dermatologie")
|
|
self.assertEqual(len(by_spec), 1)
|
|
by_prompt = dv.filter_public_items(items, "brieftext")
|
|
self.assertEqual(len(by_prompt), 1)
|
|
self.assertEqual(by_prompt[0]["doc_type"], "brief")
|
|
|
|
def test_adopt_empty_content_returns_none_without_crash(self):
|
|
path, dv = self._patch_templates_path()
|
|
dv._save(dv._default_structure())
|
|
|
|
class _FakeApp:
|
|
def _empfang_self_user_id(self):
|
|
return "u1"
|
|
def get_practice_id(self):
|
|
return "p1"
|
|
def after(self, _ms, fn):
|
|
fn()
|
|
|
|
item = {"doc_type": "verlauf", "content": "", "title": "Leer"}
|
|
new_id = dv.adopt_public_template_to_own(_FakeApp(), item, activate=True)
|
|
self.assertIsNotNone(new_id)
|
|
|
|
def test_public_window_search_field(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_doku_vorlagen.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
pub_block = src.split("def open_public_doku_vorlagen_window", 1)[-1].split("\ndef ", 1)[0]
|
|
self.assertIn("Suchen:", pub_block)
|
|
self.assertIn("filter_public_items", pub_block)
|
|
|
|
def test_main_doc_type_persistence(self):
|
|
import tempfile
|
|
import aza_persistence as ap
|
|
|
|
with tempfile.TemporaryDirectory() as td:
|
|
cfg = os.path.join(td, "autotext.json")
|
|
with patch.object(ap, "_autotext_config_path", return_value=cfg):
|
|
self.assertEqual(ap.load_main_doc_type(), "kg")
|
|
ap.save_main_doc_type("verlauf")
|
|
self.assertEqual(ap.load_main_doc_type(), "verlauf")
|
|
ap.save_main_doc_type("invalid_type")
|
|
self.assertEqual(ap.load_main_doc_type(), "kg")
|
|
ap.save_main_doc_type("brief")
|
|
data = ap.load_autotext()
|
|
self.assertEqual(data.get("main_doc_type"), "brief")
|
|
|
|
|
|
class TestModalDialogHelpers(unittest.TestCase):
|
|
def test_modal_helpers_exist(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_ui_helpers.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
for name in (
|
|
"make_modal_topmost",
|
|
"release_modal_dialog",
|
|
"aza_showinfo",
|
|
"aza_askyesno",
|
|
"_restore_main_topmost",
|
|
):
|
|
self.assertIn(f"def {name}", src)
|
|
|
|
def test_make_modal_topmost_sets_transient_grab_topmost(self):
|
|
import tkinter as tk
|
|
from aza_ui_helpers import make_modal_topmost
|
|
|
|
root = tk.Tk()
|
|
root.withdraw()
|
|
parent = tk.Toplevel(root)
|
|
parent.withdraw()
|
|
dlg = tk.Toplevel(parent)
|
|
make_modal_topmost(dlg, parent, grab=True)
|
|
self.assertEqual(str(dlg.attributes("-topmost")), "1")
|
|
self.assertEqual(dlg.grab_current(), dlg)
|
|
dlg.destroy()
|
|
parent.destroy()
|
|
root.destroy()
|
|
|
|
def test_messagebox_wrapper_passes_parent(self):
|
|
import tkinter as tk
|
|
from unittest.mock import patch
|
|
from aza_ui_helpers import aza_askyesno
|
|
|
|
root = tk.Tk()
|
|
root.withdraw()
|
|
parent = tk.Toplevel(root)
|
|
parent.withdraw()
|
|
parent._main_pinned = True # type: ignore[attr-defined]
|
|
captured = {}
|
|
|
|
def _fake_askyesno(title, message, parent=None):
|
|
captured["parent"] = parent
|
|
return True
|
|
|
|
with patch("tkinter.messagebox.askyesno", side_effect=_fake_askyesno):
|
|
result = aza_askyesno("T", "M", parent=parent)
|
|
self.assertTrue(result)
|
|
self.assertIs(captured.get("parent"), parent)
|
|
parent.destroy()
|
|
root.destroy()
|
|
|
|
def test_doku_vorlagen_uses_aza_messagebox_wrappers(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_doku_vorlagen.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
self.assertIn("from aza_ui_helpers import", src)
|
|
self.assertIn("aza_askyesno", src)
|
|
self.assertIn("make_modal_topmost", src)
|
|
pub_block = src.split("def open_public_doku_vorlagen_window", 1)[-1].split("\ndef ", 1)[0]
|
|
self.assertIn("aza_askyesno(", pub_block)
|
|
self.assertNotIn("messagebox.askyesno", pub_block)
|
|
pub_dialog = src.split("def open_doku_publish_dialog", 1)[-1].split("\ndef ", 1)[0]
|
|
self.assertIn("make_modal_topmost(dlg, parent)", pub_dialog)
|
|
self.assertIn("release_modal_dialog(dlg, parent)", pub_dialog)
|
|
|
|
def test_bibliothek_ui_uses_aza_messagebox_wrappers(self):
|
|
path = os.path.join(os.path.dirname(__file__), "aza_bibliothek_ui.py")
|
|
with open(path, encoding="utf-8") as f:
|
|
src = f.read()
|
|
self.assertIn("aza_askyesno", src)
|
|
self.assertNotIn("messagebox.", src)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|