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,91 @@
|
||||
# ========= 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 import MountMode, Workspace
|
||||
from mirage.cache.index import RAMIndexCacheStore
|
||||
from mirage.types import PathSpec
|
||||
from mirage.utils.stream import collect_bytes
|
||||
from tests.resource.databricks_volume.test_databricks_volume import (
|
||||
FakeFiles, make_resource, seed_directory, seed_file)
|
||||
|
||||
|
||||
def seed_text_command_fixture(files: FakeFiles) -> str:
|
||||
root = "/Volumes/main/default/agent_files/root"
|
||||
seed_directory(files, root)
|
||||
seed_file(files, f"{root}/words.txt", b"beta\nalpha\nalpha\n")
|
||||
seed_file(files, f"{root}/more.txt", b"delta\n")
|
||||
seed_file(files, f"{root}/table.csv", b"name,score\nann,2\nbob,3\n")
|
||||
seed_file(files, f"{root}/table_extra.csv", b"name,score\ncam,5\n")
|
||||
seed_file(files, f"{root}/data.json", b'{"name": "mirage"}\n')
|
||||
seed_file(files, f"{root}/data2.json", b'{"name": "agent"}\n')
|
||||
seed_file(files, f"{root}/events.jsonl", b'{"name": "first"}\n')
|
||||
seed_file(files, f"{root}/events_extra.jsonl", b'{"name": "second"}\n')
|
||||
seed_file(files, f"{root}/old.txt", b"same\nold\n")
|
||||
seed_file(files, f"{root}/new.txt", b"same\nnew\n")
|
||||
return root
|
||||
|
||||
|
||||
async def materialize(source) -> bytes:
|
||||
if source is None:
|
||||
return b""
|
||||
return await collect_bytes(source)
|
||||
|
||||
|
||||
class IndexTrackingReader:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.seen_indexes: list[RAMIndexCacheStore | None] = []
|
||||
|
||||
async def read_bytes(self,
|
||||
accessor,
|
||||
path,
|
||||
index=None,
|
||||
*args,
|
||||
**kwargs) -> bytes:
|
||||
self.seen_indexes.append(index)
|
||||
original = path.virtual if isinstance(path, PathSpec) else path
|
||||
if str(original).endswith(".json"):
|
||||
return b'{"name": "mirage"}\n'
|
||||
if str(original).endswith(".csv"):
|
||||
return b"name,score\nann,2\n"
|
||||
return b"beta\nalpha\nalpha\n"
|
||||
|
||||
async def read_stream(self, accessor, path, index=None, *args, **kwargs):
|
||||
self.seen_indexes.append(index)
|
||||
yield await self.read_bytes(accessor, path, index, *args, **kwargs)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def databricks_text_files() -> FakeFiles:
|
||||
files = FakeFiles()
|
||||
seed_text_command_fixture(files)
|
||||
return files
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def databricks_text_workspace(databricks_text_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(databricks_text_files)},
|
||||
mode=MountMode.READ)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def expected_index() -> RAMIndexCacheStore:
|
||||
return RAMIndexCacheStore(ttl=600)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def materialize_output():
|
||||
return materialize
|
||||
@@ -0,0 +1,25 @@
|
||||
# ========= 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
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_awk(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute(
|
||||
"awk '{print $1}' /dbx/table.csv")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"name,score\nann,2\nbob,3\n"
|
||||
@@ -0,0 +1,186 @@
|
||||
import time
|
||||
from functools import partial
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.accessor.databricks_volume import DatabricksVolumeAccessor
|
||||
from mirage.cache.index import RAMIndexCacheStore
|
||||
from mirage.commands.builtin.generic.find import parse_find_args, walk_find
|
||||
from mirage.commands.builtin.generic.ls import ls as generic_ls
|
||||
from mirage.commands.builtin.generic.tree import tree as generic_tree
|
||||
from mirage.core.databricks_volume.path import backend_path
|
||||
from mirage.core.databricks_volume.readdir import readdir
|
||||
from mirage.core.databricks_volume.stat import stat
|
||||
from mirage.resource.databricks_volume import DatabricksVolumeConfig
|
||||
from mirage.types import LsSortBy, PathSpec
|
||||
from mirage.utils.key_prefix import mount_key
|
||||
from tests.core.databricks_volume.conftest import (FakeClient, FakeFiles,
|
||||
directory_entry, file_entry)
|
||||
|
||||
MODIFIED_MS = 1_700_000_000_000
|
||||
|
||||
|
||||
def is_dir_name(_child: str) -> bool | None:
|
||||
# Databricks readdir returns slash-less paths, so classification always
|
||||
# falls back to stat.
|
||||
return None
|
||||
|
||||
|
||||
FROZEN_NOW_S = 1_700_000_000
|
||||
DAY_S = 86_400
|
||||
AGES_DAYS = (1, 2, 3, 10, 20)
|
||||
|
||||
|
||||
def _rig(
|
||||
) -> tuple[DatabricksVolumeAccessor, FakeFiles, RAMIndexCacheStore, str]:
|
||||
config = DatabricksVolumeConfig(
|
||||
catalog="main",
|
||||
schema="default",
|
||||
volume="agent_files",
|
||||
root_path="/root",
|
||||
token="secret",
|
||||
)
|
||||
files = FakeFiles()
|
||||
accessor = DatabricksVolumeAccessor(config, FakeClient(files))
|
||||
index = RAMIndexCacheStore(ttl=600)
|
||||
return accessor, files, index, backend_path(config, "/")
|
||||
|
||||
|
||||
def _seed_flat(files: FakeFiles, root: str, count: int = 5) -> None:
|
||||
files.directories[f"{root}/sub"] = [
|
||||
file_entry(f"{root}/sub/f{i}.txt",
|
||||
size=i + 1,
|
||||
modified=MODIFIED_MS + i * 1000) for i in range(count)
|
||||
]
|
||||
|
||||
|
||||
def _ls_readdir(accessor: DatabricksVolumeAccessor):
|
||||
return partial(readdir, accessor)
|
||||
|
||||
|
||||
def _ls_stat(accessor: DatabricksVolumeAccessor):
|
||||
return partial(stat, accessor)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("sort_by,long", [
|
||||
(LsSortBy.NAME, False),
|
||||
(LsSortBy.NAME, True),
|
||||
(LsSortBy.TIME, False),
|
||||
(LsSortBy.SIZE, False),
|
||||
])
|
||||
async def test_ls_lists_once_without_per_entry_metadata(sort_by, long):
|
||||
accessor, files, index, root = _rig()
|
||||
_seed_flat(files, root, count=5)
|
||||
path = PathSpec.from_str_path("/volume/sub",
|
||||
mount_key("/volume/sub", "/volume"))
|
||||
await generic_ls([path],
|
||||
readdir=_ls_readdir(accessor),
|
||||
stat=_ls_stat(accessor),
|
||||
long=long,
|
||||
sort_by=sort_by,
|
||||
index=index)
|
||||
assert files.list_directory_calls == [f"{root}/sub"]
|
||||
assert files.get_metadata_calls == []
|
||||
assert files.get_directory_metadata_calls == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ls_recursive_one_list_per_directory():
|
||||
accessor, files, index, root = _rig()
|
||||
files.directories[f"{root}/sub"] = [
|
||||
file_entry(f"{root}/sub/a.txt", size=1, modified=MODIFIED_MS),
|
||||
directory_entry(f"{root}/sub/inner"),
|
||||
]
|
||||
files.directories[f"{root}/sub/inner"] = [
|
||||
file_entry(f"{root}/sub/inner/b.txt", size=2, modified=MODIFIED_MS),
|
||||
]
|
||||
path = PathSpec.from_str_path("/volume/sub",
|
||||
mount_key("/volume/sub", "/volume"))
|
||||
await generic_ls([path],
|
||||
readdir=_ls_readdir(accessor),
|
||||
stat=_ls_stat(accessor),
|
||||
recursive=True,
|
||||
index=index)
|
||||
assert sorted(
|
||||
files.list_directory_calls) == [f"{root}/sub", f"{root}/sub/inner"]
|
||||
assert files.get_metadata_calls == []
|
||||
assert files.get_directory_metadata_calls == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tree_one_list_per_directory_without_metadata():
|
||||
accessor, files, index, root = _rig()
|
||||
files.directories[f"{root}/sub"] = [
|
||||
file_entry(f"{root}/sub/a.txt", size=1, modified=MODIFIED_MS),
|
||||
directory_entry(f"{root}/sub/inner"),
|
||||
]
|
||||
files.directories[f"{root}/sub/inner"] = [
|
||||
file_entry(f"{root}/sub/inner/b.txt", size=2, modified=MODIFIED_MS),
|
||||
]
|
||||
path = PathSpec.from_str_path("/volume/sub",
|
||||
mount_key("/volume/sub", "/volume"))
|
||||
await generic_tree(path,
|
||||
readdir=_ls_readdir(accessor),
|
||||
stat=_ls_stat(accessor),
|
||||
index=index)
|
||||
assert sorted(
|
||||
files.list_directory_calls) == [f"{root}/sub", f"{root}/sub/inner"]
|
||||
assert files.get_metadata_calls == []
|
||||
assert files.get_directory_metadata_calls == []
|
||||
|
||||
|
||||
async def _run_find(accessor, index, *, type=None, size=None, mtime=None):
|
||||
args = parse_find_args((), type=type, size=size, mtime=mtime)
|
||||
path = PathSpec.from_str_path("/volume/sub",
|
||||
mount_key("/volume/sub", "/volume"))
|
||||
return await walk_find(path,
|
||||
readdir=_ls_readdir(accessor),
|
||||
stat=_ls_stat(accessor),
|
||||
is_dir_name=is_dir_name,
|
||||
index=index,
|
||||
args=args)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_find_type_does_not_stat_children():
|
||||
accessor, files, index, root = _rig()
|
||||
_seed_flat(files, root, count=5)
|
||||
files.directory_metadata.add(f"{root}/sub")
|
||||
results = await _run_find(accessor, index, type="f")
|
||||
assert len(results) == 5
|
||||
assert files.list_directory_calls == [f"{root}/sub"]
|
||||
child_metadata = [c for c in files.get_metadata_calls if "/sub/" in c]
|
||||
assert child_metadata == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_find_size_reads_size_from_index():
|
||||
accessor, files, index, root = _rig()
|
||||
_seed_flat(files, root, count=5)
|
||||
files.directory_metadata.add(f"{root}/sub")
|
||||
results = await _run_find(accessor, index, type="f", size="+3c")
|
||||
assert sorted(r.rsplit("/", 1)[-1]
|
||||
for r in results) == ["f3.txt", "f4.txt"]
|
||||
assert files.list_directory_calls == [f"{root}/sub"]
|
||||
child_metadata = [c for c in files.get_metadata_calls if "/sub/" in c]
|
||||
assert child_metadata == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_find_mtime_reads_modified_from_index(monkeypatch):
|
||||
monkeypatch.setattr(time, "time", lambda: float(FROZEN_NOW_S))
|
||||
accessor, files, index, root = _rig()
|
||||
files.directories[f"{root}/sub"] = [
|
||||
file_entry(f"{root}/sub/f{i}.txt",
|
||||
size=i + 1,
|
||||
modified=(FROZEN_NOW_S - age * DAY_S) * 1000)
|
||||
for i, age in enumerate(AGES_DAYS)
|
||||
]
|
||||
files.directory_metadata.add(f"{root}/sub")
|
||||
results = await _run_find(accessor, index, mtime="-5")
|
||||
assert sorted(r.rsplit("/", 1)[-1]
|
||||
for r in results) == ["f0.txt", "f1.txt", "f2.txt"]
|
||||
assert files.list_directory_calls == [f"{root}/sub"]
|
||||
child_metadata = [c for c in files.get_metadata_calls if "/sub/" in c]
|
||||
assert child_metadata == []
|
||||
@@ -0,0 +1,40 @@
|
||||
# ========= 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
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cat_single_file(databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("cat /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"beta\nalpha\nalpha\n"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cat_multiple_files_concatenated(databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute(
|
||||
"cat /dbx/words.txt /dbx/more.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"beta\nalpha\nalpha\ndelta\n"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cat_n_numbers_lines(databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("cat -n /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b" 1\tbeta\n 2\talpha\n 3\talpha\n"
|
||||
@@ -0,0 +1,123 @@
|
||||
# ========= 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 import MountMode, Workspace
|
||||
from tests.resource.databricks_volume.test_databricks_volume import (
|
||||
FakeFiles, make_resource, seed_directory, seed_file)
|
||||
|
||||
ROOT = "/Volumes/main/default/agent_files/root"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dbx_files() -> FakeFiles:
|
||||
files = FakeFiles()
|
||||
seed_directory(files, ROOT)
|
||||
seed_file(files, f"{ROOT}/src.txt", b"hello")
|
||||
return files
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def write_ws(dbx_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(dbx_files)}, mode=MountMode.WRITE)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def read_ws(dbx_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(dbx_files)}, mode=MountMode.READ)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cp_file_preserves_bytes(write_ws, dbx_files):
|
||||
io = await write_ws.execute("cp /dbx/src.txt /dbx/dst.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert dbx_files.downloads[f"{ROOT}/dst.txt"] == b"hello"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cp_directory_without_recursive_fails(write_ws, dbx_files):
|
||||
seed_directory(dbx_files, f"{ROOT}/d")
|
||||
|
||||
io = await write_ws.execute("cp /dbx/d /dbx/d2")
|
||||
|
||||
assert io.exit_code != 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cp_writes_are_mount_relative(write_ws):
|
||||
io = await write_ws.execute("cp /dbx/src.txt /dbx/dst.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
for key in io.writes:
|
||||
assert key.startswith("/dbx/")
|
||||
assert not key.startswith("/dbx/dbx/")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cp_read_only_mount_rejected(read_ws, dbx_files):
|
||||
io = await read_ws.execute("cp /dbx/src.txt /dbx/dst.txt")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert b"read-only" in io.stderr
|
||||
assert f"{ROOT}/dst.txt" not in dbx_files.downloads
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cp_onto_same_path_errors_and_preserves_file(
|
||||
write_ws, dbx_files):
|
||||
io = await write_ws.execute("cp /dbx/src.txt /dbx/src.txt")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert b"are the same file" in io.stderr
|
||||
assert dbx_files.downloads[f"{ROOT}/src.txt"] == b"hello"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cp_multiple_sources_require_directory(write_ws, dbx_files):
|
||||
seed_file(dbx_files, f"{ROOT}/a.txt", b"AAA")
|
||||
seed_file(dbx_files, f"{ROOT}/b.txt", b"BBB")
|
||||
seed_file(dbx_files, f"{ROOT}/target.txt", b"target")
|
||||
|
||||
io = await write_ws.execute("cp /dbx/a.txt /dbx/b.txt /dbx/target.txt")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert b"not a directory" in io.stderr
|
||||
assert dbx_files.downloads[f"{ROOT}/a.txt"] == b"AAA"
|
||||
assert dbx_files.downloads[f"{ROOT}/b.txt"] == b"BBB"
|
||||
assert dbx_files.downloads[f"{ROOT}/target.txt"] == b"target"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cp_missing_source_reports_cannot_stat(write_ws, dbx_files):
|
||||
io = await write_ws.execute("cp /dbx/missing /dbx/missing")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert b"cannot stat" in io.stderr
|
||||
assert b"are the same file" not in io.stderr
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cp_recursive_into_itself_errors_and_preserves_tree(
|
||||
write_ws, dbx_files):
|
||||
seed_directory(dbx_files, f"{ROOT}/d")
|
||||
seed_file(dbx_files, f"{ROOT}/d/a.txt", b"aaa")
|
||||
|
||||
io = await write_ws.execute("cp -r /dbx/d /dbx/d")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert b"into itself" in io.stderr
|
||||
assert dbx_files.downloads[f"{ROOT}/d/a.txt"] == b"aaa"
|
||||
assert f"{ROOT}/d/d" not in dbx_files.directory_metadata
|
||||
@@ -0,0 +1,87 @@
|
||||
# ========= 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 import MountMode, Workspace
|
||||
from mirage.resource.ram import RAMResource
|
||||
from tests.resource.databricks_volume.test_databricks_volume import (
|
||||
FakeFiles, make_resource, seed_file)
|
||||
|
||||
ROOT = "/Volumes/main/default/agent_files/root"
|
||||
|
||||
|
||||
def _run(ws, cmd):
|
||||
|
||||
async def _inner():
|
||||
io = await ws.execute(cmd)
|
||||
return await io.stdout_str(), await io.stderr_str(), io.exit_code
|
||||
|
||||
return asyncio.run(_inner())
|
||||
|
||||
|
||||
def _ws_with_dbx_tree():
|
||||
files = FakeFiles()
|
||||
files.create_directory(ROOT)
|
||||
files.create_directory(f"{ROOT}/tree")
|
||||
files.create_directory(f"{ROOT}/tree/sub")
|
||||
seed_file(files, f"{ROOT}/tree/a.txt", b"aaa\n")
|
||||
seed_file(files, f"{ROOT}/tree/sub/b.txt", b"bbb\n")
|
||||
dbx = make_resource(files)
|
||||
ram = RAMResource()
|
||||
ws = Workspace(
|
||||
{
|
||||
"/dbx": (dbx, MountMode.WRITE),
|
||||
"/m": (ram, MountMode.WRITE),
|
||||
}, )
|
||||
return ws, files, ram
|
||||
|
||||
|
||||
def test_cp_recursive_databricks_to_ram():
|
||||
ws, _files, ram = _ws_with_dbx_tree()
|
||||
_out, _err, code = _run(ws, "cp -r /dbx/tree /m/copied")
|
||||
assert code == 0
|
||||
assert ram._store.files["/copied/a.txt"] == b"aaa\n"
|
||||
assert ram._store.files["/copied/sub/b.txt"] == b"bbb\n"
|
||||
|
||||
|
||||
def test_cp_recursive_ram_to_databricks_creates_dirs():
|
||||
ws, files, ram = _ws_with_dbx_tree()
|
||||
ram._store.dirs.update({"/src", "/src/sub"})
|
||||
ram._store.files["/src/x.txt"] = b"xxx\n"
|
||||
ram._store.files["/src/sub/y.txt"] = b"yyy\n"
|
||||
_out, _err, code = _run(ws, "cp -r /m/src /dbx/put")
|
||||
assert code == 0
|
||||
assert files.downloads[f"{ROOT}/put/x.txt"] == b"xxx\n"
|
||||
assert files.downloads[f"{ROOT}/put/sub/y.txt"] == b"yyy\n"
|
||||
assert f"{ROOT}/put/sub" in files.directory_metadata
|
||||
|
||||
|
||||
def test_mv_recursive_databricks_to_ram_removes_source():
|
||||
ws, files, ram = _ws_with_dbx_tree()
|
||||
_out, _err, code = _run(ws, "mv /dbx/tree /m/moved")
|
||||
assert code == 0
|
||||
assert ram._store.files["/moved/a.txt"] == b"aaa\n"
|
||||
assert ram._store.files["/moved/sub/b.txt"] == b"bbb\n"
|
||||
assert f"{ROOT}/tree/a.txt" not in files.downloads
|
||||
assert f"{ROOT}/tree" not in files.directory_metadata
|
||||
|
||||
|
||||
def test_single_file_missing_parent_is_posix_error():
|
||||
# Option B / POSIX: a single-file copy never creates the destination's
|
||||
# parent directory; it errors like coreutils instead of mkdir -p.
|
||||
ws, _files, ram = _ws_with_dbx_tree()
|
||||
ram._store.files["/lone.txt"] = b"z\n"
|
||||
_out, _err, code = _run(ws, "cp /m/lone.txt /dbx/nope/deep/lone.txt")
|
||||
assert code == 1
|
||||
@@ -0,0 +1,25 @@
|
||||
# ========= 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
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_cut(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("cut -d , -f 2 /dbx/table.csv"
|
||||
)
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"score\n2\n3\n"
|
||||
@@ -0,0 +1,37 @@
|
||||
# ========= 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
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_diff(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute(
|
||||
"diff -u /dbx/old.txt /dbx/new.txt")
|
||||
|
||||
assert io.exit_code == 1
|
||||
assert b"-old" in io.stdout
|
||||
assert b"+new" in io.stdout
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_diff_resolves_glob(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute(
|
||||
"diff /dbx/old*.txt /dbx/new*.txt")
|
||||
|
||||
assert io.exit_code == 1
|
||||
assert b"old" in io.stdout
|
||||
assert b"new" in io.stdout
|
||||
@@ -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 pytest
|
||||
|
||||
from mirage import MountMode, Workspace
|
||||
from tests.resource.databricks_volume.test_databricks_volume import (
|
||||
FakeFiles, make_resource, seed_directory, seed_file)
|
||||
|
||||
ROOT = "/Volumes/main/default/agent_files/root"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dbx_files() -> FakeFiles:
|
||||
files = FakeFiles()
|
||||
seed_directory(files, ROOT)
|
||||
seed_file(files, f"{ROOT}/words.txt", b"beta\nalpha\nalpha\n")
|
||||
seed_file(files, f"{ROOT}/notes.md", b"note\n")
|
||||
files.create_directory(f"{ROOT}/sub")
|
||||
seed_file(files, f"{ROOT}/sub/inner.txt", b"gamma\nalpha\n")
|
||||
return files
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ws(dbx_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(dbx_files)}, mode=MountMode.READ)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_find_name_glob(ws):
|
||||
io = await ws.execute("find /dbx/ -name '*.txt'")
|
||||
|
||||
assert io.exit_code == 0
|
||||
out = io.stdout.decode()
|
||||
assert "words.txt" in out
|
||||
assert "inner.txt" in out
|
||||
assert "notes.md" not in out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_find_type_d(ws):
|
||||
io = await ws.execute("find /dbx/ -type d")
|
||||
|
||||
assert io.exit_code == 0
|
||||
out = io.stdout.decode()
|
||||
assert "sub" in out
|
||||
assert "words.txt" not in out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_find_maxdepth(ws):
|
||||
io = await ws.execute("find /dbx/ -maxdepth 1")
|
||||
|
||||
assert io.exit_code == 0
|
||||
out = io.stdout.decode()
|
||||
assert "words.txt" in out
|
||||
assert "inner.txt" not in out
|
||||
@@ -0,0 +1,77 @@
|
||||
# ========= 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 import MountMode, Workspace
|
||||
from tests.resource.databricks_volume.test_databricks_volume import (
|
||||
FakeFiles, make_resource, seed_directory, seed_file)
|
||||
|
||||
ROOT = "/Volumes/main/default/agent_files/root"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dbx_files() -> FakeFiles:
|
||||
files = FakeFiles()
|
||||
seed_directory(files, ROOT)
|
||||
seed_file(files, f"{ROOT}/words.txt", b"beta\nalpha\nalpha\n")
|
||||
files.create_directory(f"{ROOT}/sub")
|
||||
seed_file(files, f"{ROOT}/sub/inner.txt", b"gamma\nalpha\n")
|
||||
return files
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ws(dbx_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(dbx_files)}, mode=MountMode.READ)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_grep_single_file(ws):
|
||||
io = await ws.execute("grep alpha /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"alpha\nalpha\n"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_grep_line_numbers(ws):
|
||||
io = await ws.execute("grep -n alpha /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"2:alpha\n3:alpha\n"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_grep_count_only(ws):
|
||||
io = await ws.execute("grep -c alpha /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"2\n"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_grep_recursive(ws):
|
||||
io = await ws.execute("grep -r alpha /dbx/")
|
||||
|
||||
assert io.exit_code == 0
|
||||
out = io.stdout.decode()
|
||||
assert "words.txt" in out
|
||||
assert "inner.txt" in out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_grep_no_match_exits_nonzero(ws):
|
||||
io = await ws.execute("grep zeta /dbx/words.txt")
|
||||
|
||||
assert io.exit_code != 0
|
||||
@@ -0,0 +1,34 @@
|
||||
# ========= 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
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_head_c_positive_prefix(databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("head -c 4 /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"beta"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_head_c_negative_all_but_last(databricks_text_workspace):
|
||||
# GNU `head -c -N` emits all but the last N bytes. words.txt is 17 bytes,
|
||||
# so -c -6 yields the first 11. A negative -c must not take the prefix
|
||||
# fast path (range read 0..-6 is meaningless).
|
||||
io = await databricks_text_workspace.execute("head -c -6 /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"beta\nalpha\n"
|
||||
@@ -0,0 +1,33 @@
|
||||
# ========= 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
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_jq(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("jq -r .name /dbx/data.json")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"mirage\n"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_jq_resolves_glob(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("jq -r .name /dbx/*.json")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"mirage\nagent\n"
|
||||
@@ -0,0 +1,81 @@
|
||||
# ========= 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 import MountMode, Workspace
|
||||
from tests.resource.databricks_volume.test_databricks_volume import (
|
||||
FakeFiles, make_resource, seed_directory, seed_file)
|
||||
|
||||
ROOT = "/Volumes/main/default/agent_files/root"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dbx_files() -> FakeFiles:
|
||||
files = FakeFiles()
|
||||
seed_directory(files, ROOT)
|
||||
seed_file(files, f"{ROOT}/words.txt", b"beta\nalpha\nalpha\n")
|
||||
seed_file(files, f"{ROOT}/.hidden", b"h\n")
|
||||
files.create_directory(f"{ROOT}/sub")
|
||||
seed_file(files, f"{ROOT}/sub/inner.txt", b"gamma\nalpha\n")
|
||||
return files
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ws(dbx_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(dbx_files)}, mode=MountMode.READ)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ls_lists_entries(ws):
|
||||
io = await ws.execute("ls /dbx/")
|
||||
|
||||
assert io.exit_code == 0
|
||||
out = io.stdout.decode()
|
||||
assert "words.txt" in out
|
||||
assert "sub" in out
|
||||
assert ".hidden" not in out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ls_a_includes_hidden(ws):
|
||||
io = await ws.execute("ls -a /dbx/")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert ".hidden" in io.stdout.decode()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ls_long_includes_size(ws):
|
||||
io = await ws.execute("ls -l /dbx/")
|
||||
|
||||
assert io.exit_code == 0
|
||||
out = io.stdout.decode()
|
||||
assert "words.txt" in out
|
||||
assert "17" in out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ls_recursive_descends_subdirs(ws):
|
||||
io = await ws.execute("ls -R /dbx/")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert "inner.txt" in io.stdout.decode()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ls_missing_path_warns(ws):
|
||||
io = await ws.execute("ls /dbx/missing")
|
||||
|
||||
assert io.exit_code != 0
|
||||
@@ -0,0 +1,93 @@
|
||||
# ========= 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 import MountMode, Workspace
|
||||
from tests.resource.databricks_volume.test_databricks_volume import (
|
||||
FakeFiles, make_resource, seed_directory)
|
||||
|
||||
ROOT = "/Volumes/main/default/agent_files/root"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dbx_files() -> FakeFiles:
|
||||
files = FakeFiles()
|
||||
seed_directory(files, ROOT)
|
||||
return files
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def write_ws(dbx_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(dbx_files)}, mode=MountMode.WRITE)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def read_ws(dbx_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(dbx_files)}, mode=MountMode.READ)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mkdir_creates_directory(write_ws, dbx_files):
|
||||
io = await write_ws.execute("mkdir /dbx/newdir")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert f"{ROOT}/newdir" in dbx_files.directory_metadata
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mkdir_parent_missing_fails(write_ws):
|
||||
io = await write_ws.execute("mkdir /dbx/a/b")
|
||||
|
||||
assert io.exit_code != 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mkdir_parents_creates_chain(write_ws, dbx_files):
|
||||
io = await write_ws.execute("mkdir -p /dbx/a/b/c")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert f"{ROOT}/a/b/c" in dbx_files.directory_metadata
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mkdir_writes_are_mount_relative(write_ws):
|
||||
io = await write_ws.execute("mkdir /dbx/newdir")
|
||||
|
||||
assert io.exit_code == 0
|
||||
for key in io.writes:
|
||||
assert key.startswith("/dbx/")
|
||||
assert not key.startswith("/dbx/dbx/")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mkdir_read_only_mount_rejected(read_ws, dbx_files):
|
||||
io = await read_ws.execute("mkdir /dbx/newdir")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert b"read-only" in io.stderr
|
||||
assert dbx_files.create_directory_calls == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ops_mkdir(write_ws, dbx_files):
|
||||
await write_ws.ops.mkdir("/dbx/opdir")
|
||||
|
||||
assert f"{ROOT}/opdir" in dbx_files.directory_metadata
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ops_mkdir_read_only_rejected(read_ws):
|
||||
with pytest.raises(PermissionError):
|
||||
await read_ws.ops.mkdir("/dbx/opdir")
|
||||
@@ -0,0 +1,156 @@
|
||||
# ========= 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 import MountMode, Workspace
|
||||
from tests.resource.databricks_volume.test_databricks_volume import (
|
||||
FakeFiles, make_resource, seed_directory, seed_file)
|
||||
|
||||
ROOT = "/Volumes/main/default/agent_files/root"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dbx_files() -> FakeFiles:
|
||||
files = FakeFiles()
|
||||
seed_directory(files, ROOT)
|
||||
seed_file(files, f"{ROOT}/src.txt", b"data")
|
||||
return files
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def write_ws(dbx_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(dbx_files)}, mode=MountMode.WRITE)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def read_ws(dbx_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(dbx_files)}, mode=MountMode.READ)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mv_file_moves_bytes(write_ws, dbx_files):
|
||||
io = await write_ws.execute("mv /dbx/src.txt /dbx/dst.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert dbx_files.downloads[f"{ROOT}/dst.txt"] == b"data"
|
||||
assert f"{ROOT}/src.txt" not in dbx_files.downloads
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mv_writes_both_parents_mount_relative(write_ws):
|
||||
io = await write_ws.execute("mv /dbx/src.txt /dbx/dst.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert "/dbx/src.txt" in io.writes
|
||||
assert "/dbx/dst.txt" in io.writes
|
||||
for key in io.writes:
|
||||
assert not key.startswith("/dbx/dbx/")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mv_no_clobber_skips_existing(write_ws, dbx_files):
|
||||
seed_file(dbx_files, f"{ROOT}/dst.txt", b"keep")
|
||||
|
||||
io = await write_ws.execute("mv -n /dbx/src.txt /dbx/dst.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert dbx_files.downloads[f"{ROOT}/dst.txt"] == b"keep"
|
||||
assert dbx_files.downloads[f"{ROOT}/src.txt"] == b"data"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mv_read_only_mount_rejected(read_ws, dbx_files):
|
||||
io = await read_ws.execute("mv /dbx/src.txt /dbx/dst.txt")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert b"read-only" in io.stderr
|
||||
assert dbx_files.downloads[f"{ROOT}/src.txt"] == b"data"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ops_rename(write_ws, dbx_files):
|
||||
await write_ws.ops.rename("/dbx/src.txt", "/dbx/renamed.txt")
|
||||
|
||||
assert dbx_files.downloads[f"{ROOT}/renamed.txt"] == b"data"
|
||||
assert f"{ROOT}/src.txt" not in dbx_files.downloads
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mv_onto_same_path_errors_and_preserves_file(
|
||||
write_ws, dbx_files):
|
||||
io = await write_ws.execute("mv /dbx/src.txt /dbx/src.txt")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert b"are the same file" in io.stderr
|
||||
assert dbx_files.downloads[f"{ROOT}/src.txt"] == b"data"
|
||||
assert f"{ROOT}/src.txt" not in dbx_files.delete_calls
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mv_into_dir_where_file_already_lives_errors_and_preserves_file(
|
||||
write_ws, dbx_files):
|
||||
io = await write_ws.execute("mv /dbx/src.txt /dbx/")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert b"are the same file" in io.stderr
|
||||
assert dbx_files.downloads[f"{ROOT}/src.txt"] == b"data"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ops_rename_onto_same_path_is_noop(write_ws, dbx_files):
|
||||
await write_ws.ops.rename("/dbx/src.txt", "/dbx/src.txt")
|
||||
|
||||
assert dbx_files.downloads[f"{ROOT}/src.txt"] == b"data"
|
||||
assert f"{ROOT}/src.txt" not in dbx_files.delete_calls
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mv_multiple_sources_require_directory(write_ws, dbx_files):
|
||||
seed_file(dbx_files, f"{ROOT}/a.txt", b"AAA")
|
||||
seed_file(dbx_files, f"{ROOT}/b.txt", b"BBB")
|
||||
seed_file(dbx_files, f"{ROOT}/target.txt", b"target")
|
||||
|
||||
io = await write_ws.execute("mv /dbx/a.txt /dbx/b.txt /dbx/target.txt")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert b"not a directory" in io.stderr
|
||||
assert dbx_files.downloads[f"{ROOT}/a.txt"] == b"AAA"
|
||||
assert dbx_files.downloads[f"{ROOT}/b.txt"] == b"BBB"
|
||||
assert dbx_files.downloads[f"{ROOT}/target.txt"] == b"target"
|
||||
assert f"{ROOT}/a.txt" not in dbx_files.delete_calls
|
||||
assert f"{ROOT}/b.txt" not in dbx_files.delete_calls
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mv_missing_source_reports_cannot_stat(write_ws, dbx_files):
|
||||
io = await write_ws.execute("mv /dbx/missing /dbx/missing")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert b"cannot stat" in io.stderr
|
||||
assert b"are the same file" not in io.stderr
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mv_into_itself_errors_and_preserves_source(write_ws, dbx_files):
|
||||
seed_directory(dbx_files, f"{ROOT}/d")
|
||||
seed_file(dbx_files, f"{ROOT}/d/a.txt", b"aaa")
|
||||
|
||||
io = await write_ws.execute("mv /dbx/d /dbx/d")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert b"subdirectory of itself" in io.stderr
|
||||
assert dbx_files.downloads[f"{ROOT}/d/a.txt"] == b"aaa"
|
||||
assert f"{ROOT}/d" in dbx_files.directory_metadata
|
||||
assert f"{ROOT}/d/a.txt" not in dbx_files.delete_calls
|
||||
@@ -0,0 +1,33 @@
|
||||
# ========= 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
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_nl(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("nl /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert b" 1\tbeta\n" in io.stdout
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_nl_resolves_glob(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("nl /dbx/*.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert b" 1\tdelta\n" in io.stdout
|
||||
@@ -0,0 +1,41 @@
|
||||
# ========= 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.commands.builtin.databricks_volume import COMMANDS
|
||||
|
||||
TEXT_COMMANDS = {
|
||||
"awk",
|
||||
"cut",
|
||||
"diff",
|
||||
"jq",
|
||||
"nl",
|
||||
"sed",
|
||||
"sort",
|
||||
"tr",
|
||||
"uniq",
|
||||
"wc",
|
||||
}
|
||||
|
||||
|
||||
def test_databricks_volume_text_commands_registered_read_only():
|
||||
registered = [
|
||||
registered for command in COMMANDS
|
||||
for registered in command._registered_commands
|
||||
]
|
||||
names = {command.name for command in registered}
|
||||
assert TEXT_COMMANDS <= names
|
||||
for command in registered:
|
||||
if command.name in TEXT_COMMANDS:
|
||||
assert command.resource == "databricks_volume"
|
||||
assert not command.write
|
||||
@@ -0,0 +1,61 @@
|
||||
# ========= 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 import MountMode, Workspace
|
||||
from tests.resource.databricks_volume.test_databricks_volume import (
|
||||
FakeFiles, make_resource, seed_directory, seed_file)
|
||||
|
||||
ROOT = "/Volumes/main/default/agent_files/root"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dbx_files() -> FakeFiles:
|
||||
files = FakeFiles()
|
||||
seed_directory(files, ROOT)
|
||||
seed_file(files, f"{ROOT}/words.txt", b"beta\nalpha\nalpha\n")
|
||||
files.create_directory(f"{ROOT}/sub")
|
||||
seed_file(files, f"{ROOT}/sub/inner.txt", b"gamma\nalpha\n")
|
||||
return files
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ws(dbx_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(dbx_files)}, mode=MountMode.READ)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rg_searches_path_recursively(ws):
|
||||
io = await ws.execute("rg alpha /dbx/")
|
||||
|
||||
assert io.exit_code == 0
|
||||
out = io.stdout.decode()
|
||||
assert "words.txt" in out
|
||||
assert "inner.txt" in out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rg_count_only(ws):
|
||||
io = await ws.execute("rg -c alpha /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert b"2" in io.stdout
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rg_no_match_exits_nonzero(ws):
|
||||
io = await ws.execute("rg zeta /dbx/words.txt")
|
||||
|
||||
assert io.exit_code != 0
|
||||
@@ -0,0 +1,93 @@
|
||||
# ========= 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 import MountMode, Workspace
|
||||
from tests.resource.databricks_volume.test_databricks_volume import (
|
||||
FakeFiles, make_resource, seed_directory, seed_file)
|
||||
|
||||
ROOT = "/Volumes/main/default/agent_files/root"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dbx_files() -> FakeFiles:
|
||||
files = FakeFiles()
|
||||
seed_directory(files, ROOT)
|
||||
files.create_directory(f"{ROOT}/d")
|
||||
seed_file(files, f"{ROOT}/d/a.txt", b"a")
|
||||
files.create_directory(f"{ROOT}/d/sub")
|
||||
seed_file(files, f"{ROOT}/d/sub/b.txt", b"b")
|
||||
return files
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def write_ws(dbx_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(dbx_files)}, mode=MountMode.WRITE)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def read_ws(dbx_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(dbx_files)}, mode=MountMode.READ)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rm_recursive_removes_tree(write_ws, dbx_files):
|
||||
io = await write_ws.execute("rm -r /dbx/d")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert f"{ROOT}/d" not in dbx_files.directory_metadata
|
||||
assert f"{ROOT}/d/sub/b.txt" not in dbx_files.downloads
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rm_recursive_writes_are_mount_relative(write_ws):
|
||||
io = await write_ws.execute("rm -r /dbx/d")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.writes
|
||||
for key in io.writes:
|
||||
assert key.startswith("/dbx/")
|
||||
assert not key.startswith("/dbx/dbx/")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_plain_rm_on_directory_fails(write_ws, dbx_files):
|
||||
io = await write_ws.execute("rm /dbx/d")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert f"{ROOT}/d" in dbx_files.directory_metadata
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rm_force_missing_succeeds(write_ws):
|
||||
io = await write_ws.execute("rm -f /dbx/missing.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rm_missing_fails(write_ws):
|
||||
io = await write_ws.execute("rm /dbx/missing.txt")
|
||||
|
||||
assert io.exit_code != 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rm_recursive_read_only_rejected(read_ws, dbx_files):
|
||||
io = await read_ws.execute("rm -r /dbx/d")
|
||||
|
||||
assert io.exit_code != 0
|
||||
assert b"read-only" in io.stderr
|
||||
assert f"{ROOT}/d" in dbx_files.directory_metadata
|
||||
@@ -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. =========
|
||||
|
||||
import pytest
|
||||
|
||||
from mirage.commands.builtin.databricks_volume import COMMANDS
|
||||
|
||||
|
||||
def _sed_command():
|
||||
for cmd in COMMANDS:
|
||||
for rc in cmd._registered_commands:
|
||||
if rc.name == "sed":
|
||||
return cmd
|
||||
raise LookupError("sed not registered for databricks_volume")
|
||||
|
||||
|
||||
sed_command = _sed_command()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_sed(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute(
|
||||
"sed s/alpha/ALPHA/g /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"beta\nALPHA\nALPHA\n"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_sed_resolves_glob(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute(
|
||||
"sed s/delta/DELTA/ /dbx/*.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert b"DELTA\n" in io.stdout
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_databricks_volume_sed_in_place_writes_back(
|
||||
databricks_text_workspace):
|
||||
# The volume has a write op, so -i edits in place through the shared
|
||||
# builder (the old bespoke wrapper refused it unconditionally, #382).
|
||||
io = await databricks_text_workspace.execute(
|
||||
"sed -i s/alpha/ALPHA/g /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.writes.get("/dbx/words.txt") == b"beta\nALPHA\nALPHA\n"
|
||||
@@ -0,0 +1,33 @@
|
||||
# ========= 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
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_sort(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("sort /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"alpha\nalpha\nbeta\n"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_sort_resolves_glob(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("sort /dbx/*.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert b"alpha\nalpha\nbeta\n" in io.stdout
|
||||
@@ -0,0 +1,41 @@
|
||||
# ========= 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
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_default_format(databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("stat /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
out = io.stdout.decode()
|
||||
assert "name=words.txt" in out
|
||||
assert "size=17" in out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_custom_format(databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute(
|
||||
"stat -c '%n %s' /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout.decode().strip() == "/dbx/words.txt 17"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_stat_missing_file_fails(databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("stat /dbx/missing.txt")
|
||||
|
||||
assert io.exit_code != 0
|
||||
@@ -0,0 +1,25 @@
|
||||
# ========= 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
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_tr(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute(
|
||||
"cat /dbx/words.txt | tr a-z A-Z")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"BETA\nALPHA\nALPHA\n"
|
||||
@@ -0,0 +1,69 @@
|
||||
# ========= 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 import MountMode, Workspace
|
||||
from tests.resource.databricks_volume.test_databricks_volume import (
|
||||
FakeFiles, make_resource, seed_directory, seed_file)
|
||||
|
||||
ROOT = "/Volumes/main/default/agent_files/root"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dbx_files() -> FakeFiles:
|
||||
files = FakeFiles()
|
||||
seed_directory(files, ROOT)
|
||||
seed_file(files, f"{ROOT}/words.txt", b"beta\nalpha\nalpha\n")
|
||||
files.create_directory(f"{ROOT}/sub")
|
||||
seed_file(files, f"{ROOT}/sub/inner.txt", b"gamma\nalpha\n")
|
||||
files.create_directory(f"{ROOT}/sub/deep")
|
||||
seed_file(files, f"{ROOT}/sub/deep/leaf.txt", b"leaf\n")
|
||||
return files
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ws(dbx_files: FakeFiles) -> Workspace:
|
||||
return Workspace({"/dbx/": make_resource(dbx_files)}, mode=MountMode.READ)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tree_lists_nested_entries(ws):
|
||||
io = await ws.execute("tree /dbx/")
|
||||
|
||||
assert io.exit_code == 0
|
||||
out = io.stdout.decode()
|
||||
assert "words.txt" in out
|
||||
assert "inner.txt" in out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tree_max_depth(ws):
|
||||
io = await ws.execute("tree -L 1 /dbx/")
|
||||
|
||||
assert io.exit_code == 0
|
||||
out = io.stdout.decode()
|
||||
assert "sub" in out
|
||||
assert "inner.txt" in out
|
||||
assert "leaf.txt" not in out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tree_dirs_only(ws):
|
||||
io = await ws.execute("tree -d /dbx/")
|
||||
|
||||
assert io.exit_code == 0
|
||||
out = io.stdout.decode()
|
||||
assert "sub" in out
|
||||
assert "words.txt" not in out
|
||||
@@ -0,0 +1,24 @@
|
||||
# ========= 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
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_uniq(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("uniq /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"beta\nalpha\n"
|
||||
@@ -0,0 +1,33 @@
|
||||
# ========= 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
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_wc(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("wc -l /dbx/words.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert io.stdout == b"3 /dbx/words.txt\n"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workspace_execute_databricks_volume_wc_resolves_glob(
|
||||
databricks_text_workspace):
|
||||
io = await databricks_text_workspace.execute("wc -l /dbx/*.txt")
|
||||
|
||||
assert io.exit_code == 0
|
||||
assert b"/dbx/more.txt" in io.stdout
|
||||
Reference in New Issue
Block a user