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
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
+55
View File
@@ -0,0 +1,55 @@
# ========= 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
from mirage.accessor.ram import RAMAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops import Ops
from mirage.ops.config import OpsMount
from mirage.resource.ram import RAMResource
from mirage.resource.ram.store import RAMStore
from mirage.types import MountMode
def _ram_registered_ops():
resource = RAMResource()
return resource.ops_list()
def run(coro):
return asyncio.run(coro)
def make_ops(mode=MountMode.WRITE):
store = RAMStore()
accessor = RAMAccessor(store)
mounts = [
OpsMount(
prefix="/data/",
resource_type="ram",
accessor=accessor,
index=RAMIndexCacheStore(),
mode=mode,
ops=_ram_registered_ops(),
)
]
ops = Ops(mounts)
return ops, store
def make_ops_with_dir(mode=MountMode.WRITE):
ops, store = make_ops(mode)
asyncio.run(ops.mkdir("/data/dir"))
return ops, store
+29
View File
@@ -0,0 +1,29 @@
from types import SimpleNamespace
import pytest
from mirage.cache.index import RAMIndexCacheStore
from mirage.types import FileStat, FileType, PathSpec
from mirage.utils.key_prefix import mount_key
@pytest.fixture
def dify_accessor() -> SimpleNamespace:
return SimpleNamespace(config=SimpleNamespace(dataset_id="dataset-1",
slug_metadata_name="slug"))
@pytest.fixture
def dify_index() -> RAMIndexCacheStore:
return RAMIndexCacheStore()
@pytest.fixture
def guide_path() -> PathSpec:
return PathSpec.from_str_path(
"/knowledge/guides/quickstart",
mount_key("/knowledge/guides/quickstart", "/knowledge"))
def sample_stat() -> FileStat:
return FileStat(name="quickstart", type=FileType.TEXT, size=12)
+23
View File
@@ -0,0 +1,23 @@
import importlib
import pytest
from mirage.ops.dify.grep import grep
op_grep = importlib.import_module("mirage.ops.dify.grep")
async def grep_bytes(accessor, paths, pattern, index):
return b"match", {paths[0].virtual: b"content"}
@pytest.mark.asyncio
async def test_grep_op_delegates_to_core(monkeypatch, dify_accessor,
dify_index, guide_path):
monkeypatch.setattr(op_grep, "grep_bytes", grep_bytes)
result = await grep(dify_accessor, [guide_path],
"pattern",
index=dify_index)
assert result == b"match"
+21
View File
@@ -0,0 +1,21 @@
import importlib
import pytest
from mirage.ops.dify.read import read
op_read = importlib.import_module("mirage.ops.dify.read")
async def read_bytes(accessor, path, index):
return path.virtual.encode()
@pytest.mark.asyncio
async def test_read_op_delegates_to_core(monkeypatch, dify_accessor,
dify_index, guide_path):
monkeypatch.setattr(op_read, "read_bytes", read_bytes)
result = await read(dify_accessor, guide_path, index=dify_index)
assert result == guide_path.virtual.encode()
+24
View File
@@ -0,0 +1,24 @@
import importlib
import pytest
from mirage.ops.dify.readdir import readdir
op_readdir = importlib.import_module("mirage.ops.dify.readdir")
async def core_readdir_result(accessor, path, index):
return [path.child("a"), path.child("b")]
@pytest.mark.asyncio
async def test_readdir_op_delegates_to_core(monkeypatch, dify_accessor,
dify_index, guide_path):
monkeypatch.setattr(op_readdir, "core_readdir", core_readdir_result)
result = await readdir(dify_accessor, guide_path, index=dify_index)
assert result == [
"/knowledge/guides/quickstart/a",
"/knowledge/guides/quickstart/b",
]
+37
View File
@@ -0,0 +1,37 @@
from types import SimpleNamespace
import pytest
from mirage.cache.index import RAMIndexCacheStore
from mirage.types import PathSpec
@pytest.mark.asyncio
async def test_search_op_delegates_to_core(monkeypatch):
from mirage.core.dify import search
from mirage.ops.dify.search import search as search_op
calls: list[tuple[str, list[PathSpec], dict]] = []
async def search_segments(accessor, query, paths, index, **kwargs):
calls.append((query, paths, kwargs))
return b"result"
monkeypatch.setattr(search, "search_segments", search_segments)
paths = [
PathSpec(resource_path="knowledge/a",
virtual="/knowledge/a",
directory="/knowledge/a")
]
result = await search_op(SimpleNamespace(),
paths,
"query",
index=RAMIndexCacheStore(),
method="keyword")
assert result == b"result"
assert calls == [("query", paths, {
"method": "keyword",
"mount_prefix": ""
})]
+23
View File
@@ -0,0 +1,23 @@
import importlib
import pytest
from mirage.ops.dify.stat import stat
from .conftest import sample_stat
op_stat = importlib.import_module("mirage.ops.dify.stat")
async def core_stat_result(accessor, path, index):
return sample_stat()
@pytest.mark.asyncio
async def test_stat_op_delegates_to_core(monkeypatch, dify_accessor,
dify_index, guide_path):
monkeypatch.setattr(op_stat, "core_stat", core_stat_result)
result = await stat(dify_accessor, guide_path, index=dify_index)
assert result == sample_stat()
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
+50
View File
@@ -0,0 +1,50 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.discord import DiscordAccessor
from mirage.ops.discord.read import read
from mirage.resource.discord.config import DiscordConfig
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return DiscordAccessor(config=DiscordConfig(token="test"))
@pytest.mark.asyncio
async def test_read_calls_core(accessor):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.discord.read.core_read",
new_callable=AsyncMock,
return_value=b"hello",
) as mock:
result = await fn(accessor,
_scope("/channels/general.txt"),
index=None)
mock.assert_called_once_with(accessor, _scope("/channels/general.txt"),
None)
assert result == b"hello"
+47
View File
@@ -0,0 +1,47 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.discord import DiscordAccessor
from mirage.ops.discord.readdir import readdir
from mirage.resource.discord.config import DiscordConfig
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return DiscordAccessor(config=DiscordConfig(token="test"))
@pytest.mark.asyncio
async def test_readdir_calls_core(accessor):
fn = readdir._registered_ops[0].fn
with patch(
"mirage.ops.discord.readdir.core_readdir",
new_callable=AsyncMock,
return_value=["/channels/general.txt"],
) as mock:
result = await fn(accessor, _scope("/channels"), index=None)
mock.assert_called_once_with(accessor, _scope("/channels"), None)
assert result == ["/channels/general.txt"]
+51
View File
@@ -0,0 +1,51 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.discord import DiscordAccessor
from mirage.ops.discord.stat import stat
from mirage.resource.discord.config import DiscordConfig
from mirage.types import FileStat, PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return DiscordAccessor(config=DiscordConfig(token="test"))
@pytest.mark.asyncio
async def test_stat_calls_core(accessor):
fn = stat._registered_ops[0].fn
fake_stat = FileStat(name="general.txt", size=42)
with patch(
"mirage.ops.discord.stat.core_stat",
new_callable=AsyncMock,
return_value=fake_stat,
) as mock:
result = await fn(accessor,
_scope("/channels/general.txt"),
index=None)
mock.assert_called_once_with(accessor, _scope("/channels/general.txt"),
None)
assert result == fake_stat
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
+67
View File
@@ -0,0 +1,67 @@
# ========= 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.accessor.disk import DiskAccessor
from mirage.cache.index import RAMIndexCacheStore
from mirage.ops.disk.read.read import read
from mirage.ops.disk.readdir import readdir
from mirage.ops.disk.stat import stat
from mirage.ops.disk.write import write
from mirage.types import PathSpec
@pytest.mark.asyncio
async def test_stat_op(tmp_path):
(tmp_path / "f.txt").write_text("data")
accessor = DiskAccessor(tmp_path)
index = RAMIndexCacheStore(ttl=0)
scope = PathSpec(resource_path="f.txt", virtual="/f.txt", directory="/")
result = await stat(accessor, scope, index=index)
assert result.name == "f.txt"
assert result.size == 4
@pytest.mark.asyncio
async def test_readdir_op(tmp_path):
(tmp_path / "a.txt").write_text("a")
accessor = DiskAccessor(tmp_path)
index = RAMIndexCacheStore(ttl=0)
scope = PathSpec(resource_path="",
virtual="/",
directory="/",
resolved=False)
result = await readdir(accessor, scope, index=index)
assert result == ["/a.txt"]
@pytest.mark.asyncio
async def test_read_op(tmp_path):
(tmp_path / "f.txt").write_bytes(b"content")
accessor = DiskAccessor(tmp_path)
index = RAMIndexCacheStore(ttl=0)
scope = PathSpec(resource_path="f.txt", virtual="/f.txt", directory="/")
result = await read(accessor, scope, index=index)
assert result == b"content"
@pytest.mark.asyncio
async def test_write_op(tmp_path):
accessor = DiskAccessor(tmp_path)
scope = PathSpec(resource_path="out.txt",
virtual="/out.txt",
directory="/")
await write(accessor, scope, data=b"written")
assert (tmp_path / "out.txt").read_bytes() == b"written"
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
+70
View File
@@ -0,0 +1,70 @@
# ========= 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 json
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdocs import GDocsAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops import Ops
from mirage.ops.config import OpsMount
from mirage.ops.gdocs import OPS as GDOCS_OPS
from mirage.types import MountMode
def _make_gdocs_ops():
accessor = GDocsAccessor(config=None, token_manager=None)
ops_list = []
for fn in GDOCS_OPS:
if hasattr(fn, "_registered_ops"):
ops_list.extend(fn._registered_ops)
mount = OpsMount(
prefix="/gdocs/",
resource_type="gdocs",
accessor=accessor,
index=RAMIndexCacheStore(),
mode=MountMode.READ,
ops=ops_list,
)
return Ops([mount])
@pytest.mark.asyncio
async def test_readdir_root():
ops = _make_gdocs_ops()
with patch(
"mirage.ops.gdocs.readdir.core_readdir",
new_callable=AsyncMock,
return_value=["/gdocs/owned", "/gdocs/shared"],
):
result = await ops.readdir("/gdocs/")
assert "/gdocs/owned" in result
assert "/gdocs/shared" in result
@pytest.mark.asyncio
async def test_read_doc():
ops = _make_gdocs_ops()
doc_json = json.dumps({"documentId": "doc1", "title": "Report"}).encode()
with patch(
"mirage.ops.gdocs.read.core_read",
new_callable=AsyncMock,
return_value=doc_json,
):
result = await ops.read(
"/gdocs/owned/2026-04-01_Report__doc1.gdoc.json")
parsed = json.loads(result)
assert parsed["documentId"] == "doc1"
+63
View File
@@ -0,0 +1,63 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdocs import GDocsAccessor
from mirage.ops.gdocs.read import read
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "/gdocs") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GDocsAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_read_calls_core(accessor):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.gdocs.read.core_read",
new_callable=AsyncMock,
return_value=b"doc content",
) as mock:
scope = _scope("/gdocs/owned/file.gdoc.json")
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(
accessor, _scope("/gdocs/owned/file.gdoc.json", prefix="/gdocs"),
None)
assert result == b"doc content"
@pytest.mark.asyncio
async def test_read_not_found(accessor):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.gdocs.read.core_read",
new_callable=AsyncMock,
side_effect=FileNotFoundError("not found"),
):
with pytest.raises(FileNotFoundError):
await fn(accessor,
_scope("/gdocs/owned/nonexistent.gdoc.json"),
index=None)
+64
View File
@@ -0,0 +1,64 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdocs import GDocsAccessor
from mirage.ops.gdocs.readdir import readdir
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "/gdocs") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GDocsAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_readdir_calls_core(accessor):
fn = readdir._registered_ops[0].fn
with patch(
"mirage.ops.gdocs.readdir.core_readdir",
new_callable=AsyncMock,
return_value=["/gdocs/owned", "/gdocs/shared"],
) as mock:
scope = _scope("/gdocs")
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(accessor, _scope("/gdocs",
prefix="/gdocs"), None)
assert result == ["/gdocs/owned", "/gdocs/shared"]
@pytest.mark.asyncio
async def test_readdir_owned(accessor):
fn = readdir._registered_ops[0].fn
with patch(
"mirage.ops.gdocs.readdir.core_readdir",
new_callable=AsyncMock,
return_value=["/gdocs/owned/file.gdoc.json"],
) as mock:
scope = _scope("/gdocs/owned")
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(accessor,
_scope("/gdocs/owned", prefix="/gdocs"),
None)
assert "/gdocs/owned/file.gdoc.json" in result
+80
View File
@@ -0,0 +1,80 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdocs import GDocsAccessor
from mirage.ops.gdocs.stat import stat
from mirage.types import FileStat, FileType, PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "/gdocs") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GDocsAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_stat_calls_core(accessor):
fn = stat._registered_ops[0].fn
fake_stat = FileStat(name="/", type=FileType.DIRECTORY)
with patch(
"mirage.ops.gdocs.stat.core_stat",
new_callable=AsyncMock,
return_value=fake_stat,
) as mock:
scope = _scope("/gdocs")
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(accessor, _scope("/gdocs",
prefix="/gdocs"), None)
assert result == fake_stat
@pytest.mark.asyncio
async def test_stat_doc(accessor):
fn = stat._registered_ops[0].fn
fake_stat = FileStat(name="file.gdoc.json", type=FileType.JSON)
with patch(
"mirage.ops.gdocs.stat.core_stat",
new_callable=AsyncMock,
return_value=fake_stat,
) as mock:
scope = _scope("/gdocs/owned/file.gdoc.json")
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(
accessor, _scope("/gdocs/owned/file.gdoc.json", prefix="/gdocs"),
None)
assert result.name == "file.gdoc.json"
@pytest.mark.asyncio
async def test_stat_not_found(accessor):
fn = stat._registered_ops[0].fn
with patch(
"mirage.ops.gdocs.stat.core_stat",
new_callable=AsyncMock,
side_effect=FileNotFoundError("not found"),
):
with pytest.raises(FileNotFoundError):
await fn(accessor,
_scope("/gdocs/owned/nonexistent.gdoc.json"),
index=None)
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
+100
View File
@@ -0,0 +1,100 @@
# ========= 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 json
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdrive import GDriveAccessor
from mirage.cache.index.config import IndexEntry
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops import Ops
from mirage.ops.config import OpsMount
from mirage.ops.gdrive import OPS as GDRIVE_OPS
from mirage.types import MountMode
def _make_gdrive_ops():
accessor = GDriveAccessor(config=None, token_manager=None)
index = RAMIndexCacheStore()
ops_list = []
for fn in GDRIVE_OPS:
if hasattr(fn, "_registered_ops"):
ops_list.extend(fn._registered_ops)
mount = OpsMount(
prefix="/gdrive/",
resource_type="gdrive",
accessor=accessor,
index=index,
mode=MountMode.READ,
ops=ops_list,
)
return Ops([mount]), index
@pytest.mark.asyncio
async def test_read_gdoc_via_filetype_cascade():
ops, index = _make_gdrive_ops()
await index.put(
"/gdrive/docs/report.gdoc.json",
IndexEntry(
id="doc123",
name="Report",
resource_type="gdrive/gdoc",
remote_time="2026-04-01T00:00:00Z",
vfs_name="report.gdoc.json",
))
doc_json = json.dumps({"documentId": "doc123", "title": "Report"}).encode()
with patch(
"mirage.core.gdrive.read.read_doc",
new_callable=AsyncMock,
return_value=doc_json,
):
result = await ops.read("/gdrive/docs/report.gdoc.json")
parsed = json.loads(result)
assert parsed["documentId"] == "doc123"
@pytest.mark.asyncio
async def test_read_plain_file_falls_through():
ops, index = _make_gdrive_ops()
await index.put(
"/gdrive/notes.txt",
IndexEntry(
id="file789",
name="notes",
resource_type="gdrive/file",
remote_time="2026-04-01T00:00:00Z",
vfs_name="notes.txt",
))
with patch(
"mirage.core.gdrive.read.download_file",
new_callable=AsyncMock,
return_value=b"plain content",
):
result = await ops.read("/gdrive/notes.txt")
assert result == b"plain content"
@pytest.mark.asyncio
async def test_readdir():
ops, index = _make_gdrive_ops()
with patch(
"mirage.ops.gdrive.readdir.core_readdir",
new_callable=AsyncMock,
return_value=["/docs/report.gdoc.json"],
):
result = await ops.readdir("/gdrive/docs")
assert "/docs/report.gdoc.json" in result
+53
View File
@@ -0,0 +1,53 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdrive import GDriveAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops.gdrive.read.read import read
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GDriveAccessor(config=None, token_manager=None)
@pytest.fixture
def index():
return RAMIndexCacheStore()
@pytest.mark.asyncio
async def test_read_calls_core(accessor, index):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.gdrive.read.read.core_read",
new_callable=AsyncMock,
return_value=b"file content",
) as mock:
result = await fn(accessor, _scope("/docs/readme.txt"), index=index)
mock.assert_called_once_with(accessor, _scope("/docs/readme.txt"),
index)
assert result == b"file content"
@@ -0,0 +1,57 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdrive import GDriveAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops.gdrive.read.read_feather import read_feather
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GDriveAccessor(config=None, token_manager=None)
@pytest.fixture
def index():
return RAMIndexCacheStore()
@pytest.mark.asyncio
async def test_read_feather_calls_core(accessor, index):
fn = read_feather._registered_ops[0].fn
with patch(
"mirage.ops.gdrive.read.read_feather.core_read",
new_callable=AsyncMock,
return_value=b"raw-feather",
) as mock_read, patch(
"mirage.ops.gdrive.read.read_feather.feather_cat",
return_value=b"csv-output",
) as mock_cat:
result = await fn(accessor, _scope("/data/file.feather"), index=index)
mock_read.assert_called_once_with(accessor,
_scope("/data/file.feather"), index)
mock_cat.assert_called_once_with(b"raw-feather")
assert result == b"csv-output"
+80
View File
@@ -0,0 +1,80 @@
# ========= 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 json
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdrive import GDriveAccessor
from mirage.cache.index.config import IndexEntry
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops.gdrive.read.read import read
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def index():
store = RAMIndexCacheStore()
return store
@pytest.fixture
def accessor():
return GDriveAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_read_gdoc_calls_core(accessor, index):
await index.put(
"/docs/report.gdoc.json",
IndexEntry(
id="doc123",
name="Report",
resource_type="gdrive/gdoc",
remote_time="2026-04-01T00:00:00Z",
vfs_name="report.gdoc.json",
))
fn = read._registered_ops[0].fn
doc_json = json.dumps({"documentId": "doc123"}).encode()
with patch(
"mirage.ops.gdrive.read.read.core_read",
new_callable=AsyncMock,
return_value=doc_json,
) as mock:
result = await fn(accessor,
_scope("/docs/report.gdoc.json"),
index=index)
mock.assert_called_once_with(accessor,
_scope("/docs/report.gdoc.json"), index)
assert json.loads(result)["documentId"] == "doc123"
@pytest.mark.asyncio
async def test_read_gdoc_not_found(accessor, index):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.gdrive.read.read.core_read",
new_callable=AsyncMock,
side_effect=FileNotFoundError("nonexistent.gdoc.json"),
):
with pytest.raises(FileNotFoundError):
await fn(accessor, _scope("/nonexistent.gdoc.json"), index=index)
@@ -0,0 +1,80 @@
# ========= 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 json
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdrive import GDriveAccessor
from mirage.cache.index.config import IndexEntry
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops.gdrive.read.read import read
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def index():
return RAMIndexCacheStore()
@pytest.fixture
def accessor():
return GDriveAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_read_gsheet_calls_core(accessor, index):
await index.put(
"/sheets/budget.gsheet.json",
IndexEntry(
id="sheet123",
name="Budget",
resource_type="gdrive/gsheet",
remote_time="2026-04-01T00:00:00Z",
vfs_name="budget.gsheet.json",
))
fn = read._registered_ops[0].fn
sheet_json = json.dumps({"spreadsheetId": "sheet123"}).encode()
with patch(
"mirage.ops.gdrive.read.read.core_read",
new_callable=AsyncMock,
return_value=sheet_json,
) as mock:
result = await fn(accessor,
_scope("/sheets/budget.gsheet.json"),
index=index)
mock.assert_called_once_with(accessor,
_scope("/sheets/budget.gsheet.json"),
index)
assert json.loads(result)["spreadsheetId"] == "sheet123"
@pytest.mark.asyncio
async def test_read_gsheet_not_found(accessor, index):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.gdrive.read.read.core_read",
new_callable=AsyncMock,
side_effect=FileNotFoundError("nonexistent.gsheet.json"),
):
with pytest.raises(FileNotFoundError):
await fn(accessor, _scope("/nonexistent.gsheet.json"), index=index)
@@ -0,0 +1,79 @@
# ========= 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 json
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdrive import GDriveAccessor
from mirage.cache.index.config import IndexEntry
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops.gdrive.read.read import read
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def index():
return RAMIndexCacheStore()
@pytest.fixture
def accessor():
return GDriveAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_read_gslide_calls_core(accessor, index):
await index.put(
"/slides/deck.gslide.json",
IndexEntry(
id="slide123",
name="Deck",
resource_type="gdrive/gslide",
remote_time="2026-04-01T00:00:00Z",
vfs_name="deck.gslide.json",
))
fn = read._registered_ops[0].fn
pres_json = json.dumps({"presentationId": "slide123"}).encode()
with patch(
"mirage.ops.gdrive.read.read.core_read",
new_callable=AsyncMock,
return_value=pres_json,
) as mock:
result = await fn(accessor,
_scope("/slides/deck.gslide.json"),
index=index)
mock.assert_called_once_with(accessor,
_scope("/slides/deck.gslide.json"), index)
assert json.loads(result)["presentationId"] == "slide123"
@pytest.mark.asyncio
async def test_read_gslide_not_found(accessor, index):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.gdrive.read.read.core_read",
new_callable=AsyncMock,
side_effect=FileNotFoundError("nonexistent.gslide.json"),
):
with pytest.raises(FileNotFoundError):
await fn(accessor, _scope("/nonexistent.gslide.json"), index=index)
+57
View File
@@ -0,0 +1,57 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdrive import GDriveAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops.gdrive.read.read_hdf5 import read_hdf5
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GDriveAccessor(config=None, token_manager=None)
@pytest.fixture
def index():
return RAMIndexCacheStore()
@pytest.mark.asyncio
async def test_read_hdf5_calls_core(accessor, index):
fn = read_hdf5._registered_ops[0].fn
with patch(
"mirage.ops.gdrive.read.read_hdf5.core_read",
new_callable=AsyncMock,
return_value=b"raw-hdf5",
) as mock_read, patch(
"mirage.ops.gdrive.read.read_hdf5.hdf5_cat",
return_value=b"csv-output",
) as mock_cat:
result = await fn(accessor, _scope("/data/file.hdf5"), index=index)
mock_read.assert_called_once_with(accessor, _scope("/data/file.hdf5"),
index)
mock_cat.assert_called_once_with(b"raw-hdf5")
assert result == b"csv-output"
+57
View File
@@ -0,0 +1,57 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdrive import GDriveAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops.gdrive.read.read_orc import read_orc
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GDriveAccessor(config=None, token_manager=None)
@pytest.fixture
def index():
return RAMIndexCacheStore()
@pytest.mark.asyncio
async def test_read_orc_calls_core(accessor, index):
fn = read_orc._registered_ops[0].fn
with patch(
"mirage.ops.gdrive.read.read_orc.core_read",
new_callable=AsyncMock,
return_value=b"raw-orc",
) as mock_read, patch(
"mirage.ops.gdrive.read.read_orc.orc_cat",
return_value=b"csv-output",
) as mock_cat:
result = await fn(accessor, _scope("/data/file.orc"), index=index)
mock_read.assert_called_once_with(accessor, _scope("/data/file.orc"),
index)
mock_cat.assert_called_once_with(b"raw-orc")
assert result == b"csv-output"
@@ -0,0 +1,57 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdrive import GDriveAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops.gdrive.read.read_parquet import read_parquet
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GDriveAccessor(config=None, token_manager=None)
@pytest.fixture
def index():
return RAMIndexCacheStore()
@pytest.mark.asyncio
async def test_read_parquet_calls_core(accessor, index):
fn = read_parquet._registered_ops[0].fn
with patch(
"mirage.ops.gdrive.read.read_parquet.core_read",
new_callable=AsyncMock,
return_value=b"raw-parquet",
) as mock_read, patch(
"mirage.ops.gdrive.read.read_parquet.parquet_cat",
return_value=b"csv-output",
) as mock_cat:
result = await fn(accessor, _scope("/data/file.parquet"), index=index)
mock_read.assert_called_once_with(accessor,
_scope("/data/file.parquet"), index)
mock_cat.assert_called_once_with(b"raw-parquet")
assert result == b"csv-output"
+52
View File
@@ -0,0 +1,52 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdrive import GDriveAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops.gdrive.readdir import readdir
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GDriveAccessor(config=None, token_manager=None)
@pytest.fixture
def index():
return RAMIndexCacheStore()
@pytest.mark.asyncio
async def test_readdir_calls_core(accessor, index):
fn = readdir._registered_ops[0].fn
with patch(
"mirage.ops.gdrive.readdir.core_readdir",
new_callable=AsyncMock,
return_value=["/docs/readme.txt"],
) as mock:
result = await fn(accessor, _scope("/docs"), index=index)
mock.assert_called_once_with(accessor, _scope("/docs"), index)
assert result == ["/docs/readme.txt"]
+54
View File
@@ -0,0 +1,54 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gdrive import GDriveAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops.gdrive.stat import stat
from mirage.types import FileStat, PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GDriveAccessor(config=None, token_manager=None)
@pytest.fixture
def index():
return RAMIndexCacheStore()
@pytest.mark.asyncio
async def test_stat_calls_core(accessor, index):
fn = stat._registered_ops[0].fn
fake_stat = FileStat(name="readme.txt", size=50)
with patch(
"mirage.ops.gdrive.stat.core_stat",
new_callable=AsyncMock,
return_value=fake_stat,
) as mock:
result = await fn(accessor, _scope("/docs/readme.txt"), index=index)
mock.assert_called_once_with(accessor, _scope("/docs/readme.txt"),
index)
assert result == fake_stat
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
+42
View File
@@ -0,0 +1,42 @@
# ========= 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. =========
from mirage.ops.github import OPS
def test_ops_contains_read():
fns = {fn.__name__ for fn in OPS}
assert "read" in fns
def test_ops_contains_stat():
fns = {fn.__name__ for fn in OPS}
assert "stat" in fns
def test_ops_contains_readdir():
fns = {fn.__name__ for fn in OPS}
assert "readdir" in fns
def test_ops_has_three_entries():
assert len(OPS) == 3
def test_no_write_ops():
write_ops = {
"write", "mkdir", "unlink", "rmdir", "rename", "create", "truncate"
}
fns = {fn.__name__ for fn in OPS}
assert not write_ops.intersection(fns)
+83
View File
@@ -0,0 +1,83 @@
# ========= 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. =========
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from mirage.accessor.github import GitHubAccessor
from mirage.cache.index import IndexEntry, LookupResult, LookupStatus
from mirage.ops.github.read import read
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GitHubAccessor(
config=MagicMock(),
owner="org",
repo="repo",
ref="main",
default_branch="main",
)
@pytest.fixture
def index():
mock = AsyncMock()
mock.get.return_value = LookupResult(
status=LookupStatus.EXPIRED,
entry=IndexEntry(id="abc123",
name="file.py",
resource_type="file",
size=100),
)
return mock
@pytest.mark.asyncio
async def test_read_calls_core(accessor, index):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.github.read.core_read",
new_callable=AsyncMock,
return_value=b"print('hello')",
) as mock:
scope = _scope("/github/src/file.py", prefix="/github")
result = await fn(accessor, scope, index=index)
mock.assert_called_once_with(
accessor, _scope("/github/src/file.py", prefix="/github"), index)
assert result == b"print('hello')"
@pytest.mark.asyncio
async def test_read_not_found(accessor):
fn = read._registered_ops[0].fn
index = AsyncMock()
index.get.return_value = LookupResult(status=LookupStatus.NOT_FOUND,
entry=None)
with patch(
"mirage.ops.github.read.core_read",
new_callable=AsyncMock,
side_effect=FileNotFoundError("/missing.py"),
):
with pytest.raises(FileNotFoundError):
await fn(accessor, _scope("/missing.py"), index=index)
+60
View File
@@ -0,0 +1,60 @@
# ========= 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. =========
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from mirage.accessor.github import GitHubAccessor
from mirage.ops.github.readdir import readdir
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GitHubAccessor(
config=MagicMock(),
owner="org",
repo="repo",
ref="main",
default_branch="main",
)
@pytest.fixture
def index():
return AsyncMock()
@pytest.mark.asyncio
async def test_readdir_calls_core(accessor, index):
fn = readdir._registered_ops[0].fn
with patch(
"mirage.ops.github.readdir.core_readdir",
new_callable=AsyncMock,
return_value=["/github/src/main.py", "/github/src/utils.py"],
) as mock:
scope = _scope("/github/src", prefix="/github")
result = await fn(accessor, scope, index=index)
mock.assert_called_once_with(accessor,
_scope("/github/src", prefix="/github"),
index)
assert result == ["/github/src/main.py", "/github/src/utils.py"]
+60
View File
@@ -0,0 +1,60 @@
# ========= 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. =========
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from mirage.accessor.github import GitHubAccessor
from mirage.ops.github.stat import stat
from mirage.types import FileStat, PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GitHubAccessor(
config=MagicMock(),
owner="org",
repo="repo",
ref="main",
default_branch="main",
)
@pytest.fixture
def index():
return AsyncMock()
@pytest.mark.asyncio
async def test_stat_calls_core(accessor, index):
fn = stat._registered_ops[0].fn
fake_stat = FileStat(name="file.py", size=100)
with patch(
"mirage.ops.github.stat.core_stat",
new_callable=AsyncMock,
return_value=fake_stat,
) as mock:
scope = _scope("/github/src/file.py", prefix="/github")
result = await fn(accessor, scope, index=index)
mock.assert_called_once_with(
accessor, _scope("/github/src/file.py", prefix="/github"), index)
assert result == fake_stat
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
+45
View File
@@ -0,0 +1,45 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gmail import GmailAccessor
from mirage.ops.gmail.read import read
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
@pytest.fixture
def accessor():
return GmailAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_read_calls_core(accessor):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.gmail.read.core_read",
new_callable=AsyncMock,
return_value=b"email body",
) as mock:
scope = PathSpec(
resource_path=mount_key("/gmail/inbox/msg.txt", "/gmail"),
virtual="/gmail/inbox/msg.txt",
directory="/gmail/inbox",
)
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(accessor, scope, None)
assert result == b"email body"
+45
View File
@@ -0,0 +1,45 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gmail import GmailAccessor
from mirage.ops.gmail.readdir import readdir
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
@pytest.fixture
def accessor():
return GmailAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_readdir_calls_core(accessor):
fn = readdir._registered_ops[0].fn
with patch(
"mirage.ops.gmail.readdir.core_readdir",
new_callable=AsyncMock,
return_value=["/gmail/inbox/msg.txt"],
) as mock:
scope = PathSpec(
resource_path=mount_key("/gmail/inbox", "/gmail"),
virtual="/gmail/inbox",
directory="/gmail",
)
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(accessor, scope, None)
assert result == ["/gmail/inbox/msg.txt"]
+46
View File
@@ -0,0 +1,46 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gmail import GmailAccessor
from mirage.ops.gmail.stat import stat
from mirage.types import FileStat, PathSpec
from mirage.utils.key_prefix import mount_key
@pytest.fixture
def accessor():
return GmailAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_stat_calls_core(accessor):
fn = stat._registered_ops[0].fn
fake_stat = FileStat(name="msg.txt", size=100)
with patch(
"mirage.ops.gmail.stat.core_stat",
new_callable=AsyncMock,
return_value=fake_stat,
) as mock:
scope = PathSpec(
resource_path=mount_key("/gmail/inbox/msg.txt", "/gmail"),
virtual="/gmail/inbox/msg.txt",
directory="/gmail/inbox",
)
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(accessor, scope, None)
assert result == fake_stat
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
@@ -0,0 +1,68 @@
# ========= 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 json
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gsheets import GSheetsAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops import Ops
from mirage.ops.config import OpsMount
from mirage.ops.gsheets import OPS as GSHEETS_OPS
from mirage.types import MountMode
def _make_gsheets_ops():
accessor = GSheetsAccessor(config=None, token_manager=None)
ops_list = []
for fn in GSHEETS_OPS:
if hasattr(fn, "_registered_ops"):
ops_list.extend(fn._registered_ops)
mount = OpsMount(
prefix="/gsheets/",
resource_type="gsheets",
accessor=accessor,
index=RAMIndexCacheStore(),
mode=MountMode.READ,
ops=ops_list,
)
return Ops([mount])
@pytest.mark.asyncio
async def test_readdir():
ops = _make_gsheets_ops()
with patch(
"mirage.ops.gsheets.readdir.core_readdir",
new_callable=AsyncMock,
return_value=["/gsheets/owned/budget.gsheet.json"],
):
result = await ops.readdir("/gsheets/owned")
assert "/gsheets/owned/budget.gsheet.json" in result
@pytest.mark.asyncio
async def test_read_spreadsheet():
ops = _make_gsheets_ops()
sheet_json = json.dumps({"spreadsheetId": "sheet1"}).encode()
with patch(
"mirage.ops.gsheets.read.core_read",
new_callable=AsyncMock,
return_value=sheet_json,
):
result = await ops.read("/gsheets/owned/budget.gsheet.json")
parsed = json.loads(result)
assert parsed["spreadsheetId"] == "sheet1"
+64
View File
@@ -0,0 +1,64 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gsheets import GSheetsAccessor
from mirage.ops.gsheets.read import read
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "/gsheets") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GSheetsAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_read_calls_core(accessor):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.gsheets.read.core_read",
new_callable=AsyncMock,
return_value=b'{"spreadsheetId": "sheet1"}',
) as mock:
scope = _scope("/gsheets/owned/budget.gsheet.json")
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(
accessor,
_scope("/gsheets/owned/budget.gsheet.json", prefix="/gsheets"),
None)
assert b"sheet1" in result
@pytest.mark.asyncio
async def test_read_not_found(accessor):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.gsheets.read.core_read",
new_callable=AsyncMock,
side_effect=FileNotFoundError("not found"),
):
with pytest.raises(FileNotFoundError):
await fn(accessor,
_scope("/gsheets/owned/nonexistent.gsheet.json"),
index=None)
+48
View File
@@ -0,0 +1,48 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gsheets import GSheetsAccessor
from mirage.ops.gsheets.readdir import readdir
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "/gsheets") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GSheetsAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_readdir_calls_core(accessor):
fn = readdir._registered_ops[0].fn
with patch(
"mirage.ops.gsheets.readdir.core_readdir",
new_callable=AsyncMock,
return_value=["/gsheets/owned/budget.gsheet.json"],
) as mock:
scope = _scope("/gsheets/owned")
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(
accessor, _scope("/gsheets/owned", prefix="/gsheets"), None)
assert result == ["/gsheets/owned/budget.gsheet.json"]
+51
View File
@@ -0,0 +1,51 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gsheets import GSheetsAccessor
from mirage.ops.gsheets.stat import stat
from mirage.types import FileStat, PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "/gsheets") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GSheetsAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_stat_calls_core(accessor):
fn = stat._registered_ops[0].fn
fake_stat = FileStat(name="budget.gsheet.json", size=200)
with patch(
"mirage.ops.gsheets.stat.core_stat",
new_callable=AsyncMock,
return_value=fake_stat,
) as mock:
scope = _scope("/gsheets/owned/budget.gsheet.json")
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(
accessor,
_scope("/gsheets/owned/budget.gsheet.json", prefix="/gsheets"),
None)
assert result == fake_stat
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
@@ -0,0 +1,68 @@
# ========= 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 json
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gslides import GSlidesAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops import Ops
from mirage.ops.config import OpsMount
from mirage.ops.gslides import OPS as GSLIDES_OPS
from mirage.types import MountMode
def _make_gslides_ops():
accessor = GSlidesAccessor(config=None, token_manager=None)
ops_list = []
for fn in GSLIDES_OPS:
if hasattr(fn, "_registered_ops"):
ops_list.extend(fn._registered_ops)
mount = OpsMount(
prefix="/gslides/",
resource_type="gslides",
accessor=accessor,
index=RAMIndexCacheStore(),
mode=MountMode.READ,
ops=ops_list,
)
return Ops([mount])
@pytest.mark.asyncio
async def test_readdir():
ops = _make_gslides_ops()
with patch(
"mirage.ops.gslides.readdir.core_readdir",
new_callable=AsyncMock,
return_value=["/gslides/owned/deck.gslide.json"],
):
result = await ops.readdir("/gslides/owned")
assert "/gslides/owned/deck.gslide.json" in result
@pytest.mark.asyncio
async def test_read_presentation():
ops = _make_gslides_ops()
pres_json = json.dumps({"presentationId": "slide1"}).encode()
with patch(
"mirage.ops.gslides.read.core_read",
new_callable=AsyncMock,
return_value=pres_json,
):
result = await ops.read("/gslides/owned/deck.gslide.json")
parsed = json.loads(result)
assert parsed["presentationId"] == "slide1"
+63
View File
@@ -0,0 +1,63 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gslides import GSlidesAccessor
from mirage.ops.gslides.read import read
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "/gslides") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GSlidesAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_read_calls_core(accessor):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.gslides.read.core_read",
new_callable=AsyncMock,
return_value=b'{"presentationId": "slide1"}',
) as mock:
scope = _scope("/gslides/owned/deck.gslide.json")
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(
accessor,
_scope("/gslides/owned/deck.gslide.json", prefix="/gslides"), None)
assert b"slide1" in result
@pytest.mark.asyncio
async def test_read_not_found(accessor):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.gslides.read.core_read",
new_callable=AsyncMock,
side_effect=FileNotFoundError("not found"),
):
with pytest.raises(FileNotFoundError):
await fn(accessor,
_scope("/gslides/owned/nonexistent.gslide.json"),
index=None)
+48
View File
@@ -0,0 +1,48 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gslides import GSlidesAccessor
from mirage.ops.gslides.readdir import readdir
from mirage.types import PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "/gslides") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GSlidesAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_readdir_calls_core(accessor):
fn = readdir._registered_ops[0].fn
with patch(
"mirage.ops.gslides.readdir.core_readdir",
new_callable=AsyncMock,
return_value=["/gslides/owned/deck.gslide.json"],
) as mock:
scope = _scope("/gslides/owned")
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(
accessor, _scope("/gslides/owned", prefix="/gslides"), None)
assert result == ["/gslides/owned/deck.gslide.json"]
+50
View File
@@ -0,0 +1,50 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.gslides import GSlidesAccessor
from mirage.ops.gslides.stat import stat
from mirage.types import FileStat, PathSpec
from mirage.utils.key_prefix import mount_key
def _scope(path: str, prefix: str = "/gslides") -> PathSpec:
return PathSpec(resource_path=mount_key(path, prefix),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return GSlidesAccessor(config=None, token_manager=None)
@pytest.mark.asyncio
async def test_stat_calls_core(accessor):
fn = stat._registered_ops[0].fn
fake_stat = FileStat(name="deck.gslide.json", size=300)
with patch(
"mirage.ops.gslides.stat.core_stat",
new_callable=AsyncMock,
return_value=fake_stat,
) as mock:
scope = _scope("/gslides/owned/deck.gslide.json")
result = await fn(accessor, scope, index=None)
mock.assert_called_once_with(
accessor,
_scope("/gslides/owned/deck.gslide.json", prefix="/gslides"), None)
assert result == fake_stat
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
@@ -0,0 +1,50 @@
# ========= 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. =========
from mirage.ops.postgres import OPS
from mirage.ops.postgres.read import read
from mirage.ops.postgres.readdir import readdir
from mirage.ops.postgres.stat import stat
def test_ops_list_exports_three():
assert len(OPS) == 3
assert read in OPS
assert readdir in OPS
assert stat in OPS
def test_ops_have_registered_metadata():
for fn in (read, readdir, stat):
assert hasattr(fn, "_registered_ops")
registered = fn._registered_ops
assert len(registered) >= 1
def test_read_op_is_for_postgres_resource():
registered = read._registered_ops[0]
assert registered.resource == "postgres"
assert registered.name == "read"
def test_readdir_op_is_for_postgres_resource():
registered = readdir._registered_ops[0]
assert registered.resource == "postgres"
assert registered.name == "readdir"
def test_stat_op_is_for_postgres_resource():
registered = stat._registered_ops[0]
assert registered.resource == "postgres"
assert registered.name == "stat"
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
+158
View File
@@ -0,0 +1,158 @@
# ========= 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.accessor.ram import RAMAccessor
from mirage.cache.index import RAMIndexCacheStore
from mirage.core.ram.mkdir import mkdir
from mirage.ops.ram.append import append_bytes
from mirage.ops.ram.create import create
from mirage.ops.ram.mkdir import mkdir as mkdir_op
from mirage.ops.ram.read.read import read
from mirage.ops.ram.readdir import readdir
from mirage.ops.ram.rename import rename
from mirage.ops.ram.rmdir import rmdir
from mirage.ops.ram.stat import stat
from mirage.ops.ram.truncate import truncate
from mirage.ops.ram.unlink import unlink
from mirage.ops.ram.write import write
from mirage.resource.ram.store import RAMStore
from mirage.types import FileType, PathSpec
def _scope(path: str) -> PathSpec:
return PathSpec(resource_path=(path).strip("/"),
virtual=path,
directory=path,
resolved=True)
@pytest.fixture
def store():
s = RAMStore()
s.files["/hello.txt"] = b"hello"
s.dirs.add("/sub")
s.files["/sub/child.txt"] = b"child"
return s
@pytest.fixture
def accessor(store):
return RAMAccessor(store)
@pytest.fixture
def index():
return RAMIndexCacheStore(ttl=600)
@pytest.mark.asyncio
async def test_op_read(accessor):
result = await read(accessor, _scope("/hello.txt"), index=None)
assert result == b"hello"
@pytest.mark.asyncio
async def test_op_read_not_found(accessor):
with pytest.raises(FileNotFoundError):
await read(accessor, _scope("/nope.txt"), index=None)
@pytest.mark.asyncio
async def test_op_write(accessor, store):
await write(accessor, _scope("/new.txt"), data=b"new data")
assert store.files["/new.txt"] == b"new data"
@pytest.mark.asyncio
async def test_op_write_and_read(accessor):
await write(accessor, _scope("/w.txt"), data=b"written")
result = await read(accessor, _scope("/w.txt"), index=None)
assert result == b"written"
@pytest.mark.asyncio
async def test_op_stat_file(accessor):
result = await stat(accessor, _scope("/hello.txt"), index=None)
assert result.name == "hello.txt"
assert result.size == 5
assert result.type == FileType.TEXT
@pytest.mark.asyncio
async def test_op_stat_directory(accessor):
result = await stat(accessor, _scope("/sub"), index=None)
assert result.type == FileType.DIRECTORY
@pytest.mark.asyncio
async def test_op_readdir(accessor, index):
result = await readdir(accessor, _scope("/"), index=index)
assert "/hello.txt" in result
assert "/sub" in result
@pytest.mark.asyncio
async def test_op_create(accessor, store):
await create(accessor, _scope("/created.txt"))
assert store.files["/created.txt"] == b""
@pytest.mark.asyncio
async def test_op_mkdir(accessor, store):
await mkdir_op(accessor, _scope("/newdir"))
assert "/newdir" in store.dirs
@pytest.mark.asyncio
async def test_op_unlink(accessor, store):
await unlink(accessor, _scope("/hello.txt"))
assert "/hello.txt" not in store.files
@pytest.mark.asyncio
async def test_op_unlink_not_found(accessor):
with pytest.raises(FileNotFoundError):
await unlink(accessor, _scope("/missing.txt"))
@pytest.mark.asyncio
async def test_op_rmdir(accessor, store):
await mkdir(
accessor,
PathSpec(resource_path="empty", virtual="/empty", directory="/empty"))
await rmdir(accessor, _scope("/empty"))
assert "/empty" not in store.dirs
@pytest.mark.asyncio
async def test_op_rename(accessor, store):
src = _scope("/hello.txt")
dst = _scope("/renamed.txt")
await rename(accessor, src, dst)
assert "/renamed.txt" in store.files
assert "/hello.txt" not in store.files
@pytest.mark.asyncio
async def test_op_append(accessor, store):
await append_bytes(accessor, _scope("/hello.txt"), data=b" world")
assert store.files["/hello.txt"] == b"hello world"
@pytest.mark.asyncio
async def test_op_truncate(accessor, store):
await truncate(accessor, _scope("/hello.txt"), length=3)
assert store.files["/hello.txt"] == b"hel"
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
+164
View File
@@ -0,0 +1,164 @@
# ========= 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 os
import pytest
import pytest_asyncio
from mirage.accessor.redis import RedisAccessor
from mirage.cache.index import RAMIndexCacheStore
from mirage.core.redis.mkdir import mkdir
from mirage.ops.redis.append import append_bytes
from mirage.ops.redis.create import create
from mirage.ops.redis.mkdir import mkdir as mkdir_op
from mirage.ops.redis.read.read import read
from mirage.ops.redis.readdir import readdir
from mirage.ops.redis.rename import rename
from mirage.ops.redis.rmdir import rmdir
from mirage.ops.redis.stat import stat
from mirage.ops.redis.truncate import truncate
from mirage.ops.redis.unlink import unlink
from mirage.ops.redis.write import write
from mirage.resource.redis.store import RedisStore
from mirage.types import FileType, PathSpec
REDIS_URL = os.environ.get("REDIS_URL", "")
pytestmark = pytest.mark.skipif(not REDIS_URL, reason="REDIS_URL not set")
def _scope(path: str) -> PathSpec:
return PathSpec(resource_path=(path).strip("/"),
virtual=path,
directory=path,
resolved=True)
@pytest_asyncio.fixture()
async def accessor():
s = RedisStore(url=REDIS_URL, key_prefix="test:ops:")
await s.clear()
await s.add_dir("/")
await s.add_dir("/sub")
await s.set_file("/hello.txt", b"hello")
await s.set_file("/sub/child.txt", b"child")
a = RedisAccessor(s)
yield a
await s.clear()
await s.close()
@pytest.fixture
def index():
return RAMIndexCacheStore(ttl=600)
@pytest.mark.asyncio
async def test_op_read(accessor, index):
result = await read(accessor, _scope("/hello.txt"), index=index)
assert result == b"hello"
@pytest.mark.asyncio
async def test_op_read_not_found(accessor, index):
with pytest.raises(FileNotFoundError):
await read(accessor, _scope("/nope.txt"), index=index)
@pytest.mark.asyncio
async def test_op_write(accessor):
await write(accessor, _scope("/new.txt"), data=b"new data")
assert await accessor.store.get_file("/new.txt") == b"new data"
@pytest.mark.asyncio
async def test_op_write_and_read(accessor, index):
await write(accessor, _scope("/w.txt"), data=b"written")
result = await read(accessor, _scope("/w.txt"), index=index)
assert result == b"written"
@pytest.mark.asyncio
async def test_op_stat_file(accessor, index):
result = await stat(accessor, _scope("/hello.txt"), index=index)
assert result.name == "hello.txt"
assert result.size == 5
assert result.type == FileType.TEXT
@pytest.mark.asyncio
async def test_op_stat_directory(accessor, index):
result = await stat(accessor, _scope("/sub"), index=index)
assert result.type == FileType.DIRECTORY
@pytest.mark.asyncio
async def test_op_readdir(accessor, index):
result = await readdir(accessor, _scope("/"), index=index)
assert "/hello.txt" in result
assert "/sub" in result
@pytest.mark.asyncio
async def test_op_create(accessor):
await create(accessor, _scope("/created.txt"))
assert await accessor.store.get_file("/created.txt") == b""
@pytest.mark.asyncio
async def test_op_mkdir(accessor):
await mkdir_op(accessor, _scope("/newdir"))
assert await accessor.store.has_dir("/newdir")
@pytest.mark.asyncio
async def test_op_unlink(accessor):
await unlink(accessor, _scope("/hello.txt"))
assert not await accessor.store.has_file("/hello.txt")
@pytest.mark.asyncio
async def test_op_unlink_not_found(accessor):
with pytest.raises(FileNotFoundError):
await unlink(accessor, _scope("/missing.txt"))
@pytest.mark.asyncio
async def test_op_rmdir(accessor):
await mkdir(
accessor,
PathSpec(resource_path="empty", virtual="/empty", directory="/empty"))
await rmdir(accessor, _scope("/empty"))
assert not await accessor.store.has_dir("/empty")
@pytest.mark.asyncio
async def test_op_rename(accessor):
src = _scope("/hello.txt")
dst = _scope("/renamed.txt")
await rename(accessor, src, dst)
assert await accessor.store.has_file("/renamed.txt")
assert not await accessor.store.has_file("/hello.txt")
@pytest.mark.asyncio
async def test_op_append(accessor):
await append_bytes(accessor, _scope("/hello.txt"), data=b" world")
assert await accessor.store.get_file("/hello.txt") == b"hello world"
@pytest.mark.asyncio
async def test_op_truncate(accessor):
await truncate(accessor, _scope("/hello.txt"), length=3)
assert await accessor.store.get_file("/hello.txt") == b"hel"
+13
View File
@@ -0,0 +1,13 @@
# ========= 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. =========
+48
View File
@@ -0,0 +1,48 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.slack import SlackAccessor
from mirage.ops.slack.read import read
from mirage.types import PathSpec
def _scope(path: str) -> PathSpec:
return PathSpec(resource_path=(path).strip("/"),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return SlackAccessor(config=object())
@pytest.mark.asyncio
async def test_read_calls_core(accessor):
fn = read._registered_ops[0].fn
with patch(
"mirage.ops.slack.read.core_read",
new_callable=AsyncMock,
return_value=b"hello",
) as mock:
result = await fn(accessor,
_scope("/channels/general.txt"),
index=None)
mock.assert_called_once_with(accessor, _scope("/channels/general.txt"),
None)
assert result == b"hello"
+45
View File
@@ -0,0 +1,45 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.slack import SlackAccessor
from mirage.ops.slack.readdir import readdir
from mirage.types import PathSpec
def _scope(path: str) -> PathSpec:
return PathSpec(resource_path=(path).strip("/"),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return SlackAccessor(config=object())
@pytest.mark.asyncio
async def test_readdir_calls_core(accessor):
fn = readdir._registered_ops[0].fn
with patch(
"mirage.ops.slack.readdir.core_readdir",
new_callable=AsyncMock,
return_value=["/channels/general.txt"],
) as mock:
result = await fn(accessor, _scope("/channels"), index=None)
mock.assert_called_once_with(accessor, _scope("/channels"), None)
assert result == ["/channels/general.txt"]
+49
View File
@@ -0,0 +1,49 @@
# ========= 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. =========
from unittest.mock import AsyncMock, patch
import pytest
from mirage.accessor.slack import SlackAccessor
from mirage.ops.slack.stat import stat
from mirage.types import FileStat, PathSpec
def _scope(path: str) -> PathSpec:
return PathSpec(resource_path=(path).strip("/"),
virtual=path,
directory=path.rsplit("/", 1)[0] or "/")
@pytest.fixture
def accessor():
return SlackAccessor(config=object())
@pytest.mark.asyncio
async def test_stat_calls_core(accessor):
fn = stat._registered_ops[0].fn
fake_stat = FileStat(name="general.txt", size=42)
with patch(
"mirage.ops.slack.stat.core_stat",
new_callable=AsyncMock,
return_value=fake_stat,
) as mock:
result = await fn(accessor,
_scope("/channels/general.txt"),
index=None)
mock.assert_called_once_with(accessor, _scope("/channels/general.txt"),
None)
assert result == fake_stat
+108
View File
@@ -0,0 +1,108 @@
# ========= 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
from mirage.ops.file import MirageFile
from .conftest import make_ops_with_dir
def _write(ops, path, data):
asyncio.run(ops.write(path, data))
def _read(ops, path):
return asyncio.run(ops.read(path))
class TestMirageFile:
def test_read_text(self):
ops, _ = make_ops_with_dir()
_write(ops, "/data/dir/f.txt", b"hello")
f = MirageFile(ops, "/data/dir/f.txt", "r")
assert f.read() == "hello"
f.close()
def test_read_binary(self):
ops, _ = make_ops_with_dir()
_write(ops, "/data/dir/f.bin", b"\x00\x01\x02")
f = MirageFile(ops, "/data/dir/f.bin", "rb")
assert f.read() == b"\x00\x01\x02"
f.close()
def test_write_text(self):
ops, _ = make_ops_with_dir()
f = MirageFile(ops, "/data/dir/out.txt", "w")
f.write("written")
f.close()
assert _read(ops, "/data/dir/out.txt") == b"written"
def test_write_binary(self):
ops, _ = make_ops_with_dir()
f = MirageFile(ops, "/data/dir/out.bin", "wb")
f.write(b"\xff\xfe")
f.close()
assert _read(ops, "/data/dir/out.bin") == b"\xff\xfe"
def test_context_manager(self):
ops, _ = make_ops_with_dir()
with MirageFile(ops, "/data/dir/ctx.txt", "w") as f:
f.write("ctx")
assert _read(ops, "/data/dir/ctx.txt") == b"ctx"
def test_append(self):
ops, _ = make_ops_with_dir()
_write(ops, "/data/dir/app.txt", b"hello")
with MirageFile(ops, "/data/dir/app.txt", "a") as f:
f.write(" world")
assert _read(ops, "/data/dir/app.txt") == b"hello world"
def test_readline(self):
ops, _ = make_ops_with_dir()
_write(ops, "/data/dir/lines.txt", b"line1\nline2\nline3")
f = MirageFile(ops, "/data/dir/lines.txt", "r")
assert f.readline() == "line1\n"
assert f.readline() == "line2\n"
f.close()
def test_iter(self):
ops, _ = make_ops_with_dir()
_write(ops, "/data/dir/iter.txt", b"a\nb\nc")
f = MirageFile(ops, "/data/dir/iter.txt", "r")
lines = list(f)
assert lines == ["a\n", "b\n", "c"]
f.close()
def test_seek_tell(self):
ops, _ = make_ops_with_dir()
_write(ops, "/data/dir/seek.txt", b"abcdef")
f = MirageFile(ops, "/data/dir/seek.txt", "rb")
f.seek(3)
assert f.tell() == 3
assert f.read() == b"def"
f.close()
def test_properties(self):
ops, _ = make_ops_with_dir()
_write(ops, "/data/dir/p.txt", b"data")
f = MirageFile(ops, "/data/dir/p.txt", "r")
assert f.name == "/data/dir/p.txt"
assert f.mode == "r"
assert f.readable() is True
assert f.writable() is False
assert f.closed is False
f.close()
assert f.closed is True
+52
View File
@@ -0,0 +1,52 @@
# ========= 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
from mirage.ops.open import make_open
from .conftest import make_ops_with_dir
def _write(ops, path, data):
asyncio.run(ops.write(path, data))
def _read(ops, path):
return asyncio.run(ops.read(path))
class TestPatchedOpen:
def test_read_mounted(self):
ops, _ = make_ops_with_dir()
_write(ops, "/data/dir/f.txt", b"patched")
patched = make_open(ops)
with patched("/data/dir/f.txt", "r") as f:
assert f.read() == "patched"
def test_write_mounted(self):
ops, _ = make_ops_with_dir()
patched = make_open(ops)
with patched("/data/dir/new.txt", "w") as f:
f.write("via open")
assert _read(ops, "/data/dir/new.txt") == b"via open"
def test_fallthrough_real_file(self, tmp_path):
ops, _ = make_ops_with_dir()
patched = make_open(ops)
real_file = tmp_path / "real.txt"
real_file.write_text("real content")
with patched(str(real_file), "r") as f:
assert f.read() == "real content"
+246
View File
@@ -0,0 +1,246 @@
# ========= 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.accessor.ram import RAMAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops import Ops
from mirage.ops.config import OpsMount
from mirage.resource.ram import RAMResource
from mirage.resource.ram.store import RAMStore
from mirage.types import FileType, MountMode
from .conftest import _ram_registered_ops, make_ops, run
class TestMountPrefixes:
def test_mount_prefixes_returns_prefixes(self):
ops, _ = make_ops()
assert ops.mount_prefixes() == ["/data/"]
def test_mount_prefixes_reflects_unmount(self):
ops, _ = make_ops()
ops.unmount("/data/")
assert ops.mount_prefixes() == []
class TestResolve:
def test_resolve_basic(self):
ops, _ = make_ops()
resource_type, rel_path, _, _, mode = ops._resolve("/data/file.txt")
assert resource_type == "ram"
assert rel_path == "/file.txt"
assert mode == MountMode.WRITE
def test_resolve_nested(self):
ops, _ = make_ops()
_, rel_path, _, _, _ = ops._resolve("/data/a/b/c.txt")
assert rel_path == "/a/b/c.txt"
def test_resolve_no_match(self):
ops, _ = make_ops()
with pytest.raises(ValueError, match="no mount matches"):
ops._resolve("/other/file.txt")
def test_resolve_prefix_root(self):
ops, _ = make_ops()
_, rel_path, _, _, _ = ops._resolve("/data/")
assert rel_path == "/"
class TestReadWrite:
def test_write_and_read(self):
ops, _ = make_ops()
run(ops.mkdir("/data/dir"))
run(ops.write("/data/dir/file.txt", b"hello"))
assert run(ops.read("/data/dir/file.txt")) == b"hello"
def test_read_nonexistent(self):
ops, _ = make_ops()
with pytest.raises(FileNotFoundError):
run(ops.read("/data/nope.txt"))
def test_write_read_only(self):
ops, _ = make_ops(mode=MountMode.READ)
with pytest.raises(PermissionError):
run(ops.write("/data/file.txt", b"data"))
class TestStat:
def test_stat_file(self):
ops, _ = make_ops()
run(ops.mkdir("/data/dir"))
run(ops.write("/data/dir/f.txt", b"hello"))
s = run(ops.stat("/data/dir/f.txt"))
assert s.name == "f.txt"
assert s.size == 5
def test_stat_dir(self):
ops, _ = make_ops()
run(ops.mkdir("/data/mydir"))
s = run(ops.stat("/data/mydir"))
assert s.type == FileType.DIRECTORY
def test_stat_nonexistent(self):
ops, _ = make_ops()
with pytest.raises(FileNotFoundError):
run(ops.stat("/data/nope"))
class TestReaddir:
def test_readdir(self):
ops, _ = make_ops()
run(ops.mkdir("/data/dir"))
run(ops.write("/data/dir/a.txt", b"a"))
run(ops.write("/data/dir/b.txt", b"b"))
entries = run(ops.readdir("/data/dir"))
assert len(entries) == 2
class TestMkdirRmdir:
def test_mkdir_and_rmdir(self):
ops, _ = make_ops()
run(ops.mkdir("/data/newdir"))
s = run(ops.stat("/data/newdir"))
assert s.type == FileType.DIRECTORY
run(ops.rmdir("/data/newdir"))
with pytest.raises(FileNotFoundError):
run(ops.stat("/data/newdir"))
def test_rmdir_nonempty(self):
ops, _ = make_ops()
run(ops.mkdir("/data/dir"))
run(ops.write("/data/dir/f.txt", b"data"))
with pytest.raises(OSError):
run(ops.rmdir("/data/dir"))
class TestUnlink:
def test_unlink(self):
ops, _ = make_ops()
run(ops.mkdir("/data/dir"))
run(ops.write("/data/dir/f.txt", b"data"))
run(ops.unlink("/data/dir/f.txt"))
with pytest.raises(FileNotFoundError):
run(ops.read("/data/dir/f.txt"))
class TestRename:
def test_rename(self):
ops, _ = make_ops()
run(ops.mkdir("/data/dir"))
run(ops.write("/data/dir/old.txt", b"content"))
run(ops.rename("/data/dir/old.txt", "/data/dir/new.txt"))
assert run(ops.read("/data/dir/new.txt")) == b"content"
with pytest.raises(FileNotFoundError):
run(ops.read("/data/dir/old.txt"))
class TestCreateTruncate:
def test_create(self):
ops, _ = make_ops()
run(ops.mkdir("/data/dir"))
run(ops.create("/data/dir/empty.txt"))
assert run(ops.read("/data/dir/empty.txt")) == b""
def test_truncate(self):
ops, _ = make_ops()
run(ops.mkdir("/data/dir"))
run(ops.write("/data/dir/f.txt", b"hello world"))
run(ops.truncate("/data/dir/f.txt", 5))
assert run(ops.read("/data/dir/f.txt")) == b"hello"
class TestIsMounted:
def test_mounted(self):
ops, _ = make_ops()
assert ops.is_mounted("/data/file.txt") is True
def test_not_mounted(self):
ops, _ = make_ops()
assert ops.is_mounted("/other/file.txt") is False
class TestMultiMount:
def test_two_mounts(self):
store1 = RAMStore()
store2 = RAMStore()
ram_ops = _ram_registered_ops()
mounts = [
OpsMount("/mem1/", "ram", RAMAccessor(store1),
RAMIndexCacheStore(), MountMode.WRITE, ram_ops),
OpsMount("/mem2/", "ram", RAMAccessor(store2),
RAMIndexCacheStore(), MountMode.WRITE, ram_ops),
]
ops = Ops(mounts)
run(ops.mkdir("/mem1/dir"))
run(ops.mkdir("/mem2/dir"))
run(ops.write("/mem1/dir/a.txt", b"from store1"))
run(ops.write("/mem2/dir/b.txt", b"from store2"))
assert run(ops.read("/mem1/dir/a.txt")) == b"from store1"
assert run(ops.read("/mem2/dir/b.txt")) == b"from store2"
assert store1.files.get("/dir/a.txt") == b"from store1"
assert store2.files.get("/dir/b.txt") == b"from store2"
class TestOpsViaRegistry:
@pytest.fixture
def memory_ops(self):
resource = RAMResource()
store = resource._store
store.dirs.add("/")
store.files["/test.txt"] = b"hello"
store.modified["/test.txt"] = "2024-01-01T00:00:00"
mount = OpsMount(
prefix="/data/",
resource_type="ram",
accessor=resource.accessor,
index=RAMIndexCacheStore(),
mode=MountMode.WRITE,
ops=resource.ops_list(),
)
return Ops([mount]), store
def test_read_via_registry(self, memory_ops):
ops, _ = memory_ops
assert run(ops.read("/data/test.txt")) == b"hello"
def test_write_via_registry(self, memory_ops):
ops, store = memory_ops
run(ops.write("/data/test.txt", b"world"))
assert store.files["/test.txt"] == b"world"
def test_stat_via_registry(self, memory_ops):
ops, _ = memory_ops
st = run(ops.stat("/data/test.txt"))
assert st.name == "test.txt"
def test_registry_accessible(self, memory_ops):
ops, _ = memory_ops
assert ops._registry is not None
fn = ops._registry.resolve("read", "ram")
assert fn is not None
+84
View File
@@ -0,0 +1,84 @@
# ========= 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 sys
from mirage import MountMode, Workspace
from mirage.ops.os_patch import make_os_module
from mirage.resource.ram import RAMResource
from .conftest import make_ops_with_dir, run
class TestPatchedOs:
def test_exists(self):
ops, _ = make_ops_with_dir()
run(ops.write("/data/dir/f.txt", b"data"))
patched = make_os_module(ops)
assert patched.path.exists("/data/dir/f.txt") is True
assert patched.path.exists("/data/dir/nope.txt") is False
def test_isfile(self):
ops, _ = make_ops_with_dir()
run(ops.write("/data/dir/f.txt", b"data"))
patched = make_os_module(ops)
assert patched.path.isfile("/data/dir/f.txt") is True
assert patched.path.isfile("/data/dir") is False
def test_isdir(self):
ops, _ = make_ops_with_dir()
patched = make_os_module(ops)
assert patched.path.isdir("/data/dir") is True
assert patched.path.isdir("/data/dir/f.txt") is False
def test_listdir(self):
ops, _ = make_ops_with_dir()
run(ops.write("/data/dir/a.txt", b"a"))
run(ops.write("/data/dir/b.txt", b"b"))
patched = make_os_module(ops)
entries = patched.listdir("/data/dir")
assert sorted(entries) == ["a.txt", "b.txt"]
def test_remove(self):
ops, _ = make_ops_with_dir()
run(ops.write("/data/dir/f.txt", b"data"))
patched = make_os_module(ops)
patched.remove("/data/dir/f.txt")
assert patched.path.exists("/data/dir/f.txt") is False
def test_getsize(self):
ops, _ = make_ops_with_dir()
run(ops.write("/data/dir/f.txt", b"12345"))
patched = make_os_module(ops)
assert patched.path.getsize("/data/dir/f.txt") == 5
def test_fallthrough_real_path(self):
ops, _ = make_ops_with_dir()
patched = make_os_module(ops)
assert patched.path.exists("/tmp") is True
def test_sys_modules_listdir(self):
ws = Workspace({"/mem/": RAMResource()}, mode=MountMode.WRITE)
run(ws.ops.mkdir("/mem/dir"))
run(ws.ops.write("/mem/dir/a.txt", b"a"))
run(ws.ops.write("/mem/dir/b.txt", b"b"))
original_os = sys.modules["os"]
with ws:
vos = sys.modules["os"]
entries = vos.listdir("/mem/dir")
assert sorted(entries) == ["a.txt", "b.txt"]
assert vos.path.exists("/mem/dir/a.txt") is True
assert vos.path.exists("/mem/dir/nope.txt") is False
assert sys.modules["os"] is original_os
+239
View File
@@ -0,0 +1,239 @@
# ========= 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.accessor.base import NOOPAccessor
from mirage.cache.index.ram import RAMIndexCacheStore
from mirage.ops import Ops
from mirage.ops.config import OpsMount
from mirage.ops.registry import OpsRegistry, RegisteredOp, op
from mirage.ops.s3 import OPS as S3_VFS_OPS
from mirage.resource.ram import RAMResource
from mirage.types import MountMode
class TestOpDecorator:
def test_attaches_metadata(self):
@op("read", resource="s3")
async def my_read(config, path):
return b"data"
assert hasattr(my_read, "_registered_ops")
assert len(my_read._registered_ops) == 1
ro = my_read._registered_ops[0]
assert isinstance(ro, RegisteredOp)
assert ro.name == "read"
assert ro.resource == "s3"
assert ro.filetype is None
def test_write_defaults_false(self):
@op("read", resource="s3")
async def my_read2(config, path):
return b"data"
ro = my_read2._registered_ops[0]
assert ro.write is False
def test_write_flag_true(self):
@op("write", resource="s3", write=True)
async def my_write(config, path, data):
pass
ro = my_write._registered_ops[0]
assert ro.write is True
def test_with_filetype(self):
@op("read", resource="s3", filetype=".parquet")
async def read_parquet(config, path):
return b"parquet data"
ro = read_parquet._registered_ops[0]
assert ro.filetype == ".parquet"
assert ro.resource == "s3"
def test_stacks(self):
@op("read", resource="s3")
@op("read", resource="ram")
async def read_multi(bind_arg, path):
return b"data"
assert len(read_multi._registered_ops) == 2
class TestOpsRegistry:
@pytest.mark.asyncio
async def test_resource_lookup(self):
registry = OpsRegistry()
@op("read", resource="ram")
async def mem_read(store, path):
return b"memory data"
registry.register(mem_read)
fn = registry.resolve("read", "ram")
assert fn is not None
result = await fn(None, "/test")
assert result == b"memory data"
@pytest.mark.asyncio
async def test_filetype_priority(self):
registry = OpsRegistry()
@op("read", resource="s3", filetype=".parquet")
async def read_parquet(config, path):
return b"parquet"
@op("read", resource="s3")
async def read_default(config, path):
return b"default"
registry.register(read_parquet)
registry.register(read_default)
fn = registry.resolve("read", "s3", filetype=".parquet")
result = await fn(None, "/test.parquet")
assert result == b"parquet"
fn = registry.resolve("read", "s3", filetype=".txt")
result = await fn(None, "/test.txt")
assert result == b"default"
@pytest.mark.asyncio
async def test_none_fallthrough(self):
registry = OpsRegistry()
@op("read", resource="s3", filetype=".custom")
async def read_custom(config, path):
return None
@op("read", resource="s3")
async def read_default(config, path):
return b"fallback"
registry.register(read_custom)
registry.register(read_default)
result = await registry.call("read",
"s3", (None, ),
"/test.custom",
filetype=".custom")
assert result == b"fallback"
@pytest.mark.asyncio
async def test_not_found(self):
registry = OpsRegistry()
with pytest.raises(KeyError):
registry.resolve("read", "redis")
def test_register_registered_op(self):
registry = OpsRegistry()
async def my_fn(store, path):
return b"data"
ro = RegisteredOp(name="read", resource="ram", filetype=None, fn=my_fn)
registry.register(ro)
assert registry.resolve("read", "ram") is my_fn
def test_register_type_error(self):
registry = OpsRegistry()
with pytest.raises(TypeError):
registry.register("not a function")
class TestUserOpOverride:
@pytest.mark.asyncio
async def test_user_op_overrides_builtin(self):
registry = OpsRegistry()
builtin = RegisteredOp(name="read",
resource="disk",
filetype=None,
fn=lambda acc, p, **kw: b"builtin")
registry.register(builtin)
@op("read", resource="disk")
async def custom_read(accessor, path, **kwargs):
return b"custom"
registry.register(custom_read)
fn = registry.resolve("read", "disk")
result = await fn(None, "/test")
assert result == b"custom"
@pytest.mark.asyncio
async def test_user_filetype_op_overrides_builtin(self):
registry = OpsRegistry()
builtin = RegisteredOp(name="read",
resource="s3",
filetype=".parquet",
fn=lambda acc, p, **kw: b"builtin-parquet")
registry.register(builtin)
@op("read", resource="s3", filetype=".parquet")
async def my_parquet(accessor, path, **kwargs):
return b"my-parquet"
registry.register(my_parquet)
fn = registry.resolve("read", "s3", filetype=".parquet")
result = await fn(None, "/data.parquet")
assert result == b"my-parquet"
class TestFiletypeOps:
@pytest.mark.asyncio
async def test_registered_for_s3(self):
s3_ops = []
for fn in S3_VFS_OPS:
for ro in fn._registered_ops:
s3_ops.append(ro)
mount = OpsMount(
prefix="/s3/",
resource_type="s3",
accessor=NOOPAccessor(),
index=RAMIndexCacheStore(),
mode=MountMode.READ,
ops=s3_ops,
)
ops = Ops([mount])
fn = ops._registry.resolve("read", "s3", filetype=".parquet")
assert fn is not None
@pytest.mark.asyncio
async def test_default_read_still_works(self):
resource = RAMResource()
store = resource._store
store.dirs.add("/")
store.files["/test.txt"] = b"hello"
store.modified["/test.txt"] = "2024-01-01T00:00:00"
mount = OpsMount(
prefix="/data/",
resource_type="ram",
accessor=resource.accessor,
index=RAMIndexCacheStore(),
mode=MountMode.READ,
ops=resource.ops_list(),
)
ops = Ops([mount])
result = await ops.read("/data/test.txt")
assert result == b"hello"