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
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:
@@ -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,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
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
@@ -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()
|
||||
@@ -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",
|
||||
]
|
||||
@@ -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": ""
|
||||
})]
|
||||
@@ -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()
|
||||
@@ -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 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"
|
||||
@@ -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"]
|
||||
@@ -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
|
||||
@@ -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,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"
|
||||
@@ -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,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"
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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,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
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"]
|
||||
@@ -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
|
||||
@@ -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,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)
|
||||
@@ -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)
|
||||
@@ -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"]
|
||||
@@ -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
|
||||
@@ -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,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"
|
||||
@@ -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"]
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
@@ -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"]
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
@@ -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"]
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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,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"
|
||||
@@ -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,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"
|
||||
@@ -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,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"
|
||||
@@ -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"]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user