gateway/tests/demo/test_pwg_demo_bootstrap.py
2026-04-26 08:31:35 +02:00

226 lines
9.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Copyright (c) 2026 Patrick Motsch
# All rights reserved.
"""T6 — PWG-Pilot demo bootstrap & idempotency tests.
Covers AC 11 + AC 12 of the PWG-Pilot plan:
- ``PwgDemo2026.load()`` is idempotent (twice → no errors).
- All expected objects exist after load (mandate, demo user,
4 feature instances, trustee seed data, imported pilot workflow with
``active=False``).
- ``remove()`` cleans up cleanly and a subsequent ``load()`` rebuilds
the demo without error (idempotency over the full lifecycle).
Mirrors the structure of ``tests/demo/test_demo_bootstrap.py`` and reuses
its session-scoped ``db`` fixture from ``tests/demo/conftest.py``.
Marked ``expensive + live`` because they hit the real Postgres databases
(``poweron_app``, ``poweron_trustee``, ``poweron_graphicaleditor``); run
them explicitly with::
pytest -m "expensive or live" tests/demo/test_pwg_demo_bootstrap.py
"""
import pytest
from modules.datamodels.datamodelFeatures import FeatureInstance
from modules.datamodels.datamodelMembership import UserMandate
from modules.datamodels.datamodelUam import Mandate, UserInDB
from tests.demo.conftest import _getFeatureInstances
pytestmark = [pytest.mark.expensive, pytest.mark.live]
# ---------------------------------------------------------------------------
# Fixtures (function-scoped so they always reflect current DB state)
# ---------------------------------------------------------------------------
@pytest.fixture(scope="session")
def pwgDemoConfig():
"""Auto-discovered ``PwgDemo2026`` instance."""
from modules.demoConfigs import getDemoConfigByCode
cfg = getDemoConfigByCode("pwg-demo-2026")
assert cfg is not None, (
"Demo config 'pwg-demo-2026' not found — check modules/demoConfigs/pwgDemo2026.py"
)
return cfg
@pytest.fixture
def mandatePwg(db):
records = db.getRecordset(Mandate, recordFilter={"name": "stiftung-pwg"})
assert records, "Mandate 'stiftung-pwg' not found — run pwgDemoConfig.load() first"
return records[0]
@pytest.fixture
def pwgUser(db):
records = db.getRecordset(UserInDB, recordFilter={"username": "pwg.demo"})
assert records, "User 'pwg.demo' not found — run pwgDemoConfig.load() first"
return records[0]
# ---------------------------------------------------------------------------
# Bootstrap idempotency
# ---------------------------------------------------------------------------
class TestPwgDemoBootstrap:
def test_loadIsIdempotent(self, db, pwgDemoConfig):
"""Loading the PWG demo twice in a row must not raise errors."""
s1 = pwgDemoConfig.load(db)
assert len(s1.get("errors", [])) == 0, f"First load errors: {s1['errors']}"
s2 = pwgDemoConfig.load(db)
assert len(s2.get("errors", [])) == 0, f"Second load errors: {s2['errors']}"
def test_credentialsAreSurfacedFromLoadSummary(self, db, pwgDemoConfig):
s = pwgDemoConfig.load(db)
creds = s.get("credentials") or []
assert any(c.get("username") == "pwg.demo" for c in creds), (
"PWG demo must surface 'pwg.demo' credentials so the SysAdmin "
"doesn't have to grep source code for the password."
)
def test_mandateStiftungPwgExists(self, db):
records = db.getRecordset(Mandate, recordFilter={"name": "stiftung-pwg"})
assert len(records) == 1
assert records[0].get("label") == "Stiftung PWG"
assert records[0].get("enabled") is True
def test_pwgDemoUserExists(self, db):
records = db.getRecordset(UserInDB, recordFilter={"username": "pwg.demo"})
assert len(records) == 1
user = records[0]
assert user.get("email") == "pwg.demo@poweron.swiss"
assert user.get("isSysAdmin") is True
assert user.get("language") == "de"
def test_pwgUserMembership(self, db, pwgUser, mandatePwg):
memberships = db.getRecordset(UserMandate, recordFilter={
"userId": pwgUser.get("id"),
"mandateId": mandatePwg.get("id"),
})
assert len(memberships) >= 1, "PWG demo user not a member of Stiftung PWG"
@pytest.mark.parametrize(
"featureCode",
["workspace", "trustee", "graphicalEditor", "neutralization"],
)
def test_pwgFeaturesExist(self, db, mandatePwg, featureCode):
instances = _getFeatureInstances(db, mandatePwg.get("id"), featureCode)
assert len(instances) >= 1, f"Feature '{featureCode}' missing in Stiftung PWG"
def test_pwgFourFeatureInstances(self, db, mandatePwg):
instances = db.getRecordset(FeatureInstance, recordFilter={
"mandateId": mandatePwg.get("id"),
}) or []
codes = sorted({i.get("featureCode") for i in instances})
assert codes == ["graphicalEditor", "neutralization", "trustee", "workspace"], (
f"Expected exactly 4 feature instances, got {codes}"
)
# ---------------------------------------------------------------------------
# Trustee seed data — 5 fictitious tenants × 12 monthly bookings each
# ---------------------------------------------------------------------------
class TestPwgTrusteeSeed:
def test_trusteeRentAccountExists(self, db, mandatePwg):
from modules.features.trustee.datamodelFeatureTrustee import TrusteeDataAccount
instances = _getFeatureInstances(db, mandatePwg.get("id"), "trustee")
assert instances, "No trustee instance for PWG"
instId = instances[0].get("id")
from modules.demoConfigs.pwgDemo2026 import _openTrusteeDb
trusteeDb = _openTrusteeDb()
accounts = trusteeDb.getRecordset(TrusteeDataAccount, recordFilter={
"featureInstanceId": instId,
"accountNumber": "6000",
}) or []
assert len(accounts) == 1, f"Expected exactly 1 rent account 6000, got {len(accounts)}"
assert accounts[0].get("isActive") is True
def test_trusteeFiveTenants(self, db, mandatePwg):
from modules.features.trustee.datamodelFeatureTrustee import TrusteeDataContact
instances = _getFeatureInstances(db, mandatePwg.get("id"), "trustee")
instId = instances[0].get("id")
from modules.demoConfigs.pwgDemo2026 import _openTrusteeDb
trusteeDb = _openTrusteeDb()
contacts = trusteeDb.getRecordset(TrusteeDataContact, recordFilter={
"featureInstanceId": instId,
}) or []
# Some installations may already have other trustee contacts, but the
# 5 PWG seed tenants must be present.
names = {c.get("name") for c in contacts}
for expected in (
"Anna Müller", "Beat Schneider", "Carla Weber",
"Daniel Frey", "Eva Lang",
):
assert expected in names, f"PWG seed tenant '{expected}' missing"
def test_trusteeMonthlyBookingsForTenant(self, db, mandatePwg):
"""Every tenant gets 12 monthly journal entries."""
from modules.features.trustee.datamodelFeatureTrustee import TrusteeDataJournalEntry
instances = _getFeatureInstances(db, mandatePwg.get("id"), "trustee")
instId = instances[0].get("id")
from modules.demoConfigs.pwgDemo2026 import _openTrusteeDb
trusteeDb = _openTrusteeDb()
entries = trusteeDb.getRecordset(TrusteeDataJournalEntry, recordFilter={
"featureInstanceId": instId,
}) or []
# 5 tenants × 12 months = 60; >= so reload doesn't false-fail.
pwgEntries = [e for e in entries if (e.get("reference") or "").startswith("PWG-")]
assert len(pwgEntries) >= 60, (
f"Expected >=60 PWG journal entries (5 tenants × 12 months), got {len(pwgEntries)}"
)
# ---------------------------------------------------------------------------
# Pilot workflow — imported envelope, must be active=False
# ---------------------------------------------------------------------------
class TestPwgPilotWorkflow:
def test_pilotWorkflowImported(self, db, mandatePwg):
from modules.features.graphicalEditor.datamodelFeatureGraphicalEditor import AutoWorkflow
from modules.demoConfigs.pwgDemo2026 import _openGraphicalEditorDb
instances = _getFeatureInstances(db, mandatePwg.get("id"), "graphicalEditor")
assert instances, "No graphicalEditor instance for PWG"
instId = instances[0].get("id")
geDb = _openGraphicalEditorDb()
wfs = geDb.getRecordset(AutoWorkflow, recordFilter={
"mandateId": mandatePwg.get("id"),
"featureInstanceId": instId,
"label": "PWG Pilot: Jahresmietzinsbestätigung",
}) or []
assert len(wfs) == 1, f"Expected exactly 1 PWG pilot workflow, got {len(wfs)}"
wf = wfs[0]
# AC 10: imports must be inactive by default
assert wf.get("active") is False, "PWG pilot workflow must be imported with active=false"
graph = wf.get("graph") or {}
assert (graph.get("nodes") or []), "PWG pilot workflow has no nodes"
# ---------------------------------------------------------------------------
# Lifecycle: remove + reload (mirrors investor demo TestDemoRemoveAndReload)
# ---------------------------------------------------------------------------
class TestPwgRemoveAndReload:
def test_removeAndReload(self, db, pwgDemoConfig):
"""Remove the PWG demo, verify it is gone, then reload it."""
rs = pwgDemoConfig.remove(db)
assert len(rs.get("errors", [])) == 0, f"Remove errors: {rs['errors']}"
mandates = db.getRecordset(Mandate, recordFilter={"name": "stiftung-pwg"})
assert len(mandates) == 0, "Stiftung PWG mandate should be gone after remove"
users = db.getRecordset(UserInDB, recordFilter={"username": "pwg.demo"})
assert len(users) == 0, "pwg.demo user should be gone after remove"
ls = pwgDemoConfig.load(db)
assert len(ls.get("errors", [])) == 0, f"Reload errors: {ls['errors']}"
mandates = db.getRecordset(Mandate, recordFilter={"name": "stiftung-pwg"})
assert len(mandates) == 1, "Stiftung PWG must exist after reload"