Files
OpenOPC/tests/test_context_assembler_view.py
T
2026-07-01 17:56:31 +08:00

145 lines
5.3 KiB
Python

"""Work-item metadata sync: verify context_assembler reads employee prompt /
delta / progress_log via ``WorkItemContextView`` with proper fallback.
Covers:
* ``_build_self_section`` reads ``employee_prompt_context`` /
``employee_delta_context`` from work_item side when mirrored (company mode)
* Falls back to task.metadata when view is None / task-mode
* ``_build_working_summary`` reads ``progress_log`` with same precedence
* Precedence (wi wins) when both sides populated
"""
from __future__ import annotations
import unittest
from types import SimpleNamespace
from unittest.mock import MagicMock
from opc.core.models import Task
from opc.layer1_perception.context_assembler import ContextAssembler
from opc.layer2_organization.work_item_context_view import WorkItemContextView
def _assembler() -> ContextAssembler:
"""Build an assembler sufficient for the context helpers under test.
The helpers we target (_build_self_section, _build_working_summary)
don't touch memory / store directly, so mocks are enough.
"""
return ContextAssembler(memory=MagicMock(), store=None)
def _wi(metadata: dict | None = None) -> SimpleNamespace:
return SimpleNamespace(metadata=metadata or {})
def _task(metadata: dict | None = None, context_snapshot: dict | None = None) -> Task:
return Task(
id="t-1",
title="t",
metadata=metadata or {},
context_snapshot=context_snapshot or {},
)
class BuildSelfSectionViewTests(unittest.TestCase):
def test_prompt_context_from_work_item_when_mirrored(self) -> None:
assembler = _assembler()
task = _task({"employee_assignment": {"name": "Alice"}})
view = WorkItemContextView(
_wi({"employee_prompt_context": "WI persona content"}),
task,
)
out = assembler._build_self_section(task, view)
self.assertIn("### Employee Persona", out)
self.assertIn("WI persona content", out)
def test_prompt_context_falls_back_to_task_metadata(self) -> None:
assembler = _assembler()
task = _task({"employee_prompt_context": "Task persona content"})
view = WorkItemContextView(_wi({}), task)
out = assembler._build_self_section(task, view)
self.assertIn("Task persona content", out)
def test_no_view_passed_degrades_to_task_only(self) -> None:
"""Backwards-compat: callers that don't pass view still get the
task-side fallback (view=None constructs task-only internally)."""
assembler = _assembler()
task = _task({"employee_prompt_context": "fallback"})
out = assembler._build_self_section(task)
self.assertIn("fallback", out)
def test_wi_wins_over_task_when_both_populated(self) -> None:
assembler = _assembler()
task = _task({"employee_prompt_context": "task-loses"})
view = WorkItemContextView(
_wi({"employee_prompt_context": "wi-wins"}),
task,
)
out = assembler._build_self_section(task, view)
self.assertIn("wi-wins", out)
self.assertNotIn("task-loses", out)
def test_delta_context_from_work_item(self) -> None:
assembler = _assembler()
task = _task({})
view = WorkItemContextView(
_wi({"employee_delta_context": "wi-delta"}),
task,
)
out = assembler._build_self_section(task, view)
self.assertIn("### Learned Working Profile", out)
self.assertIn("wi-delta", out)
def test_empty_everything_returns_empty(self) -> None:
assembler = _assembler()
task = _task({})
view = WorkItemContextView(_wi({}), task)
self.assertEqual(assembler._build_self_section(task, view), "")
class BuildWorkingSummaryViewTests(unittest.TestCase):
def test_progress_log_from_work_item(self) -> None:
assembler = _assembler()
task = _task({})
view = WorkItemContextView(
_wi({"progress_log": ["wi step 1", "wi step 2"]}),
task,
)
out = assembler._build_working_summary(task, view)
self.assertIn("Recent progress:", out)
self.assertIn("wi step 1", out)
self.assertIn("wi step 2", out)
def test_progress_log_fallback_from_task_metadata(self) -> None:
assembler = _assembler()
task = _task({"progress_log": ["task fallback step"]})
view = WorkItemContextView(_wi({}), task)
out = assembler._build_working_summary(task, view)
self.assertIn("task fallback step", out)
def test_no_view_passed_still_reads_task_metadata(self) -> None:
assembler = _assembler()
task = _task({"progress_log": ["legacy step"]})
out = assembler._build_working_summary(task)
self.assertIn("legacy step", out)
def test_progress_log_limited_to_last_4(self) -> None:
"""Prompt budget guard: summary renders only last 4 items."""
assembler = _assembler()
task = _task({})
view = WorkItemContextView(
_wi({"progress_log": ["s1", "s2", "s3", "s4", "s5", "s6"]}),
task,
)
out = assembler._build_working_summary(task, view)
self.assertIn("s3", out)
self.assertIn("s4", out)
self.assertIn("s5", out)
self.assertIn("s6", out)
self.assertNotIn("s1", out)
self.assertNotIn("s2", out)
if __name__ == "__main__":
unittest.main()