"""R4 (partial) — forwarder containment-guard acceptance check. Acceptance predicate (instrument-validation gate item 5, the offline-verifiable half of the roadmap R4 check): a SOR forwarder is isolated-engine-only — ``assert engine != local`` or it refuses to run. This suite pins exactly that: a plan/prefix for ``local`` (or any non-isolated / unknown engine, or a missing container) is refused; an isolated engine kind is accepted and yields the correct isolation exec prefix. The traffic-moving half of R4 (gate item 1: 3-hop e2e delivery + per-hop pcaps across the grid) is deliberately NOT covered here — it is HELD for a live isolated engine + grid. This guard moves no traffic and spawns no engine, so it is fully validatable offline. """ import pytest from cmd_chat.sor.forwarder import ( ISOLATED_ENGINES, LOCAL_ENGINE, ContainmentError, ForwarderPlan, assert_isolated, isolation_prefix, ) def test_local_engine_is_refused(): # The core containment invariant: never the host. with pytest.raises(ContainmentError): assert_isolated(LOCAL_ENGINE) with pytest.raises(ContainmentError): ForwarderPlan(engine="local", container="c0") with pytest.raises(ContainmentError): isolation_prefix("local", "c0") @pytest.mark.parametrize("engine", ["", "host", "podman", "vmware", "LOCAL", "docker "]) def test_unknown_or_non_isolated_engines_refused(engine): # Anything not on the manifest allow-list is refused, not assumed safe. with pytest.raises(ContainmentError): assert_isolated(engine) with pytest.raises(ContainmentError): ForwarderPlan(engine=engine, container="c0") @pytest.mark.parametrize("engine", list(ISOLATED_ENGINES)) def test_isolated_engines_are_accepted(engine): # An isolated engine passes the gate and builds a plan. assert_isolated(engine) # does not raise plan = ForwarderPlan(engine=engine, container="node-1") prefix = plan.exec_prefix() assert isinstance(prefix, list) and prefix # The container name is present as its own argv item (no shell interpolation). assert "node-1" in prefix def test_exec_prefix_shapes_match_the_isolation_convention(): assert isolation_prefix("docker", "n") == ["docker", "exec", "-i", "n"] assert isolation_prefix("multipass", "n") == ["multipass", "exec", "n", "--"] def test_missing_container_is_refused(): with pytest.raises(ContainmentError): ForwarderPlan(engine="docker", container="") with pytest.raises(ContainmentError): isolation_prefix("docker", "") def test_no_prefix_path_ever_targets_the_host(): # Structural check: for every isolated engine, the exec prefix's leading # argv is the engine tool itself (docker/multipass), never a bare host shell. for engine in ISOLATED_ENGINES: prefix = isolation_prefix(engine, "c") assert prefix[0] == engine assert prefix != [] # a host-shell prefix (empty) is never produced