Initial commit
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
"""Unit tests for WorkItemContextView — work-item read adapter.
|
||||
|
||||
Verifies the precedence rules (work_item.metadata first, task.metadata
|
||||
fallback), defensive copies for list / dict returns, and graceful
|
||||
degradation when either side is missing.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
|
||||
from opc.layer2_organization.work_item_context_view import WorkItemContextView
|
||||
|
||||
|
||||
def _wi(metadata: dict | None = None) -> SimpleNamespace:
|
||||
return SimpleNamespace(metadata=metadata)
|
||||
|
||||
|
||||
def _task(metadata: dict | None = None) -> SimpleNamespace:
|
||||
return SimpleNamespace(metadata=metadata)
|
||||
|
||||
|
||||
class WorkItemContextViewTests(unittest.TestCase):
|
||||
def test_prefers_work_item_metadata_when_both_present(self) -> None:
|
||||
view = WorkItemContextView(
|
||||
work_item=_wi({"role": "senior_engineer", "shared": "wi-wins"}),
|
||||
task=_task({"role": "legacy_role", "shared": "task-loses"}),
|
||||
)
|
||||
self.assertEqual(view.get("role"), "senior_engineer")
|
||||
self.assertEqual(view.get("shared"), "wi-wins")
|
||||
|
||||
def test_falls_back_to_task_metadata_when_wi_key_missing(self) -> None:
|
||||
view = WorkItemContextView(
|
||||
work_item=_wi({"role": "engineer"}),
|
||||
task=_task({"work_item_role_name": "Senior Engineer"}),
|
||||
)
|
||||
self.assertEqual(view.get("role"), "engineer")
|
||||
self.assertEqual(view.get("work_item_role_name"), "Senior Engineer")
|
||||
|
||||
def test_returns_default_when_neither_present(self) -> None:
|
||||
view = WorkItemContextView(work_item=_wi({}), task=_task({}))
|
||||
self.assertIsNone(view.get("missing"))
|
||||
self.assertEqual(view.get("missing", "fallback"), "fallback")
|
||||
|
||||
def test_construct_from_none_work_item_degrades_to_task_only(self) -> None:
|
||||
"""task-mode path: no work_item linked. View must not crash and
|
||||
must serve all values from task.metadata."""
|
||||
view = WorkItemContextView(work_item=None, task=_task({"key": "val"}))
|
||||
self.assertEqual(view.get("key"), "val")
|
||||
self.assertEqual(view.get("missing", "d"), "d")
|
||||
|
||||
def test_construct_from_none_task_degrades_to_wi_only(self) -> None:
|
||||
"""company-mode path where caller only has the work_item. View
|
||||
serves everything from work_item.metadata."""
|
||||
view = WorkItemContextView(work_item=_wi({"key": "val"}), task=None)
|
||||
self.assertEqual(view.get("key"), "val")
|
||||
|
||||
def test_both_none_is_empty_view(self) -> None:
|
||||
view = WorkItemContextView()
|
||||
self.assertFalse(view.has("anything"))
|
||||
self.assertEqual(view.get("anything", "d"), "d")
|
||||
|
||||
def test_get_list_returns_copy_not_reference(self) -> None:
|
||||
"""Callers must not be able to accidentally mutate the source
|
||||
through the returned list."""
|
||||
source = ["a", "b", "c"]
|
||||
view = WorkItemContextView(work_item=_wi({"items": source}))
|
||||
returned = view.get_list("items")
|
||||
returned.append("d")
|
||||
self.assertEqual(source, ["a", "b", "c"]) # unchanged
|
||||
self.assertEqual(view.get_list("items"), ["a", "b", "c"]) # view still clean
|
||||
|
||||
def test_get_list_coerces_tuple_to_list(self) -> None:
|
||||
view = WorkItemContextView(work_item=_wi({"items": ("a", "b")}))
|
||||
self.assertEqual(view.get_list("items"), ["a", "b"])
|
||||
|
||||
def test_get_list_missing_or_wrong_type_returns_empty(self) -> None:
|
||||
view = WorkItemContextView(work_item=_wi({"k": "not a list"}))
|
||||
self.assertEqual(view.get_list("k"), [])
|
||||
self.assertEqual(view.get_list("missing"), [])
|
||||
|
||||
def test_get_dict_returns_copy_not_reference(self) -> None:
|
||||
source = {"a": 1, "b": 2}
|
||||
view = WorkItemContextView(work_item=_wi({"m": source}))
|
||||
returned = view.get_dict("m")
|
||||
returned["c"] = 3
|
||||
self.assertEqual(source, {"a": 1, "b": 2})
|
||||
self.assertEqual(view.get_dict("m"), {"a": 1, "b": 2})
|
||||
|
||||
def test_get_dict_missing_or_wrong_type_returns_empty(self) -> None:
|
||||
view = WorkItemContextView(work_item=_wi({"k": "scalar"}))
|
||||
self.assertEqual(view.get_dict("k"), {})
|
||||
self.assertEqual(view.get_dict("missing"), {})
|
||||
|
||||
def test_has_detects_visible_keys_only(self) -> None:
|
||||
view = WorkItemContextView(
|
||||
work_item=_wi({"wi_only": 1}),
|
||||
task=_task({"work_item_role_name": "legacy visible", "task_only": 2}),
|
||||
)
|
||||
self.assertTrue(view.has("wi_only"))
|
||||
self.assertTrue(view.has("work_item_role_name"))
|
||||
self.assertFalse(view.has("task_only"))
|
||||
self.assertFalse(view.has("neither"))
|
||||
|
||||
def test_company_view_hides_unknown_task_fallback_keys(self) -> None:
|
||||
view = WorkItemContextView(
|
||||
work_item=_wi({}),
|
||||
task=_task({"task_only": "hidden", "progress_log": ["legacy"]}),
|
||||
)
|
||||
self.assertEqual(view.get("task_only", "default"), "default")
|
||||
self.assertEqual(view.get_list("progress_log"), ["legacy"])
|
||||
|
||||
def test_none_valued_wi_key_not_treated_as_missing(self) -> None:
|
||||
"""dict.get semantics: if work_item has the key with value None,
|
||||
the view returns None — it does NOT fall through to task.metadata.
|
||||
This is the same rule the original task.metadata.get() used, so
|
||||
migrated callers see identical behaviour."""
|
||||
view = WorkItemContextView(
|
||||
work_item=_wi({"k": None}),
|
||||
task=_task({"k": "task_value"}),
|
||||
)
|
||||
self.assertIsNone(view.get("k"))
|
||||
|
||||
def test_object_without_metadata_attribute_degrades_gracefully(self) -> None:
|
||||
"""Defensive: passing an object that lacks .metadata (or has it as
|
||||
non-dict) should not crash — view becomes empty on that side."""
|
||||
weird = SimpleNamespace(metadata="not a dict")
|
||||
view = WorkItemContextView(work_item=weird, task=_task({"k": "v"}))
|
||||
self.assertEqual(view.get("k"), "v")
|
||||
|
||||
def test_mutating_source_dict_after_construction_does_not_affect_view(self) -> None:
|
||||
"""Construction snapshots the dicts (shallow copy). If the source
|
||||
dict changes later, the view keeps its construction-time snapshot."""
|
||||
src = {"key": "initial"}
|
||||
view = WorkItemContextView(work_item=_wi(src))
|
||||
src["key"] = "mutated"
|
||||
self.assertEqual(view.get("key"), "initial")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user