bcbd1bdb22
Test (Python) / test-python-gate (push) Blocked by required conditions
Test (TypeScript) / test-typescript-gate (push) Blocked by required conditions
CLI exit codes / cli-gate (push) Blocked by required conditions
Test (Install) / test-install-gate (push) Blocked by required conditions
Integ / integ-gate (push) Blocked by required conditions
CLI exit codes / Python CLI (push) Waiting to run
Integ / changes (push) Has been skipped
Test (Install) / python-minimal (3.11) (push) Waiting to run
Test (Install) / python-minimal (3.12) (push) Waiting to run
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Waiting to run
Integ / integ-ts (push) Waiting to run
Integ / integ (push) Waiting to run
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Waiting to run
Integ / integ-database (push) Waiting to run
Test (Install) / python-extra (email, mirage.resource.email) (push) Waiting to run
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Waiting to run
Integ / integ-ssh (push) Waiting to run
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
CLI exit codes / TypeScript CLI (push) Waiting to run
CLI exit codes / Cross-language snapshot interop (push) Waiting to run
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Waiting to run
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Waiting to run
Test (Python) / changes (push) Has been skipped
Integ / integ-database-ts (push) Waiting to run
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Waiting to run
Integ / integ-data (push) Waiting to run
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Waiting to run
Test (Python) / test (push) Waiting to run
Integ / integ-fuse (push) Waiting to run
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Waiting to run
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Waiting to run
Test (Python) / audit (push) Waiting to run
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Waiting to run
Integ / integ-ssh-ts (push) Waiting to run
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Waiting to run
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Waiting to run
Test (TypeScript) / changes (push) Has been skipped
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Waiting to run
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Waiting to run
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Waiting to run
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Waiting to run
Test (TypeScript) / test (push) Waiting to run
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Waiting to run
Test (TypeScript) / python-fs-shim (push) Waiting to run
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Waiting to run
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Waiting to run
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Waiting to run
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Waiting to run
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Waiting to run
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Waiting to run
Test (Install) / ts-minimal (push) Waiting to run
188 lines
6.3 KiB
Python
188 lines
6.3 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 base64
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from mirage import MountMode, Workspace
|
|
from mirage.resource.disk import DiskResource
|
|
from mirage.resource.ram import RAMResource
|
|
from mirage.resource.redis import RedisResource
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[3]
|
|
CONFORMANCE_DIR = REPO_ROOT / "conformance"
|
|
REDIS_URL = os.environ.get("REDIS_URL", "")
|
|
SUPPORTED_MATRIX = {
|
|
"python": {"ram", "disk", "redis"},
|
|
"typescript": {"ram"},
|
|
}
|
|
|
|
|
|
def _decode_bytes(record: dict, text_key: str, base64_key: str) -> bytes:
|
|
has_text = text_key in record
|
|
has_base64 = base64_key in record
|
|
if has_text == has_base64:
|
|
raise ValueError(
|
|
f"record must set exactly one of {text_key}/{base64_key}: "
|
|
f"{record}")
|
|
if has_text:
|
|
return record[text_key].encode()
|
|
return base64.b64decode(record[base64_key])
|
|
|
|
|
|
def _load_seeds() -> dict[str, bytes]:
|
|
raw = json.loads((CONFORMANCE_DIR / "seeds.json").read_text())
|
|
return {
|
|
path: _decode_bytes(spec, "text", "base64")
|
|
for path, spec in raw.items()
|
|
}
|
|
|
|
|
|
def _validate_matrix(case: dict, spec_name: str) -> None:
|
|
matrix = case["matrix"]
|
|
unknown_languages = set(matrix) - set(SUPPORTED_MATRIX)
|
|
if unknown_languages:
|
|
names = ", ".join(sorted(unknown_languages))
|
|
raise ValueError(
|
|
f"case {case['id']} in {spec_name} has unknown matrix "
|
|
f"language(s): {names}")
|
|
|
|
for language, backends in matrix.items():
|
|
unsupported = set(backends) - SUPPORTED_MATRIX[language]
|
|
if unsupported:
|
|
names = ", ".join(sorted(unsupported))
|
|
raise ValueError(
|
|
f"case {case['id']} in {spec_name} has unsupported "
|
|
f"{language} backend(s): {names}")
|
|
|
|
if not any(matrix.values()):
|
|
raise ValueError(
|
|
f"case {case['id']} in {spec_name} applies to no backend")
|
|
|
|
|
|
def _load_cases() -> list[dict]:
|
|
cases = []
|
|
for spec_path in sorted((CONFORMANCE_DIR / "cases").glob("*.json")):
|
|
doc = json.loads(spec_path.read_text())
|
|
for case in doc["cases"]:
|
|
_validate_matrix(case, spec_path.name)
|
|
cases.append(case)
|
|
return cases
|
|
|
|
|
|
SEEDS = _load_seeds()
|
|
CASES = _load_cases()
|
|
|
|
|
|
def _params() -> list:
|
|
params = []
|
|
for case in CASES:
|
|
for backend in case["matrix"].get("python", []):
|
|
marks = []
|
|
if backend == "redis" and not REDIS_URL:
|
|
marks.append(pytest.mark.skip(reason="REDIS_URL not set"))
|
|
params.append(
|
|
pytest.param(backend,
|
|
case,
|
|
id=f"{backend}-{case['id']}",
|
|
marks=marks))
|
|
return params
|
|
|
|
|
|
async def _build_workspace(
|
|
backend: str, tmp_path: Path,
|
|
case_id: str) -> tuple[Workspace, RedisResource | None]:
|
|
if backend == "ram":
|
|
return Workspace({"/": RAMResource()}, mode=MountMode.WRITE), None
|
|
if backend == "disk":
|
|
return Workspace({"/": DiskResource(root=str(tmp_path))},
|
|
mode=MountMode.WRITE), None
|
|
if backend == "redis":
|
|
resource = RedisResource(url=REDIS_URL,
|
|
key_prefix=f"test:conformance:{case_id}:")
|
|
await resource._store.clear()
|
|
return Workspace({"/": resource}, mode=MountMode.WRITE), resource
|
|
raise ValueError(f"unknown python backend in matrix: {backend}")
|
|
|
|
|
|
async def _seed(ws: Workspace) -> None:
|
|
made: set[str] = set()
|
|
for path, content in SEEDS.items():
|
|
parts = [p for p in path.rsplit("/", 1)[0].split("/") if p]
|
|
for depth in range(1, len(parts) + 1):
|
|
directory = "/" + "/".join(parts[:depth])
|
|
if directory not in made:
|
|
made.add(directory)
|
|
await ws.ops.mkdir(directory)
|
|
await ws.ops.write(path, content)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.parametrize(("backend", "case"), _params())
|
|
async def test_conformance(backend: str, case: dict, tmp_path: Path) -> None:
|
|
ws, resource = await _build_workspace(backend, tmp_path, case["id"])
|
|
try:
|
|
await _seed(ws)
|
|
stdin = None
|
|
if "stdin_text" in case or "stdin_base64" in case:
|
|
stdin = _decode_bytes(case, "stdin_text", "stdin_base64")
|
|
result = await ws.execute(case["cmd"], stdin=stdin)
|
|
stdout = await result.materialize_stdout()
|
|
stderr = await result.materialize_stderr()
|
|
expect = case["expect"]
|
|
assert result.exit_code == expect["exit"]
|
|
assert stdout == _decode_bytes(expect, "stdout_text", "stdout_base64")
|
|
assert stderr == _decode_bytes(expect, "stderr_text", "stderr_base64")
|
|
finally:
|
|
if resource is not None:
|
|
await resource._store.clear()
|
|
await resource._store.close()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("matrix", "message"),
|
|
[
|
|
({
|
|
"pyhton": ["ram"]
|
|
}, "unknown matrix language"),
|
|
({
|
|
"typescript": ["disk"]
|
|
}, "unsupported typescript backend"),
|
|
({
|
|
"python": [],
|
|
"typescript": []
|
|
}, "applies to no backend"),
|
|
],
|
|
)
|
|
def test_validate_matrix_rejects_invalid_targets(matrix: dict,
|
|
message: str) -> None:
|
|
case = {"id": "invalid_matrix", "matrix": matrix}
|
|
with pytest.raises(ValueError, match=message):
|
|
_validate_matrix(case, "invalid.json")
|
|
|
|
|
|
def test_validate_matrix_accepts_supported_targets() -> None:
|
|
case = {
|
|
"id": "valid_matrix",
|
|
"matrix": {
|
|
"python": ["ram", "disk", "redis"],
|
|
"typescript": ["ram"],
|
|
},
|
|
}
|
|
_validate_matrix(case, "valid.json")
|