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
79 lines
2.4 KiB
Python
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 == ""
|