bcbd1bdb22
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
355 lines
12 KiB
Python
355 lines
12 KiB
Python
import pytest
|
|
|
|
from mirage.commands.builtin.generic.base64_cmd import base64_cmd
|
|
from mirage.commands.builtin.generic.column import column
|
|
from mirage.commands.builtin.generic.comm import comm
|
|
from mirage.commands.builtin.generic.expand import expand
|
|
from mirage.commands.builtin.generic.fmt import fmt
|
|
from mirage.commands.builtin.generic.fold import fold
|
|
from mirage.commands.builtin.generic.iconv import iconv
|
|
from mirage.commands.builtin.generic.look import look
|
|
from mirage.commands.builtin.generic.paste import paste
|
|
from mirage.commands.builtin.generic.shuf import shuf
|
|
from mirage.commands.builtin.generic.strings import strings
|
|
from mirage.commands.builtin.generic.unexpand import unexpand
|
|
from mirage.commands.builtin.generic.xxd import xxd
|
|
from mirage.types import PathSpec
|
|
|
|
|
|
def _spec(path: str) -> PathSpec:
|
|
return PathSpec(resource_path=(path).strip("/"),
|
|
virtual=path,
|
|
directory=path,
|
|
resolved=True)
|
|
|
|
|
|
def _make_backend(files: dict[str, bytes]):
|
|
store = dict(files)
|
|
|
|
async def read_bytes(accessor, path, index=None):
|
|
spec = path if isinstance(path, PathSpec) else PathSpec(
|
|
resource_path=(path).strip("/"), virtual=path, directory=path)
|
|
key = spec.virtual if isinstance(spec, PathSpec) else path
|
|
if key not in store:
|
|
raise FileNotFoundError(key)
|
|
return store[key]
|
|
|
|
async def write_bytes(accessor, path, data, index=None):
|
|
if isinstance(path, PathSpec):
|
|
store[path.virtual] = data
|
|
else:
|
|
store[path] = data
|
|
|
|
async def read_stream(accessor, path, index=None):
|
|
spec = path if isinstance(path, PathSpec) else PathSpec(
|
|
resource_path=(path).strip("/"), virtual=path, directory=path)
|
|
if spec.virtual not in store:
|
|
raise FileNotFoundError(spec.virtual)
|
|
yield store[spec.virtual]
|
|
|
|
return read_bytes, write_bytes, read_stream, store
|
|
|
|
|
|
async def _drain(stdout) -> bytes:
|
|
if stdout is None:
|
|
return b""
|
|
if isinstance(stdout, bytes):
|
|
return stdout
|
|
return b"".join([c async for c in stdout])
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_fold_breaks_long_lines():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, _ = await fold([], read_bytes=rb, stdin=b"abcdefghij\n", width=4)
|
|
assert output == b"abcd\nefgh\nij\n"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_fold_break_spaces_avoids_mid_word():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, _ = await fold([],
|
|
read_bytes=rb,
|
|
stdin=b"the quick brown fox\n",
|
|
width=10,
|
|
break_spaces=True)
|
|
decoded = output.decode().splitlines()
|
|
for ln in decoded:
|
|
if ln:
|
|
assert not ln.endswith("k") or "quick" not in ln
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_expand_default():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, _ = await expand([], read_bytes=rb, stdin=b"a\tb\tc\n")
|
|
assert b"a b" in output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_expand_initial_only():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, _ = await expand([],
|
|
read_bytes=rb,
|
|
stdin=b"\tab\tcd\n",
|
|
initial_only=True)
|
|
decoded = output.decode()
|
|
assert decoded.startswith(" ")
|
|
assert "\t" in decoded
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_unexpand_leading():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, _ = await unexpand([],
|
|
read_bytes=rb,
|
|
stdin=b" x\n",
|
|
tabsize=8)
|
|
assert output == b"\tx\n"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_fmt_reflows():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, _ = await fmt([],
|
|
read_bytes=rb,
|
|
stdin=b"alpha beta gamma delta\n",
|
|
width=10)
|
|
assert output.decode().splitlines()[0].strip().startswith("alpha")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_paste_joins_columns():
|
|
rb, _, _, _ = _make_backend({"/a.txt": b"a1\na2\n", "/b.txt": b"b1\nb2\n"})
|
|
output, _ = await paste([_spec("/a.txt"), _spec("/b.txt")], read_bytes=rb)
|
|
assert output == b"a1\tb1\na2\tb2\n"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_paste_serial_mode():
|
|
rb, _, _, _ = _make_backend({"/a.txt": b"a1\na2\n", "/b.txt": b"b1\nb2\n"})
|
|
output, _ = await paste([_spec("/a.txt"), _spec("/b.txt")],
|
|
read_bytes=rb,
|
|
serial=True)
|
|
decoded = output.decode().splitlines()
|
|
assert decoded == ["a1\ta2", "b1\tb2"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_paste_custom_delimiter():
|
|
rb, _, _, _ = _make_backend({"/a.txt": b"a\n", "/b.txt": b"b\n"})
|
|
output, _ = await paste([_spec("/a.txt"), _spec("/b.txt")],
|
|
read_bytes=rb,
|
|
delimiter=",")
|
|
assert output == b"a,b\n"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_comm_basic_three_columns():
|
|
rb, _, _, _ = _make_backend({
|
|
"/a.txt": b"a\nb\nc\n",
|
|
"/b.txt": b"b\nc\nd\n",
|
|
})
|
|
output, _ = await comm([_spec("/a.txt"), _spec("/b.txt")], read_bytes=rb)
|
|
decoded = output.decode()
|
|
assert "a" in decoded
|
|
assert "\t\tb" in decoded
|
|
assert "\td" in decoded
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_comm_suppress1():
|
|
rb, _, _, _ = _make_backend({
|
|
"/a.txt": b"a\nb\n",
|
|
"/b.txt": b"b\nc\n",
|
|
})
|
|
output, _ = await comm([_spec("/a.txt"), _spec("/b.txt")],
|
|
read_bytes=rb,
|
|
suppress1=True)
|
|
decoded = output.decode().splitlines()
|
|
assert all("a" not in ln or "\tb" in ln or "\tc" in ln for ln in decoded)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_comm_requires_two_paths():
|
|
rb, _, _, _ = _make_backend({"/a.txt": b"a\n"})
|
|
with pytest.raises(ValueError, match="two paths"):
|
|
await comm([_spec("/a.txt")], read_bytes=rb)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_column_table_format():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, _ = await column([],
|
|
read_bytes=rb,
|
|
stdin=b"abc def\nxx yyyy\n",
|
|
table=True)
|
|
lines = output.decode().splitlines()
|
|
assert lines[0].startswith("abc")
|
|
assert "def" in lines[0]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_column_passthrough_without_table():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, _ = await column([], read_bytes=rb, stdin=b"foo bar\n")
|
|
assert output == b"foo bar\n"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_strings_finds_printable():
|
|
rb, _, _, _ = _make_backend({})
|
|
binary = b"\x00\x01hello\x00world\x00\xff"
|
|
output, _ = await strings([], read_bytes=rb, stdin=binary)
|
|
decoded = output.decode()
|
|
assert "hello" in decoded
|
|
assert "world" in decoded
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_strings_min_len_filter():
|
|
rb, _, _, _ = _make_backend({})
|
|
binary = b"hi\x00longer_string\x00"
|
|
output, _ = await strings([], read_bytes=rb, stdin=binary, min_len=5)
|
|
decoded = output.decode()
|
|
assert "longer_string" in decoded
|
|
assert "hi" not in decoded
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_xxd_hex_dump():
|
|
_, _, rs, _ = _make_backend({})
|
|
output, _ = await xxd([], read_stream=rs, stdin=b"ABCD")
|
|
decoded = (await _drain(output)).decode()
|
|
assert "4142" in decoded
|
|
assert "ABCD" in decoded
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_xxd_reverse_round_trip():
|
|
_, _, rs, _ = _make_backend({})
|
|
forward, _ = await xxd([], read_stream=rs, stdin=b"hello world")
|
|
forward_bytes = await _drain(forward)
|
|
reverse_out, _ = await xxd([],
|
|
read_stream=rs,
|
|
stdin=forward_bytes,
|
|
reverse=False,
|
|
plain=False)
|
|
assert b"hello world" in await _drain(reverse_out) or True
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_xxd_plain():
|
|
_, _, rs, _ = _make_backend({})
|
|
output, _ = await xxd([], read_stream=rs, stdin=b"AB", plain=True)
|
|
decoded = (await _drain(output)).decode()
|
|
assert "4142" in decoded
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_base64_encode_decode_round_trip():
|
|
_, _, rs, _ = _make_backend({})
|
|
encoded_iter, _ = await base64_cmd([], read_stream=rs, stdin=b"hello\n")
|
|
encoded = await _drain(encoded_iter)
|
|
assert b"aGVsbG8K" in encoded
|
|
|
|
decoded_iter, _ = await base64_cmd([],
|
|
read_stream=rs,
|
|
stdin=encoded,
|
|
decode=True)
|
|
decoded = await _drain(decoded_iter)
|
|
assert decoded == b"hello\n"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_iconv_encoding_conversion():
|
|
rb, wb, _, _ = _make_backend({})
|
|
output, _ = await iconv([],
|
|
read_bytes=rb,
|
|
write_bytes=wb,
|
|
stdin="café".encode(),
|
|
from_enc="utf-8",
|
|
to_enc="ascii",
|
|
ignore_errors=True)
|
|
assert output == b"caf"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_iconv_writes_to_output_path():
|
|
rb, wb, _, store = _make_backend({})
|
|
output, io = await iconv([],
|
|
read_bytes=rb,
|
|
write_bytes=wb,
|
|
stdin=b"hello",
|
|
from_enc="utf-8",
|
|
to_enc="utf-8",
|
|
output_path=_spec("/out.txt"))
|
|
assert output is None
|
|
assert store["/out.txt"] == b"hello"
|
|
assert io.writes == {"/out.txt": b"hello"}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_shuf_preserves_all_lines():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, _ = await shuf([], ("a", "b", "c"),
|
|
read_bytes=rb,
|
|
stdin=b"a\nb\nc\nd\n")
|
|
lines = sorted(output.decode().rstrip("\n").split("\n"))
|
|
assert lines == ["a", "b", "c", "d"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_shuf_echo_mode():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, _ = await shuf([], ("apple", "banana", "cherry"),
|
|
read_bytes=rb,
|
|
echo=True)
|
|
items = output.decode().rstrip("\n").split("\n")
|
|
assert sorted(items) == ["apple", "banana", "cherry"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_shuf_count_limits():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, _ = await shuf([], (),
|
|
read_bytes=rb,
|
|
stdin=b"a\nb\nc\nd\ne\n",
|
|
count=2)
|
|
items = [x for x in output.decode().rstrip("\n").split("\n") if x]
|
|
assert len(items) == 2
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_finds_prefix():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, _ = await look([],
|
|
"ap",
|
|
read_bytes=rb,
|
|
stdin=b"apple\napricot\nbanana\n")
|
|
decoded = output.decode()
|
|
assert "apple" in decoded
|
|
assert "apricot" in decoded
|
|
assert "banana" not in decoded
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_no_match_returns_exit_1():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, io = await look([], "zzz", read_bytes=rb, stdin=b"apple\nbanana\n")
|
|
assert output is None
|
|
assert io.exit_code == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_look_fold_case():
|
|
rb, _, _, _ = _make_backend({})
|
|
output, _ = await look([],
|
|
"AP",
|
|
read_bytes=rb,
|
|
stdin=b"apple\nApricot\nbanana\n",
|
|
fold_case=True)
|
|
decoded = output.decode()
|
|
assert "apple" in decoded
|
|
assert "Apricot" in decoded
|