145 lines
5.3 KiB
Python
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()
|