Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:30:44 +08:00

421 lines
12 KiB
Python

import pytest
from mirage.commands.builtin.generic.sed import sed
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)
if spec.virtual not in store:
raise FileNotFoundError(spec.virtual)
return store[spec.virtual]
async def write_bytes(accessor, path, data, index=None):
spec = path if isinstance(path, PathSpec) else PathSpec(
resource_path=(path).strip("/"), virtual=path, directory=path)
store[spec.virtual] = data
return read_bytes, write_bytes, store
@pytest.mark.asyncio
async def test_sed_stdin_simple_sub():
rb, wb, _ = _make_backend({})
output, _ = await sed(
[],
"s/hello/bye/",
read_bytes=rb,
write_bytes=wb,
stdin=b"hello world\n",
)
assert output == b"bye world\n"
@pytest.mark.asyncio
async def test_sed_file_simple_sub_emits_output():
rb, wb, _ = _make_backend({"/a.txt": b"hello world\n"})
output, _ = await sed(
[_spec("/a.txt")],
"s/hello/bye/",
read_bytes=rb,
write_bytes=wb,
)
assert output == b"bye world\n"
@pytest.mark.asyncio
async def test_sed_inplace_simple_sub_writes_file():
rb, wb, store = _make_backend({"/a.txt": b"hello world\n"})
output, io = await sed(
[_spec("/a.txt")],
"s/hello/bye/",
read_bytes=rb,
write_bytes=wb,
in_place=True,
)
assert output is None
assert store["/a.txt"] == b"bye world\n"
assert io.writes == {"/a.txt": b"bye world\n"}
@pytest.mark.asyncio
async def test_sed_inplace_multi_path_writes_all():
rb, wb, store = _make_backend({
"/a.txt": b"hello a\n",
"/b.txt": b"hello b\n",
})
_output, io = await sed(
[_spec("/a.txt"), _spec("/b.txt")],
"s/hello/bye/",
read_bytes=rb,
write_bytes=wb,
in_place=True,
)
assert store["/a.txt"] == b"bye a\n"
assert store["/b.txt"] == b"bye b\n"
assert set(io.writes.keys()) == {"/a.txt", "/b.txt"}
@pytest.mark.asyncio
async def test_sed_global_flag_replaces_all():
rb, wb, _ = _make_backend({})
output, _ = await sed(
[],
"s/a/X/g",
read_bytes=rb,
write_bytes=wb,
stdin=b"banana\n",
)
assert output == b"bXnXnX\n"
@pytest.mark.asyncio
async def test_sed_first_match_only_by_default():
rb, wb, _ = _make_backend({})
output, _ = await sed(
[],
"s/a/X/",
read_bytes=rb,
write_bytes=wb,
stdin=b"banana\n",
)
assert output == b"bXnana\n"
@pytest.mark.asyncio
async def test_sed_delete_program():
"""Delete command 'd' should drop matching lines."""
rb, wb, _ = _make_backend({})
output, _ = await sed(
[],
"/skip/d",
read_bytes=rb,
write_bytes=wb,
stdin=b"keep\nskip me\nkeep too\n",
)
decoded = output.decode()
assert "keep" in decoded
assert "skip me" not in decoded
@pytest.mark.asyncio
async def test_sed_n_suppress_with_p():
"""-n suppresses default output; only explicit 'p' prints."""
rb, wb, _ = _make_backend({})
output, _ = await sed(
[],
"/match/p",
read_bytes=rb,
write_bytes=wb,
stdin=b"no\nmatch line\nno\n",
suppress=True,
)
decoded = output.decode()
assert "match line" in decoded
@pytest.mark.asyncio
async def test_sed_no_paths_no_stdin_raises():
rb, wb, _ = _make_backend({})
with pytest.raises(ValueError, match="usage"):
await sed([], "s/a/b/", read_bytes=rb, write_bytes=wb)
@pytest.mark.asyncio
async def test_sed_numeric_count_replaces_nth_occurrence():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"s/o/O/2",
read_bytes=rb,
write_bytes=wb,
stdin=b"oooo\n")
assert output == b"oOoo\n"
@pytest.mark.asyncio
async def test_sed_numeric_count_with_g_replaces_nth_onward():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"s/o/O/2g",
read_bytes=rb,
write_bytes=wb,
stdin=b"oooo\n")
assert output == b"oOOO\n"
@pytest.mark.asyncio
async def test_sed_count_is_per_line():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"s/o/O/2",
read_bytes=rb,
write_bytes=wb,
stdin=b"oo\noo\n")
assert output == b"oO\noO\n"
@pytest.mark.asyncio
async def test_sed_p_flag_prints_substituted_line_twice():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"s/hi/HI/p",
read_bytes=rb,
write_bytes=wb,
stdin=b"hi\nbye\n")
assert output == b"HI\nHI\nbye\n"
@pytest.mark.asyncio
async def test_sed_p_flag_under_suppress_prints_only_substituted():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"s/hi/HI/p",
read_bytes=rb,
write_bytes=wb,
stdin=b"hi\nbye\n",
suppress=True)
assert output == b"HI\n"
@pytest.mark.asyncio
async def test_sed_y_transliterate():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"y/el/ip/",
read_bytes=rb,
write_bytes=wb,
stdin=b"hello\n")
assert output == b"hippo\n"
@pytest.mark.asyncio
async def test_sed_y_mismatched_lengths_raises():
rb, wb, _ = _make_backend({})
with pytest.raises(ValueError, match="different lengths"):
await sed([], "y/ab/x/", read_bytes=rb, write_bytes=wb, stdin=b"a\n")
@pytest.mark.asyncio
async def test_sed_c_no_address_changes_every_line():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"c\\\nX",
read_bytes=rb,
write_bytes=wb,
stdin=b"a\nb\nc\n")
assert output == b"X\nX\nX\n"
@pytest.mark.asyncio
async def test_sed_c_single_address():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"2c\\\nX",
read_bytes=rb,
write_bytes=wb,
stdin=b"a\nb\nc\n")
assert output == b"a\nX\nc\n"
@pytest.mark.asyncio
async def test_sed_c_range_emits_once():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"2,3c\\\nX",
read_bytes=rb,
write_bytes=wb,
stdin=b"a\nb\nc\nd\n")
assert output == b"a\nX\nd\n"
@pytest.mark.asyncio
async def test_sed_bre_group_and_backref():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
r"s/\(foo\)/[\1]/",
read_bytes=rb,
write_bytes=wb,
stdin=b"foo\n")
assert output == b"[foo]\n"
@pytest.mark.asyncio
async def test_sed_bre_plus_is_literal():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"s/a+/X/",
read_bytes=rb,
write_bytes=wb,
stdin=b"a+b\n")
assert output == b"Xb\n"
@pytest.mark.asyncio
async def test_sed_bre_backslash_plus_is_one_or_more():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
r"s/a\+/X/",
read_bytes=rb,
write_bytes=wb,
stdin=b"aaab\n")
assert output == b"Xb\n"
@pytest.mark.asyncio
async def test_sed_ere_group_and_plus():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
r"s/(foo)/[\1]/",
read_bytes=rb,
write_bytes=wb,
stdin=b"foo\n",
extended=True)
assert output == b"[foo]\n"
output, _ = await sed([],
"s/a+/X/",
read_bytes=rb,
write_bytes=wb,
stdin=b"aaab\n",
extended=True)
assert output == b"Xb\n"
@pytest.mark.asyncio
async def test_sed_ere_address():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"/a+/d",
read_bytes=rb,
write_bytes=wb,
stdin=b"aaa\nbbb\n",
extended=True)
assert output == b"bbb\n"
@pytest.mark.asyncio
async def test_sed_negate_line():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"2!d",
read_bytes=rb,
write_bytes=wb,
stdin=b"a\nb\nc\n")
assert output == b"b\n"
@pytest.mark.asyncio
async def test_sed_negate_regex():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"/b/!d",
read_bytes=rb,
write_bytes=wb,
stdin=b"a\nb\nc\n")
assert output == b"b\n"
@pytest.mark.asyncio
async def test_sed_negate_last_with_suppress():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"$!p",
read_bytes=rb,
write_bytes=wb,
stdin=b"a\nb\nc\n",
suppress=True)
assert output == b"a\nb\n"
@pytest.mark.asyncio
async def test_sed_negate_range():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"1,2!s/./X/",
read_bytes=rb,
write_bytes=wb,
stdin=b"a\nb\nc\nd\n")
assert output == b"a\nb\nX\nX\n"
@pytest.mark.asyncio
async def test_sed_join_all_idiom():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
r":a;N;$!ba;s/\n/,/g",
read_bytes=rb,
write_bytes=wb,
stdin=b"a\nb\nc\n")
assert output == b"a,b,c\n"
@pytest.mark.asyncio
async def test_sed_hold_accumulate():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"H;${x;p}",
read_bytes=rb,
write_bytes=wb,
stdin=b"a\nb\n",
suppress=True)
assert output == b"\na\nb\n"
@pytest.mark.asyncio
async def test_sed_preserves_missing_final_newline():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
"s/o/O/",
read_bytes=rb,
write_bytes=wb,
stdin=b"foo")
assert output == b"fOo"
@pytest.mark.asyncio
async def test_sed_escaped_delimiter():
rb, wb, _ = _make_backend({})
output, _ = await sed([],
r"s/a\/b/c/",
read_bytes=rb,
write_bytes=wb,
stdin=b"a/b\n")
assert output == b"c\n"
@pytest.mark.asyncio
async def test_sed_zero_count_rejected():
rb, wb, _ = _make_backend({})
with pytest.raises(ValueError, match="may not be zero"):
await sed([], "s/o/O/0", read_bytes=rb, write_bytes=wb, stdin=b"oo\n")