[verified] feat: complete Ralph task workstreams
This commit is contained in:
644
tests/test_node_doctor.py
Normal file
644
tests/test_node_doctor.py
Normal file
@@ -0,0 +1,644 @@
|
||||
"""NCA-002 tests for `meshnet-node doctor`.
|
||||
|
||||
The unit tests inject a fake backend, so none of them download a model, import
|
||||
Torch, or need a GPU. The one test that runs a real model is `integration`-marked
|
||||
and takes its model identity from the environment — it has no model default, on
|
||||
purpose: the doctor is model-agnostic and so is its test.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import struct
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from meshnet_node import doctor
|
||||
from meshnet_node.capability import STATUS_FAILED, STATUS_PASSED, CapabilityReport
|
||||
from meshnet_node.doctor import (
|
||||
CATEGORY_FORWARD_FAILED,
|
||||
CATEGORY_INSUFFICIENT_MEMORY,
|
||||
CATEGORY_INVALID_SHARD,
|
||||
CATEGORY_MISSING_DEPENDENCY,
|
||||
CATEGORY_NO_MODEL,
|
||||
CATEGORY_UNSUPPORTED_RECIPE,
|
||||
PROBE_TOKENS,
|
||||
DoctorError,
|
||||
DoctorSelection,
|
||||
build_probe_input,
|
||||
classify_failure,
|
||||
probe_forward,
|
||||
render_result,
|
||||
resolve_selection,
|
||||
run_doctor,
|
||||
select_recipes,
|
||||
write_reports,
|
||||
)
|
||||
from meshnet_node.model_backend import (
|
||||
InsufficientVRAMError,
|
||||
MissingModelDependencyError,
|
||||
UnsupportedRecipeParam,
|
||||
validate_recipe_params,
|
||||
)
|
||||
from meshnet_node.recipe_manifest import parse_recipe_manifest
|
||||
|
||||
# Deliberately not a model this project ships against: nothing here may special-case it.
|
||||
FIXTURE_MODEL = "acme-labs/Widget-9000-Instruct"
|
||||
|
||||
MANIFEST = parse_recipe_manifest(
|
||||
{
|
||||
"schema_version": 1,
|
||||
"catalogue_version": "test-1",
|
||||
"recipes": [
|
||||
{"id": "baseline", "version": "1", "backend_id": "torch-transformers"},
|
||||
{
|
||||
"id": "stateless",
|
||||
"version": "2",
|
||||
"backend_id": "torch-transformers",
|
||||
"params": {"use_cache": False},
|
||||
},
|
||||
],
|
||||
},
|
||||
source="<test manifest>",
|
||||
)
|
||||
|
||||
|
||||
class _Payload:
|
||||
"""Stands in for model_backend.TensorPayload."""
|
||||
|
||||
def __init__(self, body: bytes, shape: list[int]) -> None:
|
||||
self.body = body
|
||||
self.shape = shape
|
||||
self.attention_mask_header = None
|
||||
self.position_ids_header = None
|
||||
|
||||
|
||||
class _TailToken:
|
||||
"""Stands in for model_backend.TailTokenResult."""
|
||||
|
||||
def __init__(self, token_id: int = 7) -> None:
|
||||
self.token_id = token_id
|
||||
self.text = "ok"
|
||||
|
||||
|
||||
class _Device:
|
||||
def __init__(self, type_: str = "cpu") -> None:
|
||||
self.type = type_
|
||||
|
||||
|
||||
class _FakeBackend:
|
||||
"""A backend that loads but records exactly how it was driven."""
|
||||
|
||||
hidden_size = 8
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
is_head: bool = True,
|
||||
is_tail: bool = False,
|
||||
shard_start: int = 0,
|
||||
shard_end: int = 3,
|
||||
forward_error: Exception | None = None,
|
||||
) -> None:
|
||||
self.model_id = FIXTURE_MODEL
|
||||
self.is_head = is_head
|
||||
self.is_tail = is_tail
|
||||
self.shard_start = shard_start
|
||||
self.shard_end = shard_end
|
||||
self.device = _Device("cpu")
|
||||
self.forward_error = forward_error
|
||||
self.encoded_prompts: list[str] = []
|
||||
self.forwards: list[dict] = []
|
||||
|
||||
def encode_prompt(self, prompt: str):
|
||||
if self.forward_error is not None:
|
||||
raise self.forward_error
|
||||
self.encoded_prompts.append(prompt)
|
||||
return _Payload(b"\x00" * (PROBE_TOKENS * self.hidden_size * 2),
|
||||
[1, PROBE_TOKENS, self.hidden_size])
|
||||
|
||||
def forward_bytes(
|
||||
self,
|
||||
body,
|
||||
shape,
|
||||
attention_mask_header,
|
||||
position_ids_header,
|
||||
start_layer=None,
|
||||
**kwargs,
|
||||
):
|
||||
if self.forward_error is not None:
|
||||
raise self.forward_error
|
||||
self.forwards.append(
|
||||
{
|
||||
"body_len": len(body),
|
||||
"shape": shape,
|
||||
"start_layer": start_layer,
|
||||
"attention_mask_header": attention_mask_header,
|
||||
"position_ids_header": position_ids_header,
|
||||
}
|
||||
)
|
||||
if self.is_tail:
|
||||
return _TailToken()
|
||||
return _Payload(body, shape)
|
||||
|
||||
|
||||
def _loader(backend=None, *, error: Exception | None = None):
|
||||
"""A load_backend stub that records the (selection, recipe) pairs it saw."""
|
||||
calls: list[tuple[DoctorSelection, object]] = []
|
||||
|
||||
def load(selection, recipe):
|
||||
calls.append((selection, recipe))
|
||||
if error is not None:
|
||||
raise error
|
||||
return backend if backend is not None else _FakeBackend()
|
||||
|
||||
load.calls = calls # type: ignore[attr-defined]
|
||||
return load
|
||||
|
||||
|
||||
def _selection(**overrides) -> DoctorSelection:
|
||||
kwargs = dict(model_id=FIXTURE_MODEL, shard_start=0, shard_end=3)
|
||||
kwargs.update(overrides)
|
||||
return DoctorSelection(**kwargs)
|
||||
|
||||
|
||||
# --- selection resolves the same as startup ---------------------------------
|
||||
|
||||
|
||||
def test_resolve_selection_uses_the_configured_repo_shard_and_quantization():
|
||||
selection = resolve_selection(
|
||||
{
|
||||
"model_hf_repo": FIXTURE_MODEL,
|
||||
"model_name": "Widget-9000-Instruct",
|
||||
"shard_start": 4,
|
||||
"shard_end": 11,
|
||||
"quantization": "bf16", # startup normalizes this to bfloat16
|
||||
"download_dir": "/models",
|
||||
}
|
||||
)
|
||||
|
||||
assert selection.model_id == FIXTURE_MODEL
|
||||
assert (selection.shard_start, selection.shard_end) == (4, 11)
|
||||
assert selection.quantization == "bfloat16"
|
||||
assert selection.cache_dir == Path("/models")
|
||||
|
||||
|
||||
def test_resolve_selection_defaults_to_the_whole_model_like_startup():
|
||||
"""With no pinned shard, startup serves layers 0..n-1 — so doctor validates that."""
|
||||
seen: list[tuple[str, Path | None]] = []
|
||||
|
||||
def detect(model_id, cache_dir):
|
||||
seen.append((model_id, cache_dir))
|
||||
return 24
|
||||
|
||||
selection = resolve_selection(
|
||||
{"model_hf_repo": FIXTURE_MODEL}, detect_layers=detect
|
||||
)
|
||||
|
||||
assert (selection.shard_start, selection.shard_end) == (0, 23)
|
||||
assert seen == [(FIXTURE_MODEL, None)]
|
||||
|
||||
|
||||
def test_resolve_selection_without_a_model_is_actionable():
|
||||
with pytest.raises(DoctorError) as exc:
|
||||
resolve_selection({"model_hf_repo": "", "model_name": ""})
|
||||
|
||||
assert exc.value.category == CATEGORY_NO_MODEL
|
||||
assert "--model" in exc.value.hint
|
||||
|
||||
|
||||
def test_resolve_selection_rejects_an_inverted_shard_range():
|
||||
with pytest.raises(DoctorError) as exc:
|
||||
resolve_selection(
|
||||
{"model_hf_repo": FIXTURE_MODEL, "shard_start": 9, "shard_end": 2}
|
||||
)
|
||||
|
||||
assert exc.value.category == CATEGORY_INVALID_SHARD
|
||||
|
||||
|
||||
def test_resolve_selection_reports_an_unreadable_model_config():
|
||||
with pytest.raises(DoctorError) as exc:
|
||||
resolve_selection(
|
||||
{"model_hf_repo": FIXTURE_MODEL}, detect_layers=lambda *_: None
|
||||
)
|
||||
|
||||
assert exc.value.category == doctor.CATEGORY_MODEL_UNAVAILABLE
|
||||
assert "--shard-start" in str(exc.value)
|
||||
|
||||
|
||||
# --- the bounded real forward ------------------------------------------------
|
||||
|
||||
|
||||
def test_a_pass_requires_a_real_forward_through_the_selected_shard():
|
||||
"""Hardware being fine is not the bar: the shard itself has to execute."""
|
||||
backend = _FakeBackend(is_head=True)
|
||||
|
||||
result = run_doctor(
|
||||
_selection(), manifest=MANIFEST, load_backend=_loader(backend), now=lambda: 1000.0
|
||||
)
|
||||
|
||||
assert result.passed
|
||||
assert backend.encoded_prompts == [doctor.PROBE_PROMPT]
|
||||
report = result.reports[0]
|
||||
assert report.status == STATUS_PASSED
|
||||
assert report.model.model_id == FIXTURE_MODEL
|
||||
assert (report.shard.start, report.shard.end) == (0, 3)
|
||||
|
||||
|
||||
def test_a_backend_that_loads_but_cannot_forward_never_passes():
|
||||
"""The regression this story exists for: a load is not a validation."""
|
||||
backend = _FakeBackend(forward_error=RuntimeError("kernel exploded"))
|
||||
|
||||
result = run_doctor(
|
||||
_selection(), manifest=MANIFEST, load_backend=_loader(backend), now=lambda: 1.0
|
||||
)
|
||||
|
||||
assert not result.passed
|
||||
assert result.exit_code == 1
|
||||
report = result.reports[0]
|
||||
assert report.status == STATUS_FAILED
|
||||
assert result.results[0].category == CATEGORY_FORWARD_FAILED
|
||||
assert any("kernel exploded" in d for d in report.diagnostics)
|
||||
|
||||
|
||||
def test_a_mid_shard_is_probed_with_peer_shaped_hidden_states():
|
||||
backend = _FakeBackend(is_head=False, shard_start=4, shard_end=7)
|
||||
|
||||
detail = probe_forward(backend)
|
||||
|
||||
assert detail["probe"] == "hidden-states"
|
||||
assert backend.encoded_prompts == []
|
||||
forward = backend.forwards[0]
|
||||
assert forward["shape"] == [1, PROBE_TOKENS, backend.hidden_size]
|
||||
# bfloat16 == 2 bytes per element, and the probe stays bounded to PROBE_TOKENS.
|
||||
assert forward["body_len"] == PROBE_TOKENS * backend.hidden_size * 2
|
||||
assert forward["start_layer"] == 4
|
||||
|
||||
|
||||
def test_a_head_and_tail_shard_also_decodes_so_the_lm_head_is_covered():
|
||||
backend = _FakeBackend(is_head=True, is_tail=True, shard_end=5)
|
||||
|
||||
detail = probe_forward(backend)
|
||||
|
||||
assert detail["probe"] == "prompt+decode"
|
||||
assert detail["output"] == "token"
|
||||
# Re-entering above the last layer decodes without re-running any layer.
|
||||
assert backend.forwards[0]["start_layer"] == 6
|
||||
|
||||
|
||||
def test_a_tail_shard_that_decodes_a_token_passes():
|
||||
backend = _FakeBackend(is_head=False, is_tail=True, shard_start=8, shard_end=11)
|
||||
|
||||
detail = probe_forward(backend)
|
||||
|
||||
assert detail == {
|
||||
"probe": "hidden-states",
|
||||
"tokens": PROBE_TOKENS,
|
||||
"output": "token",
|
||||
"token_id": 7,
|
||||
}
|
||||
|
||||
|
||||
def test_an_empty_forward_result_is_a_failure_not_a_pass():
|
||||
backend = _FakeBackend(is_head=False)
|
||||
backend.forward_bytes = lambda *a, **k: _Payload(b"", []) # type: ignore[assignment]
|
||||
|
||||
with pytest.raises(DoctorError) as exc:
|
||||
probe_forward(backend)
|
||||
|
||||
assert exc.value.category == CATEGORY_FORWARD_FAILED
|
||||
|
||||
|
||||
def test_a_backend_with_no_hidden_size_cannot_be_probed():
|
||||
with pytest.raises(DoctorError) as exc:
|
||||
build_probe_input(0)
|
||||
|
||||
assert exc.value.category == CATEGORY_FORWARD_FAILED
|
||||
|
||||
|
||||
def test_probe_headers_decode_as_int64_tensors():
|
||||
probe = build_probe_input(hidden_size=8, tokens=3)
|
||||
|
||||
shape, encoded = probe.position_ids_header.split(":", 1)
|
||||
raw = base64.b64decode(encoded)
|
||||
assert shape == "1,3"
|
||||
assert list(struct.unpack("<3q", raw)) == [0, 1, 2]
|
||||
|
||||
|
||||
# --- recipes -----------------------------------------------------------------
|
||||
|
||||
|
||||
def test_the_default_run_validates_only_the_selected_recipe():
|
||||
"""Onboarding must not pay to validate recipes the node was not asked to serve."""
|
||||
load = _loader()
|
||||
|
||||
result = run_doctor(_selection(), manifest=MANIFEST, load_backend=load)
|
||||
|
||||
assert [r.recipe.id for r in result.results] == ["baseline"]
|
||||
assert len(load.calls) == 1
|
||||
|
||||
|
||||
def test_all_recipes_is_explicit_and_validates_every_recipe():
|
||||
load = _loader()
|
||||
|
||||
result = run_doctor(
|
||||
_selection(), manifest=MANIFEST, load_backend=load, all_recipes=True
|
||||
)
|
||||
|
||||
assert [r.recipe.id for r in result.results] == ["baseline", "stateless"]
|
||||
assert len(load.calls) == 2
|
||||
assert result.passed
|
||||
|
||||
|
||||
def test_each_recipe_reaches_the_backend_that_runs_it():
|
||||
"""A recipe that never reaches the loader was not really validated."""
|
||||
load = _loader()
|
||||
|
||||
run_doctor(_selection(), manifest=MANIFEST, load_backend=load, all_recipes=True)
|
||||
|
||||
params = [recipe.params for _, recipe in load.calls]
|
||||
assert params == [{}, {"use_cache": False}]
|
||||
|
||||
|
||||
def test_an_unknown_recipe_names_the_ones_that_exist():
|
||||
with pytest.raises(DoctorError) as exc:
|
||||
select_recipes(MANIFEST, recipe_id="does-not-exist")
|
||||
|
||||
assert exc.value.category == CATEGORY_UNSUPPORTED_RECIPE
|
||||
assert "baseline" in str(exc.value)
|
||||
|
||||
|
||||
def test_recipe_and_all_recipes_are_mutually_exclusive():
|
||||
with pytest.raises(DoctorError):
|
||||
select_recipes(MANIFEST, recipe_id="baseline", all_recipes=True)
|
||||
|
||||
|
||||
def test_a_recipe_the_backend_cannot_apply_is_a_failure_not_a_silent_pass():
|
||||
validate_recipe_params({"use_cache": False, "attn_implementation": "eager"})
|
||||
|
||||
with pytest.raises(UnsupportedRecipeParam) as exc:
|
||||
validate_recipe_params({"sparkle_mode": True})
|
||||
|
||||
assert "sparkle_mode" in str(exc.value)
|
||||
assert classify_failure(exc.value) == CATEGORY_UNSUPPORTED_RECIPE
|
||||
|
||||
|
||||
def test_the_shipped_recipes_are_all_applicable_by_the_backend():
|
||||
"""recipes.json and the backend's supported params must not drift apart."""
|
||||
from meshnet_node.recipe_manifest import load_recipe_manifest
|
||||
|
||||
for recipe in load_recipe_manifest().recipes:
|
||||
validate_recipe_params(recipe.params)
|
||||
|
||||
|
||||
# --- failure reporting -------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"exc, category",
|
||||
[
|
||||
(MissingModelDependencyError("no torch"), CATEGORY_MISSING_DEPENDENCY),
|
||||
(InsufficientVRAMError("too big"), CATEGORY_INSUFFICIENT_MEMORY),
|
||||
(UnsupportedRecipeParam("nope"), CATEGORY_UNSUPPORTED_RECIPE),
|
||||
(ValueError("shard_end 99 exceeds last layer index 23"), CATEGORY_INVALID_SHARD),
|
||||
(FileNotFoundError("config.json"), doctor.CATEGORY_MODEL_UNAVAILABLE),
|
||||
(RuntimeError("something else"), doctor.CATEGORY_LOAD_FAILED),
|
||||
],
|
||||
)
|
||||
def test_load_failures_are_classified_into_actionable_categories(exc, category):
|
||||
result = run_doctor(
|
||||
_selection(), manifest=MANIFEST, load_backend=_loader(error=exc)
|
||||
)
|
||||
|
||||
assert not result.passed
|
||||
item = result.results[0]
|
||||
assert item.category == category
|
||||
assert item.hint # every category tells the operator what to do next
|
||||
assert item.report.status == STATUS_FAILED
|
||||
|
||||
|
||||
def test_a_failure_report_carries_the_hint_and_no_traceback():
|
||||
result = run_doctor(
|
||||
_selection(),
|
||||
manifest=MANIFEST,
|
||||
load_backend=_loader(error=InsufficientVRAMError("insufficient VRAM to load")),
|
||||
)
|
||||
|
||||
diagnostics = " ".join(result.reports[0].diagnostics)
|
||||
assert "insufficient VRAM to load" in diagnostics
|
||||
assert "--shard-start" in diagnostics # the actionable next step
|
||||
assert "Traceback" not in diagnostics
|
||||
assert ".py" not in diagnostics # no file/line noise from a stack
|
||||
|
||||
|
||||
def test_a_failure_report_still_identifies_what_was_being_validated():
|
||||
"""NCA-003 refuses to register without a matching report — including a failed one."""
|
||||
result = run_doctor(
|
||||
_selection(shard_start=4, shard_end=9, quantization="int8"),
|
||||
manifest=MANIFEST,
|
||||
load_backend=_loader(error=RuntimeError("boom")),
|
||||
now=lambda: 4242.0,
|
||||
)
|
||||
|
||||
report = result.reports[0]
|
||||
assert report.identity_key() == (
|
||||
FIXTURE_MODEL, 4, 9, "baseline", "1", "torch-transformers", "unknown",
|
||||
)
|
||||
assert report.validated_at == 4242.0
|
||||
assert report.recipe.catalogue_version == "test-1"
|
||||
|
||||
|
||||
def test_the_report_records_the_device_the_forward_actually_ran_on():
|
||||
result = run_doctor(
|
||||
_selection(), manifest=MANIFEST, load_backend=_loader(_FakeBackend())
|
||||
)
|
||||
|
||||
assert result.reports[0].backend.device == "cpu"
|
||||
assert result.reports[0].backend.backend_id == "torch-transformers"
|
||||
|
||||
|
||||
def test_reports_round_trip_through_the_written_json(tmp_path):
|
||||
result = run_doctor(
|
||||
_selection(), manifest=MANIFEST, load_backend=_loader(), all_recipes=True
|
||||
)
|
||||
|
||||
path = write_reports(result.reports, tmp_path / "nested" / "capability.json")
|
||||
payload = json.loads(path.read_text())
|
||||
|
||||
assert [CapabilityReport.from_dict(d).recipe.recipe_id for d in payload] == [
|
||||
"baseline",
|
||||
"stateless",
|
||||
]
|
||||
|
||||
|
||||
def test_a_single_report_is_written_as_one_object():
|
||||
"""One selected recipe writes one report — the shape NCA-003 will read."""
|
||||
import tempfile
|
||||
|
||||
result = run_doctor(_selection(), manifest=MANIFEST, load_backend=_loader())
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = write_reports(result.reports, Path(tmp) / "capability.json")
|
||||
report = CapabilityReport.from_json(path.read_text())
|
||||
|
||||
assert report.passed
|
||||
|
||||
|
||||
def test_the_summary_tells_a_failing_operator_what_to_fix():
|
||||
result = run_doctor(
|
||||
_selection(),
|
||||
manifest=MANIFEST,
|
||||
load_backend=_loader(error=MissingModelDependencyError("torch is not installed")),
|
||||
)
|
||||
|
||||
text = render_result(result, report_path=Path("/tmp/capability.json"))
|
||||
|
||||
assert "FAIL" in text
|
||||
assert CATEGORY_MISSING_DEPENDENCY in text
|
||||
assert "torch is not installed" in text
|
||||
assert "/tmp/capability.json" in text
|
||||
assert "Traceback" not in text
|
||||
|
||||
|
||||
def test_the_summary_names_the_shard_that_passed():
|
||||
result = run_doctor(_selection(), manifest=MANIFEST, load_backend=_loader())
|
||||
|
||||
text = render_result(result)
|
||||
|
||||
assert "PASS" in text
|
||||
assert FIXTURE_MODEL in text
|
||||
assert "layers 0–3" in text
|
||||
|
||||
|
||||
# --- the CLI wiring ----------------------------------------------------------
|
||||
|
||||
|
||||
def _run_cli(monkeypatch, argv, backend=None, error=None):
|
||||
"""Drive `meshnet-node doctor` end to end with an injected backend."""
|
||||
import sys
|
||||
|
||||
from meshnet_node import cli, config
|
||||
|
||||
monkeypatch.setattr(
|
||||
config, "load_config", lambda *a, **k: {
|
||||
"model_hf_repo": FIXTURE_MODEL,
|
||||
"shard_start": 0,
|
||||
"shard_end": 3,
|
||||
"quantization": "auto",
|
||||
}
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
doctor, "default_load_backend", _loader(backend, error=error)
|
||||
)
|
||||
monkeypatch.setattr(doctor, "load_recipe_manifest", lambda *a, **k: MANIFEST)
|
||||
monkeypatch.setattr(sys, "argv", ["meshnet-node", *argv])
|
||||
|
||||
with pytest.raises(SystemExit) as exit_info:
|
||||
cli.main()
|
||||
return exit_info.value.code
|
||||
|
||||
|
||||
def test_cli_doctor_exits_zero_and_writes_a_passing_report(monkeypatch, capsys, tmp_path):
|
||||
report = tmp_path / "capability.json"
|
||||
|
||||
code = _run_cli(monkeypatch, ["doctor", "--report", str(report)], backend=_FakeBackend())
|
||||
|
||||
assert code == 0
|
||||
assert capsys.readouterr().out.count("PASS") == 1
|
||||
assert CapabilityReport.from_json(report.read_text()).passed
|
||||
|
||||
|
||||
def test_cli_doctor_exits_non_zero_and_writes_the_failed_report(monkeypatch, capsys, tmp_path):
|
||||
report = tmp_path / "capability.json"
|
||||
|
||||
code = _run_cli(
|
||||
monkeypatch,
|
||||
["doctor", "--report", str(report)],
|
||||
error=InsufficientVRAMError("insufficient VRAM to load 24 layers"),
|
||||
)
|
||||
|
||||
out = capsys.readouterr().out
|
||||
assert code == 1
|
||||
assert "FAIL" in out
|
||||
assert CATEGORY_INSUFFICIENT_MEMORY in out
|
||||
assert "Traceback" not in out # no raw traceback by default
|
||||
assert CapabilityReport.from_json(report.read_text()).status == STATUS_FAILED
|
||||
|
||||
|
||||
def test_cli_doctor_all_recipes_is_opt_in(monkeypatch, capsys, tmp_path):
|
||||
report = tmp_path / "capability.json"
|
||||
|
||||
code = _run_cli(
|
||||
monkeypatch,
|
||||
["doctor", "--all-recipes", "--report", str(report)],
|
||||
backend=_FakeBackend(),
|
||||
)
|
||||
|
||||
assert code == 0
|
||||
assert capsys.readouterr().out.count("PASS") == 2
|
||||
assert len(json.loads(report.read_text())) == 2
|
||||
|
||||
|
||||
def test_cli_doctor_json_prints_the_capability_report(monkeypatch, capsys, tmp_path):
|
||||
code = _run_cli(
|
||||
monkeypatch,
|
||||
["doctor", "--json", "--report", str(tmp_path / "c.json")],
|
||||
backend=_FakeBackend(),
|
||||
)
|
||||
|
||||
payload = json.loads(capsys.readouterr().out)
|
||||
assert code == 0
|
||||
assert payload[0]["model"]["model_id"] == FIXTURE_MODEL
|
||||
|
||||
|
||||
def test_cli_doctor_flags_select_what_is_validated(monkeypatch, capsys, tmp_path):
|
||||
"""`doctor --shard-start/--shard-end` validates the shard startup would load."""
|
||||
report = tmp_path / "capability.json"
|
||||
|
||||
code = _run_cli(
|
||||
monkeypatch,
|
||||
["doctor", "--shard-start", "2", "--shard-end", "5", "--report", str(report)],
|
||||
backend=_FakeBackend(),
|
||||
)
|
||||
|
||||
written = CapabilityReport.from_json(report.read_text())
|
||||
assert code == 0
|
||||
assert (written.shard.start, written.shard.end) == (2, 5)
|
||||
|
||||
|
||||
# --- the real-model smoke test ----------------------------------------------
|
||||
|
||||
# Model identity comes from the environment; there is no default, so this test
|
||||
# never smuggles a vendor-specific assumption into the suite.
|
||||
DOCTOR_MODEL = os.environ.get("MESHNET_DOCTOR_MODEL")
|
||||
DOCTOR_SHARD_START = int(os.environ.get("MESHNET_DOCTOR_SHARD_START", "0"))
|
||||
DOCTOR_SHARD_END = os.environ.get("MESHNET_DOCTOR_SHARD_END")
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.skipif(
|
||||
not DOCTOR_MODEL,
|
||||
reason="set MESHNET_DOCTOR_MODEL (and optionally MESHNET_DOCTOR_SHARD_START/END) to run",
|
||||
)
|
||||
def test_doctor_smoke_runs_a_real_forward_on_a_real_model(tmp_path):
|
||||
cfg = {
|
||||
"model_hf_repo": DOCTOR_MODEL,
|
||||
"quantization": os.environ.get("MESHNET_DOCTOR_QUANTIZATION", "auto"),
|
||||
"download_dir": os.environ.get("MESHNET_DOWNLOAD_DIR") or None,
|
||||
"shard_start": DOCTOR_SHARD_START,
|
||||
"shard_end": int(DOCTOR_SHARD_END) if DOCTOR_SHARD_END else None,
|
||||
"force_cpu": os.environ.get("MESHNET_DOCTOR_CPU") == "1",
|
||||
}
|
||||
selection = resolve_selection(cfg)
|
||||
|
||||
result = run_doctor(selection)
|
||||
|
||||
report = result.reports[0]
|
||||
assert result.passed, f"doctor failed: {report.diagnostics}"
|
||||
assert report.status == STATUS_PASSED
|
||||
assert report.model.model_id == DOCTOR_MODEL
|
||||
assert report.duration_ms > 0
|
||||
assert report.model.config_fingerprint.startswith("sha256:")
|
||||
|
||||
path = write_reports(result.reports, tmp_path / "capability.json")
|
||||
assert CapabilityReport.from_json(path.read_text()).passed
|
||||
Reference in New Issue
Block a user