import subprocess from types import SimpleNamespace import pytest from scripts import build_mcp_hub_release_images as producer def test_diagnostic_reports_only_closed_values_and_handles_split_tokens(): diagnostic = producer.ContextForgeDiagnostic() diagnostic.feed( b"=== BUILD UPSTREAM SOURCE IMAGE ===\nsecret=never-export\nCould not resolve ho" ) diagnostic.feed(b"st: private.example\n") assert diagnostic.stage == "upstream_build" assert diagnostic.category == "dns" assert "secret" not in diagnostic.message(1) assert "private" not in diagnostic.message(1) def test_new_stage_resets_previous_cause_and_memory_is_bounded(): diagnostic = producer.ContextForgeDiagnostic() diagnostic.feed(b"=== FETCH EXACT CONTEXTFORGE SOURCE ===\ncertificate verify failed\n") assert diagnostic.category == "tls" diagnostic.feed(b"=== VERIFY RUNTIME CONTRACT ===\n" + b"x" * 1_000_000) assert diagnostic.stage == "runtime_verify" assert diagnostic.category == "unknown" assert len(diagnostic.window) <= 4096 def test_unrecognized_and_secret_shaped_error_is_never_echoed(): diagnostic = producer.ContextForgeDiagnostic() diagnostic.feed(b"Authorization: Bearer fixture-sensitive-value\n") assert diagnostic.message(1) == ( "fixed release stage failed: stage=contextforge_build category=nonzero_exit " "exitCode=1 substep=validation cause=unknown" ) @pytest.mark.parametrize("failure", ["exit", "timeout", "register", "read", "success"]) def test_fixed_process_discards_output_and_always_cleans_up(monkeypatch, tmp_path, failure): closed = [] terminated = [] class Stream: def fileno(self): return 12 def close(self): closed.append("stream") code = 0 if failure == "success" else 1 process = SimpleNamespace(stdout=Stream(), poll=lambda: code, wait=lambda timeout: code) def popen(command, **kwargs): assert command == [ "/bin/bash", "scripts/build_contextforge_compat_image.sh", "--release-candidate", ] assert kwargs["stdin"] == subprocess.DEVNULL assert kwargs["stderr"] == subprocess.STDOUT assert kwargs["start_new_session"] is True return process class Selector: def __init__(self): self.active = True def register(self, *_args): if failure == "register": raise OSError("fixture-sensitive-registration") def get_map(self): return {12: True} if self.active else {} def select(self, **_kwargs): return [(SimpleNamespace(fd=12, fileobj=process.stdout), 1)] def unregister(self, _stream): self.active = False def close(self): closed.append("selector") blocks = iter([b"=== FETCH EXACT CONTEXTFORGE SOURCE ===\nPermission denied secret\n", b""]) ticks = iter([0, producer.COMMAND_TIMEOUT + 1 if failure == "timeout" else 1, 2]) monkeypatch.setattr(producer.subprocess, "Popen", popen) monkeypatch.setattr(producer.selectors, "DefaultSelector", Selector) monkeypatch.setattr(producer.os, "set_blocking", lambda *_args: None) def read(*_args): if failure == "read": raise OSError("fixture-sensitive-read") return next(blocks) monkeypatch.setattr(producer.os, "read", read) monkeypatch.setattr(producer.time, "monotonic", lambda: next(ticks)) monkeypatch.setattr(producer, "_terminate_child", lambda child: terminated.append(child)) if failure == "success": producer._run_contextforge_diagnostic(tmp_path, {}) else: with pytest.raises(producer.BuildRejected) as error: producer._run_contextforge_diagnostic(tmp_path, {}) assert "secret" not in str(error.value) assert "fixture-sensitive" not in str(error.value) expected = { "exit": "substep=source_fetch cause=permission", "timeout": "category=timeout", "register": "category=io_error", "read": "category=io_error", }[failure] assert expected in str(error.value) assert closed == ["selector", "stream"] assert terminated == [process]