Files
rune/tests/test_native_interop_proof.py
Nick 72afb1a55a feat(rebase): wave 0 — whole-project inventory, per-function issue factory, Rust interop proof unit
Wave 0 of the approved language-rebasing tranche (R1):

- scripts/rebase_inventory.py: AST-based whole-project inventory →
  docs/rebase-inventory.json (1,852 true units = 1,030 top-level fns
  + 822 methods; module-level walk skips defs; class-method leak fixed)
- docs/rebase-wave-coverage.json: waves 2–7 coverage {239,251,129,544,415,274}
- scripts/rebase_issue_factory.py: one-issue-per-unit Gitea factory,
  dry-run default, deterministic titles + dedup, --limit defaults to
  unlimited for create; 1,852 issues created across waves 2–7
- interop/rebase-proof + shared/native_units.py + tests: std-only Rust
  proof unit for the §5 native-interop contract (argv→JSON, --version,
  exit 2 on failure); thin typed Python adapter; 8/8 tests pass with
  corrected ok-semantics (ok = exit 0 AND not timed out)
- config/umbrellas.json: native_units registered in capability umbrella
  (manifest law — the map travels with the change)
- docs/rebase-wave0-report.md: Wave 0 close-out report
2026-09-29 12:47:34 +01:00

79 lines
2.4 KiB
Python

"""Wave 0 interop proof tests (R1 §5): Rust unit callable from tests via the
thin Python adapter. Skips when the binary is not built so CI without Rust
stays green; build locally with:
cargo build --release --quiet (from interop/rebase-proof/)
"""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "src"))
sys.path.insert(0, str(ROOT))
import pytest # noqa: E402
from shared.native_units import PROOF_UNIT, NativeResult, run_native, run_proof # noqa: E402
pytestmark = pytest.mark.skipif(
not PROOF_UNIT.is_file(), reason="rebase-proof binary not built (cargo build --release in interop/rebase-proof)"
)
def test_version_flag_is_mandatory_contract():
result = run_proof(["--version"])
assert result.ok
assert result.exit_code == 0
assert result.data is None or result.data == {} # version prints plain text, not JSON
assert result.raw_stdout.strip() == "rebase-proof 0.1.0"
def test_sum_happy_path_json_shape():
result = run_proof(["sum", "1", "2", "3"])
assert result.ok
assert result.data == {"ok": True, "op": "sum", "result": 6}
def test_hash_is_deterministic():
first = run_proof(["hash", "hello"])
second = run_proof(["hash", "hello"])
assert first.ok and second.ok
assert first.data == second.data
assert first.data["fnv1a64"] == "a430d84680aabd0b"
def test_error_op_exits_nonzero_with_json_error():
result = run_proof(["fail"])
assert not result.ok
assert result.exit_code == 2
assert result.data == {"ok": False, "error": "deliberate failure"}
assert "deliberate failure for adapter proof" in result.stderr
def test_unknown_op_is_data_not_exception():
result = run_proof(["no-such-op"])
assert not result.ok
assert result.exit_code == 2
assert "unknown op" in result.data["error"]
def test_timeout_enforcement_kills_unit():
result = run_proof(["sleep", "30000"], timeout_s=0.5)
assert result.timed_out
assert not result.ok
assert result.exit_code == -1
def test_missing_binary_raises_adapter_misuse():
with pytest.raises(FileNotFoundError):
run_native(ROOT / "interop" / "rebase-proof" / "target" / "release" / "does-not-exist", ["sum", "1"])
def test_typed_result_is_the_contract_surface():
result = run_proof(["sum", "10", "20"])
assert isinstance(result, NativeResult)
assert result.duration_ms >= 0
assert result.stderr == ""