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

130 lines
5.1 KiB
Python

from __future__ import annotations
import tempfile
import unittest
from pathlib import Path
from opc.core.config import OPCConfig
from opc.core.events import EventBus
from opc.core.models import (
ReorgChangeSet,
ReorgProposalStatus,
ReorgRoleChange,
ReorgTaskAdjustment,
Task,
TaskStatus,
)
from opc.database.store import OPCStore
from opc.layer2_organization.communication import CommunicationManager
from opc.layer2_organization.org_engine import OrgEngine
from opc.layer2_organization.reorg_manager import ReorgManager
from tests._temp_paths import WorkspaceTemporaryDirectory
tempfile.TemporaryDirectory = WorkspaceTemporaryDirectory # type: ignore[assignment]
class CompanyReorgTests(unittest.IsolatedAsyncioTestCase):
async def asyncSetUp(self) -> None:
self.tmpdir = tempfile.TemporaryDirectory()
self.addCleanup(self.tmpdir.cleanup)
self.root = Path(self.tmpdir.name)
self.store = OPCStore(self.root / "tasks.db")
await self.store.initialize()
self.config = OPCConfig()
self.org_engine = OrgEngine(self.config, self.root)
self.communication = CommunicationManager(self.store, EventBus(), org_engine=self.org_engine)
self.manager = ReorgManager(
store=self.store,
org_engine=self.org_engine,
approval_engine=None,
communication=self.communication,
)
async def asyncTearDown(self) -> None:
await self.store.close()
async def test_org_level_reorg_requires_user_confirmation_before_apply(self) -> None:
proposal = await self.manager.propose_reorg(
project_id="proj1",
summary="Replace senior_engineer with implementer.",
changeset=ReorgChangeSet(
role_changes=[
ReorgRoleChange(
action="replace",
role_id="senior_engineer",
replacement_role_id="implementer",
role={
"id": "implementer",
"name": "Implementer",
"responsibility": "Concrete implementation and delivery.",
},
)
]
),
source_role_id="coordinator",
)
self.assertTrue(proposal.user_confirmation_required)
self.assertEqual(proposal.status, ReorgProposalStatus.PROPOSED)
with self.assertRaises(ValueError):
await self.manager.apply_reorg(proposal.proposal_id)
await self.manager.set_reorg_approval(proposal.proposal_id, approved=True, notes="Looks good.")
result = await self.manager.apply_reorg(proposal.proposal_id)
self.assertEqual(result["status"], ReorgProposalStatus.APPLIED.value)
self.assertIsNotNone(self.org_engine.get_agent("implementer"))
async def test_low_risk_task_adjustment_can_auto_apply_for_top_level_role(self) -> None:
task = Task(
title="Engineering Execution",
project_id="proj1",
assigned_to="senior_engineer",
status=TaskStatus.PENDING,
metadata={"work_item_role_id": "senior_engineer", "work_item_projection_id": "engineering_execution"},
)
await self.store.save_task(task)
result = await self.manager.suggest_task_adjustment(
project_id="proj1",
source_role_id="coordinator",
summary="Reassign engineering execution to qa_analyst for a quick validation pass.",
changeset=ReorgChangeSet(
task_adjustments=[
ReorgTaskAdjustment(
task_id=task.id,
action="reassign",
new_role_id="qa_analyst",
)
]
),
)
self.assertTrue(result["auto_applied"])
updated = await self.store.get_task(task.id)
assert updated is not None
self.assertEqual(updated.assigned_to, "qa_analyst")
self.assertEqual(updated.metadata["reorg_proposal_id"], result["proposal"].proposal_id)
async def test_deny_reorg_keeps_existing_roles(self) -> None:
proposal = await self.manager.propose_reorg(
project_id="proj1",
summary="Add temporary architecture role.",
changeset=ReorgChangeSet(
role_changes=[
ReorgRoleChange(
action="add",
role={
"id": "architect",
"name": "Architect",
"responsibility": "Architecture design.",
},
)
]
),
source_role_id="coordinator",
)
await self.manager.set_reorg_approval(proposal.proposal_id, approved=False, notes="Not needed yet.")
denied = await self.store.get_reorg_proposal(proposal.proposal_id)
assert denied is not None
self.assertEqual(denied.status, ReorgProposalStatus.DENIED)
self.assertIsNone(self.org_engine.get_agent("architect"))