Files
aza/AzA march 2026/_test_doku_prompt_system.py
2026-06-13 22:47:31 +02:00

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()