gateway/tests/unit/bootstrap/test_mandateNameMigration.py

133 lines
4.7 KiB
Python

#!/usr/bin/env python3
# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Unit tests for ``_migrateMandateNameLabelSlugRules`` in interfaceBootstrap.
Covers:
- legacy ``name``/``label`` rows get fixed (label fill, slug rename),
- collisions across legacy rows resolve via -2/-3 suffixes in stable id order,
- valid rows are left untouched (idempotency),
- second invocation is a no-op.
"""
from typing import Any, Dict, List, Optional
import pytest
from modules.datamodels.datamodelUam import Mandate
from modules.interfaces.interfaceBootstrap import _migrateMandateNameLabelSlugRules
from modules.shared.mandateNameUtils import isValidMandateName
class _FakeDb:
"""Minimal connector simulating getRecordset(Mandate)+recordModify(Mandate, id, data)."""
def __init__(self, rows: List[Dict[str, Any]]):
self.rows: List[Dict[str, Any]] = [dict(r) for r in rows]
self.modifyCalls: List[Dict[str, Any]] = []
def getRecordset(self, model, recordFilter: Optional[Dict[str, Any]] = None):
if model is not Mandate:
return []
if not recordFilter:
return [dict(r) for r in self.rows]
out = []
for r in self.rows:
if all(r.get(k) == v for k, v in recordFilter.items()):
out.append(dict(r))
return out
def recordModify(self, model, recordId: str, data: Dict[str, Any]):
self.modifyCalls.append({"id": str(recordId), "data": dict(data)})
for r in self.rows:
if str(r.get("id")) == str(recordId):
r.update(data)
return r
return None
def _row(mid: str, name: Any, label: Any = None) -> Dict[str, Any]:
return {"id": mid, "name": name, "label": label}
class TestMigrationFillsLabel:
def test_emptyLabelGetsNameAsLabel(self):
db = _FakeDb([_row("a1", "good-name", None)])
_migrateMandateNameLabelSlugRules(db)
assert db.rows[0]["label"] == "good-name"
assert db.rows[0]["name"] == "good-name"
def test_emptyLabelAndEmptyNameFallsBackToMandate(self):
db = _FakeDb([_row("a1", "", "")])
_migrateMandateNameLabelSlugRules(db)
assert db.rows[0]["label"] == "Mandate"
assert isValidMandateName(db.rows[0]["name"])
class TestMigrationRenamesInvalidNames:
def test_invalidNameGetsSlugFromLabel(self):
db = _FakeDb([_row("a1", "Home patrick", "Home Patrick")])
_migrateMandateNameLabelSlugRules(db)
assert db.rows[0]["name"] == "home-patrick"
assert db.rows[0]["label"] == "Home Patrick"
def test_umlautsTransliterated(self):
db = _FakeDb([_row("a1", "Müller AG", "Müller AG")])
_migrateMandateNameLabelSlugRules(db)
assert db.rows[0]["name"] == "mueller-ag"
class TestMigrationCollisions:
def test_collisionsResolveByStableIdOrder(self):
rows = [
_row("z1", "Home patrick", "Home Patrick"),
_row("a1", "home-patrick", "Home Patrick Two"),
]
db = _FakeDb(rows)
_migrateMandateNameLabelSlugRules(db)
byId = {r["id"]: r for r in db.rows}
assert byId["a1"]["name"] == "home-patrick"
assert byId["z1"]["name"] == "home-patrick-2"
def test_threeWayCollisionGetsThirdSuffix(self):
rows = [
_row("id-aaa", "home-patrick", "Home Patrick"),
_row("id-bbb", "Home patrick", "Home Patrick"),
_row("id-ccc", "home patrick", "Home Patrick"),
]
db = _FakeDb(rows)
_migrateMandateNameLabelSlugRules(db)
names = sorted(r["name"] for r in db.rows)
assert names == ["home-patrick", "home-patrick-2", "home-patrick-3"]
class TestMigrationIdempotency:
def test_secondRunIsNoop(self):
rows = [
_row("a1", "home-patrick", "Home Patrick"),
_row("b1", "Home Müller", ""),
]
db = _FakeDb(rows)
_migrateMandateNameLabelSlugRules(db)
assert all(isValidMandateName(r["name"]) for r in db.rows)
firstChanges = list(db.modifyCalls)
db.modifyCalls.clear()
_migrateMandateNameLabelSlugRules(db)
assert db.modifyCalls == [], (
f"expected no further changes after first migration, got {db.modifyCalls}; "
f"firstRun changes: {firstChanges}"
)
def test_validRowsLeftUntouched(self):
rows = [_row("a1", "root", "Root"), _row("b1", "alpina-treuhand", "Alpina Treuhand AG")]
db = _FakeDb(rows)
_migrateMandateNameLabelSlugRules(db)
assert db.modifyCalls == []
class TestMigrationEmpty:
def test_emptyDbDoesNothing(self):
db = _FakeDb([])
_migrateMandateNameLabelSlugRules(db)
assert db.modifyCalls == []