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

297 lines
9.5 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 pytest
from mirage import MountMode, Workspace
from mirage.resource.ram import RAMResource
from mirage.types import DEFAULT_SESSION_ID
def _make_ws():
resource = RAMResource()
store = resource._store
store.dirs.add("/")
store.dirs.add("/subdir")
store.dirs.add("/other")
store.files["/subdir/file.txt"] = b"hello"
store.modified["/subdir/file.txt"] = "2024-01-01"
return Workspace({"/ram/": resource}, mode=MountMode.WRITE)
# ── cwd tests ────────────────────────────────────────────────────────────
@pytest.mark.asyncio
async def test_cwd_runs_in_override():
ws = _make_ws()
r = await ws.execute("pwd", cwd="/ram/subdir")
assert (await r.stdout_str()).strip() == "/ram/subdir"
@pytest.mark.asyncio
async def test_cwd_does_not_mutate_session():
ws = _make_ws()
before = ws.get_session(DEFAULT_SESSION_ID).cwd
await ws.execute("pwd", cwd="/ram/subdir")
after = ws.get_session(DEFAULT_SESSION_ID).cwd
assert before == after
@pytest.mark.asyncio
async def test_cwd_cd_does_not_leak():
ws = _make_ws()
before = ws.get_session(DEFAULT_SESSION_ID).cwd
await ws.execute("cd /ram/subdir", cwd="/ram")
after = ws.get_session(DEFAULT_SESSION_ID).cwd
assert before == after
@pytest.mark.asyncio
async def test_cwd_parallel_isolation():
ws = _make_ws()
r1, r2 = await asyncio.gather(
ws.execute("pwd", cwd="/ram/subdir"),
ws.execute("pwd", cwd="/ram/other"),
)
assert (await r1.stdout_str()).strip() == "/ram/subdir"
assert (await r2.stdout_str()).strip() == "/ram/other"
@pytest.mark.asyncio
async def test_setup_persists_overrides_inherit():
ws = _make_ws()
await ws.execute("export DEBUG=1")
assert ws.get_session(DEFAULT_SESSION_ID).env.get("DEBUG") == "1"
before_cwd = ws.get_session(DEFAULT_SESSION_ID).cwd
r = await ws.execute("printenv DEBUG", cwd="/ram/subdir")
assert (await r.stdout_str()).strip() == "1"
assert ws.get_session(DEFAULT_SESSION_ID).cwd == before_cwd
assert ws.get_session(DEFAULT_SESSION_ID).env.get("DEBUG") == "1"
@pytest.mark.asyncio
async def test_function_definitions_do_not_leak():
ws = _make_ws()
await ws.execute("greet() { echo hi; }", cwd="/ram/subdir")
session = ws.get_session(DEFAULT_SESSION_ID)
assert "greet" not in session.functions
# ── env tests ────────────────────────────────────────────────────────────
@pytest.mark.asyncio
async def test_env_exposes_override():
ws = _make_ws()
r = await ws.execute("printenv FOO", env={"FOO": "bar"})
assert r.exit_code == 0
assert (await r.stdout_str()).strip() == "bar"
@pytest.mark.asyncio
async def test_env_does_not_mutate_session():
ws = _make_ws()
before = dict(ws.get_session(DEFAULT_SESSION_ID).env)
await ws.execute("printenv FOO", env={"FOO": "bar"})
after = dict(ws.get_session(DEFAULT_SESSION_ID).env)
assert before == after
@pytest.mark.asyncio
async def test_env_export_does_not_leak():
ws = _make_ws()
await ws.execute("export LEAKED=yes", env={"FOO": "bar"})
assert "LEAKED" not in ws.get_session(DEFAULT_SESSION_ID).env
@pytest.mark.asyncio
async def test_env_layers_onto_session():
ws = _make_ws()
await ws.execute("export BASE=keep")
assert ws.get_session(DEFAULT_SESSION_ID).env.get("BASE") == "keep"
r_base = await ws.execute("printenv BASE", env={"FOO": "bar"})
assert (await r_base.stdout_str()).strip() == "keep"
r_foo = await ws.execute("printenv FOO", env={"FOO": "bar"})
assert (await r_foo.stdout_str()).strip() == "bar"
session_env = ws.get_session(DEFAULT_SESSION_ID).env
assert session_env.get("BASE") == "keep"
assert "FOO" not in session_env
@pytest.mark.asyncio
async def test_env_parallel_isolation():
ws = _make_ws()
r1, r2 = await asyncio.gather(
ws.execute("printenv FOO", env={"FOO": "one"}),
ws.execute("printenv FOO", env={"FOO": "two"}),
)
assert (await r1.stdout_str()).strip() == "one"
assert (await r2.stdout_str()).strip() == "two"
# ── mid-flight cancel ─────────────────────────────────────────────
@pytest.mark.asyncio
async def test_cancel_pre_set_raises_immediately():
ws = _make_ws()
cancel = asyncio.Event()
cancel.set()
with pytest.raises(Exception) as exc_info:
await ws.execute("echo hi", cancel=cancel)
assert "abort" in str(exc_info.value).lower()
@pytest.mark.asyncio
async def test_cancel_aborts_sleep_within_timeout():
ws = _make_ws()
cancel = asyncio.Event()
async def trigger() -> None:
await asyncio.sleep(0.1)
cancel.set()
asyncio.create_task(trigger())
t0 = asyncio.get_event_loop().time()
with pytest.raises(Exception):
await ws.execute("sleep 5", cancel=cancel)
assert asyncio.get_event_loop().time() - t0 < 1.0
@pytest.mark.asyncio
async def test_cancel_inside_for_loop():
ws = _make_ws()
cancel = asyncio.Event()
async def trigger() -> None:
await asyncio.sleep(0.1)
cancel.set()
asyncio.create_task(trigger())
t0 = asyncio.get_event_loop().time()
with pytest.raises(Exception):
await ws.execute(
"for i in 1 2 3 4 5 6 7 8 9 10; do sleep 1; done",
cancel=cancel,
)
assert asyncio.get_event_loop().time() - t0 < 1.5
@pytest.mark.asyncio
async def test_cancel_between_list_stages():
ws = _make_ws()
cancel = asyncio.Event()
async def trigger() -> None:
await asyncio.sleep(0.1)
cancel.set()
asyncio.create_task(trigger())
t0 = asyncio.get_event_loop().time()
with pytest.raises(Exception):
await ws.execute(
"sleep 1 && sleep 1 && sleep 1 && echo done",
cancel=cancel,
)
assert asyncio.get_event_loop().time() - t0 < 2.0
@pytest.mark.asyncio
async def test_cancel_inside_command_substitution():
ws = _make_ws()
cancel = asyncio.Event()
async def trigger() -> None:
await asyncio.sleep(0.1)
cancel.set()
asyncio.create_task(trigger())
t0 = asyncio.get_event_loop().time()
with pytest.raises(Exception):
await ws.execute('echo "$(sleep 5)"', cancel=cancel)
assert asyncio.get_event_loop().time() - t0 < 1.0
@pytest.mark.asyncio
async def test_cancel_workspace_remains_usable():
ws = _make_ws()
cancel = asyncio.Event()
async def trigger() -> None:
await asyncio.sleep(0.05)
cancel.set()
asyncio.create_task(trigger())
with pytest.raises(Exception):
await ws.execute("sleep 5", cancel=cancel)
r = await ws.execute("echo recovered")
assert r.exit_code == 0
assert r.stdout.decode().strip() == "recovered"
# ── agent harness pattern ─────────────────────────────────────────
async def _tool_call(ws, cmd, cwd_v, env_v, timeout):
cancel = asyncio.Event()
asyncio.get_event_loop().call_later(timeout, cancel.set)
return await ws.execute(cmd, cwd=cwd_v, env=env_v, cancel=cancel)
@pytest.mark.asyncio
async def test_agent_pattern_parallel_tool_calls_each_with_own_options():
ws = _make_ws()
a, b = await asyncio.gather(
_tool_call(ws, "pwd; printenv DEBUG", "/ram/subdir", {"DEBUG": "one"},
5.0),
_tool_call(ws, "pwd; printenv DEBUG", "/ram", {"DEBUG": "two"}, 5.0),
)
assert "/ram/subdir" in a.stdout.decode()
assert "one" in a.stdout.decode()
assert "/ram" in b.stdout.decode()
assert "two" in b.stdout.decode()
session = ws.get_session(DEFAULT_SESSION_ID)
assert session.cwd != "/ram/subdir"
assert "DEBUG" not in session.env
@pytest.mark.asyncio
async def test_execute_uses_custom_default_session_id():
resource = RAMResource()
resource._store.dirs.add("/")
ws = Workspace({"/ram/": resource},
mode=MountMode.WRITE,
session_id="mysess")
r = await ws.execute("echo hi > /ram/f.txt")
assert r.exit_code == 0
r2 = await ws.execute("cat /ram/f.txt")
assert (await r2.stdout_str()).strip() == "hi"
@pytest.mark.asyncio
async def test_agent_pattern_one_aborts_while_siblings_complete():
ws = _make_ws()
settled = await asyncio.gather(
_tool_call(ws, "sleep 5", "/ram/subdir", {"DEBUG": "one"}, 0.1),
_tool_call(ws, "echo ok", "/ram", {"DEBUG": "two"}, 5.0),
return_exceptions=True,
)
assert isinstance(settled[0], Exception)
assert "abort" in str(settled[0]).lower()
assert not isinstance(settled[1], Exception)
assert settled[1].stdout.decode().strip() == "ok"