chore: import upstream snapshot with attribution
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

This commit is contained in:
wehub-resource-sync
2026-07-13 12:30:44 +08:00
commit bcbd1bdb22
5748 changed files with 562488 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
import inspect
from types import SimpleNamespace
import aioresponses.core
from aiohttp import ClientResponse
_NEEDS_STREAM_WRITER = "stream_writer" in inspect.signature(
ClientResponse.__init__).parameters
class _PatchedClientResponse(ClientResponse):
def __init__(self, *args: object, **kwargs: object) -> None:
kwargs.setdefault("stream_writer", SimpleNamespace(output_size=0))
super().__init__(*args, **kwargs)
if _NEEDS_STREAM_WRITER:
aioresponses.core.ClientResponse = _PatchedClientResponse
+187
View File
@@ -0,0 +1,187 @@
import pytest
from aioresponses import aioresponses
from mirage.accessor.onedrive import OneDriveConfig
from mirage.core.onedrive._client import (GraphError, drive_base, graph_get,
graph_get_bytes, graph_list, headers,
item_url, split_path)
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def test_split_path_strips_real_prefix():
p = PathSpec(resource_path=mount_key("/od/a.txt", "/od"),
virtual="/od/a.txt",
directory="/od/a.txt")
assert split_path(p) == ("/od", "a.txt")
def test_split_path_does_not_strip_sibling_prefix_match():
# `/data` must not be stripped from the sibling `/database.txt`; the
# boundary guard lives in mount_key, whose output split_path returns.
p = PathSpec(resource_path=mount_key("/database.txt", "/data"),
virtual="/database.txt",
directory="/database.txt")
assert split_path(p) == ("", "database.txt")
def test_headers_resolves_callable_token():
h = headers(OneDriveConfig(access_token=lambda: "live-token"))
assert h["Authorization"] == "Bearer live-token"
def _cfg(**kw) -> OneDriveConfig:
return OneDriveConfig(access_token="tok", **kw)
def test_drive_base_defaults_to_me_drive():
assert drive_base(_cfg()) == "https://graph.microsoft.com/v1.0/me/drive"
def test_drive_base_uses_drive_id():
base = drive_base(_cfg(drive_id="b!abc"))
assert base == "https://graph.microsoft.com/v1.0/drives/b!abc"
def test_drive_base_uses_site_default_drive():
base = drive_base(_cfg(site_id="site123"))
assert base == "https://graph.microsoft.com/v1.0/sites/site123/drive"
def test_item_url_root_children():
url = item_url(_cfg(), "/", action="/children")
assert url == "https://graph.microsoft.com/v1.0/me/drive/root/children"
def test_item_url_nested_metadata_no_action():
url = item_url(_cfg(), "/Docs/report.docx")
assert url == (
"https://graph.microsoft.com/v1.0/me/drive/root:/Docs/report.docx")
def test_item_url_nested_content():
url = item_url(_cfg(), "/Docs/report.docx", action="/content")
assert url == ("https://graph.microsoft.com/v1.0/me/drive"
"/root:/Docs/report.docx:/content")
def test_item_url_nested_children():
url = item_url(_cfg(), "/Docs", action="/children")
assert url == (
"https://graph.microsoft.com/v1.0/me/drive/root:/Docs:/children")
def test_item_url_applies_key_prefix():
url = item_url(_cfg(key_prefix="team/files"), "/a.txt", action="/content")
assert url == ("https://graph.microsoft.com/v1.0/me/drive"
"/root:/team/files/a.txt:/content")
def test_item_url_quotes_spaces():
url = item_url(_cfg(), "/My Folder/a b.txt")
assert url == ("https://graph.microsoft.com/v1.0/me/drive"
"/root:/My%20Folder/a%20b.txt")
def test_headers_carry_bearer_token():
h = headers(_cfg())
assert h["Authorization"] == "Bearer tok"
_ROOT = "https://graph.microsoft.com/v1.0/me/drive/root"
@pytest.mark.asyncio
async def test_graph_get_returns_parsed_json():
with aioresponses() as m:
m.get(_ROOT, payload={"id": "01ABC", "name": "root"})
result = await graph_get(_cfg(), _ROOT)
assert result["id"] == "01ABC"
@pytest.mark.asyncio
async def test_graph_get_raises_grapherror_with_status():
with aioresponses() as m:
m.get(_ROOT,
status=404,
payload={"error": {
"code": "itemNotFound",
"message": "nope"
}})
with pytest.raises(GraphError) as exc:
await graph_get(_cfg(), _ROOT)
assert exc.value.status == 404
assert exc.value.code == "itemNotFound"
@pytest.mark.asyncio
async def test_graph_list_follows_odata_nextlink():
page2 = _ROOT + "/children?$skiptoken=abc"
with aioresponses() as m:
m.get(_ROOT + "/children",
payload={
"value": [{
"id": "a"
}],
"@odata.nextLink": page2
})
m.get(page2, payload={"value": [{"id": "b"}]})
items = await graph_list(_cfg(), _ROOT + "/children")
assert [i["id"] for i in items] == ["a", "b"]
@pytest.mark.asyncio
async def test_graph_get_bytes_returns_raw_content():
url = _ROOT + ":/a.txt:/content"
with aioresponses() as m:
m.get(url, body=b"hello bytes")
data = await graph_get_bytes(_cfg(), url)
assert data == b"hello bytes"
@pytest.mark.asyncio
async def test_request_retries_on_429_then_succeeds():
with aioresponses() as m:
m.get(_ROOT, status=429, headers={"Retry-After": "0"})
m.get(_ROOT, payload={"id": "ok"})
result = await graph_get(_cfg(), _ROOT)
assert result["id"] == "ok"
@pytest.mark.asyncio
async def test_request_gives_up_after_max_retries():
with aioresponses() as m:
for _ in range(3):
m.get(_ROOT, status=429, headers={"Retry-After": "0"})
with pytest.raises(GraphError) as exc:
await graph_get(_cfg(max_retries=2), _ROOT)
assert exc.value.status == 429
@pytest.mark.asyncio
async def test_401_refreshes_callable_token_once_then_succeeds():
calls = {"n": 0}
def provider():
calls["n"] += 1
return "fresh" if calls["n"] > 1 else "stale"
with aioresponses() as m:
m.get(_ROOT,
status=401,
payload={"error": {
"code": "InvalidAuthenticationToken"
}})
m.get(_ROOT, payload={"id": "ok"})
result = await graph_get(OneDriveConfig(access_token=provider), _ROOT)
assert result["id"] == "ok"
assert calls["n"] == 2
@pytest.mark.asyncio
async def test_401_with_static_token_does_not_retry():
with aioresponses() as m:
m.get(_ROOT, status=401, payload={"error": {"code": "x"}})
with pytest.raises(GraphError) as exc:
await graph_get(_cfg(), _ROOT)
assert exc.value.status == 401
@@ -0,0 +1,135 @@
import pytest
from aioresponses import CallbackResult, aioresponses
from mirage.accessor.onedrive import OneDriveAccessor, OneDriveConfig
from mirage.commands.builtin.onedrive import COMMANDS
from mirage.core.onedrive._client import GraphError
from mirage.core.onedrive.copy import copy
from mirage.core.onedrive.truncate import truncate
from mirage.types import PathSpec
def _accessor(**kw) -> OneDriveAccessor:
return OneDriveAccessor(OneDriveConfig(access_token="tok", **kw))
_BASE = "https://graph.microsoft.com/v1.0/me/drive"
_cp = next(c for c in COMMANDS if any(
rc.name == "cp" for rc in getattr(c, "_registered_commands", [])))
@pytest.mark.asyncio
async def test_copy_posts_copy_action_with_name():
body = {}
def _cb(url, **kwargs):
body.update(kwargs.get("json") or {})
return CallbackResult(status=202, payload={})
with aioresponses() as m:
m.post(_BASE + "/root:/a.txt:/copy", callback=_cb)
await copy(_accessor(), PathSpec.from_str_path("/a.txt"),
PathSpec.from_str_path("/sub/b.txt"))
assert body["name"] == "b.txt"
assert "/root:/sub" in body["parentReference"]["path"]
@pytest.mark.asyncio
async def test_copy_polls_monitor_until_completed():
monitor = "https://monitor.example/op/123"
with aioresponses() as m:
m.post(_BASE + "/root:/a.txt:/copy",
status=202,
headers={"Location": monitor})
m.get(monitor, payload={"status": "completed"})
await copy(_accessor(), PathSpec.from_str_path("/a.txt"),
PathSpec.from_str_path("/b.txt"))
@pytest.mark.asyncio
async def test_copy_raises_when_monitor_reports_failed():
monitor = "https://monitor.example/op/456"
with aioresponses() as m:
m.post(_BASE + "/root:/a.txt:/copy",
status=202,
headers={"Location": monitor})
m.get(monitor,
payload={
"status": "failed",
"error": {
"code": "nameAlreadyExists",
"message": "x"
}
})
with pytest.raises(GraphError):
await copy(_accessor(), PathSpec.from_str_path("/a.txt"),
PathSpec.from_str_path("/b.txt"))
@pytest.mark.asyncio
async def test_cp_recursive_uses_server_side_folder_copy():
src = PathSpec.from_str_path("/src")
dst = PathSpec.from_str_path("/dst")
with aioresponses() as m:
m.get(_BASE + "/root:/src:/children",
payload={
"value": [
{
"id": "1",
"name": "a.txt",
"size": 3,
"file": {}
},
{
"id": "2",
"name": "sub",
"folder": {
"childCount": 1
}
},
]
})
m.get(_BASE + "/root:/src/sub:/children",
payload={
"value": [{
"id": "3",
"name": "b.txt",
"size": 4,
"file": {}
}]
})
m.get(_BASE + "/root:/dst",
status=404,
payload={"error": {
"code": "itemNotFound"
}})
m.get(_BASE + "/root:/src",
payload={
"id": "0",
"name": "src",
"folder": {
"childCount": 2
}
})
m.post(_BASE + "/root:/src:/copy", status=202, payload={})
_out, io = await _cp.__wrapped__(_accessor(), [src, dst],
r=True,
index=None)
assert set(io.writes) == {"/dst/a.txt", "/dst/sub/b.txt"}
@pytest.mark.asyncio
async def test_truncate_shrinks_content():
captured = {}
def _put_cb(url, **kwargs):
captured["body"] = kwargs.get("data")
return CallbackResult(status=200, payload={"id": "X"})
content = _BASE + "/root:/a.txt:/content"
with aioresponses() as m:
m.get(content, body=b"hello")
m.put(content, callback=_put_cb)
await truncate(_accessor(), PathSpec.from_str_path("/a.txt"), 3)
assert captured["body"] == b"hel"
+152
View File
@@ -0,0 +1,152 @@
import pytest
from aioresponses import aioresponses
from mirage.accessor.onedrive import OneDriveAccessor, OneDriveConfig
from mirage.commands.builtin.find_eval import Name, Not, Or
from mirage.core.onedrive.du import du, du_all
from mirage.core.onedrive.find import find
from mirage.types import PathSpec
def _accessor(**kw) -> OneDriveAccessor:
return OneDriveAccessor(OneDriveConfig(access_token="tok", **kw))
_BASE = "https://graph.microsoft.com/v1.0/me/drive"
def _tree(m):
m.get(_BASE + "/root/children",
payload={
"value": [
{
"id": "1",
"name": "a.txt",
"size": 3,
"file": {}
},
{
"id": "2",
"name": "sub",
"folder": {
"childCount": 1
}
},
]
})
m.get(_BASE + "/root:/sub:/children",
payload={
"value": [{
"id": "3",
"name": "b.txt",
"size": 5,
"file": {}
}]
})
@pytest.mark.asyncio
async def test_du_sums_all_files_recursively():
with aioresponses() as m:
_tree(m)
total = await du(_accessor(), PathSpec.from_str_path("/"))
assert total == 8
@pytest.mark.asyncio
async def test_du_all_lists_files_plus_total():
with aioresponses() as m:
_tree(m)
rows = await du_all(_accessor(), PathSpec.from_str_path("/"))
assert ("/a.txt", 3) in rows
assert ("/sub/b.txt", 5) in rows
assert rows[-1][1] == 8
@pytest.mark.asyncio
async def test_du_of_file_returns_its_own_size():
with aioresponses() as m:
m.get(_BASE + "/root:/a.txt",
payload={
"id": "1",
"name": "a.txt",
"size": 3,
"file": {}
})
total = await du(_accessor(), PathSpec.from_str_path("/a.txt"))
assert total == 3
@pytest.mark.asyncio
async def test_du_all_of_file_is_empty():
with aioresponses() as m:
m.get(_BASE + "/root:/a.txt",
payload={
"id": "1",
"name": "a.txt",
"size": 3,
"file": {}
})
rows = await du_all(_accessor(), PathSpec.from_str_path("/a.txt"))
assert rows == []
@pytest.mark.asyncio
async def test_find_returns_files_and_folders():
with aioresponses() as m:
_tree(m)
out = await find(_accessor(), PathSpec.from_str_path("/"))
assert out == ["/", "/a.txt", "/sub", "/sub/b.txt"]
@pytest.mark.asyncio
async def test_find_type_file_excludes_folders():
with aioresponses() as m:
_tree(m)
out = await find(_accessor(), PathSpec.from_str_path("/"), type="file")
assert out == ["/a.txt", "/sub/b.txt"]
@pytest.mark.asyncio
async def test_find_name_glob():
with aioresponses() as m:
_tree(m)
out = await find(_accessor(), PathSpec.from_str_path("/"), name="b.*")
assert out == ["/sub/b.txt"]
@pytest.mark.asyncio
async def test_find_honors_not_tree():
tree = Not(Name("a.txt"))
with aioresponses() as m:
_tree(m)
out = await find(_accessor(), PathSpec.from_str_path("/"), tree=tree)
assert "/a.txt" not in out
assert "/sub/b.txt" in out
@pytest.mark.asyncio
async def test_find_honors_or_tree():
tree = Or([Name("a.txt"), Name("b.txt")])
with aioresponses() as m:
_tree(m)
out = await find(_accessor(), PathSpec.from_str_path("/"), tree=tree)
assert out == ["/a.txt", "/sub/b.txt"]
@pytest.mark.asyncio
async def test_find_empty_folder_emits_start_path():
with aioresponses() as m:
m.get(_BASE + "/root:/empty:/children", payload={"value": []})
m.get(_BASE + "/root:/empty",
payload={
"id": "9",
"name": "empty",
"folder": {
"childCount": 0
}
})
out = await find(_accessor(),
PathSpec.from_str_path("/empty"),
type="d")
assert out == ["/empty"]
+137
View File
@@ -0,0 +1,137 @@
import re
import pytest
from aioresponses import CallbackResult, aioresponses
from mirage.accessor.onedrive import OneDriveAccessor, OneDriveConfig
from mirage.core.onedrive.read import read_bytes
from mirage.observe.context import (RecordingScope, push_revisions,
reset_revisions)
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _accessor(**kw) -> OneDriveAccessor:
return OneDriveAccessor(OneDriveConfig(access_token="tok", **kw))
_BASE = "https://graph.microsoft.com/v1.0/me/drive"
_CONTENT = _BASE + "/root:/Docs/a.txt:/content"
@pytest.mark.asyncio
async def test_read_returns_current_content():
with aioresponses() as m:
m.get(_CONTENT, body=b"current bytes")
data = await read_bytes(_accessor(),
PathSpec.from_str_path("/Docs/a.txt"))
assert data == b"current bytes"
@pytest.mark.asyncio
async def test_read_pinned_revision_hits_version_content():
version_url = _BASE + "/root:/Docs/a.txt:/versions/3.0/content"
token = push_revisions({"/Docs/a.txt": "3.0"})
try:
with aioresponses() as m:
m.get(version_url, body=b"old version bytes")
data = await read_bytes(_accessor(),
PathSpec.from_str_path("/Docs/a.txt"))
finally:
reset_revisions(token)
assert data == b"old version bytes"
@pytest.mark.asyncio
async def test_read_range_sends_range_header():
captured = {}
def _cb(url, **kwargs):
captured["range"] = kwargs["headers"].get("Range")
return CallbackResult(body=b"llo")
with aioresponses() as m:
m.get(_CONTENT, callback=_cb)
data = await read_bytes(_accessor(),
PathSpec.from_str_path("/Docs/a.txt"),
offset=2,
size=3)
assert captured["range"] == "bytes=2-4"
assert data == b"llo"
_META = re.compile(r".*/root:/Docs/a\.txt(\?.*)?$")
_DOWNLOAD = "https://download.example/pinned-bytes"
def _meta_payload():
return {
"id":
"01",
"cTag":
"ctag-xyz",
"@microsoft.graph.downloadUrl":
_DOWNLOAD,
"versions": [
{
"id": "1.0",
"lastModifiedDateTime": "2026-01-01T00:00:00Z"
},
{
"id": "2.0",
"lastModifiedDateTime": "2026-02-01T00:00:00Z"
},
],
}
@pytest.mark.asyncio
async def test_read_captures_fingerprint_and_revision_when_recording():
scope = RecordingScope()
sink = scope.records
try:
with aioresponses() as m:
m.get(_META, payload=_meta_payload())
m.get(_DOWNLOAD, body=b"old version bytes")
data = await read_bytes(_accessor(),
PathSpec.from_str_path("/Docs/a.txt"))
finally:
scope.close()
rec = sink[0]
assert rec.fingerprint == "ctag-xyz"
assert rec.revision == "2.0"
assert data == b"old version bytes"
@pytest.mark.asyncio
async def test_capture_reads_pinned_download_url_not_live_content():
scope = RecordingScope()
sink = scope.records
try:
with aioresponses() as m:
m.get(_META, payload=_meta_payload())
m.get(_DOWNLOAD, body=b"snapshot bytes")
m.get(_CONTENT, body=b"live mutated bytes")
data = await read_bytes(_accessor(),
PathSpec.from_str_path("/Docs/a.txt"))
finally:
scope.close()
assert data == b"snapshot bytes"
assert sink[0].fingerprint == "ctag-xyz"
@pytest.mark.asyncio
async def test_read_missing_raises_file_not_found():
with aioresponses() as m:
m.get(_CONTENT,
status=404,
payload={"error": {
"code": "itemNotFound",
"message": "no"
}})
with pytest.raises(FileNotFoundError) as exc:
await read_bytes(
_accessor(),
PathSpec.from_str_path("/od/Docs/a.txt",
mount_key("/od/Docs/a.txt", "/od")))
assert str(exc.value) == "/od/Docs/a.txt"
+147
View File
@@ -0,0 +1,147 @@
import pytest
from aioresponses import aioresponses
from mirage.accessor.onedrive import OneDriveAccessor, OneDriveConfig
from mirage.cache.index import RAMIndexCacheStore
from mirage.core.onedrive.readdir import readdir
from mirage.types import PathSpec
def _accessor(**kw) -> OneDriveAccessor:
return OneDriveAccessor(OneDriveConfig(access_token="tok", **kw))
_BASE = "https://graph.microsoft.com/v1.0/me/drive"
@pytest.mark.asyncio
async def test_readdir_root_lists_children():
index = RAMIndexCacheStore()
with aioresponses() as m:
m.get(_BASE + "/root/children",
payload={
"value": [
{
"id": "1",
"name": "a.txt",
"size": 10,
"file": {}
},
{
"id": "2",
"name": "Docs",
"folder": {
"childCount": 0
}
},
]
})
names = await readdir(_accessor(), PathSpec.from_str_path("/"), index)
assert names == ["/Docs", "/a.txt"]
@pytest.mark.asyncio
async def test_readdir_folder_records_file_and_folder_entries():
index = RAMIndexCacheStore()
with aioresponses() as m:
m.get(_BASE + "/root:/Docs:/children",
payload={
"value": [{
"id": "9",
"name": "report.docx",
"size": 55,
"file": {}
}]
})
names = await readdir(_accessor(), PathSpec.from_str_path("/Docs"),
index)
assert names == ["/Docs/report.docx"]
lookup = await index.get("/Docs/report.docx")
assert lookup.entry.resource_type == "file"
assert lookup.entry.size == 55
@pytest.mark.asyncio
async def test_readdir_of_file_raises_not_a_directory():
index = RAMIndexCacheStore()
with aioresponses() as m:
m.get(_BASE + "/root:/a.txt:/children",
status=404,
payload={"error": {
"code": "itemNotFound",
"message": "x"
}})
m.get(_BASE + "/root:/a.txt",
payload={
"id": "1",
"name": "a.txt",
"size": 3,
"file": {}
})
with pytest.raises(NotADirectoryError):
await readdir(_accessor(), PathSpec.from_str_path("/a.txt"), index)
@pytest.mark.asyncio
async def test_readdir_paginates_nextlink():
index = RAMIndexCacheStore()
page2 = _BASE + "/root/children?$skiptoken=x"
with aioresponses() as m:
m.get(_BASE + "/root/children",
payload={
"value": [{
"id": "1",
"name": "a.txt",
"file": {}
}],
"@odata.nextLink": page2,
})
m.get(page2,
payload={"value": [{
"id": "2",
"name": "b.txt",
"file": {}
}]})
names = await readdir(_accessor(), PathSpec.from_str_path("/"), index)
assert names == ["/a.txt", "/b.txt"]
@pytest.mark.asyncio
async def test_readdir_stores_remote_time_for_files():
index = RAMIndexCacheStore()
with aioresponses() as m:
m.get(_BASE + "/root/children",
payload={
"value": [{
"id": "1",
"name": "a.txt",
"size": 10,
"file": {},
"lastModifiedDateTime": "2026-06-19T09:28:00Z",
}]
})
await readdir(_accessor(), PathSpec.from_str_path("/"), index)
lookup = await index.get("/a.txt")
assert lookup.entry.remote_time == "2026-06-19T09:28:00Z"
@pytest.mark.asyncio
async def test_readdir_stores_remote_time_for_folders():
index = RAMIndexCacheStore()
with aioresponses() as m:
m.get(_BASE + "/root/children",
payload={
"value": [{
"id": "2",
"name": "Docs",
"folder": {
"childCount": 3
},
"size": 5000,
"lastModifiedDateTime": "2026-05-28T02:10:00Z",
}]
})
await readdir(_accessor(), PathSpec.from_str_path("/"), index)
lookup = await index.get("/Docs")
assert lookup.entry.remote_time == "2026-05-28T02:10:00Z"
assert lookup.entry.size == 5000
@@ -0,0 +1,57 @@
import asyncio
import re
import pytest
from aioresponses import aioresponses
from mirage.accessor.onedrive import OneDriveConfig
from mirage.resource.onedrive import OneDriveResource
from mirage.types import MountMode
from mirage.workspace import Workspace
from mirage.workspace.snapshot.drift import ContentDriftError, check_drift
_ITEM = re.compile(r".*/root:/a\.txt(\?.*)?$")
def _ws() -> Workspace:
backend = OneDriveResource(OneDriveConfig(access_token="tok"))
return Workspace({"/od": (backend, MountMode.WRITE)}, mode=MountMode.WRITE)
def test_drift_raises_when_live_ctag_differs():
with aioresponses() as m:
m.get(_ITEM,
payload={
"name": "a.txt",
"size": 3,
"cTag": "live-ctag",
"file": {}
})
with pytest.raises(ContentDriftError) as exc:
asyncio.run(check_drift(_ws(), "/od/a.txt", "snapshot-ctag"))
assert exc.value.path == "/od/a.txt"
assert exc.value.live_fingerprint == "live-ctag"
def test_no_drift_when_ctag_matches():
with aioresponses() as m:
m.get(_ITEM,
payload={
"name": "a.txt",
"size": 3,
"cTag": "same-ctag",
"file": {}
})
asyncio.run(check_drift(_ws(), "/od/a.txt", "same-ctag"))
def test_drift_raises_when_file_missing():
with aioresponses() as m:
m.get(_ITEM,
status=404,
payload={"error": {
"code": "itemNotFound",
"message": "no"
}})
with pytest.raises(ContentDriftError):
asyncio.run(check_drift(_ws(), "/od/a.txt", "snapshot-ctag"))
+130
View File
@@ -0,0 +1,130 @@
import pytest
from aioresponses import aioresponses
from mirage.accessor.onedrive import OneDriveAccessor, OneDriveConfig
from mirage.cache.index import RAMIndexCacheStore
from mirage.core.onedrive.readdir import readdir
from mirage.core.onedrive.stat import stat
from mirage.types import FileType, PathSpec
from mirage.utils.key_prefix import mount_key
def _accessor(**kw) -> OneDriveAccessor:
return OneDriveAccessor(OneDriveConfig(access_token="tok", **kw))
_FILE_URL = ("https://graph.microsoft.com/v1.0/me/drive"
"/root:/Docs/report.docx")
_DIR_URL = "https://graph.microsoft.com/v1.0/me/drive/root:/Docs"
@pytest.mark.asyncio
async def test_stat_root_is_directory():
result = await stat(_accessor(), PathSpec.from_str_path("/"))
assert result.type == FileType.DIRECTORY
@pytest.mark.asyncio
async def test_stat_file_carries_size_and_ctag_fingerprint():
with aioresponses() as m:
m.get(_FILE_URL,
payload={
"id": "01ITEM",
"name": "report.docx",
"size": 1234,
"lastModifiedDateTime": "2026-05-01T10:00:00Z",
"cTag": "ctag-abc",
"eTag": "etag-xyz",
"file": {
"mimeType": "application/vnd.openxml"
},
})
result = await stat(_accessor(),
PathSpec.from_str_path("/Docs/report.docx"))
assert result.name == "report.docx"
assert result.size == 1234
assert result.fingerprint == "ctag-abc"
assert result.extra["id"] == "01ITEM"
@pytest.mark.asyncio
async def test_stat_folder_is_directory():
with aioresponses() as m:
m.get(_DIR_URL,
payload={
"id": "01FOLDER",
"name": "Docs",
"size": 4096,
"lastModifiedDateTime": "2026-05-01T10:00:00Z",
"folder": {
"childCount": 2
},
})
result = await stat(_accessor(), PathSpec.from_str_path("/Docs"))
assert result.type == FileType.DIRECTORY
assert result.name == "Docs"
assert result.size == 4096
assert result.modified == "2026-05-01T10:00:00Z"
@pytest.mark.asyncio
async def test_stat_missing_raises_file_not_found():
with aioresponses() as m:
m.get(_FILE_URL,
status=404,
payload={"error": {
"code": "itemNotFound",
"message": "no"
}})
with pytest.raises(FileNotFoundError) as exc:
await stat(
_accessor(),
PathSpec.from_str_path(
"/od/Docs/report.docx",
mount_key("/od/Docs/report.docx", "/od")))
assert str(exc.value) == "/od/Docs/report.docx"
@pytest.mark.asyncio
async def test_stat_from_index_returns_modified():
index = RAMIndexCacheStore()
with aioresponses() as m:
m.get("https://graph.microsoft.com/v1.0/me/drive/root/children",
payload={
"value": [{
"id": "1",
"name": "notes.txt",
"size": 42,
"file": {},
"lastModifiedDateTime": "2026-06-19T09:28:00Z",
}]
})
await readdir(_accessor(), PathSpec.from_str_path("/"), index)
result = await stat(_accessor(), PathSpec.from_str_path("/notes.txt"),
index)
assert result.name == "notes.txt"
assert result.size == 42
assert result.modified == "2026-06-19T09:28:00Z"
@pytest.mark.asyncio
async def test_stat_from_index_folder_returns_modified():
index = RAMIndexCacheStore()
with aioresponses() as m:
m.get("https://graph.microsoft.com/v1.0/me/drive/root/children",
payload={
"value": [{
"id": "2",
"name": "Docs",
"folder": {
"childCount": 5
},
"size": 9000,
"lastModifiedDateTime": "2026-05-28T02:10:00Z",
}]
})
await readdir(_accessor(), PathSpec.from_str_path("/"), index)
result = await stat(_accessor(), PathSpec.from_str_path("/Docs"), index)
assert result.type == FileType.DIRECTORY
assert result.modified == "2026-05-28T02:10:00Z"
assert result.size == 9000
+82
View File
@@ -0,0 +1,82 @@
import pytest
from aioresponses import aioresponses
from mirage.accessor.onedrive import OneDriveAccessor, OneDriveConfig
from mirage.core.onedrive.stream import range_read, read_stream
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _accessor(**kw) -> OneDriveAccessor:
return OneDriveAccessor(OneDriveConfig(access_token="tok", **kw))
_CONTENT = ("https://graph.microsoft.com/v1.0/me/drive"
"/root:/Docs/a.txt:/content")
@pytest.mark.asyncio
async def test_read_stream_yields_full_content():
with aioresponses() as m:
m.get(_CONTENT, body=b"abcdef")
chunks = []
async for chunk in read_stream(_accessor(),
PathSpec.from_str_path("/Docs/a.txt")):
chunks.append(chunk)
assert b"".join(chunks) == b"abcdef"
@pytest.mark.asyncio
async def test_read_stream_missing_raises_file_not_found():
with aioresponses() as m:
m.get(_CONTENT,
status=404,
payload={"error": {
"code": "itemNotFound",
"message": "no"
}})
with pytest.raises(FileNotFoundError) as exc:
async for _ in read_stream(
_accessor(),
PathSpec.from_str_path("/od/Docs/a.txt",
mount_key("/od/Docs/a.txt",
"/od"))):
pass
assert str(exc.value) == "/od/Docs/a.txt"
@pytest.mark.asyncio
async def test_read_stream_refreshes_callable_token_on_401():
calls = {"n": 0}
def provider():
calls["n"] += 1
return "fresh" if calls["n"] > 1 else "stale"
accessor = OneDriveAccessor(OneDriveConfig(access_token=provider))
with aioresponses() as m:
m.get(_CONTENT, status=401, payload={"error": {"code": "expired"}})
m.get(_CONTENT, body=b"abcdef")
chunks = [
c async for c in read_stream(accessor,
PathSpec.from_str_path("/Docs/a.txt"))
]
assert b"".join(chunks) == b"abcdef"
assert calls["n"] == 2
@pytest.mark.asyncio
async def test_range_read_returns_requested_bytes():
captured = {}
def _cb(url, **kwargs):
from aioresponses import CallbackResult
captured["range"] = kwargs["headers"].get("Range")
return CallbackResult(body=b"cde")
with aioresponses() as m:
m.get(_CONTENT, callback=_cb)
data = await range_read(_accessor(),
PathSpec.from_str_path("/Docs/a.txt"), 2, 5)
assert data == b"cde"
assert captured["range"] == "bytes=2-4"
@@ -0,0 +1,106 @@
import pytest
from aioresponses import CallbackResult, aioresponses
from mirage.accessor.onedrive import OneDriveAccessor, OneDriveConfig
from mirage.core.onedrive.create import create
from mirage.core.onedrive.exists import exists
from mirage.core.onedrive.mkdir import mkdir
from mirage.core.onedrive.rename import rename
from mirage.core.onedrive.rm import rm_r
from mirage.core.onedrive.rmdir import rmdir
from mirage.core.onedrive.unlink import unlink
from mirage.types import PathSpec
def _accessor(**kw) -> OneDriveAccessor:
return OneDriveAccessor(OneDriveConfig(access_token="tok", **kw))
_BASE = "https://graph.microsoft.com/v1.0/me/drive"
@pytest.mark.asyncio
async def test_mkdir_posts_folder_to_parent_children():
body = {}
def _cb(url, **kwargs):
body.update(kwargs.get("json") or {})
return CallbackResult(status=201, payload={"id": "F"})
with aioresponses() as m:
m.post(_BASE + "/root:/a:/children", callback=_cb)
await mkdir(_accessor(), PathSpec.from_str_path("/a/b"))
assert body["name"] == "b"
assert "folder" in body
@pytest.mark.asyncio
async def test_create_puts_empty_content():
captured = {}
def _cb(url, **kwargs):
captured["body"] = kwargs.get("data")
return CallbackResult(status=201, payload={"id": "X"})
with aioresponses() as m:
m.put(_BASE + "/root:/a.txt:/content", callback=_cb)
await create(_accessor(), PathSpec.from_str_path("/a.txt"))
assert captured["body"] in (b"", None)
@pytest.mark.asyncio
async def test_unlink_deletes_item():
with aioresponses() as m:
m.delete(_BASE + "/root:/a.txt", status=204)
await unlink(_accessor(), PathSpec.from_str_path("/a.txt"))
@pytest.mark.asyncio
async def test_rmdir_deletes_folder():
with aioresponses() as m:
m.delete(_BASE + "/root:/docs", status=204)
await rmdir(_accessor(), PathSpec.from_str_path("/docs"))
@pytest.mark.asyncio
async def test_rm_r_deletes_tree():
with aioresponses() as m:
m.delete(_BASE + "/root:/docs", status=204)
await rm_r(_accessor(), PathSpec.from_str_path("/docs"))
@pytest.mark.asyncio
async def test_rename_patches_name():
body = {}
def _cb(url, **kwargs):
body.update(kwargs.get("json") or {})
return CallbackResult(status=200, payload={"id": "X", "name": "b.txt"})
with aioresponses() as m:
m.patch(_BASE + "/root:/a.txt", callback=_cb)
await rename(_accessor(), PathSpec.from_str_path("/a.txt"),
PathSpec.from_str_path("/b.txt"))
assert body["name"] == "b.txt"
@pytest.mark.asyncio
async def test_exists_true_and_false():
with aioresponses() as m:
m.get(_BASE + "/root:/a.txt",
payload={
"id": "X",
"name": "a.txt",
"file": {}
})
assert await exists(_accessor(),
PathSpec.from_str_path("/a.txt")) is True
with aioresponses() as m:
m.get(_BASE + "/root:/missing.txt",
status=404,
payload={"error": {
"code": "itemNotFound",
"message": "no"
}})
assert await exists(_accessor(),
PathSpec.from_str_path("/missing.txt")) is False
+82
View File
@@ -0,0 +1,82 @@
import pytest
from aioresponses import CallbackResult, aioresponses
import mirage.core.onedrive.write as write_mod
from mirage.accessor.onedrive import OneDriveAccessor, OneDriveConfig
from mirage.core.onedrive.write import write_bytes
from mirage.types import PathSpec
def _accessor(**kw) -> OneDriveAccessor:
return OneDriveAccessor(OneDriveConfig(access_token="tok", **kw))
_BASE = "https://graph.microsoft.com/v1.0/me/drive"
_CONTENT = _BASE + "/root:/Docs/a.txt:/content"
_SESSION = _BASE + "/root:/Docs/a.txt:/createUploadSession"
@pytest.mark.asyncio
async def test_write_small_file_puts_content():
captured = {}
def _cb(url, **kwargs):
captured["body"] = kwargs.get("data")
return CallbackResult(status=201, payload={"id": "X", "name": "a.txt"})
with aioresponses() as m:
m.put(_CONTENT, callback=_cb)
result = await write_bytes(_accessor(),
PathSpec.from_str_path("/Docs/a.txt"),
b"hello")
assert result is None
assert captured["body"] == b"hello"
@pytest.mark.asyncio
async def test_write_large_file_uses_upload_session(monkeypatch):
monkeypatch.setattr(write_mod, "SIMPLE_UPLOAD_MAX", 4)
monkeypatch.setattr(write_mod, "UPLOAD_CHUNK", 4)
ranges = []
def _chunk_cb(url, **kwargs):
ranges.append(kwargs["headers"]["Content-Range"])
return CallbackResult(status=202, payload={})
def _final_cb(url, **kwargs):
ranges.append(kwargs["headers"]["Content-Range"])
return CallbackResult(status=201, payload={"id": "X"})
upload_url = "https://upload.example/session1"
with aioresponses() as m:
m.post(_SESSION, payload={"uploadUrl": upload_url})
m.put(upload_url, callback=_chunk_cb)
m.put(upload_url, callback=_final_cb)
await write_bytes(_accessor(), PathSpec.from_str_path("/Docs/a.txt"),
b"abcdef")
assert ranges == ["bytes 0-3/6", "bytes 4-5/6"]
@pytest.mark.asyncio
async def test_upload_resumes_from_next_expected_ranges(monkeypatch):
monkeypatch.setattr(write_mod, "SIMPLE_UPLOAD_MAX", 4)
monkeypatch.setattr(write_mod, "UPLOAD_CHUNK", 4)
ranges = []
def _chunk_cb(url, **kwargs):
ranges.append(kwargs["headers"]["Content-Range"])
return CallbackResult(status=202,
payload={"nextExpectedRanges": ["2-5"]})
def _final_cb(url, **kwargs):
ranges.append(kwargs["headers"]["Content-Range"])
return CallbackResult(status=201, payload={"id": "X"})
upload_url = "https://upload.example/session2"
with aioresponses() as m:
m.post(_SESSION, payload={"uploadUrl": upload_url})
m.put(upload_url, callback=_chunk_cb)
m.put(upload_url, callback=_final_cb)
await write_bytes(_accessor(), PathSpec.from_str_path("/Docs/a.txt"),
b"abcdef")
assert ranges == ["bytes 0-3/6", "bytes 2-5/6"]