Files
2026-08-24 08:11:59 -07:00

182 lines
6.6 KiB
Python

from __future__ import annotations
import contextlib
import os
import tempfile
from collections.abc import Mapping
from pathlib import Path
from typing import Any
from unittest import mock
ROOT = Path(__file__).resolve().parents[1]
FAKE_CODEX = ROOT / "tests" / "helpers" / "fake_codex.py"
FAKE_SWITCHYARD = ROOT / "tests" / "helpers" / "fake_switchyard.py"
def root_thread_binding(thread_id: str | None) -> dict[str, object]:
"""Return one internally consistent root-thread lineage for state-fixture tests."""
if thread_id is None:
return {
"root_thread_id": None,
"root_thread_generation": 0,
"root_thread_lineage": [],
"root_thread_transition": None,
}
return {
"root_thread_id": thread_id,
"root_thread_generation": 1,
"root_thread_lineage": [
{
"generation": 1,
"thread_id": thread_id,
"codex_session_id": thread_id,
"adopted_at": "2000-01-01T00:00:00+00:00",
"reason": "test_fixture",
}
],
"root_thread_transition": None,
}
def create_access_lab_session(
*, cwd: Path, profile: str | Path = "access-efficient-escalation-lab"
) -> dict[str, Any]:
"""Create the local-first fixture without depending on a host llama.cpp process."""
import mmo_runtime
real_availability = mmo_runtime.route_availability
def available_local_route(snapshot: Mapping[str, Any]) -> dict[str, dict[str, Any]]:
availability = real_availability(snapshot)
availability["llama_cpp_local_openai_chat"] = {
"available": True,
"selected_credential_env": None,
"reason": None,
}
return availability
with mock.patch.object(
mmo_runtime,
"route_availability",
side_effect=available_local_route,
):
return mmo_runtime.create_session(profile=profile, cwd=cwd)
class RuntimeSandbox:
def __init__(self) -> None:
self.temp = tempfile.TemporaryDirectory(prefix="codex-mmo-test-")
self.root = Path(self.temp.name)
self.config = self.root / "config with spaces"
self.state = self.root / "state with spaces"
self.runtime = self.root / "runtime with spaces"
self.workspace = self.root / "workspace with spaces"
self.base_codex_home = self.root / "base codex home"
self.old_env: dict[str, str | None] = {}
def __enter__(self) -> RuntimeSandbox:
for path in (
self.config,
self.state,
self.runtime,
self.workspace,
self.base_codex_home,
):
path.mkdir(parents=True, exist_ok=True)
self.old_env = {
key: os.environ.get(key)
for key in (
"MMO_INSTALL_ROOT",
"MMO_CONFIG_ROOT",
"MMO_STATE_ROOT",
"XDG_RUNTIME_DIR",
"MMO_CODEX_BIN",
"ZAI_CODING_API_KEY",
"OPENCODE_API_KEY",
"OPENROUTER_API_KEY",
"OPENAI_API_KEY",
"FIRECRAWL_API_KEY",
"IDA_MCP_TOKEN",
"CODEX_HOME",
)
}
os.environ.update(
{
"MMO_INSTALL_ROOT": str(ROOT),
"MMO_CONFIG_ROOT": str(self.config),
"MMO_STATE_ROOT": str(self.state),
"XDG_RUNTIME_DIR": str(self.runtime),
"MMO_CODEX_BIN": str(FAKE_CODEX),
"ZAI_CODING_API_KEY": "fake-zai-coding",
"OPENCODE_API_KEY": "fake-opencode",
"OPENROUTER_API_KEY": "fake-openrouter",
"OPENAI_API_KEY": "fake-openai",
}
)
(self.config / "profiles.d").mkdir()
(self.config / "catalog.d").mkdir()
(self.config / "tool-mcp.d").mkdir()
settings = (ROOT / "config" / "settings.toml").read_text(encoding="utf-8")
settings = settings.replace(
'base_codex_home = "~/.codex"', f"base_codex_home = {self.base_codex_home.as_posix()!r}"
)
# TOML accepts JSON-style double quoted paths, not Python repr single quotes.
settings = settings.replace(
f"base_codex_home = '{self.base_codex_home.as_posix()}'",
f'base_codex_home = "{self.base_codex_home.as_posix()}"',
)
settings = settings.replace(
'switchyard_bin = "switchyard-server"',
f'switchyard_bin = "{FAKE_SWITCHYARD.as_posix()}"',
)
settings = settings.replace(
"gateway_start_timeout_seconds = 15", "gateway_start_timeout_seconds = 5"
)
settings = settings.replace(
"gateway_idle_timeout_seconds = 3600", "gateway_idle_timeout_seconds = 1"
)
(self.config / "settings.toml").write_text(settings, encoding="utf-8")
(self.config / "credentials.env").write_text(
"ZAI_CODING_API_KEY=fake-zai-coding\nOPENCODE_API_KEY=fake-opencode\n"
"OPENROUTER_API_KEY=fake-openrouter\nOPENAI_API_KEY=fake-openai\n",
encoding="utf-8",
)
(self.base_codex_home / "auth.json").write_text('{"fake":true}\n', encoding="utf-8")
return self
def __exit__(self, *_exc: object) -> None:
with contextlib.suppress(Exception):
from mmo_runtime import cancel_session, iter_sessions
for session in iter_sessions(strict=False):
with contextlib.suppress(Exception):
cancel_session(str(session["session_id"]))
with contextlib.suppress(Exception):
from mmo_gateway import list_gateways, stop_gateway
for gateway in list_gateways():
stop_gateway(str(gateway["snapshot_hash"]))
for key, value in self.old_env.items():
if value is None:
os.environ.pop(key, None)
else:
os.environ[key] = value
self.temp.cleanup()
def init_git(self) -> None:
import subprocess
subprocess.run(["git", "init", "-q", str(self.workspace)], check=True)
subprocess.run(
["git", "-C", str(self.workspace), "config", "user.email", "test@example.invalid"],
check=True,
)
subprocess.run(
["git", "-C", str(self.workspace), "config", "user.name", "MMO Test"], check=True
)
(self.workspace / "README.md").write_text("fixture\n", encoding="utf-8")
subprocess.run(["git", "-C", str(self.workspace), "add", "."], check=True)
subprocess.run(["git", "-C", str(self.workspace), "commit", "-qm", "fixture"], check=True)