bcbd1bdb22
Test (Python) / test-python-gate (push) Blocked by required conditions
Test (TypeScript) / test-typescript-gate (push) Blocked by required conditions
CLI exit codes / cli-gate (push) Blocked by required conditions
Test (Install) / test-install-gate (push) Blocked by required conditions
Integ / integ-gate (push) Blocked by required conditions
CLI exit codes / Python CLI (push) Waiting to run
Integ / changes (push) Has been skipped
Test (Install) / python-minimal (3.11) (push) Waiting to run
Test (Install) / python-minimal (3.12) (push) Waiting to run
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Waiting to run
Integ / integ-ts (push) Waiting to run
Integ / integ (push) Waiting to run
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Waiting to run
Integ / integ-database (push) Waiting to run
Test (Install) / python-extra (email, mirage.resource.email) (push) Waiting to run
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Waiting to run
Integ / integ-ssh (push) Waiting to run
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
CLI exit codes / TypeScript CLI (push) Waiting to run
CLI exit codes / Cross-language snapshot interop (push) Waiting to run
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Waiting to run
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Waiting to run
Test (Python) / changes (push) Has been skipped
Integ / integ-database-ts (push) Waiting to run
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Waiting to run
Integ / integ-data (push) Waiting to run
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Waiting to run
Test (Python) / test (push) Waiting to run
Integ / integ-fuse (push) Waiting to run
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Waiting to run
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Waiting to run
Test (Python) / audit (push) Waiting to run
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Waiting to run
Integ / integ-ssh-ts (push) Waiting to run
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Waiting to run
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Waiting to run
Test (TypeScript) / changes (push) Has been skipped
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Waiting to run
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Waiting to run
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Waiting to run
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Waiting to run
Test (TypeScript) / test (push) Waiting to run
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Waiting to run
Test (TypeScript) / python-fs-shim (push) Waiting to run
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Waiting to run
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Waiting to run
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Waiting to run
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Waiting to run
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Waiting to run
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Waiting to run
Test (Install) / ts-minimal (push) Waiting to run
304 lines
11 KiB
Python
304 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 pytest
|
|
|
|
from mirage.resource.ram import RAMResource
|
|
from mirage.server.version.api import (branch, checkout, commit, commit_state,
|
|
diff_live_vs_ref, read_version,
|
|
resolve_ref, snapshot_tree, status,
|
|
status_state, version_diff, version_log)
|
|
from mirage.server.version.backend import LocalBackend
|
|
from mirage.server.version.errors import NoSuchBranchError
|
|
from mirage.server.version.state_tree import META_PATH
|
|
from mirage.server.version.store import VersionStore
|
|
from mirage.types import CacheKey, MountMode, StateKey
|
|
from mirage.workspace import Workspace
|
|
from mirage.workspace.snapshot import to_state_dict
|
|
|
|
|
|
def _cache_entry(data: bytes) -> dict:
|
|
return {
|
|
CacheKey.KEY: "k",
|
|
CacheKey.DATA: data,
|
|
CacheKey.FINGERPRINT: None,
|
|
CacheKey.TTL: None,
|
|
CacheKey.CACHED_AT: 0.0,
|
|
CacheKey.SIZE: len(data),
|
|
}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_snapshot_tree_contains_files_and_meta(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
await ws.execute("echo hello > /m/a.txt")
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
|
|
tree = await snapshot_tree(store, ws)
|
|
contents = await store.read_tree(tree)
|
|
|
|
assert META_PATH in contents
|
|
assert await store.read_blob(contents["m/a.txt"]) == b"hello\n"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_commit_advances_branch_and_links_parent(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
|
|
await ws.execute("echo one > /m/a.txt")
|
|
c1 = await commit(store, ws, branch="main", message="first")
|
|
await ws.execute("echo two > /m/a.txt")
|
|
c2 = await commit(store, ws, branch="main", message="second")
|
|
|
|
assert await store.head("main") == c2
|
|
assert (await store.read_commit(c2)).parents == [c1]
|
|
assert await store.log("main") == [c2, c1]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_version_log_lists_messages_newest_first(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo one > /m/a.txt")
|
|
await commit(store, ws, message="first")
|
|
await ws.execute("echo two > /m/a.txt")
|
|
await commit(store, ws, message="second")
|
|
|
|
log = await version_log(store, "main")
|
|
assert [entry["message"] for entry in log] == ["second", "first"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_version_diff_reports_changed_files_only(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo one > /m/a.txt")
|
|
c1 = await commit(store, ws, message="first")
|
|
await ws.execute("echo two > /m/a.txt")
|
|
await ws.execute("echo new > /m/b.txt")
|
|
c2 = await commit(store, ws, message="second")
|
|
|
|
diff = await version_diff(store, c1, c2)
|
|
assert diff["modified"] == ["m/a.txt"]
|
|
assert diff["added"] == ["m/b.txt"]
|
|
assert META_PATH not in diff["modified"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_diff_live_vs_ref_reports_changes_against_version(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo one > /m/a.txt")
|
|
c1 = await commit(store, ws, branch="main", message="first")
|
|
await ws.execute("echo two > /m/a.txt")
|
|
await ws.execute("echo new > /m/b.txt")
|
|
|
|
by_oid = await diff_live_vs_ref(store, await to_state_dict(ws), c1)
|
|
assert by_oid["modified"] == ["m/a.txt"]
|
|
assert by_oid["added"] == ["m/b.txt"]
|
|
|
|
by_branch = await diff_live_vs_ref(store, await to_state_dict(ws), "main")
|
|
assert by_branch == by_oid
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_status_reports_uncommitted_changes(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo one > /m/a.txt")
|
|
await commit(store, ws, message="first")
|
|
await ws.execute("echo changed > /m/a.txt")
|
|
|
|
st = await status(store, ws, "main")
|
|
assert st["modified"] == ["m/a.txt"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_diff_ignores_cache_churn(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo one > /m/a.txt")
|
|
|
|
s1 = await to_state_dict(ws)
|
|
s1[StateKey.CACHE][CacheKey.ENTRIES] = [_cache_entry(b"AAA")]
|
|
c1 = await commit_state(store, s1, message="first")
|
|
|
|
s2 = await to_state_dict(ws)
|
|
s2[StateKey.CACHE][CacheKey.ENTRIES] = [_cache_entry(b"BBB")]
|
|
c2 = await commit_state(store, s2, message="second")
|
|
|
|
assert await version_diff(store, c1, c2) == {
|
|
"added": [],
|
|
"modified": [],
|
|
"deleted": [],
|
|
}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_status_state_reports_uncommitted_changes(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo one > /m/a.txt")
|
|
await commit(store, ws, message="first")
|
|
await ws.execute("echo changed > /m/a.txt")
|
|
|
|
st = await status_state(store, await to_state_dict(ws), "main")
|
|
assert st["modified"] == ["m/a.txt"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_status_state_no_commit_yet_lists_all_as_added(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo one > /m/a.txt")
|
|
|
|
st = await status_state(store, await to_state_dict(ws), "main")
|
|
assert st == {"added": ["m/a.txt"], "modified": [], "deleted": []}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_status_ignores_cache_churn(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo one > /m/a.txt")
|
|
|
|
s1 = await to_state_dict(ws)
|
|
s1[StateKey.CACHE][CacheKey.ENTRIES] = [_cache_entry(b"AAA")]
|
|
await commit_state(store, s1, message="first")
|
|
|
|
live = await to_state_dict(ws)
|
|
live[StateKey.CACHE][CacheKey.ENTRIES] = [_cache_entry(b"BBB")]
|
|
st = await status_state(store, live, "main")
|
|
|
|
assert st == {"added": [], "modified": [], "deleted": []}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_resolve_ref_branch_and_oid(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo one > /m/a.txt")
|
|
c1 = await commit(store, ws, branch="main", message="first")
|
|
|
|
assert await resolve_ref(store, "main") == c1
|
|
assert await resolve_ref(store, c1) == c1
|
|
assert await resolve_ref(store, c1.decode()) == c1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_commit_state_creates_version_from_state(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo hi > /m/a.txt")
|
|
|
|
version = await commit_state(store,
|
|
await to_state_dict(ws),
|
|
branch="main",
|
|
message="from state")
|
|
|
|
entries, _ = await read_version(store, version)
|
|
assert entries["m/a.txt"] == b"hi\n"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_commit_to_unknown_branch_errors(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo one > /m/a.txt")
|
|
await commit(store, ws, branch="main", message="first")
|
|
|
|
with pytest.raises(NoSuchBranchError):
|
|
await commit(store, ws, branch="exp", message="oops")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_commit_diverges_after_branch_created(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo one > /m/a.txt")
|
|
main_head = await commit(store, ws, branch="main", message="first")
|
|
|
|
await branch(store, "exp", from_branch="main")
|
|
await ws.execute("echo two > /m/a.txt")
|
|
exp_head = await commit(store, ws, branch="exp", message="on exp")
|
|
|
|
assert (await store.read_commit(exp_head)).parents == [main_head]
|
|
assert await store.head("main") == main_head
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_branch_creates_line_at_current(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo one > /m/a.txt")
|
|
c1 = await commit(store, ws, branch="main", message="first")
|
|
|
|
await branch(store, "exp", from_branch="main")
|
|
|
|
assert await store.head("exp") == c1
|
|
assert "exp" in await store.branches()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_version_reads_back_files_and_meta(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo hello > /m/a.txt")
|
|
version = await commit(store, ws, message="first")
|
|
|
|
entries, meta = await read_version(store, version)
|
|
|
|
assert entries["m/a.txt"] == b"hello\n"
|
|
assert META_PATH not in entries
|
|
assert "/m/" in [m["prefix"] for m in meta["mounts"]]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_checkout_rebuilds_content_in_place(tmp_path):
|
|
ws = Workspace({"/m": (RAMResource(), MountMode.WRITE)},
|
|
mode=MountMode.WRITE)
|
|
store = await VersionStore.open(LocalBackend(tmp_path), "ws")
|
|
await ws.execute("echo original > /m/a.txt")
|
|
await commit(store, ws, branch="main", message="first")
|
|
|
|
await ws.execute("echo mutated > /m/a.txt")
|
|
await ws.execute("echo extra > /m/b.txt")
|
|
|
|
await checkout(store, ws, "main")
|
|
|
|
result = await ws.execute("cat /m/a.txt")
|
|
assert (await result.stdout_str()) == "original\n"
|
|
assert await status(store, ws, "main") == {
|
|
"added": [],
|
|
"modified": [],
|
|
"deleted": [],
|
|
}
|