Files
wehub-resource-sync bcbd1bdb22
Integ / changes (push) Has been skipped
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
Test (Python) / changes (push) Has been skipped
Test (TypeScript) / changes (push) Has been skipped
CLI exit codes / cli-gate (push) Has been cancelled
Test (Install) / test-install-gate (push) Has been cancelled
Integ / integ-gate (push) Has been cancelled
Test (Python) / test-python-gate (push) Has been cancelled
Test (TypeScript) / test-typescript-gate (push) Has been cancelled
Test (Install) / python-minimal (3.12) (push) Has been cancelled
Test (Install) / python-minimal (3.11) (push) Has been cancelled
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Has been cancelled
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Has been cancelled
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Has been cancelled
Integ / integ (push) Has been cancelled
Integ / integ-database (push) Has been cancelled
Integ / integ-database-ts (push) Has been cancelled
Integ / integ-data (push) Has been cancelled
Integ / integ-ssh (push) Has been cancelled
Integ / integ-ssh-ts (push) Has been cancelled
Test (Python) / audit (push) Has been cancelled
Test (TypeScript) / test (push) Has been cancelled
Test (TypeScript) / python-fs-shim (push) Has been cancelled
CLI exit codes / Python CLI (push) Has been cancelled
CLI exit codes / TypeScript CLI (push) Has been cancelled
CLI exit codes / Cross-language snapshot interop (push) Has been cancelled
Test (Python) / test (push) Has been cancelled
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Integ / integ-ts (push) Has been cancelled
Integ / integ-fuse (push) Has been cancelled
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Has been cancelled
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Has been cancelled
Test (Install) / python-extra (email, mirage.resource.email) (push) Has been cancelled
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Has been cancelled
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Has been cancelled
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Has been cancelled
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Has been cancelled
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Has been cancelled
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Has been cancelled
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Has been cancelled
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Has been cancelled
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Has been cancelled
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Has been cancelled
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Has been cancelled
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Has been cancelled
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Has been cancelled
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Has been cancelled
Test (Install) / ts-minimal (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:44 +08:00

332 lines
11 KiB
Python

# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import asyncio
import logging
import pytest
from mirage import MountMode, Workspace
from mirage.commands import safeguard as sg
from mirage.resource.ram import RAMResource
from mirage.types import CommandSafeguard, OnExceed
async def _slow_provision(*args, **kwargs):
await asyncio.sleep(5)
@pytest.fixture
def restore_defaults():
snapshot = dict(sg.DEFAULT_COMMAND_SAFEGUARDS)
yield
sg.DEFAULT_COMMAND_SAFEGUARDS.clear()
sg.DEFAULT_COMMAND_SAFEGUARDS.update(snapshot)
def _ws(safeguards: dict | None = None) -> Workspace:
if safeguards:
return Workspace(
{"/data": (RAMResource(), MountMode.WRITE, safeguards)},
mode=MountMode.WRITE)
return Workspace({"/data": RAMResource()}, mode=MountMode.WRITE)
@pytest.mark.asyncio
async def test_quick_builtin_under_default_does_not_fire():
ws = _ws()
r = await ws.execute("echo hi")
assert r.exit_code == 0
assert (await r.stdout_str()) == "hi\n"
@pytest.mark.asyncio
async def test_builtin_default_safeguard_fires(restore_defaults):
sg.DEFAULT_COMMAND_SAFEGUARDS["sleep"] = CommandSafeguard(
timeout_seconds=0.1)
ws = _ws()
r = await ws.execute("sleep 2")
assert r.exit_code == 124
assert "sleep: timed out after 0.1s" in (await r.stderr_str())
@pytest.mark.asyncio
async def test_fallback_safeguard_applies_to_unknown_command(restore_defaults):
sg.DEFAULT_COMMAND_SAFEGUARDS["sleep"] = CommandSafeguard(
timeout_seconds=0.05)
ws = _ws()
r = await ws.execute("sleep 1")
assert r.exit_code == 124
@pytest.mark.asyncio
async def test_pipeline_first_stage_to_trip_wins(restore_defaults):
sg.DEFAULT_COMMAND_SAFEGUARDS["sleep"] = CommandSafeguard(
timeout_seconds=0.1)
ws = _ws()
r = await ws.execute("sleep 2 | echo done")
assert r.exit_code == 124
assert "sleep: timed out" in (await r.stderr_str())
@pytest.mark.asyncio
async def test_timeout_zero_disables(restore_defaults):
sg.DEFAULT_COMMAND_SAFEGUARDS["sleep"] = CommandSafeguard(
timeout_seconds=0)
ws = _ws()
r = await ws.execute("sleep 0.1")
assert r.exit_code == 0
@pytest.mark.asyncio
async def test_mount_override_threaded_via_constructor():
overrides = {"cat": CommandSafeguard(timeout_seconds=42.0, max_lines=99)}
ws = _ws(safeguards=overrides)
mount = next(m for m in ws._registry._mounts if m.prefix == "/data/")
assert mount.command_safeguards["cat"].timeout_seconds == 42.0
assert mount.command_safeguards["cat"].max_lines == 99
@pytest.mark.asyncio
async def test_mount_override_beats_command_default():
overrides = {"cat": CommandSafeguard(timeout_seconds=999.0)}
ws = _ws(safeguards=overrides)
mount = next(m for m in ws._registry._mounts if m.prefix == "/data/")
from mirage.commands.safeguard import resolve_safeguard
resolved = resolve_safeguard("cat", None,
mount.command_safeguards.get("cat"))
assert resolved.timeout_seconds == 999.0
@pytest.mark.asyncio
async def test_timeout_sets_shared_cancel_event(restore_defaults):
sg.DEFAULT_COMMAND_SAFEGUARDS["sleep"] = CommandSafeguard(
timeout_seconds=0.05)
ws = _ws()
cancel = asyncio.Event()
r = await ws.execute("sleep 1", cancel=cancel)
assert r.exit_code == 124
assert cancel.is_set()
@pytest.mark.asyncio
async def test_cross_mount_cat_honors_command_default_safeguard(
restore_defaults):
sg.DEFAULT_COMMAND_SAFEGUARDS["cat"] = CommandSafeguard(max_lines=4)
a = RAMResource()
b = RAMResource()
a._store.dirs.add("/")
b._store.dirs.add("/")
a._store.files["/x.txt"] = b"a\n" * 20
b._store.files["/y.txt"] = b"b\n" * 20
ws = Workspace({"/a/": a, "/b/": b}, mode=MountMode.WRITE)
r = await ws.execute("cat /a/x.txt /b/y.txt")
out = await r.stdout_str()
err = await r.stderr_str()
assert out.count("\n") == 4
assert "output truncated" in err
@pytest.mark.asyncio
async def test_fan_out_find_has_safeguard_set():
a = RAMResource()
a._store.dirs.add("/")
a._store.files["/x.txt"] = b"hi\n"
ws = Workspace({"/a/": a}, mode=MountMode.WRITE)
r = await ws.execute("find /")
assert r.safeguard is not None
assert r.safeguard.timeout_seconds is not None
@pytest.mark.asyncio
async def test_cross_mount_honors_per_mount_timeout_override():
a = RAMResource()
b = RAMResource()
a._store.dirs.add("/")
b._store.dirs.add("/")
a._store.files["/x.txt"] = b"hi\n"
b._store.files["/y.txt"] = b"yo\n"
ws = Workspace(
{
"/a/": (a, MountMode.WRITE, {
"cat": CommandSafeguard(timeout_seconds=3.0)
}),
"/b/":
b,
},
mode=MountMode.WRITE)
r = await ws.execute("cat /a/x.txt /b/y.txt")
assert r.safeguard is not None
assert r.safeguard.timeout_seconds == 3.0
@pytest.mark.asyncio
async def test_fan_out_uses_tightest_timeout_among_mounts():
parent = RAMResource()
child = RAMResource()
parent._store.dirs.add("/")
parent._store.files["/a.txt"] = b"hi\n"
child._store.dirs.add("/")
child._store.files["/b.txt"] = b"yo\n"
ws = Workspace(
{
"/p/":
parent,
"/p/sub/": (child, MountMode.WRITE, {
"find": CommandSafeguard(timeout_seconds=2.0)
}),
},
mode=MountMode.WRITE)
r = await ws.execute("find /p")
assert r.safeguard is not None
assert r.safeguard.timeout_seconds == 2.0
@pytest.mark.asyncio
async def test_fan_out_tightest_when_parent_is_tighter():
parent = RAMResource()
child = RAMResource()
parent._store.dirs.add("/")
parent._store.files["/a.txt"] = b"hi\n"
child._store.dirs.add("/")
child._store.files["/b.txt"] = b"yo\n"
ws = Workspace(
{
"/p/": (parent, MountMode.WRITE, {
"find": CommandSafeguard(timeout_seconds=2.0)
}),
"/p/sub/": (child, MountMode.WRITE, {
"find": CommandSafeguard(timeout_seconds=9.0)
}),
},
mode=MountMode.WRITE)
r = await ws.execute("find /p")
assert r.safeguard is not None
assert r.safeguard.timeout_seconds == 2.0
@pytest.mark.asyncio
async def test_background_job_propagates_timeout(restore_defaults):
sg.DEFAULT_COMMAND_SAFEGUARDS["sleep"] = CommandSafeguard(
timeout_seconds=0.05)
ws = _ws()
r1 = await ws.execute("sleep 2 &")
assert r1.exit_code == 0
r2 = await ws.execute("wait %1")
assert r2.exit_code == 124
err = await r2.stderr_str()
assert "sleep: timed out" in err
@pytest.mark.asyncio
async def test_stderr_redirect_does_not_swallow_timeout_exit(restore_defaults):
sg.DEFAULT_COMMAND_SAFEGUARDS["sleep"] = CommandSafeguard(
timeout_seconds=0.05)
ws = _ws()
r = await ws.execute("sleep 2 2>&1")
assert r.exit_code == 124
r = await ws.execute("sleep 2 2>&1 | cat")
assert r.exit_code == 124
@pytest.mark.asyncio
async def test_job_table_reports_completed_bg_without_wait(restore_defaults):
sg.DEFAULT_COMMAND_SAFEGUARDS["sleep"] = CommandSafeguard(
timeout_seconds=0.05)
ws = _ws()
await ws.execute("sleep 5 &")
await asyncio.sleep(0.2)
jobs = ws.job_table.list_jobs()
assert len(jobs) == 1
assert jobs[0].status.value == "completed"
assert jobs[0].exit_code == 124
assert b"sleep: timed out" in jobs[0].stderr
@pytest.mark.asyncio
async def test_timeout_wrap_preserves_lazy_nonzero_exit_on_no_match():
ws = _ws()
await ws.execute("echo hello > /data/f.txt")
r = await ws.execute("grep zzz /data/f.txt")
assert r.exit_code == 1
@pytest.mark.asyncio
async def test_timeout_wrap_preserves_lazy_zero_exit_on_match():
ws = _ws()
await ws.execute("echo hello > /data/f.txt")
r = await ws.execute("grep hello /data/f.txt")
assert r.exit_code == 0
assert (await r.stdout_str()) == "hello\n"
@pytest.mark.asyncio
async def test_truncation_keeps_lazy_exit_zero_on_match():
ws = _ws({"grep": CommandSafeguard(max_lines=2, timeout_seconds=600)})
await ws.execute("printf 'a\\na\\na\\na\\n' > /data/f.txt")
r = await ws.execute("grep a /data/f.txt")
assert r.exit_code == 0
assert (await r.stdout_str()) == "a\na\n"
@pytest.mark.asyncio
async def test_provision_dry_run_honors_timeout(monkeypatch, restore_defaults):
sg.DEFAULT_COMMAND_SAFEGUARDS["cat"] = CommandSafeguard(
timeout_seconds=0.1)
monkeypatch.setattr("mirage.workspace.workspace.provision_node",
_slow_provision)
ws = _ws()
r = await ws.execute("cat /data/f.txt", provision=True)
assert r.exit_code == 124
@pytest.mark.asyncio
async def test_timeout_preserves_partial_records_and_logs(
caplog, restore_defaults):
sg.DEFAULT_COMMAND_SAFEGUARDS["sleep"] = CommandSafeguard(
timeout_seconds=0.1)
ws = _ws()
await ws.execute("echo hello > /data/f.txt")
before = len(ws._ops.records)
with caplog.at_level(logging.DEBUG, logger="mirage.workspace.workspace"):
r = await ws.execute("cat /data/f.txt; sleep 5")
assert r.exit_code == 124
assert len(ws._ops.records) > before
assert any("timed out" in m for m in caplog.messages)
@pytest.mark.asyncio
async def test_cross_mount_cat_aggregates_tightest_safeguard():
a = RAMResource()
b = RAMResource()
a._store.dirs.add("/")
b._store.dirs.add("/")
a._store.files["/x.txt"] = b"1\n2\n3\n"
b._store.files["/y.txt"] = b"4\n5\n6\n"
ws = Workspace(
{
"/a/": (a, MountMode.WRITE, {
"cat":
CommandSafeguard(max_lines=100, on_exceed=OnExceed.TRUNCATE)
}),
"/b/":
(b, MountMode.WRITE, {
"cat": CommandSafeguard(max_lines=1, on_exceed=OnExceed.ERROR)
}),
},
mode=MountMode.WRITE)
r = await ws.execute("cat /a/x.txt /b/y.txt")
assert r.safeguard.max_lines == 1
assert r.safeguard.on_exceed is OnExceed.ERROR