Skip to content
544 changes: 508 additions & 36 deletions tests/cli/test_machines_commands.py

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions tests/cli/test_runtime_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,26 @@ def test_legacy_parser_tracks_stage_and_classifies_nccl_error():
assert result["underlying_error"] == "ERROR: NCCL unhandled system error during allreduce"


@pytest.mark.parametrize(
("line", "stage"),
[
("Running ResNet18 test on all GPUs...", diag.STAGE_RESNET),
("Running ECC test on all GPUs...", diag.STAGE_ECC),
("Running NCCL distributed test with 2 GPUs...", diag.STAGE_NCCL),
(
"Running stress-ng and gpu-burn tests simultaneously for 60 seconds...",
diag.STAGE_STRESS_GPU_BURN,
),
],
)
def test_legacy_parser_tracks_current_self_test_image_stage_lines(line, stage):
parser = diag.LegacyProgressParser()

assert parser.process_line(line) is None

assert parser.stage == stage


def test_legacy_parser_classifies_unknown_error_as_legacy_progress_error():
result = diag.parse_legacy_progress(
"\n".join(
Expand Down Expand Up @@ -171,6 +191,30 @@ def test_status_msg_classifies_generic_daemon_startup_failure():
assert result["underlying_error"] == status_msg


def test_status_msg_failure_detection_ignores_package_names_with_error_substring():
status_msg = (
"#7 0.829 libappstream5 libargon2-1 libbrotli1 libcap2-bin libcbor0.10\n"
"#7 0.829 liberror-perl libevent-core-2.1-7t64 libfdisk1 libfido2-1"
)

assert diag.status_msg_indicates_failure(status_msg) is False


def test_status_msg_classifies_buildkit_output_when_instance_status_is_terminal():
status_msg = (
"#7 0.829 libappstream5 libargon2-1 libbrotli1 libcap2-bin libcbor0.10\n"
"#7 0.829 liberror-perl libevent-core-2.1-7t64 libfdisk1 libfido2-1"
)

result = diag.classify_status_msg(status_msg)

assert result["code"] == diag.DAEMON_STARTUP_FAILED
assert result["stage"] == diag.STAGE_STARTUP
assert result["underlying_error"] == status_msg
assert "Docker/BuildKit output from Vast daemon" in result["details"]
assert "daemon source" in " ".join(result["suggested_steps"])


def test_status_msg_classifies_other_errors_as_status_error():
result = diag.classify_status_msg("Error: host reported an unknown fault")

Expand Down
56 changes: 51 additions & 5 deletions tests/cli/test_self_test_support_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@ def test_support_bundle_contains_redacted_result_and_cli_output(tmp_path):
"error": "request failed: https://console.vast.ai/?api_key=secret",
"diagnostics": {"jupyter_token": "secret-token"},
},
cli_output=["Starting self-test", "token=supersecret"],
cli_output=[
"Starting self-test",
"token=supersecret",
"Captured instance_info: {'success': True, 'instance_api_key': 'instance-secret'}",
'Captured show-instance: {"jupyter_token": "jupyter-secret"}',
],
run_started_at="20260602T100000Z",
command=["vastai", "--api-key", "secret", "self-test", "machine", "42"],
secrets=["secret", "supersecret"],
secrets=["secret", "supersecret", "instance-secret", "jupyter-secret"],
include_host_logs=False,
)

Expand All @@ -34,11 +39,48 @@ def test_support_bundle_contains_redacted_result_and_cli_output(tmp_path):
assert {"manifest.json", "self-test-result.json", "self-test-output.log", "collection-errors.json"} <= names
assert "secret" not in result_json
assert "secret" not in output_log
assert "instance-secret" not in output_log
assert "jupyter-secret" not in output_log
assert "REDACTED" in result_json
assert "REDACTED" in output_log
assert manifest["machine_id"] == "42"
assert manifest["run_started_at_utc"] == "20260602T100000Z"
assert manifest["includes_local_host_artifacts"] is False
assert manifest["includes_cli_visible_instance_artifacts"] is False
assert manifest["cli_visible_instance_artifacts"] == []
assert manifest["local_host_artifacts"] == []


def test_support_bundle_manifest_describes_instance_and_host_artifacts(tmp_path, monkeypatch):
from vastai.cli.self_test import support_bundle

monkeypatch.setattr(
support_bundle,
"_collect_host_artifacts",
lambda secrets=None: ({"host/dmesg-filtered.log": "host kernel warning"}, []),
)

bundle = support_bundle.create_support_bundle(
machine_id="42",
output_dir=str(tmp_path),
extra_files={
"instance/container.log": "container startup failure",
"instance/daemon.log": "daemon startup failure",
},
include_local_host_artifacts=True,
)

with tarfile.open(bundle["path"], "r:gz") as tar:
manifest = json.loads(tar.extractfile("manifest.json").read().decode())

assert manifest["includes_cli_visible_instance_artifacts"] is True
assert manifest["cli_visible_instance_artifacts"] == [
"instance/container.log",
"instance/daemon.log",
]
assert manifest["includes_local_host_artifacts"] is True
assert manifest["local_host_artifacts"] == ["host/dmesg-filtered.log"]
assert "cli_visible_instance_artifacts are collected through the Vast instance logs API" in manifest["note"]


def test_support_bundle_sanitizes_archive_names(tmp_path):
Expand All @@ -62,6 +104,8 @@ def test_self_test_failure_creates_support_bundle(
):
from vastai.cli.commands import machines

bundle_path = str(tmp_path / "vast_selftest_42_20260602T100000Z.tar.gz")

def fake_bundle(**kwargs):
assert kwargs["machine_id"] == "42"
assert kwargs["output_dir"] == str(tmp_path)
Expand All @@ -71,7 +115,7 @@ def fake_bundle(**kwargs):
assert kwargs["result"]["failure_code"] == "preflight_requirements_failed"
assert any("Preflight diagnostics for machine 42 failed:" in line for line in kwargs["cli_output"])
return {
"path": str(tmp_path / "vast_selftest_42_20260602T100000Z.tar.gz"),
"path": bundle_path,
"created_at_utc": "20260602T100000Z",
"size_bytes": 123,
"files": ["manifest.json", "self-test-result.json", "self-test-output.log"],
Expand Down Expand Up @@ -107,7 +151,9 @@ def fake_bundle(**kwargs):
assert exc_info.value.code == 1
assert "Self-test diagnostic bundle saved to:" in captured.out
assert "self-test-result.json" in captured.out
assert "Review this tarball before sharing it with support." in captured.out
assert "Review this tarball before sharing it outside your own diagnostic workflow." in captured.out
assert f"Support bundle: {bundle_path}" in captured.out
assert captured.out.rfind("Test failed:") < captured.out.rfind("Support bundle:")


def test_self_test_bundle_creation_error_preserves_original_failure(
Expand Down Expand Up @@ -144,7 +190,7 @@ def test_self_test_bundle_creation_error_preserves_original_failure(
captured = capsys.readouterr()
assert exc_info.value.code == 1
assert "WARNING: failed to create self-test diagnostic bundle: disk full" in captured.out
assert "Test failed: 8 preflight requirement check(s) failed." in captured.out
assert "Test failed: 7 preflight requirement check(s) failed." in captured.out


def test_self_test_runtime_failure_bundle_includes_instance_logs(
Expand Down
Loading