platform-core/tests/unit/services/test_buildTree.py
ValueOn AG 51b789b5aa
All checks were successful
Deploy Plattform-Core / test (push) Successful in 53s
Deploy Plattform-Core / deploy (push) Successful in 4s
icon toggle
2026-05-27 15:36:03 +02:00

189 lines
8.1 KiB
Python

"""Unit tests for the generic UDB tree builder (`_buildTree.py`).
Most node-level behavior moved into the polymorphic class hierarchy
(`udbNodes.py`) and has its own dedicated tests in `test_udbNodes.py`.
This file covers the orchestrator (`getChildrenForParents`) and the
remaining lookup helpers.
"""
from __future__ import annotations
import asyncio
import unittest
from unittest.mock import MagicMock, patch
from modules.serviceCenter.services.serviceKnowledge import _buildTree
class TestKeyCoding(unittest.TestCase):
def test_encode_decode_roundtrip(self):
key = _buildTree._encode("ds", "conn-1", "sharepointFolder", "/sites/x")
kind, parts = _buildTree._decode(key)
self.assertEqual(kind, "ds")
self.assertEqual(parts, ["conn-1", "sharepointFolder", "/sites/x"])
def test_top_level_kinds(self):
self.assertEqual(_buildTree._decode("conn|abc")[0], "conn")
self.assertEqual(_buildTree._decode("mgrp|m1")[0], "mgrp")
self.assertEqual(_buildTree._decode("feat|m1|trustee|fi-1")[1], ["m1", "trustee", "fi-1"])
class TestRecordLookup(unittest.TestCase):
def test_finds_ds_record_by_normalised_path(self):
rec = {"id": "x", "connectionId": "c", "sourceType": "msft", "path": "/folder"}
self.assertEqual(_buildTree._findDsRecord([rec], "c", "msft", "/folder/").get("id"), "x")
self.assertIsNone(_buildTree._findDsRecord([rec], "c", "msft", "/other"))
def test_finds_fds_record_with_matching_filter(self):
rec = {"id": "f", "featureInstanceId": "fi1", "tableName": "Pos",
"recordFilter": {"id": "5"}}
self.assertEqual(_buildTree._findFdsRecord([rec], "fi1", "Pos", {"id": "5"}).get("id"), "f")
self.assertIsNone(_buildTree._findFdsRecord([rec], "fi1", "Pos", {"id": "99"}))
def test_fds_record_with_none_filter_matches_only_none(self):
rec = {"id": "f", "featureInstanceId": "fi1", "tableName": "*", "recordFilter": None}
self.assertEqual(_buildTree._findFdsRecord([rec], "fi1", "*", None).get("id"), "f")
self.assertIsNone(_buildTree._findFdsRecord([rec], "fi1", "*", {"id": "1"}))
class TestWiredTableFieldAggregation(unittest.TestCase):
"""`_wireTableFieldsAsLogicalChildren` rewraps `FdsTableNode.getEffectiveFlag`
so the table aggregates with its declared field children. The aggregate
must respect the new FdsField inheritance: if all fields inherit from the
table (no list entries), the table stays non-mixed."""
def _buildTableWithFields(self, *, tableNeutralize, neutralizeFields, fieldNames):
from modules.serviceCenter.services.serviceKnowledge.udbNodes import (
FdsTableNode, FdsFieldNode,
)
rec = {
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
"recordFilter": None,
"neutralize": tableNeutralize,
"neutralizeFields": neutralizeFields,
}
tableNode = FdsTableNode("fi1", "trustee", "Pos", "key", "Positions",
"feat|m1|trustee|fi1", rec=rec, hasFields=True)
fields = [
FdsFieldNode("fi1", "Pos", name, tableNode.key,
tableRec=rec, featureCode="trustee")
for name in fieldNames
]
tableNode._logicalFieldChildren = fields # type: ignore[attr-defined]
_buildTree._wireTableFieldsAsLogicalChildren(tableNode)
return tableNode, [rec]
def test_table_true_no_overrides_stays_true(self):
"""Regression: toggling a table to True must NOT leave it 'mixed'
because the declared field children should inherit the table value."""
tableNode, allFds = self._buildTableWithFields(
tableNeutralize=True, neutralizeFields=None,
fieldNames=["amount", "currency"],
)
self.assertTrue(tableNode.getEffectiveFlag("neutralize", [], allFds, "aggregate"))
def test_table_false_with_override_is_mixed(self):
tableNode, allFds = self._buildTableWithFields(
tableNeutralize=False, neutralizeFields=["amount"],
fieldNames=["amount", "currency"],
)
self.assertEqual(
tableNode.getEffectiveFlag("neutralize", [], allFds, "aggregate"),
"mixed",
)
def test_table_inherit_no_overrides_walks_default(self):
"""Implicit table + no overrides + no workspace -> default False."""
tableNode, allFds = self._buildTableWithFields(
tableNeutralize=None, neutralizeFields=None,
fieldNames=["amount", "currency"],
)
self.assertFalse(tableNode.getEffectiveFlag("neutralize", [], allFds, "aggregate"))
class TestGetChildrenForParents(unittest.TestCase):
"""End-to-end orchestrator tests with mocked dependencies. The
orchestrator returns serialised node dicts produced by
`UdbNode.toDict(...)`, so the keys/kinds/parentKey wiring is what
matters here -- not the inheritance arithmetic (covered in
test_udbNodes.py)."""
def _runAsync(self, coro):
return asyncio.run(coro)
def test_unknown_parent_key_returns_empty_list(self):
with patch("modules.interfaces.interfaceDbApp.getRootInterface") as mockRoot:
rootIf = MagicMock()
rootIf.db.getRecordset.return_value = []
mockRoot.return_value = rootIf
ctx = MagicMock()
ctx.user.id = "u1"
ctx.mandateId = "m1"
result = self._runAsync(
_buildTree.getChildrenForParents(["bogus|key"], ctx)
)
self.assertEqual(result["bogus|key"], [])
def test_top_level_emits_personal_root_first(self):
with patch("modules.interfaces.interfaceDbApp.getRootInterface") as mockRoot:
rootIf = MagicMock()
rootIf.db.getRecordset.return_value = []
rootIf.getUserMandates.return_value = []
mockRoot.return_value = rootIf
ctx = MagicMock()
ctx.user.id = "u1"
ctx.mandateId = "m1"
result = self._runAsync(
_buildTree.getChildrenForParents([None], ctx)
)
children = result["__root__"]
self.assertGreaterEqual(len(children), 1)
personalRoot = children[0]
self.assertEqual(personalRoot["key"], "personalRoot")
self.assertEqual(personalRoot["kind"], "synthRoot")
self.assertIsNone(personalRoot["parentKey"])
self.assertTrue(personalRoot["hasChildren"])
self.assertTrue(personalRoot["defaultExpanded"])
def test_top_level_emits_mandate_groups_inline(self):
with patch("modules.interfaces.interfaceDbApp.getRootInterface") as mockRoot, \
patch("modules.security.rbacCatalog.getCatalogService") as mockCatalog:
rootIf = MagicMock()
rootIf.db.getRecordset.return_value = []
userMandate = MagicMock()
userMandate.mandateId = "m1"
rootIf.getUserMandates.return_value = [userMandate]
featureInst = MagicMock()
featureInst.id = "fi-1"
featureInst.featureCode = "trustee"
featureInst.enabled = True
rootIf.getFeatureInstancesByMandate.return_value = [featureInst]
featureAccess = MagicMock(enabled=True)
rootIf.getFeatureAccess.return_value = featureAccess
mockRoot.return_value = rootIf
catalog = MagicMock()
catalog.getFeaturesWithDataObjects.return_value = ["trustee"]
mockCatalog.return_value = catalog
ctx = MagicMock()
ctx.user.id = "u1"
ctx.mandateId = None
result = self._runAsync(_buildTree.getChildrenForParents([None], ctx))
byKey = {c["key"]: c for c in result["__root__"]}
self.assertIn("personalRoot", byKey)
self.assertIn("mgrp|m1", byKey)
mgroup = byKey["mgrp|m1"]
self.assertEqual(mgroup["kind"], "mandateGroup")
self.assertIsNone(mgroup["parentKey"])
self.assertEqual(mgroup["mandateId"], "m1")
self.assertTrue(mgroup["defaultExpanded"])
if __name__ == "__main__":
unittest.main()