Initial commit
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
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"))
|
||||
Reference in New Issue
Block a user