"""Contract pins for the mtest API the warden's ``mtest_runner`` depends on (D-MTEST-API).

The adapter drives ``mgf-test-supervisor`` as a library in a child process. If
upstream renames/removes any of the surface used here, the adapter breaks silently
at runtime (in a child proc, where the failure is hard to see). These tests pin the
exact shape so an upstream change surfaces LOUDLY, here, in this repo's own suite.
"""

from __future__ import annotations

import dataclasses
import inspect
from pathlib import Path

import pytest


@pytest.mark.contract
def test_runconfig_accepts_the_selection_kwargs_the_adapter_uses() -> None:
    from mgf.test_supervisor.supervisor import RunConfig

    fields = {f.name for f in dataclasses.fields(RunConfig)}
    # The selection kwarg + the one-test chunk knob the child relies on.
    assert "test_ids" in fields, "RunConfig lost its `test_ids` selection field"
    assert "chunk_size" in fields, "RunConfig lost its `chunk_size` field"
    # The other required fields _mtest_child must supply.
    assert {"repo_root", "gates_dir", "test_markers", "run_id", "pytest_cmd"} <= fields

    # Construct with EXACTLY the kwargs _mtest_child passes — pins the constructor.
    cfg = RunConfig(
        repo_root=Path("/proj"),
        gates_dir=Path("/proj/.gates"),
        test_ids=["t.py::test_x"],
        test_markers={},
        run_id="run-x",
        pytest_cmd=["python", "-m", "pytest"],
        chunk_size=1,
    )
    assert cfg.test_ids == ["t.py::test_x"]
    assert cfg.chunk_size == 1


@pytest.mark.contract
def test_supervisor_has_run_and_takes_a_single_config() -> None:
    from mgf.test_supervisor.supervisor import Supervisor

    assert hasattr(Supervisor, "run"), "Supervisor lost its `.run` method"
    assert callable(Supervisor.run)
    # The adapter calls ``Supervisor(cfg).run()`` — pin the single-config ctor.
    params = list(inspect.signature(Supervisor.__init__).parameters)
    assert params[:2] == ["self", "cfg"]


@pytest.mark.contract
def test_read_jsonl_is_importable_and_tolerates_a_missing_file() -> None:
    from mgf.test_supervisor.records import read_jsonl

    assert callable(read_jsonl)
    # The adapter reads results from run.jsonl via read_jsonl; it must not crash
    # when the file is absent (no run happened) — it returns nothing.
    assert list(read_jsonl(Path("/nonexistent/.gates/last-run/run.jsonl"))) == []


@pytest.mark.contract
def test_testrecord_exposes_the_fields_the_adapter_reads() -> None:
    from mgf.test_supervisor.records import TestRecord

    fields = {f.name for f in dataclasses.fields(TestRecord)}
    assert {"test_id", "outcome", "duration_ms"} <= fields, (
        f"TestRecord shape drifted; have {sorted(fields)}"
    )
