Files

159 lines
5.6 KiB
Python
Raw Permalink Normal View History

2026-06-24 12:44:34 -07:00
"""Tests for the ``--current-time`` fake-clock wiring.
The proxy runs every MCP service under the ``faketime`` wrapper when a
``--current-time`` value is supplied, so the service (and anything it spawns,
notably syntara's executeBash / executePython sandboxes) observes a clock
anchored to that instant and advancing at real wall-clock rate.
Unit tests here run anywhere — they exercise the pure mapping and the argv
assembly without launching a process. The integration test is skipped unless
the ``faketime`` wrapper is actually installed on PATH.
"""
import shutil
import subprocess
import sys
from datetime import UTC, datetime
import pytest
from mcp_proxy.commands import mcp as mcp_cmd
from mcp_proxy.commands.mcp import resolve_faketime_launch, to_faketime_spec
from mcp_proxy.service import HookStep, _service_argv
# ---------------------------------------------------------------------------
# to_faketime_spec — RFC3339/ISO-8601 -> faketime wrapper's absolute timestamp
# ---------------------------------------------------------------------------
def test_spec_utc_zulu():
assert to_faketime_spec("2025-01-15T09:00:00Z") == "2025-01-15 09:00:00"
def test_spec_naive_assumed_utc():
assert to_faketime_spec("2025-01-15T09:00:00") == "2025-01-15 09:00:00"
def test_spec_offset_converted_to_utc():
# 09:00 at +05:00 is 04:00 UTC.
assert to_faketime_spec("2025-01-15T09:00:00+05:00") == "2025-01-15 04:00:00"
def test_spec_negative_offset_crosses_midnight():
# 22:00 at -05:00 is 03:00 UTC the next day.
assert to_faketime_spec("2025-01-15T22:00:00-05:00") == "2025-01-16 03:00:00"
def test_spec_invalid_raises():
with pytest.raises(ValueError):
to_faketime_spec("not-a-timestamp")
def test_spec_empty_raises():
# An explicitly-passed empty --current-time is a bad value, not "no clock":
# run() routes it here (via `is not None`) so it fails fast rather than
# silently falling back to the real clock.
with pytest.raises(ValueError):
to_faketime_spec("")
# ---------------------------------------------------------------------------
# _service_argv — prepend the faketime wrapper when a prefix is given
# ---------------------------------------------------------------------------
# ``python``/``python3`` resolve to the proxy's own interpreter (see
# service.resolve_command), so the argv carries ``sys.executable``, not the bare
# name. The faketime prefix, when present, wraps that resolved command.
def test_argv_no_prefix():
step = HookStep(command="python", args=["-m", "server", "--http"])
assert _service_argv(step, None) == [sys.executable, "-m", "server", "--http"]
def test_argv_empty_prefix_is_noop():
step = HookStep(command="python", args=["-m", "server"])
assert _service_argv(step, []) == [sys.executable, "-m", "server"]
def test_argv_with_faketime_prefix():
step = HookStep(command="python", args=["-m", "server"])
prefix = ["faketime", "2025-01-15 09:00:00"]
assert _service_argv(step, prefix) == [
"faketime",
"2025-01-15 09:00:00",
sys.executable,
"-m",
"server",
]
# ---------------------------------------------------------------------------
# resolve_faketime_launch — command prefix + env, with fail-fast
# ---------------------------------------------------------------------------
def test_resolve_returns_prefix_and_utc_env(monkeypatch):
monkeypatch.setattr(mcp_cmd.shutil, "which", lambda _name: "/usr/bin/faketime")
prefix, env = resolve_faketime_launch("2025-01-15T09:00:00Z")
assert prefix == ["faketime", "2025-01-15 09:00:00"]
assert env == {"TZ": "UTC"}
def test_resolve_fails_fast_when_wrapper_missing(monkeypatch):
monkeypatch.setattr(mcp_cmd.shutil, "which", lambda _name: None)
with pytest.raises(SystemExit):
resolve_faketime_launch("2025-01-15T09:00:00Z")
def test_resolve_invalid_timestamp_fails(monkeypatch):
monkeypatch.setattr(mcp_cmd.shutil, "which", lambda _name: "/usr/bin/faketime")
with pytest.raises(SystemExit):
resolve_faketime_launch("not-a-timestamp")
# ---------------------------------------------------------------------------
# Integration — only when the faketime wrapper is installed
# ---------------------------------------------------------------------------
_HAS_FAKETIME = shutil.which("faketime") is not None
_SKIP_REASON = "faketime wrapper not installed on PATH"
# 2025-01-15T09:00:00Z
_FAKE_EPOCH = 1736931600
_SPEC = "2025-01-15 09:00:00"
@pytest.mark.skipif(not _HAS_FAKETIME, reason=_SKIP_REASON)
def test_faketime_fakes_date():
out = subprocess.run(
["faketime", _SPEC, "date", "+%s"],
capture_output=True,
text=True,
env={"TZ": "UTC", "PATH": __import__("os").environ["PATH"]},
timeout=30,
)
assert out.returncode == 0, out.stderr
reported = int(out.stdout.strip())
# Clock advances from the anchor, so allow a small forward window.
assert _FAKE_EPOCH <= reported < _FAKE_EPOCH + 60
@pytest.mark.skipif(not _HAS_FAKETIME, reason=_SKIP_REASON)
def test_faketime_fakes_python():
out = subprocess.run(
["faketime", _SPEC, sys.executable, "-c", "import time; print(time.time())"],
capture_output=True,
text=True,
env={"TZ": "UTC", "PATH": __import__("os").environ["PATH"]},
timeout=30,
)
assert out.returncode == 0, out.stderr
reported = float(out.stdout.strip())
assert _FAKE_EPOCH <= reported < _FAKE_EPOCH + 60
def test_fake_epoch_matches_spec():
"""Guard the integration constants against drift."""
dt = datetime(2025, 1, 15, 9, 0, 0, tzinfo=UTC)
assert int(dt.timestamp()) == _FAKE_EPOCH