|
| 1 | +"""Unit tests for scripts/hf-cache-seed.py.""" |
| 2 | + |
| 3 | +import importlib.util |
| 4 | +import sys |
| 5 | +import types |
| 6 | +from pathlib import Path |
| 7 | +from unittest.mock import MagicMock, patch |
| 8 | + |
| 9 | +import pytest |
| 10 | + |
| 11 | +# Filename has a hyphen, so load it via importlib (can't `import`). |
| 12 | +_spec = importlib.util.spec_from_file_location( |
| 13 | + "hf_cache_seed", str(Path(__file__).resolve().parent.parent / "hf-cache-seed.py") |
| 14 | +) |
| 15 | +_mod = importlib.util.module_from_spec(_spec) |
| 16 | +_spec.loader.exec_module(_mod) |
| 17 | + |
| 18 | +CLUSTERS = { |
| 19 | + "meta-staging-aws-ue1": {"region": "us-east-1", "modules": ["arc", "hf-cache"]}, |
| 20 | + "meta-staging-aws-uw1": {"region": "us-west-1", "modules": ["arc", "hf-cache"]}, |
| 21 | + "meta-prod-aws-ue1": {"region": "us-east-1", "modules": ["arc", "nodepools"]}, |
| 22 | +} |
| 23 | + |
| 24 | + |
| 25 | +def test_bucket_for(): |
| 26 | + assert _mod.bucket_for("meta-staging-aws-ue1") == "pytorch-hf-model-cache-meta-staging-aws-ue1" |
| 27 | + |
| 28 | + |
| 29 | +def test_resolve_targets_all_selects_hf_cache_clusters(): |
| 30 | + assert set(_mod.resolve_targets(CLUSTERS, None, True)) == {"meta-staging-aws-ue1", "meta-staging-aws-uw1"} |
| 31 | + |
| 32 | + |
| 33 | +def test_resolve_targets_all_none_enabled_exits(): |
| 34 | + with pytest.raises(SystemExit): |
| 35 | + _mod.resolve_targets({"c": {"region": "r", "modules": []}}, None, True) |
| 36 | + |
| 37 | + |
| 38 | +def test_resolve_targets_explicit(): |
| 39 | + assert _mod.resolve_targets(CLUSTERS, ["meta-staging-aws-ue1"], False) == ["meta-staging-aws-ue1"] |
| 40 | + |
| 41 | + |
| 42 | +def test_resolve_targets_unknown_exits(): |
| 43 | + with pytest.raises(SystemExit): |
| 44 | + _mod.resolve_targets(CLUSTERS, ["nope"], False) |
| 45 | + |
| 46 | + |
| 47 | +def test_resolve_targets_warns_when_module_disabled(capsys): |
| 48 | + assert _mod.resolve_targets(CLUSTERS, ["meta-prod-aws-ue1"], False) == ["meta-prod-aws-ue1"] |
| 49 | + assert "does not enable" in capsys.readouterr().err |
| 50 | + |
| 51 | + |
| 52 | +def test_load_clusters(tmp_path, monkeypatch): |
| 53 | + f = tmp_path / "clusters.yaml" |
| 54 | + f.write_text("clusters:\n c1:\n region: us-east-1\n") |
| 55 | + monkeypatch.setattr(_mod, "CLUSTERS_YAML", f) |
| 56 | + assert _mod.load_clusters() == {"c1": {"region": "us-east-1"}} |
| 57 | + |
| 58 | + |
| 59 | +def test_download_models(monkeypatch, tmp_path): |
| 60 | + fake = types.ModuleType("huggingface_hub") |
| 61 | + fake.snapshot_download = lambda model, cache_dir, allow_patterns=None: f"{cache_dir}/{model}" |
| 62 | + monkeypatch.setitem(sys.modules, "huggingface_hub", fake) |
| 63 | + assert _mod.download_models(["org/m"], tmp_path) == [] |
| 64 | + assert (tmp_path / "hub").is_dir() |
| 65 | + |
| 66 | + |
| 67 | +def test_download_models_config_only_passes_allow_patterns(monkeypatch, tmp_path): |
| 68 | + seen = {} |
| 69 | + |
| 70 | + def cap(model, cache_dir, allow_patterns=None): |
| 71 | + seen["allow"] = allow_patterns |
| 72 | + return f"{cache_dir}/{model}" |
| 73 | + |
| 74 | + fake = types.ModuleType("huggingface_hub") |
| 75 | + fake.snapshot_download = cap |
| 76 | + monkeypatch.setitem(sys.modules, "huggingface_hub", fake) |
| 77 | + _mod.download_models(["org/m"], tmp_path, config_only=True) |
| 78 | + assert seen["allow"] == _mod.CONFIG_ONLY_PATTERNS |
| 79 | + |
| 80 | + |
| 81 | +def test_download_models_skips_failure(monkeypatch, tmp_path): |
| 82 | + def boom(model, cache_dir, allow_patterns=None): |
| 83 | + if model == "org/bad": |
| 84 | + raise OSError("gated repo") |
| 85 | + return f"{cache_dir}/{model}" |
| 86 | + |
| 87 | + fake = types.ModuleType("huggingface_hub") |
| 88 | + fake.snapshot_download = boom |
| 89 | + monkeypatch.setitem(sys.modules, "huggingface_hub", fake) |
| 90 | + assert _mod.download_models(["org/ok", "org/bad"], tmp_path) == ["org/bad"] |
| 91 | + |
| 92 | + |
| 93 | +def _fake_popen(lines, rc): |
| 94 | + fake = MagicMock() |
| 95 | + fake.stdout = list(lines) # iterable + truthy |
| 96 | + fake.wait.return_value = rc |
| 97 | + return fake |
| 98 | + |
| 99 | + |
| 100 | +def test_sync_to_cluster_ok(): |
| 101 | + with patch.object(_mod.subprocess, "Popen", return_value=_fake_popen(["upload: a\n"], 0)): |
| 102 | + cid, ok, _ = _mod.sync_to_cluster("c1", "us-east-1", Path("/tmp/x")) |
| 103 | + assert ok |
| 104 | + assert cid == "c1" |
| 105 | + |
| 106 | + |
| 107 | +def test_sync_to_cluster_fail(): |
| 108 | + with patch.object(_mod.subprocess, "Popen", return_value=_fake_popen(["AccessDenied\n"], 1)): |
| 109 | + _, ok, detail = _mod.sync_to_cluster("c1", "us-east-1", Path("/tmp/x")) |
| 110 | + assert not ok |
| 111 | + assert "exited 1" in detail |
| 112 | + |
| 113 | + |
| 114 | +def _run_main(argv, sync=lambda cid, region, staging: (cid, True, "ok")): |
| 115 | + with ( |
| 116 | + patch.object(_mod, "load_clusters", return_value=CLUSTERS), |
| 117 | + patch.object(_mod, "download_models", return_value=[]), |
| 118 | + patch.object(_mod.shutil, "which", return_value="/usr/bin/aws"), |
| 119 | + patch.object(_mod, "sync_to_cluster", side_effect=sync), |
| 120 | + ): |
| 121 | + return _mod.main(argv) |
| 122 | + |
| 123 | + |
| 124 | +def test_main_success(capsys): |
| 125 | + assert _run_main(["-c", "meta-staging-aws-ue1", "org/m"]) == 0 |
| 126 | + assert "Seeded" in capsys.readouterr().out |
| 127 | + |
| 128 | + |
| 129 | +def test_main_all_success(): |
| 130 | + assert _run_main(["--all", "org/m"]) == 0 |
| 131 | + |
| 132 | + |
| 133 | +def test_main_reports_failure(capsys): |
| 134 | + rc = _run_main(["-c", "meta-staging-aws-ue1", "org/m"], sync=lambda cid, region, staging: (cid, False, "e1\ne2")) |
| 135 | + assert rc == 1 |
| 136 | + assert "FAIL" in capsys.readouterr().out |
| 137 | + |
| 138 | + |
| 139 | +def test_main_reports_download_skip(capsys): |
| 140 | + with ( |
| 141 | + patch.object(_mod, "load_clusters", return_value=CLUSTERS), |
| 142 | + patch.object(_mod, "download_models", return_value=["org/bad"]), |
| 143 | + patch.object(_mod.shutil, "which", return_value="/usr/bin/aws"), |
| 144 | + patch.object(_mod, "sync_to_cluster", side_effect=lambda cid, region, staging: (cid, True, "")), |
| 145 | + ): |
| 146 | + rc = _mod.main(["-c", "meta-staging-aws-ue1", "org/ok", "org/bad"]) |
| 147 | + assert rc == 1 |
| 148 | + assert "SKIPPED" in capsys.readouterr().out |
| 149 | + |
| 150 | + |
| 151 | +def test_main_requires_aws(): |
| 152 | + with ( |
| 153 | + patch.object(_mod, "load_clusters", return_value=CLUSTERS), |
| 154 | + patch.object(_mod.shutil, "which", return_value=None), |
| 155 | + pytest.raises(SystemExit), |
| 156 | + ): |
| 157 | + _mod.main(["-c", "meta-staging-aws-ue1", "org/m"]) |
0 commit comments