test: repair full suite — hang fix, stale-test updates, patch hygiene, timeout backstop
Suite went from 27 failures plus one permanent hang (never finished) to 1846 passed / 0 failed in ~85s, including under FORCE_COLOR. - office_shutdown_lifecycle: construct WSHandler via the real __init__ (helper) instead of hand-copied __new__ stubs that drift from the constructor (#11 added _runtime_status_sync_task and the stubs hung); the formerly-hanging wait now has a 5s wait_for. - import-time patch hygiene: company_recruiter / company_reorg / engine_session_defaults replaced module-level permanent tempfile.TemporaryDirectory monkeypatching with paired setUpModule/tearDownModule, fixing order-dependent sqlite failures in transcript_pagination during full runs. - stale tests updated to current product semantics: resume stubs use status="done" (failed is deliberately non-resumable), fix4 asserts the native review contract through build_company_work_item_contract, delivery fixture carries user_visible/feedback_scope=final, ownership doc names progress_log, session compression calls maybe_compact_session(force=True) explicitly, hard delete removes the work item row, parallel-isolation asserts delegate rebind and stubs _get_project_delegate, role update goes through OrgService on an editable custom org (plus read-only rejection case), collab_rpc patches the single os.name decision point instead of poisoning pathlib, codex no-pty builds inside the patch, identity-guard false positives reworded. - cli_board actions rewritten against the real OfficeServiceFactory seam with a tempdir OPC_HOME (old direct-engine stubs were never consulted and the tests wrote into the real OPC home). - cli_app assertions strip ANSI via _plain_output so a color-forcing shell (FORCE_COLOR) cannot break plain-text expectations. - deleted never-runnable test_org_concurrency (pytest.mark.asyncio without the plugin, stdlib-only assertions) and three dead skipped filesystem-handoff tests. - pyproject: dev extra (pytest, pytest-timeout) and a 300s per-test timeout backstop so a wedged test fails instead of stalling the suite. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,51 +2,82 @@ from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from opc.core.config import OPCConfig, RoleConfig
|
||||
|
||||
|
||||
class UpdateRoleHandlerTests(unittest.IsolatedAsyncioTestCase):
|
||||
def _make_role() -> RoleConfig:
|
||||
return RoleConfig(
|
||||
id="student",
|
||||
name="Student",
|
||||
responsibility="Learn.",
|
||||
reports_to="owner",
|
||||
tools=["file_read"],
|
||||
)
|
||||
|
||||
|
||||
def _make_context(cfg: OPCConfig, *, exec_mode: str, company_profile: str):
|
||||
from opc.plugins.office_ui.services.context import ModeState, OfficeServiceContext
|
||||
|
||||
engine = SimpleNamespace(config=cfg, org_engine=MagicMock())
|
||||
context = OfficeServiceContext(
|
||||
engine=engine,
|
||||
agent_store=None,
|
||||
chat_store=MagicMock(),
|
||||
event_adapter=MagicMock(),
|
||||
mode_state=ModeState(exec_mode=exec_mode, company_profile=company_profile),
|
||||
)
|
||||
return engine, context
|
||||
|
||||
|
||||
class UpdateRolePersistenceTests(unittest.IsolatedAsyncioTestCase):
|
||||
async def test_update_role_persists_tools(self) -> None:
|
||||
from opc.plugins.office_ui.ws_handler import WSHandler
|
||||
"""Role tool edits persist when the active org is an editable custom org.
|
||||
|
||||
Role mutation now lives in OrgService (the WS handler delegates to it),
|
||||
and writes are intentionally refused for built-in read-only orgs, so
|
||||
persistence must be asserted against an editable custom org.
|
||||
"""
|
||||
from opc.plugins.office_ui.services.org import OrgService
|
||||
|
||||
cfg = OPCConfig()
|
||||
cfg.org.roles = [
|
||||
RoleConfig(
|
||||
id="student",
|
||||
name="Student",
|
||||
responsibility="Learn.",
|
||||
reports_to="owner",
|
||||
tools=["file_read"],
|
||||
)
|
||||
]
|
||||
cfg.org.company_profile = "custom"
|
||||
cfg.org.organization_id = "lab_org"
|
||||
cfg.org.organization_name = "Lab Org"
|
||||
cfg.org.roles = [_make_role()]
|
||||
engine, context = _make_context(cfg, exec_mode="custom", company_profile="custom")
|
||||
|
||||
handler = WSHandler.__new__(WSHandler)
|
||||
handler.engine = SimpleNamespace(config=cfg, org_engine=MagicMock())
|
||||
handler._clients = set()
|
||||
handler._shutting_down = False
|
||||
handler._ws_is_open = lambda _ws: True
|
||||
handler._config_lock = AsyncMock()
|
||||
handler._config_lock.__aenter__ = AsyncMock(return_value=None)
|
||||
handler._config_lock.__aexit__ = AsyncMock(return_value=None)
|
||||
handler._broadcast_org_info = AsyncMock()
|
||||
|
||||
ws = AsyncMock()
|
||||
with patch.object(OPCConfig, "save", autospec=True) as save:
|
||||
await handler._handle_update_role(
|
||||
ws,
|
||||
{
|
||||
"role_id": "student",
|
||||
"tools": ["file_read", " ", "web_search", ""],
|
||||
},
|
||||
result = await OrgService(context).update_role(
|
||||
"student",
|
||||
{"tools": ["file_read", " ", "web_search", ""]},
|
||||
)
|
||||
|
||||
self.assertEqual(cfg.org.roles[0].tools, ["file_read", "web_search"])
|
||||
handler.engine.org_engine.reload_from_config.assert_called_once()
|
||||
save.assert_called_once_with(cfg)
|
||||
handler._broadcast_org_info.assert_awaited_once()
|
||||
ws.send_json.assert_awaited()
|
||||
payload = ws.send_json.call_args.args[0]
|
||||
self.assertEqual(payload["type"], "ack")
|
||||
self.assertTrue(payload["payload"]["ok"])
|
||||
engine.org_engine.reload_from_config.assert_called_once()
|
||||
self.assertEqual(result.payload["action"], "role_updated")
|
||||
self.assertEqual(result.payload["role_id"], "student")
|
||||
self.assertEqual(result.payload["role"]["tools"], ["file_read", "web_search"])
|
||||
|
||||
async def test_update_role_tools_rejected_for_readonly_builtin_org(self) -> None:
|
||||
"""Built-in (corporate) orgs are read-only: tool edits must not persist."""
|
||||
from opc.plugins.office_ui.services.models import ServiceError
|
||||
from opc.plugins.office_ui.services.org import OrgService
|
||||
|
||||
cfg = OPCConfig()
|
||||
cfg.org.roles = [_make_role()]
|
||||
engine, context = _make_context(cfg, exec_mode="company", company_profile="corporate")
|
||||
|
||||
with patch.object(OPCConfig, "save", autospec=True) as save:
|
||||
with self.assertRaises(ServiceError) as raised:
|
||||
await OrgService(context).update_role(
|
||||
"student",
|
||||
{"tools": ["file_read", "web_search"]},
|
||||
)
|
||||
|
||||
self.assertEqual(raised.exception.code, "org_read_only")
|
||||
self.assertEqual(cfg.org.roles[0].tools, ["file_read"])
|
||||
save.assert_not_called()
|
||||
engine.org_engine.reload_from_config.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user