Files
roboco/tests/unit/gateway/test_assembly_integrity.py
T

85 lines
2.7 KiB
Python
Raw Normal View History

"""Assembly integrity at submit_up — every completed child's work must be in
the assembled branch (incident #11).
Live break (2026-07-02, S6 cell PR #183): revert subtask 3b9cc162 COMPLETED,
but its commit never landed on the assembled cell branch — the reviewer
re-flagged the exact violation the revert fixed, spawning another revision
cycle. The gate verifies patch-equivalence (rebase-safe) per completed child
and refuses the submit naming the children whose work is missing.
"""
from __future__ import annotations
from typing import Any
from unittest.mock import AsyncMock, MagicMock
from uuid import uuid4
import pytest
from roboco.services.gateway.choreographer import Choreographer, ChoreographerDeps
def _make_deps(**overrides: Any) -> ChoreographerDeps:
base: dict[str, Any] = {
"task": AsyncMock(),
"work_session": AsyncMock(),
"git": AsyncMock(),
"a2a": AsyncMock(),
"journal": AsyncMock(),
"audit": AsyncMock(),
"evidence_repo": AsyncMock(),
}
base.update(overrides)
return ChoreographerDeps(**base)
def _cell_task() -> MagicMock:
return MagicMock(
id=uuid4(), branch_name="feature/frontend/root--cell", team="frontend"
)
@pytest.mark.asyncio
async def test_integrity_passes_when_all_children_merged() -> None:
git = AsyncMock()
git.unmerged_child_commits.return_value = []
c = Choreographer(_make_deps(git=git))
env = await c._assembly_integrity_guard(_cell_task(), verb="submit_up")
assert env is None
@pytest.mark.asyncio
async def test_integrity_rejects_naming_missing_children() -> None:
git = AsyncMock()
git.unmerged_child_commits.return_value = [
{
"task_id": "3b9cc162",
"title": "Revert stats.json artifact",
"unmerged": 1,
}
]
c = Choreographer(_make_deps(git=git))
env = await c._assembly_integrity_guard(_cell_task(), verb="submit_up")
assert env is not None
body = env.as_dict()
assert body["error"] == "invalid_state"
assert "Revert stats.json artifact" in body["message"]
@pytest.mark.asyncio
async def test_integrity_fails_open_on_git_error() -> None:
git = AsyncMock()
git.unmerged_child_commits.side_effect = RuntimeError("git sad")
c = Choreographer(_make_deps(git=git))
env = await c._assembly_integrity_guard(_cell_task(), verb="submit_up")
assert env is None
@pytest.mark.asyncio
async def test_integrity_skips_branchless_task() -> None:
git = AsyncMock()
c = Choreographer(_make_deps(git=git))
t = MagicMock(id=uuid4(), branch_name=None)
env = await c._assembly_integrity_guard(t, verb="submit_up")
assert env is None
git.unmerged_child_commits.assert_not_awaited()