449 lines
21 KiB
Python
449 lines
21 KiB
Python
"""Unit tests for the polymorphic UDB node hierarchy (udbNodes.py).
|
|
|
|
Each concrete node class is exercised for:
|
|
- `supportsFlag` returns the right set per kind
|
|
- `canEdit` enforces DS-owner vs FDS-feature-admin
|
|
- `getEffectiveFlag` resolves walk + aggregate correctly
|
|
- `setFlag` writes the right record and (where applicable) cascades
|
|
- `toDict` produces the expected wire shape
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from modules.serviceCenter.services.serviceKnowledge.udbNodes import (
|
|
UdbNode,
|
|
SyntheticContainerNode,
|
|
MandateGroupNode,
|
|
ConnectionNode,
|
|
ServiceNode,
|
|
FolderNode,
|
|
FileNode,
|
|
FdsWorkspaceNode,
|
|
FdsTableNode,
|
|
FdsFieldNode,
|
|
_isFeatureAdmin,
|
|
)
|
|
|
|
|
|
class _FakeUser:
|
|
def __init__(self, userId: str = "user-1"):
|
|
self.id = userId
|
|
|
|
|
|
class _FakeContext:
|
|
def __init__(self, userId: str = "user-1"):
|
|
self.user = _FakeUser(userId)
|
|
self.mandateId = "m1"
|
|
|
|
|
|
class TestSupportsFlag(unittest.TestCase):
|
|
def test_synthetic_container_supports_nothing(self):
|
|
n = SyntheticContainerNode("k", "label", icon="x")
|
|
self.assertFalse(n.supportsFlag("neutralize"))
|
|
self.assertFalse(n.supportsFlag("scope"))
|
|
self.assertFalse(n.supportsFlag("ragIndexEnabled"))
|
|
|
|
def test_connection_supports_neutralize_and_rag(self):
|
|
n = ConnectionNode("c1", "msft", label="m", parentKey="personalRoot", rec=None)
|
|
self.assertTrue(n.supportsFlag("neutralize"))
|
|
self.assertFalse(n.supportsFlag("scope"))
|
|
self.assertTrue(n.supportsFlag("ragIndexEnabled"))
|
|
|
|
def test_fds_table_supports_neutralize_and_rag_but_not_scope(self):
|
|
n = FdsTableNode(
|
|
featureInstanceId="fi1", featureCode="trustee", tableName="Pos",
|
|
objectKey="data.feature.trustee.Pos", label="Positions",
|
|
parentKey="feat|m1|trustee|fi1", rec=None, hasFields=False,
|
|
)
|
|
self.assertTrue(n.supportsFlag("neutralize"))
|
|
self.assertTrue(n.supportsFlag("ragIndexEnabled"))
|
|
self.assertFalse(n.supportsFlag("scope"))
|
|
|
|
def test_fds_field_supports_only_neutralize(self):
|
|
n = FdsFieldNode(
|
|
featureInstanceId="fi1", tableName="Pos", fieldName="amount",
|
|
parentKey="fdstbl|fi1|Pos", tableRec=None, featureCode="trustee",
|
|
)
|
|
self.assertTrue(n.supportsFlag("neutralize"))
|
|
self.assertFalse(n.supportsFlag("scope"))
|
|
self.assertFalse(n.supportsFlag("ragIndexEnabled"))
|
|
|
|
|
|
class TestCanEditDataSourceOwner(unittest.TestCase):
|
|
def test_owner_can_edit(self):
|
|
rec = {"id": "ds1", "userId": "user-1"}
|
|
node = ConnectionNode("c1", "msft", "m", "personalRoot", rec=rec)
|
|
self.assertTrue(node.canEdit(_FakeContext("user-1"), MagicMock()))
|
|
|
|
def test_non_owner_cannot_edit(self):
|
|
rec = {"id": "ds1", "userId": "user-other"}
|
|
node = ConnectionNode("c1", "msft", "m", "personalRoot", rec=rec)
|
|
self.assertFalse(node.canEdit(_FakeContext("user-1"), MagicMock()))
|
|
|
|
def test_virtual_node_own_connection_can_edit(self):
|
|
rootIf = MagicMock()
|
|
rootIf.db.getRecord.return_value = {"id": "c1", "userId": "user-1"}
|
|
node = ConnectionNode("c1", "msft", "m", "personalRoot", rec=None)
|
|
self.assertTrue(node.canEdit(_FakeContext("user-1"), rootIf))
|
|
|
|
def test_virtual_node_other_connection_cannot_edit(self):
|
|
rootIf = MagicMock()
|
|
rootIf.db.getRecord.return_value = {"id": "c1", "userId": "user-other"}
|
|
node = ConnectionNode("c1", "msft", "m", "personalRoot", rec=None)
|
|
self.assertFalse(node.canEdit(_FakeContext("user-1"), rootIf))
|
|
|
|
def test_virtual_node_missing_connection_cannot_edit(self):
|
|
rootIf = MagicMock()
|
|
rootIf.db.getRecord.return_value = None
|
|
node = ConnectionNode("c1", "msft", "m", "personalRoot", rec=None)
|
|
self.assertFalse(node.canEdit(_FakeContext("user-1"), rootIf))
|
|
|
|
|
|
class TestCanEditFdsFeatureAdmin(unittest.TestCase):
|
|
def _buildRootIfWithAdminRole(self, hasAdmin: bool):
|
|
rootIf = MagicMock()
|
|
access = MagicMock(id="acc1", enabled=True)
|
|
rootIf.getFeatureAccess.return_value = access
|
|
rootIf.getRoleIdsForFeatureAccess.return_value = ["role-1"]
|
|
rootIf.db.getRecord.return_value = {
|
|
"id": "role-1",
|
|
"roleLabel": "trustee-admin" if hasAdmin else "trustee-user",
|
|
}
|
|
return rootIf
|
|
|
|
def test_admin_can_edit_fds_table(self):
|
|
rootIf = self._buildRootIfWithAdminRole(hasAdmin=True)
|
|
node = FdsTableNode("fi1", "trustee", "Pos", "key", "Positions",
|
|
"feat|m1|trustee|fi1", rec={"id": "fds1"}, hasFields=False)
|
|
self.assertTrue(node.canEdit(_FakeContext(), rootIf))
|
|
|
|
def test_non_admin_cannot_edit_fds_table(self):
|
|
rootIf = self._buildRootIfWithAdminRole(hasAdmin=False)
|
|
node = FdsTableNode("fi1", "trustee", "Pos", "key", "Positions",
|
|
"feat|m1|trustee|fi1", rec={"id": "fds1"}, hasFields=False)
|
|
self.assertFalse(node.canEdit(_FakeContext(), rootIf))
|
|
|
|
def test_fds_field_uses_feature_admin_check(self):
|
|
rootIf = self._buildRootIfWithAdminRole(hasAdmin=True)
|
|
field = FdsFieldNode("fi1", "Pos", "amount", "fdstbl|fi1|Pos",
|
|
tableRec={"id": "fds1"}, featureCode="trustee")
|
|
self.assertTrue(field.canEdit(_FakeContext(), rootIf))
|
|
|
|
|
|
class TestGetEffectiveFlag(unittest.TestCase):
|
|
def test_ds_walk_inherits_from_authority_root(self):
|
|
root = {
|
|
"id": "r", "connectionId": "c", "sourceType": "msft", "path": "/",
|
|
"userId": "user-1", "neutralize": True, "ragIndexEnabled": None,
|
|
}
|
|
node = FolderNode(
|
|
connectionId="c", service="sharepoint", sourceType="sharepointFolder",
|
|
path="/sites/x", label="x", parentKey="svc|c|sharepoint",
|
|
rec=None, hasChildren=True,
|
|
)
|
|
self.assertTrue(node.getEffectiveFlag("neutralize", [root], [], "walk"))
|
|
|
|
def test_fds_field_neutralize_from_neutralize_fields(self):
|
|
rec = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"recordFilter": None, "neutralizeFields": ["amount"],
|
|
}
|
|
node = FdsFieldNode("fi1", "Pos", "amount", "fdstbl|fi1|Pos",
|
|
tableRec=rec, featureCode="trustee")
|
|
self.assertTrue(node.getEffectiveFlag("neutralize", [], [rec], "aggregate"))
|
|
|
|
other = FdsFieldNode("fi1", "Pos", "currency", "fdstbl|fi1|Pos",
|
|
tableRec=rec, featureCode="trustee")
|
|
# currency is not in the override list and the table has no
|
|
# explicit neutralize -> inherits the default (False).
|
|
self.assertFalse(other.getEffectiveFlag("neutralize", [], [rec], "aggregate"))
|
|
|
|
def test_fds_field_inherits_true_from_table(self):
|
|
"""Field without explicit override inherits the table's explicit
|
|
neutralize. Regression: previously fell through to False, so
|
|
toggling the table left the field icon unchanged."""
|
|
rec = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"recordFilter": None, "neutralize": True, "neutralizeFields": None,
|
|
}
|
|
node = FdsFieldNode("fi1", "Pos", "amount", "fdstbl|fi1|Pos",
|
|
tableRec=rec, featureCode="trustee")
|
|
self.assertTrue(node.getEffectiveFlag("neutralize", [], [rec], "aggregate"))
|
|
|
|
def test_fds_field_inherits_from_workspace_via_table(self):
|
|
"""Field walks the whole FDS ancestor chain: table -> workspace."""
|
|
ws = {
|
|
"id": "fds-ws", "featureInstanceId": "fi1", "tableName": "*",
|
|
"recordFilter": None, "neutralize": True,
|
|
}
|
|
tbl = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"recordFilter": None, "neutralize": None, "neutralizeFields": None,
|
|
}
|
|
node = FdsFieldNode("fi1", "Pos", "amount", "fdstbl|fi1|Pos",
|
|
tableRec=tbl, featureCode="trustee")
|
|
self.assertTrue(node.getEffectiveFlag("neutralize", [], [ws, tbl], "aggregate"))
|
|
|
|
def test_fds_field_explicit_override_beats_table_false(self):
|
|
"""Per-column override (True via list entry) beats an explicit
|
|
table False -- this is the one case where the two-source model
|
|
diverges intentionally and produces the 'mixed' aggregate."""
|
|
rec = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"recordFilter": None, "neutralize": False, "neutralizeFields": ["amount"],
|
|
}
|
|
amount = FdsFieldNode("fi1", "Pos", "amount", "fdstbl|fi1|Pos",
|
|
tableRec=rec, featureCode="trustee")
|
|
other = FdsFieldNode("fi1", "Pos", "currency", "fdstbl|fi1|Pos",
|
|
tableRec=rec, featureCode="trustee")
|
|
self.assertTrue(amount.getEffectiveFlag("neutralize", [], [rec], "aggregate"))
|
|
# currency inherits from table -> False
|
|
self.assertFalse(other.getEffectiveFlag("neutralize", [], [rec], "aggregate"))
|
|
|
|
def test_fds_table_mixed_when_field_and_table_disagree(self):
|
|
# table.neutralize=False, field "amount" is in neutralizeFields => mixed
|
|
rec = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"recordFilter": None, "neutralize": False, "ragIndexEnabled": None,
|
|
"neutralizeFields": ["amount"],
|
|
}
|
|
table = FdsTableNode("fi1", "trustee", "Pos", "key", "Positions",
|
|
"feat|m1|trustee|fi1", rec=rec, hasFields=True)
|
|
# FdsTableNode.getEffectiveFlag itself only consults FDS records,
|
|
# not field nodes. The aggregation across field nodes is wired
|
|
# in _buildTree via `_wireTableFieldsAsLogicalChildren`. So we
|
|
# exercise the explicit FDS walk here:
|
|
val = table.getEffectiveFlag("neutralize", [], [rec], "walk")
|
|
self.assertFalse(val)
|
|
|
|
|
|
class TestSetFlag(unittest.TestCase):
|
|
def test_setflag_writes_value_on_ds(self):
|
|
rec = {"id": "ds1", "connectionId": "c", "sourceType": "msft", "path": "/",
|
|
"userId": "user-1"}
|
|
node = ConnectionNode("c", "msft", "m", "personalRoot", rec=rec)
|
|
rootIf = MagicMock()
|
|
rootIf.db.getRecordset.return_value = [] # no siblings -> no cascade
|
|
node.setFlag("neutralize", True, rootIf)
|
|
rootIf.db.recordModify.assert_called()
|
|
args = rootIf.db.recordModify.call_args[0]
|
|
self.assertEqual(args[1], "ds1")
|
|
self.assertEqual(args[2], {"neutralize": True})
|
|
|
|
def test_setflag_virtual_ds_auto_creates_record(self):
|
|
"""Toggling a flag on a virtual DS node must auto-create the
|
|
DataSource record so the flag can be persisted."""
|
|
node = FolderNode(
|
|
connectionId="c1", service="sharepoint",
|
|
sourceType="sharepointFolder", path="/sites/x/docs",
|
|
label="docs", parentKey="svc|c1|sharepoint",
|
|
rec=None, hasChildren=True,
|
|
)
|
|
rootIf = MagicMock()
|
|
rootIf.db.getRecordset.return_value = []
|
|
rootIf.db.getRecord.return_value = {"id": "c1", "userId": "user-1"}
|
|
createdRec = {"id": "ds-new", "connectionId": "c1",
|
|
"sourceType": "sharepointFolder", "path": "/sites/x/docs",
|
|
"userId": "user-1"}
|
|
rootIf.db.recordCreate.return_value = createdRec
|
|
node.setFlag("neutralize", True, rootIf)
|
|
rootIf.db.recordCreate.assert_called_once()
|
|
rootIf.db.recordModify.assert_called()
|
|
args = rootIf.db.recordModify.call_args[0]
|
|
self.assertEqual(args[1], "ds-new")
|
|
self.assertEqual(args[2], {"neutralize": True})
|
|
|
|
def test_fds_field_setflag_mutates_neutralize_fields(self):
|
|
rec = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"recordFilter": None, "neutralize": False, "neutralizeFields": None,
|
|
}
|
|
node = FdsFieldNode("fi1", "Pos", "amount", "fdstbl|fi1|Pos",
|
|
tableRec=rec, featureCode="trustee")
|
|
rootIf = MagicMock()
|
|
node.setFlag("neutralize", True, rootIf)
|
|
rootIf.db.recordModify.assert_called()
|
|
# last call: set neutralizeFields to ["amount"]
|
|
args = rootIf.db.recordModify.call_args[0]
|
|
self.assertEqual(args[1], "fds-tbl")
|
|
self.assertEqual(args[2], {"neutralizeFields": ["amount"]})
|
|
|
|
def test_fds_field_setflag_removes_field_when_toggled_off(self):
|
|
rec = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"recordFilter": None, "neutralizeFields": ["amount", "currency"],
|
|
}
|
|
node = FdsFieldNode("fi1", "Pos", "amount", "fdstbl|fi1|Pos",
|
|
tableRec=rec, featureCode="trustee")
|
|
rootIf = MagicMock()
|
|
node.setFlag("neutralize", False, rootIf)
|
|
args = rootIf.db.recordModify.call_args[0]
|
|
self.assertEqual(args[2], {"neutralizeFields": ["currency"]})
|
|
|
|
def test_fds_field_setflag_roundtrip(self):
|
|
rec = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"recordFilter": None, "neutralizeFields": None,
|
|
}
|
|
node = FdsFieldNode("fi1", "Pos", "amount", "fdstbl|fi1|Pos",
|
|
tableRec=rec, featureCode="trustee")
|
|
rootIf = MagicMock()
|
|
node.setFlag("neutralize", True, rootIf)
|
|
self.assertTrue(node.getEffectiveFlag("neutralize", [], [rec], "aggregate"))
|
|
node.setFlag("neutralize", False, rootIf)
|
|
self.assertFalse(node.getEffectiveFlag("neutralize", [], [rec], "aggregate"))
|
|
|
|
def test_fds_table_explicit_neutralize_wipes_own_neutralize_fields(self):
|
|
"""Setting an explicit neutralize on a table must clear its own
|
|
`neutralizeFields` list. Otherwise the table's aggregate stays
|
|
'mixed' because field children walk to True via that list and the
|
|
UI shows no change after the toggle."""
|
|
rec = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"recordFilter": None, "neutralize": None,
|
|
"neutralizeFields": ["amount", "currency"],
|
|
}
|
|
node = FdsTableNode("fi1", "trustee", "Pos", "key", "Positions",
|
|
"feat|m1|trustee|fi1", rec=rec, hasFields=True)
|
|
rootIf = MagicMock()
|
|
rootIf.db.getRecordset.return_value = [rec] # no descendants
|
|
node.setFlag("neutralize", False, rootIf)
|
|
rootIf.db.recordModify.assert_called()
|
|
args = rootIf.db.recordModify.call_args[0]
|
|
self.assertEqual(args[1], "fds-tbl")
|
|
self.assertEqual(
|
|
args[2], {"neutralize": False, "neutralizeFields": None},
|
|
)
|
|
|
|
def test_fds_table_setflag_inherit_keeps_neutralize_fields(self):
|
|
"""`value=None` (reset to inherit) must NOT cascade and must NOT
|
|
wipe `neutralizeFields`; that matches the cascade-reset spec
|
|
(only explicit toggles clear descendants)."""
|
|
rec = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"recordFilter": None, "neutralize": True,
|
|
"neutralizeFields": ["amount"],
|
|
}
|
|
node = FdsTableNode("fi1", "trustee", "Pos", "key", "Positions",
|
|
"feat|m1|trustee|fi1", rec=rec, hasFields=True)
|
|
rootIf = MagicMock()
|
|
rootIf.db.getRecordset.return_value = [rec]
|
|
node.setFlag("neutralize", None, rootIf)
|
|
args = rootIf.db.recordModify.call_args[0]
|
|
self.assertEqual(args[2], {"neutralize": None})
|
|
|
|
def test_fds_table_setflag_rag_does_not_touch_neutralize_fields(self):
|
|
"""A RAG toggle on the table must leave `neutralizeFields` alone
|
|
(it is neutralize-only field state)."""
|
|
rec = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"recordFilter": None, "ragIndexEnabled": None,
|
|
"neutralizeFields": ["amount"],
|
|
}
|
|
node = FdsTableNode("fi1", "trustee", "Pos", "key", "Positions",
|
|
"feat|m1|trustee|fi1", rec=rec, hasFields=True)
|
|
rootIf = MagicMock()
|
|
rootIf.db.getRecordset.return_value = [rec]
|
|
node.setFlag("ragIndexEnabled", True, rootIf)
|
|
args = rootIf.db.recordModify.call_args[0]
|
|
self.assertEqual(args[2], {"ragIndexEnabled": True})
|
|
|
|
def test_fds_workspace_neutralize_clears_descendant_neutralize_fields(self):
|
|
"""Workspace toggle must clear per-column overrides on descendant
|
|
tables; otherwise the table aggregate stays 'mixed' because some
|
|
field children still read True from the list."""
|
|
wsRec = {
|
|
"id": "fds-ws", "featureInstanceId": "fi1", "tableName": "*",
|
|
"recordFilter": None, "neutralize": None,
|
|
}
|
|
tblRec = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"recordFilter": None, "neutralize": None,
|
|
"neutralizeFields": ["amount", "currency"],
|
|
}
|
|
node = FdsWorkspaceNode("m1", "trustee", "fi1", label="Trustee",
|
|
icon="trustee", parentKey="mgrp|m1", rec=wsRec)
|
|
rootIf = MagicMock()
|
|
rootIf.db.getRecordset.return_value = [wsRec, tblRec]
|
|
node.setFlag("neutralize", True, rootIf)
|
|
calls = rootIf.db.recordModify.call_args_list
|
|
modifyMap = {c[0][1]: c[0][2] for c in calls}
|
|
self.assertEqual(modifyMap["fds-tbl"], {"neutralizeFields": None})
|
|
self.assertEqual(modifyMap["fds-ws"], {"neutralize": True})
|
|
|
|
def test_fds_workspace_rag_does_not_clear_descendant_neutralize_fields(self):
|
|
"""A RAG toggle on the workspace must not touch descendant
|
|
`neutralizeFields`."""
|
|
wsRec = {
|
|
"id": "fds-ws", "featureInstanceId": "fi1", "tableName": "*",
|
|
"recordFilter": None, "ragIndexEnabled": None,
|
|
}
|
|
tblRec = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"recordFilter": None, "ragIndexEnabled": None,
|
|
"neutralizeFields": ["amount"],
|
|
}
|
|
node = FdsWorkspaceNode("m1", "trustee", "fi1", label="Trustee",
|
|
icon="trustee", parentKey="mgrp|m1", rec=wsRec)
|
|
rootIf = MagicMock()
|
|
rootIf.db.getRecordset.return_value = [wsRec, tblRec]
|
|
node.setFlag("ragIndexEnabled", True, rootIf)
|
|
calls = rootIf.db.recordModify.call_args_list
|
|
modifyIds = [c[0][1] for c in calls]
|
|
self.assertNotIn("fds-tbl", modifyIds)
|
|
|
|
|
|
class TestToDict(unittest.TestCase):
|
|
def test_fds_table_dict_has_neutralize_fields(self):
|
|
rec = {
|
|
"id": "fds-tbl", "featureInstanceId": "fi1", "tableName": "Pos",
|
|
"neutralizeFields": ["amount"],
|
|
}
|
|
node = FdsTableNode("fi1", "trustee", "Pos", "key", "Positions",
|
|
"feat|m1|trustee|fi1", rec=rec, hasFields=True)
|
|
out = node.toDict([], [rec])
|
|
self.assertEqual(out["neutralizeFields"], ["amount"])
|
|
self.assertEqual(out["kind"], "fdsTable")
|
|
self.assertEqual(out["modelType"], "FeatureDataSource")
|
|
self.assertEqual(out["effectiveScope"], "personal") # FDS has no scope
|
|
|
|
def test_synthetic_container_has_no_dataSourceId(self):
|
|
n = SyntheticContainerNode("personalRoot", "Personal", icon="person",
|
|
defaultExpanded=True)
|
|
d = n.toDict([], [])
|
|
self.assertIsNone(d["dataSourceId"])
|
|
self.assertEqual(d["effectiveNeutralize"], False)
|
|
|
|
|
|
class TestIsFeatureAdmin(unittest.TestCase):
|
|
def test_no_access_returns_false(self):
|
|
rootIf = MagicMock()
|
|
rootIf.getFeatureAccess.return_value = None
|
|
self.assertFalse(_isFeatureAdmin(rootIf, "user-1", "fi1"))
|
|
|
|
def test_no_roles_returns_false(self):
|
|
rootIf = MagicMock()
|
|
rootIf.getFeatureAccess.return_value = MagicMock(id="acc1", enabled=True)
|
|
rootIf.getRoleIdsForFeatureAccess.return_value = []
|
|
self.assertFalse(_isFeatureAdmin(rootIf, "user-1", "fi1"))
|
|
|
|
def test_non_admin_role_returns_false(self):
|
|
rootIf = MagicMock()
|
|
rootIf.getFeatureAccess.return_value = MagicMock(id="acc1", enabled=True)
|
|
rootIf.getRoleIdsForFeatureAccess.return_value = ["r1"]
|
|
rootIf.db.getRecord.return_value = {"id": "r1", "roleLabel": "trustee-user"}
|
|
self.assertFalse(_isFeatureAdmin(rootIf, "user-1", "fi1"))
|
|
|
|
def test_admin_role_returns_true(self):
|
|
rootIf = MagicMock()
|
|
rootIf.getFeatureAccess.return_value = MagicMock(id="acc1", enabled=True)
|
|
rootIf.getRoleIdsForFeatureAccess.return_value = ["r1"]
|
|
rootIf.db.getRecord.return_value = {"id": "r1", "roleLabel": "workspace-admin"}
|
|
self.assertTrue(_isFeatureAdmin(rootIf, "user-1", "fi1"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|