Files
aza/AzA march 2026/tests/test_sync_items.py

214 lines
7.0 KiB
Python
Raw Normal View History

2026-05-23 21:31:34 +02:00
# -*- coding: utf-8 -*-
"""Tests für practice_id-Sync (Textblöcke, Korrekturen, Autotext) und renewal_date_de."""
import json
import os
import sqlite3
import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import MagicMock
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from aza_sync_items import (
ensure_sync_items_schema,
items_to_local_textblocks,
list_sync_items,
local_autotext_to_items,
local_korrekturen_to_items,
local_textblocks_to_items,
soft_delete_sync_item,
upsert_sync_item,
)
from aza_ai_budget import budget_json_for_client
class TestSyncItemsSchema(unittest.TestCase):
def test_schema_idempotent(self):
td = tempfile.mkdtemp()
try:
db = Path(td) / "t.sqlite"
con = sqlite3.connect(db)
try:
ensure_sync_items_schema(con)
ensure_sync_items_schema(con)
cols = {
r[1]
for r in con.execute("PRAGMA table_info(synced_user_items)").fetchall()
}
finally:
con.close()
self.assertIn("practice_id", cols)
self.assertIn("item_type", cols)
finally:
try:
os.remove(Path(td) / "t.sqlite")
except OSError:
pass
os.rmdir(td)
class TestSyncItemsDb(unittest.TestCase):
def tearDown(self):
import shutil
try:
shutil.rmtree(self.td, ignore_errors=True)
except Exception:
pass
def setUp(self):
self.td = tempfile.mkdtemp()
self.db = Path(self.td) / "stripe_webhook.sqlite"
with sqlite3.connect(self.db) as con:
ensure_sync_items_schema(con)
con.execute(
"""
CREATE TABLE licenses (
subscription_id TEXT PRIMARY KEY,
customer_id TEXT,
status TEXT,
lookup_key TEXT,
allowed_users INTEGER,
devices_per_user INTEGER,
customer_email TEXT,
client_reference_id TEXT,
current_period_end INTEGER,
updated_at INTEGER NOT NULL,
practice_id TEXT,
current_period_start INTEGER
)
"""
)
con.execute(
"""
INSERT INTO licenses (
subscription_id, customer_id, status, lookup_key,
allowed_users, devices_per_user, customer_email,
client_reference_id, current_period_end, updated_at,
practice_id, current_period_start
) VALUES ('sub_a', 'c1', 'active', 'k1', 5, 2, 'a@test.ch',
'', 2000000000, 1, 'prac_alpha', 1900000000)
"""
)
con.execute(
"""
INSERT INTO licenses (
subscription_id, customer_id, status, lookup_key,
allowed_users, devices_per_user, customer_email,
client_reference_id, current_period_end, updated_at,
practice_id, current_period_start
) VALUES ('sub_b', 'c2', 'active', 'k2', 5, 2, 'b@test.ch',
'', 2000000000, 1, 'prac_beta', 1900000000)
"""
)
con.commit()
def test_practice_isolation_and_soft_delete(self):
with sqlite3.connect(self.db) as con:
upsert_sync_item(
con,
"prac_alpha",
{
"id": "tb_1",
"item_type": "textblock",
"title": "A",
"content": "alpha",
"sort_order": 1,
},
)
upsert_sync_item(
con,
"prac_beta",
{
"id": "tb_beta_1",
"item_type": "textblock",
"title": "B",
"content": "beta",
"sort_order": 1,
},
)
a_items = list_sync_items(con, "prac_alpha", "textblock")
b_items = list_sync_items(con, "prac_beta", "textblock")
self.assertEqual(len(a_items), 1)
self.assertEqual(a_items[0]["content"], "alpha")
self.assertEqual(b_items[0]["content"], "beta")
soft_delete_sync_item(con, "prac_alpha", "tb_1")
a2 = list_sync_items(con, "prac_alpha", "textblock")
self.assertEqual(len(a2), 0)
def test_item_types_separate(self):
with sqlite3.connect(self.db) as con:
upsert_sync_item(
con,
"prac_alpha",
{
"id": "at_x",
"item_type": "autotext",
"trigger": "kg",
"content": "Krankengeschichte",
"sort_order": 1,
},
)
upsert_sync_item(
con,
"prac_alpha",
{
"id": "corr_1",
"item_type": "correction",
"title": "medikamente",
"trigger": "asprin",
"content": "Aspirin",
"sort_order": 1,
},
)
at = list_sync_items(con, "prac_alpha", "autotext")
co = list_sync_items(con, "prac_alpha", "correction")
self.assertEqual(len(at), 1)
self.assertEqual(len(co), 1)
class TestLocalMapping(unittest.TestCase):
def test_textblock_roundtrip(self):
local = {
"1": {"name": "Urtikaria", "content": "Haut", "updated_at": "t"},
"2": {"name": "TB2", "content": "", "updated_at": "t"},
}
items = local_textblocks_to_items(local)
back = items_to_local_textblocks(items)
self.assertEqual(back["1"]["name"], "Urtikaria")
self.assertGreaterEqual(len(back), 2)
def test_korrekturen_mapping(self):
k = {"medikamente": {"asprin": "Aspirin"}}
items = local_korrekturen_to_items(k)
self.assertEqual(items[0]["item_type"], "correction")
def test_autotext_mapping(self):
at = {"entries": {"kg": "Krankengeschichte"}}
items = local_autotext_to_items(at)
self.assertEqual(items[0]["trigger"], "kg")
class TestBudgetRenewalField(unittest.TestCase):
def test_renewal_date_de_in_client_json(self):
snap = {
"ok": True,
"active": True,
"available_percent": 76,
"period_end": 1781817600,
"show_warning": False,
"user_label": "KI-Kontingent: 76 % verfügbar",
}
body = budget_json_for_client(snap)
self.assertIn("renewal_date_de", body)
self.assertTrue(body["renewal_date_de"])
if __name__ == "__main__":
unittest.main()