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

1059 lines
36 KiB
Python

# ========= 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
import json
from functools import partial
import pytest
from mirage.accessor.s3 import S3Accessor
from mirage.commands.builtin.generic_bind.adapter import CommandIO
from mirage.commands.builtin.generic_bind.builders.jq import jq as jq_builder
from mirage.core.jq import JQ_EMPTY, jq_eval
from mirage.types import MountMode, PathSpec
from .conftest import collect, jq, mem_ws, run_raw, write_to_backend
async def _const_bytes(data, accessor, path, index=None):
return data
async def _collect_jq(ops, accessor, paths, expr):
out, _ = await jq_builder(ops, accessor, paths, expr)
if isinstance(out, bytes):
return out
if hasattr(out, "__aiter__"):
return b"".join([chunk async for chunk in out])
return b"".join(out)
def test_jq_dot_returns_full_object(backend):
data = {"a": 1, "b": 2}
write_to_backend(backend, "/tmp/f.json", json.dumps(data).encode())
result = jq(backend, "/tmp/f.json", ".")
assert result == data
def test_jq_key_access(backend):
write_to_backend(backend, "/tmp/f.json", b'{"name": "alice"}')
assert jq(backend, "/tmp/f.json", ".name") == "alice"
def test_jq_nested_key(backend):
write_to_backend(backend, "/tmp/f.json", b'{"a": {"b": 42}}')
assert jq(backend, "/tmp/f.json", ".a.b") == 42
def test_jq_array_iteration(backend):
write_to_backend(backend, "/tmp/f.json", b'{"items": [1, 2, 3]}')
result = jq(backend, "/tmp/f.json", ".items[]")
assert result == [1, 2, 3]
def test_jq_invalid_expression_raises(backend):
write_to_backend(backend, "/tmp/f.json", b'{"a": 1}')
with pytest.raises(ValueError):
jq(backend, "/tmp/f.json", "a.b")
def test_jq_pipe(backend):
write_to_backend(backend, "/tmp/f.json", b'{"items": [1, 2, 3]}')
result = jq(backend, "/tmp/f.json", ".items | length")
assert result == 3
def test_jq_select(backend):
write_to_backend(backend, "/tmp/f.json", b'[{"a": 1}, {"a": 2}, {"a": 3}]')
result = jq(backend, "/tmp/f.json", ".[] | select(.a > 1)")
assert result == [{"a": 2}, {"a": 3}]
def test_jq_map(backend):
write_to_backend(backend, "/tmp/f.json", b'[1, 2, 3]')
result = jq(backend, "/tmp/f.json", "map(. > 1)")
assert result == [False, True, True]
def test_jq_keys(backend):
write_to_backend(backend, "/tmp/f.json", b'{"b": 1, "a": 2}')
result = jq(backend, "/tmp/f.json", "keys")
assert result == ["a", "b"]
def test_jq_values_passes_through_non_null(backend):
write_to_backend(backend, "/tmp/f.json", b'{"a": 1, "b": 2}')
result = jq(backend, "/tmp/f.json", "values")
assert result == {"a": 1, "b": 2}
def test_jq_values_drops_null(backend):
write_to_backend(backend, "/tmp/f.json", b'{"a": 1}')
assert jq(backend, "/tmp/f.json", ".missing | values") is JQ_EMPTY
def test_jq_object_values_via_spread(backend):
write_to_backend(backend, "/tmp/f.json", b'{"a": 1, "b": 2}')
result = jq(backend, "/tmp/f.json", "[.[]]")
assert result == [1, 2]
def test_jq_array_slice(backend):
write_to_backend(backend, "/tmp/f.json", b'[10, 20, 30, 40, 50]')
result = jq(backend, "/tmp/f.json", ".[1:3]")
assert result == [20, 30]
def test_jq_length(backend):
write_to_backend(backend, "/tmp/f.json", b'[1, 2, 3]')
result = jq(backend, "/tmp/f.json", "length")
assert result == 3
def test_jq_string_interpolation():
result = jq_eval({"name": "world"}, '"Hello \\(.name)"')
assert result == "Hello world"
def test_jq_string_interpolation_nested():
result = jq_eval({"a": 1, "b": 2}, '"\\(.a) + \\(.b)"')
assert result == "1 + 2"
def test_jq_try_catch_returns_null_when_no_error():
# In real jq, .missing.x is NOT an error; it returns null.
# Hence catch is never triggered.
assert jq_eval({}, 'try .missing.x catch "fallback"') is None
def test_jq_try_catch_triggered_on_real_error():
assert jq_eval([1, 2, 3], 'try .name catch "fallback"') == "fallback"
def test_jq_try_no_catch_returns_null():
assert jq_eval({}, "try .missing.x") is None
def test_jq_try_no_catch_swallows_real_error():
assert jq_eval([1, 2, 3], "try .name") is JQ_EMPTY
class TestJqMapValues:
def test_map_values_dict(self):
result = jq_eval({"a": 1, "b": 2}, "map_values(. > 1)")
assert result == {"a": False, "b": True}
def test_map_values_list(self):
result = jq_eval([1, 2, 3], "map_values(. > 1)")
assert result == [False, True, True]
def test_map_values_dict_arithmetic(self):
result = jq_eval({"x": 10, "y": 20}, 'map_values(type)')
assert result == {"x": "number", "y": "number"}
class TestJqHas:
def test_has_present_key(self):
assert jq_eval({"name": "alice"}, 'has("name")') is True
def test_has_absent_key(self):
assert jq_eval({"name": "alice"}, 'has("age")') is False
def test_has_array_with_int_key(self):
# In real jq, has() on arrays takes an integer index.
assert jq_eval([1, 2, 3], "has(1)") is True
assert jq_eval([1, 2, 3], "has(5)") is False
class TestJqContains:
def test_contains_string_in_string(self):
assert jq_eval("foobar", 'contains("foo")') is True
def test_contains_string_not_found(self):
assert jq_eval("foobar", 'contains("xyz")') is False
def test_contains_element_requires_array_arg(self):
# In real jq, contains(x) requires arg of same type.
# For arrays, use contains([item]) — see test_contains_subarray.
with pytest.raises(ValueError):
jq_eval([1, 2, 3], "contains(2)")
def test_contains_subarray(self):
assert jq_eval([1, 2, 3], "contains([1, 2])") is True
def test_contains_subarray_not_found(self):
assert jq_eval([1, 2, 3], "contains([4, 5])") is False
class TestJqComparisons:
def test_eq_true(self):
assert jq_eval({"a": 1}, ".a == 1") is True
def test_eq_false(self):
assert jq_eval({"a": 1}, ".a == 2") is False
def test_neq_true(self):
assert jq_eval({"a": 1}, ".a != 2") is True
def test_neq_false(self):
assert jq_eval({"a": 1}, ".a != 1") is False
def test_gt(self):
assert jq_eval({"a": 5}, ".a > 3") is True
def test_gt_false(self):
assert jq_eval({"a": 1}, ".a > 3") is False
def test_lt(self):
assert jq_eval({"a": 1}, ".a < 3") is True
def test_lt_false(self):
assert jq_eval({"a": 5}, ".a < 3") is False
def test_gte(self):
assert jq_eval({"a": 3}, ".a >= 3") is True
def test_gte_false(self):
assert jq_eval({"a": 2}, ".a >= 3") is False
def test_lte(self):
assert jq_eval({"a": 3}, ".a <= 3") is True
def test_lte_false(self):
assert jq_eval({"a": 5}, ".a <= 3") is False
def test_string_eq(self):
assert jq_eval({"s": "hello"}, '.s == "hello"') is True
class TestJqPipes:
def test_three_stage_pipe(self, backend):
write_to_backend(backend, "/tmp/f.json",
b'{"data": {"items": [1, 2, 3]}}')
result = jq(backend, "/tmp/f.json", ".data | .items | length")
assert result == 3
def test_pipe_with_iteration_and_select(self, backend):
write_to_backend(backend, "/tmp/f.json",
b'[{"x": 1}, {"x": 5}, {"x": 10}]')
result = jq(backend, "/tmp/f.json", ".[] | select(.x > 3)")
assert result == [{"x": 5}, {"x": 10}]
def test_pipe_keys_then_length(self):
result = jq_eval({"a": 1, "b": 2, "c": 3}, "keys | length")
assert result == 3
def test_pipe_object_values_via_spread_then_sort(self):
# Real jq: `values` returns the object as-is (it's the identity for
# non-null). To get the object's values list, use [.[]].
result = jq_eval({"a": 3, "b": 1, "c": 2}, "[.[]] | sort")
assert result == [1, 2, 3]
def test_pipe_map_then_select(self):
data = [1, 2, 3, 4, 5]
result = jq_eval(data, "map(select(. > 2))")
assert result == [3, 4, 5]
class TestJqArrayAccess:
def test_index_access(self):
result = jq_eval({"items": ["a", "b", "c"]}, ".items[0]")
assert result == "a"
def test_index_access_last(self):
result = jq_eval({"items": ["a", "b", "c"]}, ".items[2]")
assert result == "c"
def test_nested_array_iteration(self, backend):
write_to_backend(backend, "/tmp/f.json",
b'{"users": [{"name": "alice"}, {"name": "bob"}]}')
result = jq(backend, "/tmp/f.json", ".users[].name")
assert result == ["alice", "bob"]
def test_slice_from_start(self):
result = jq_eval([1, 2, 3, 4, 5], ".[:2]")
assert result == [1, 2]
def test_slice_to_end(self):
result = jq_eval([1, 2, 3, 4, 5], ".[3:]")
assert result == [4, 5]
class TestJqEdgeCases:
def test_empty_object(self):
assert jq_eval({}, ".") == {}
def test_empty_array(self):
assert jq_eval([], ".") == []
def test_deeply_nested(self):
data = {"a": {"b": {"c": {"d": 42}}}}
assert jq_eval(data, ".a.b.c.d") == 42
def test_unicode_content(self, backend):
data = {"name": "café", "emoji": "hello"}
write_to_backend(backend, "/tmp/f.json", json.dumps(data).encode())
result = jq(backend, "/tmp/f.json", ".name")
assert result == "café"
def test_json_literal_number(self):
assert jq_eval({}, "42") == 42
def test_json_literal_string(self):
assert jq_eval({}, '"hello"') == "hello"
def test_json_literal_array(self):
assert jq_eval({}, "[1, 2, 3]") == [1, 2, 3]
def test_length_of_string(self):
assert jq_eval("hello", "length") == 5
def test_length_of_dict(self):
assert jq_eval({"a": 1, "b": 2}, "length") == 2
def test_keys_of_array(self):
assert jq_eval(["a", "b", "c"], "keys") == [0, 1, 2]
def test_string_interpolation_with_nested_expr(self):
data = {"items": [1, 2, 3]}
result = jq_eval(data, '"count: \\(.items | length)"')
assert result == "count: 3"
def test_select_drops_non_matching(self):
assert jq_eval({"a": 1}, "select(.a > 10)") is JQ_EMPTY
def test_map_requires_iterable(self):
# Real jq: map(.) on a non-iterable raises ValueError.
with pytest.raises(ValueError):
jq_eval(5, "map(.)")
def test_missing_key_returns_null(self):
# Real jq: missing keys silently return null (no exception).
assert jq_eval({"a": 1}, ".b") is None
def test_type_error_dot_on_list(self):
with pytest.raises((TypeError, KeyError, ValueError)):
jq_eval([1, 2, 3], ".name")
def test_try_catch_type_error(self):
result = jq_eval([1, 2], 'try .name catch "no key"')
assert result == "no key"
class TestJqComplex:
def test_map_select_keys(self):
data = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
result = jq_eval(data, "map(select(.a > 1)) | .[] | .b")
assert result == [4]
def test_iteration_pipe_type(self):
data = [1, "hello", True, None, [1], {"a": 1}]
result = jq_eval(data, ".[] | type")
assert result == [
"number", "string", "boolean", "null", "array", "object"
]
def test_flatten_then_unique_then_sort(self):
data = [[3, 1], [2, 1], [3, 2]]
result = jq_eval(data, "flatten | unique | sort")
assert result == [1, 2, 3]
def test_map_length(self):
data = ["hello", "hi", "hey"]
result = jq_eval(data, "map(length)")
assert result == [5, 2, 3]
def test_select_with_has(self):
data = [{"name": "alice"}, {"age": 30}, {"name": "bob"}]
result = jq_eval(data, 'map(select(has("name")))')
assert result == [{"name": "alice"}, {"name": "bob"}]
class TestJqArithmetic:
def test_add_numbers(self):
assert jq_eval({"a": 1, "b": 2}, ".a + .b") == 3
def test_subtract(self):
assert jq_eval({"a": 10, "b": 3}, ".a - .b") == 7
def test_multiply(self):
assert jq_eval({"a": 4, "b": 5}, ".a * .b") == 20
def test_divide(self):
assert jq_eval({"a": 10, "b": 2}, ".a / .b") == 5.0
def test_add_string_concat(self):
assert jq_eval({
"a": "hello",
"b": " world"
}, ".a + .b") == "hello world"
def test_add_literal_number(self):
assert jq_eval({"a": 1}, ".a + 10") == 11
class TestJqObjectConstruction:
def test_object_construction(self):
data = {"name": "alice", "age": 30}
result = jq_eval(data, "{name: .name}")
assert result == {"name": "alice"}
def test_object_construction_multiple_keys(self):
data = {"x": 1, "y": 2, "z": 3}
result = jq_eval(data, "{a: .x, b: .y}")
assert result == {"a": 1, "b": 2}
class TestJqAlternative:
def test_alternative_with_null(self):
assert jq_eval({"a": None}, '.a // "default"') == "default"
def test_alternative_with_value(self):
assert jq_eval({"a": 42}, '.a // "default"') == 42
def test_alternative_missing_key(self):
assert jq_eval({}, '.missing // "fallback"') == "fallback"
class TestJqIfThenElse:
def test_if_then_else_true(self):
result = jq_eval({"x": 5}, 'if .x > 3 then "big" else "small" end')
assert result == "big"
def test_if_then_else_false(self):
result = jq_eval({"x": 1}, 'if .x > 3 then "big" else "small" end')
assert result == "small"
class TestJqSortByGroupBy:
def test_sort_by(self):
data = [{"a": 3}, {"a": 1}, {"a": 2}]
result = jq_eval(data, "sort_by(.a)")
assert result == [{"a": 1}, {"a": 2}, {"a": 3}]
def test_group_by(self):
data = [{"k": "a", "v": 1}, {"k": "b", "v": 2}, {"k": "a", "v": 3}]
result = jq_eval(data, "group_by(.k)")
assert result == [
[{
"k": "a",
"v": 1
}, {
"k": "a",
"v": 3
}],
[{
"k": "b",
"v": 2
}],
]
class TestJqBugFixes:
def test_double_array_iteration(self):
data = [[1, 2], [3, 4]]
result = jq_eval(data, ".[] | .[]")
assert result == [1, 2, 3, 4]
def test_double_array_iteration_with_tail(self):
data = [["ab", "cd"], ["ef"]]
result = jq_eval(data, ".[] | .[] | length")
assert result == [2, 2, 2]
def test_dot_key_on_list_raises(self):
# Real jq raises ValueError "Cannot index array with string".
with pytest.raises(ValueError):
jq_eval([1, 2, 3], ".name")
def test_contains_dict_subset(self):
assert jq_eval({"a": 1, "b": 2}, 'contains({"a": 1})') is True
def test_contains_dict_missing_key(self):
assert jq_eval({"a": 1}, 'contains({"b": 2})') is False
def test_contains_dict_wrong_value(self):
assert jq_eval({"a": 1}, 'contains({"a": 2})') is False
def test_contains_dict_full_match(self):
assert jq_eval({"a": 1}, 'contains({"a": 1})') is True
class TestJqParensAndArrayConstruction:
def test_parens_unwrap_single(self):
assert jq_eval({"items": [1, 2, 3]}, "(.items | length)") == 3
def test_parens_in_object_value(self):
result = jq_eval({"items": [1, 2, 3]},
"{n: (.items | length), first: .items[0]}")
assert result == {"n": 3, "first": 1}
def test_array_construction_collects_spread(self):
data = {"slides": [{"id": "a"}, {"id": "b"}, {"id": "c"}]}
assert jq_eval(data, "[.slides[].id]") == ["a", "b", "c"]
def test_array_construction_wraps_single_value(self):
assert jq_eval({"x": 5}, "[.x]") == [5]
def test_object_with_array_literal_value_does_not_split_inside(self):
assert jq_eval({}, "{x: 1, y: [1,2,3]}") == {"x": 1, "y": [1, 2, 3]}
def test_object_with_nested_object_value(self):
result = jq_eval({"a": 1, "b": 2}, "{outer: {x: .a, y: .b}}")
assert result == {"outer": {"x": 1, "y": 2}}
def test_join_separator(self):
assert jq_eval([1, 2, 3], 'join("-")') == "1-2-3"
def test_join_empty_separator(self):
assert jq_eval(["foo", "bar"], 'join("")') == "foobar"
def test_array_construction_then_join(self):
data = {"slides": [{"id": "a"}, {"id": "b"}]}
assert jq_eval(data, '[.slides[].id] | join(",")') == "a,b"
def test_full_slides_summary_expression(self):
data = {
"title":
"Deck",
"slides": [
{
"objectId":
"s1",
"pageElements": [
{
"shape": {
"shapeType": "TITLE",
"text": {
"textElements": [
{
"textRun": {
"content": "Hello "
}
},
{
"textRun": {
"content": "world"
}
},
]
},
}
},
{
"image": {
"url": "..."
}
},
],
},
{
"objectId":
"s2",
"pageElements": [{
"shape": {
"shapeType": "TEXT_BOX",
"text": {
"textElements": [{
"textRun": {
"content": "Bye"
}
}]
},
}
}],
},
],
}
expr = ('{title: .title, slideCount: (.slides | length), '
'slides: [.slides[] | {objectId, '
'elements: [.pageElements[] | select(.shape != null) | '
'{type: .shape.shapeType, '
'text: [.shape.text.textElements[].textRun.content] '
'| join("")}]}]}')
assert jq_eval(data, expr) == {
"title":
"Deck",
"slideCount":
2,
"slides": [
{
"objectId": "s1",
"elements": [{
"type": "TITLE",
"text": "Hello world"
}],
},
{
"objectId": "s2",
"elements": [{
"type": "TEXT_BOX",
"text": "Bye"
}],
},
],
}
class TestJqRealLibjqExpressions:
"""Expressions that broke the homegrown parser before the libjq swap.
Each is a real-world query an agent might run; if any of these fail
again it means the engine regressed."""
def _slides_doc(self) -> dict:
return {
"title":
"Deck",
"slides": [
{
"objectId":
"s1",
"pageElements": [
{
"shape": {
"shapeType": "TITLE",
"text": {
"textElements": [
{
"textRun": {
"content": "Hello "
}
},
{
"paragraphMarker": {}
},
{
"textRun": {
"content": "world"
}
},
]
},
}
},
],
},
{
"objectId":
"s2",
"pageElements": [{
"shape": {
"shapeType": "TEXT_BOX",
"text": {
"textElements": [{
"textRun": {
"content": "Bye"
}
}]
},
}
}],
},
],
}
def test_user_query_with_select_textRun_then_join(self):
expr = ('[.slides[].pageElements[].shape.text.textElements[] | '
'select(.textRun != null) | .textRun.content] | join("")')
assert jq_eval(self._slides_doc(), expr) == "Hello worldBye"
def test_user_query_array_per_slide_then_join(self):
expr = ('[.slides[] | '
'[.pageElements[].shape.text.textElements[].textRun.content] '
'| join("")]')
# paragraphMarker has no textRun -> jq returns null; join treats null
# as empty -> "Hello world" for s1, "Bye" for s2.
assert jq_eval(self._slides_doc(), expr) == ["Hello world", "Bye"]
def test_user_query_full_summary_object(self):
expr = ('{title: .title, '
'slideCount: (.slides | length), '
'slides: [.slides[] | {objectId, '
'elements: [.pageElements[] | select(.shape != null) | '
'{type: .shape.shapeType, '
'text: [.shape.text.textElements[].textRun.content] '
'| join("")}]}]}')
assert jq_eval(self._slides_doc(), expr) == {
"title":
"Deck",
"slideCount":
2,
"slides": [
{
"objectId": "s1",
"elements": [{
"type": "TITLE",
"text": "Hello world"
}],
},
{
"objectId": "s2",
"elements": [{
"type": "TEXT_BOX",
"text": "Bye"
}],
},
],
}
def test_recurse_then_select(self):
# `recurse` (..) is a real jq builtin the homegrown didn't have.
data = {"a": 1, "b": {"c": 2, "d": {"e": 3}}}
result = jq_eval(data, "[.. | numbers] | sort")
assert result == [1, 2, 3]
def test_string_split_join_round_trip(self):
assert jq_eval("a-b-c", 'split("-")') == ["a", "b", "c"]
assert jq_eval(["a", "b", "c"], 'join("-")') == "a-b-c"
def test_alternative_operator_default(self):
# `// "default"` falls back when LHS is null/false.
assert jq_eval({"a": None}, '.a // "fallback"') == "fallback"
assert jq_eval({"a": "real"}, '.a // "fallback"') == "real"
def test_to_entries_from_entries(self):
result = jq_eval({"a": 1, "b": 2}, "to_entries")
assert result == [{"key": "a", "value": 1}, {"key": "b", "value": 2}]
result2 = jq_eval([{"key": "x", "value": 9}], "from_entries")
assert result2 == {"x": 9}
def test_string_endswith_startswith(self):
assert jq_eval("foobar", 'startswith("foo")') is True
assert jq_eval("foobar", 'endswith("bar")') is True
assert jq_eval("foobar", 'startswith("xyz")') is False
def test_test_regex(self):
assert jq_eval("hello world", 'test("w.rld")') is True
assert jq_eval("hello world", 'test("xyz")') is False
def test_walk_transform(self):
# `walk` is a real jq builtin the homegrown didn't have.
data = {"a": "FOO", "b": ["BAR", "BAZ"]}
result = jq_eval(
data, 'walk(if type == "string" then ascii_downcase else . end)')
assert result == {"a": "foo", "b": ["bar", "baz"]}
def test_nested_object_with_paren_value(self):
data = {"items": [1, 2, 3]}
result = jq_eval(
data, "{count: (.items | length), max: (.items | max), "
"sum: ([.items[]] | add)}")
assert result == {"count": 3, "max": 3, "sum": 6}
def test_select_chain_inside_array_construction(self):
data = {
"users": [
{
"name": "alice",
"active": True
},
{
"name": "bob",
"active": False
},
{
"name": "carol",
"active": True
},
]
}
result = jq_eval(
data, "[.users[] | select(.active) | .name] | join(\", \")")
assert result == "alice, carol"
def test_top_level_select_returns_empty_sentinel(self):
# Real jq: select(false) produces 0 outputs.
# Our adapter returns JQ_EMPTY so callers serialize as empty bytes.
assert jq_eval({"x": 5}, "select(.x > 100)") is JQ_EMPTY
class TestJqMemoryBackend:
def test_single_path(self):
ws = mem_ws({"/data.json": b'{"key": "value"}'})
stdout, _ = run_raw(ws, 'jq .key /data/data.json')
result = json.loads(collect(stdout))
assert result == "value"
def test_multiple_paths(self):
ws = mem_ws({"/a.json": b'{"x": 1}', "/b.json": b'{"x": 2}'})
stdout, _ = run_raw(ws, "jq .x /data/a.json /data/b.json")
raw = collect(stdout).decode()
assert "1" in raw
assert "2" in raw
def test_stdin_bytes(self):
ws = mem_ws()
stdout, _ = run_raw(ws, "jq .name", stdin=b'{"name": "stdin-test"}')
result = json.loads(collect(stdout))
assert result == "stdin-test"
def test_dot_returns_full(self):
ws = mem_ws({"/f.json": b'[1, 2, 3]'})
stdout, _ = run_raw(ws, "jq . /data/f.json")
result = json.loads(collect(stdout))
assert result == [1, 2, 3]
def test_missing_expression_defaults_dot(self):
ws = mem_ws({"/f.json": b'{"a": 1}'})
stdout, io = run_raw(ws, "jq")
assert io.exit_code == 0
assert collect(stdout) == b""
def test_pipe_in_command(self):
ws = mem_ws({"/f.json": b'{"items": [1, 2, 3]}'})
stdout, _ = run_raw(ws, "jq '.items | length' /data/f.json")
result = json.loads(collect(stdout))
assert result == 3
def test_select_via_command(self):
ws = mem_ws({"/f.json": b'[{"a": 1}, {"a": 5}]'})
stdout, _ = run_raw(ws, "jq '.[] | select(.a > 2)' /data/f.json")
result = json.loads(collect(stdout))
assert result == {"a": 5}
def test_keys_via_command(self):
ws = mem_ws({"/f.json": b'{"b": 1, "a": 2}'})
stdout, _ = run_raw(ws, "jq keys /data/f.json")
result = json.loads(collect(stdout))
assert result == ["a", "b"]
def test_spread_array_iteration(self):
ws = mem_ws({"/f.json": b'[10, 20, 30]'})
stdout, _ = run_raw(ws, "jq '.[]' /data/f.json")
lines = collect(stdout).strip().splitlines()
result = [json.loads(line) for line in lines]
assert result == [10, 20, 30]
def test_no_spread_dot_on_array(self):
ws = mem_ws({"/f.json": b'[10, 20, 30]'})
stdout, _ = run_raw(ws, "jq '.' /data/f.json")
result = json.loads(collect(stdout))
assert result == [10, 20, 30]
def test_spread_nested_iteration(self):
ws = mem_ws({"/f.json": b'[{"x": 1}, {"x": 2}]'})
stdout, _ = run_raw(ws, "jq '.[].x' /data/f.json")
lines = collect(stdout).strip().splitlines()
result = [json.loads(line) for line in lines]
assert result == [1, 2]
def test_spread_via_stdin(self):
ws = mem_ws()
stdout, _ = run_raw(ws, "jq '.[]'", stdin=b'[1, 2, 3]')
lines = collect(stdout).strip().splitlines()
result = [json.loads(line) for line in lines]
assert result == [1, 2, 3]
def test_no_spread_dot_via_stdin(self):
ws = mem_ws()
stdout, _ = run_raw(ws, "jq '.'", stdin=b'[1, 2, 3]')
result = json.loads(collect(stdout))
assert result == [1, 2, 3]
def test_spread_with_raw_flag(self):
ws = mem_ws({"/f.json": b'["hello", "world"]'})
stdout, _ = run_raw(ws, "jq -r '.[]' /data/f.json")
output = collect(stdout).decode()
assert output == "hello\nworld\n"
def test_spread_select_multiple(self):
ws = mem_ws({"/f.json": b'[{"a": 1}, {"a": 5}, {"a": 10}]'})
stdout, _ = run_raw(ws, "jq -c '.[] | select(.a > 4)' /data/f.json")
lines = collect(stdout).strip().splitlines()
result = [json.loads(line) for line in lines]
assert result == [{"a": 5}, {"a": 10}]
class TestJqDiskBackend:
def _disk_ws(self, tmp_path, files):
from mirage.resource.disk.disk import DiskResource
for name, data in files.items():
fp = tmp_path / name.lstrip("/")
fp.parent.mkdir(parents=True, exist_ok=True)
fp.write_bytes(data)
disk = DiskResource(str(tmp_path))
from mirage.workspace import Workspace
return Workspace(
{"/disk": (disk, MountMode.WRITE)},
mode=MountMode.WRITE,
)
def test_single_path(self, tmp_path):
ws = self._disk_ws(tmp_path, {"/f.json": b'{"val": 42}'})
stdout, _ = run_raw(ws, "jq .val /disk/f.json")
result = json.loads(collect(stdout))
assert result == 42
def test_stdin_bytes(self, tmp_path):
ws = self._disk_ws(tmp_path, {})
stdout, _ = run_raw(ws, "jq .x", stdin=b'{"x": "disk-stdin"}')
result = json.loads(collect(stdout))
assert result == "disk-stdin"
def test_nested_key(self, tmp_path):
ws = self._disk_ws(tmp_path, {"/f.json": b'{"a": {"b": 99}}'})
stdout, _ = run_raw(ws, "jq .a.b /disk/f.json")
result = json.loads(collect(stdout))
assert result == 99
def test_array_iteration(self, tmp_path):
ws = self._disk_ws(tmp_path, {"/f.json": b'{"items": [10, 20, 30]}'})
stdout, _ = run_raw(ws, "jq '.items[]' /disk/f.json")
lines = collect(stdout).strip().splitlines()
result = [json.loads(line) for line in lines]
assert result == [10, 20, 30]
def test_pipe_length(self, tmp_path):
ws = self._disk_ws(tmp_path, {"/f.json": b'[1, 2, 3, 4]'})
stdout, _ = run_raw(ws, "jq '. | length' /disk/f.json")
result = json.loads(collect(stdout))
assert result == 4
def test_file_not_found(self, tmp_path):
ws = self._disk_ws(tmp_path, {})
_, io = run_raw(ws, "jq . /disk/missing.json")
assert io.exit_code != 0
def test_spread_iteration(self, tmp_path):
ws = self._disk_ws(tmp_path, {"/f.json": b'[1, 2, 3]'})
stdout, _ = run_raw(ws, "jq '.[]' /disk/f.json")
lines = collect(stdout).strip().splitlines()
result = [json.loads(line) for line in lines]
assert result == [1, 2, 3]
def test_no_spread_dot_array(self, tmp_path):
ws = self._disk_ws(tmp_path, {"/f.json": b'[1, 2, 3]'})
stdout, _ = run_raw(ws, "jq '.' /disk/f.json")
result = json.loads(collect(stdout))
assert result == [1, 2, 3]
def test_spread_raw(self, tmp_path):
ws = self._disk_ws(tmp_path, {"/f.json": b'["aa", "bb"]'})
stdout, _ = run_raw(ws, "jq -r '.[]' /disk/f.json")
output = collect(stdout).decode()
assert output == "aa\nbb\n"
class TestJqS3Backend:
def _s3_ws(self):
from mirage.resource.s3.s3 import S3Config, S3Resource
from mirage.workspace import Workspace
config = S3Config(bucket="test-bucket", region="us-east-1")
s3 = S3Resource(config)
return Workspace(
{"/s3": (s3, MountMode.READ)},
mode=MountMode.READ,
)
def test_stdin_bytes(self):
ws = self._s3_ws()
stdout, _ = run_raw(ws, "jq .key", stdin=b'{"key": "s3-stdin"}')
result = json.loads(collect(stdout))
assert result == "s3-stdin"
def test_stdin_dot(self):
ws = self._s3_ws()
data = {"a": 1, "b": [2, 3]}
stdout, _ = run_raw(ws, "jq .", stdin=json.dumps(data).encode())
result = json.loads(collect(stdout))
assert result == data
def test_stdin_length(self):
ws = self._s3_ws()
stdout, _ = run_raw(ws, "jq length", stdin=b'[1, 2, 3]')
result = json.loads(collect(stdout))
assert result == 3
def _run_jq_path(self, data, expr):
ops = CommandIO(readdir=None,
read_bytes=partial(_const_bytes, data),
read_stream=None,
stat=None,
is_mounted=lambda a: True,
local=False)
path = PathSpec(resource_path="s3/data.json",
virtual="/s3/data.json",
directory="/s3",
resolved=True)
return asyncio.run(
_collect_jq(ops, S3Accessor.__new__(S3Accessor), [path], expr))
def test_path_with_mock(self):
out = self._run_jq_path(b'{"name": "from-s3"}', ".name")
assert json.loads(out) == "from-s3"
def test_path_array_iteration_mock(self):
out = self._run_jq_path(b'[{"id": 1}, {"id": 2}]', ".[].id")
result = [json.loads(line) for line in out.strip().splitlines()]
assert result == [1, 2]
def test_path_pipe_length_mock(self):
out = self._run_jq_path(b'{"items": [1, 2, 3]}', ".items | length")
assert json.loads(out) == 3
def test_spread_iteration_mock(self):
out = self._run_jq_path(b'[10, 20, 30]', ".[]")
result = [json.loads(line) for line in out.strip().splitlines()]
assert result == [10, 20, 30]
def test_no_spread_dot_mock(self):
out = self._run_jq_path(b'[10, 20, 30]', ".")
assert json.loads(out) == [10, 20, 30]
def test_spread_stdin(self):
ws = self._s3_ws()
stdout, _ = run_raw(ws, "jq '.[]'", stdin=b'[1, 2, 3]')
lines = collect(stdout).strip().splitlines()
result = [json.loads(line) for line in lines]
assert result == [1, 2, 3]
def test_no_spread_dot_stdin(self):
ws = self._s3_ws()
stdout, _ = run_raw(ws, "jq '.'", stdin=b'[1, 2, 3]')
result = json.loads(collect(stdout))
assert result == [1, 2, 3]
def test_disk_multiple_paths(self, tmp_path):
from mirage.resource.disk.disk import DiskResource
from mirage.workspace import Workspace
(tmp_path / "a.json").write_bytes(b'{"x": 1}')
(tmp_path / "b.json").write_bytes(b'{"x": 2}')
disk = DiskResource(str(tmp_path))
ws = Workspace(
{"/disk": (disk, MountMode.WRITE)},
mode=MountMode.WRITE,
)
stdout, _ = run_raw(ws, "jq .x /disk/a.json /disk/b.json")
raw = collect(stdout).decode()
assert "1" in raw
assert "2" in raw