import sys import pytest def test_lib_path_env_override(tmp_path, monkeypatch): import needle fake = tmp_path / "libneedle.dylib" fake.write_bytes(b"x") monkeypatch.setenv("NEEDLE_LIB_PATH", str(fake)) assert needle._library_path() == str(fake) def test_legacy_lib_override_cannot_capture_v3(tmp_path, monkeypatch): import needle from needle.agent import fetch v2 = tmp_path / "libneedle-v2.dylib" v3 = tmp_path / "libneedle-v3.dylib" v2.write_bytes(b"v2") v3.write_bytes(b"v3") monkeypatch.setenv("NEEDLE_LIB_PATH", str(v2)) monkeypatch.setenv("NEEDLE3_LIB_PATH", str(v3)) assert needle._library_path(2) == str(v2) assert needle._library_path(3) == str(v3) monkeypatch.delenv("NEEDLE3_LIB_PATH") monkeypatch.setenv("HOME", str(tmp_path)) monkeypatch.setenv("USERPROFILE", str(tmp_path)) monkeypatch.setattr(fetch, "fetch_library", lambda *args, **kwargs: str(v3)) assert needle._library_path(3) == str(v3) def test_download_target_kinds(): import pytest from needle.cli import _download_target assert _download_target("macos-arm64") == ("platform", "macos-arm64") assert _download_target("needle3") == ("base", 3) assert _download_target("needle2.cact") == ("base", 2) assert _download_target("needle3.safetensors") == ("checkpoint", "needle3.safetensors") assert _download_target("acme/tuned/model.cact") == ("hub", "acme/tuned/model.cact") with pytest.raises(SystemExit, match="unknown download"): _download_target("needle4") def test_fetch_weights_copies_the_base_archive_and_reuses_it(tmp_path, monkeypatch): from needle.agent import fetch archive = tmp_path / "needle3.cact" archive.write_bytes(b"weights") calls = [] def fake_download(**kwargs): calls.append(kwargs["filename"]) return str(archive) monkeypatch.setattr(fetch, "_register_download", lambda generation: None) monkeypatch.setattr("huggingface_hub.hf_hub_download", fake_download) dest = tmp_path / "cache" out = fetch.fetch_weights(3, str(dest)) assert out == str(dest / "needle3.cact") and open(out, "rb").read() == b"weights" assert calls == ["needle3.cact"] assert fetch.fetch_weights(3, str(dest)) == out and calls == ["needle3.cact"] def test_fetch_checkpoint_prefers_the_checkpoints_folder(tmp_path, monkeypatch): from huggingface_hub.errors import EntryNotFoundError from needle.agent import fetch source = tmp_path / "needle3.safetensors" source.write_bytes(b"ckpt") def fake_download(**kwargs): if kwargs["filename"] != "checkpoints/needle3.safetensors": raise EntryNotFoundError("missing") return str(source) monkeypatch.setattr(fetch, "_register_download", lambda generation: None) monkeypatch.setattr("huggingface_hub.hf_hub_download", fake_download) out = fetch.fetch_checkpoint("needle3.safetensors", str(tmp_path / "dl" / "checkpoints")) assert out.endswith("checkpoints/needle3.safetensors") and open(out, "rb").read() == b"ckpt" def test_weights_spec_parsing(): from needle.cli import _weights_spec assert _weights_spec("acme/tuned/model.cact") == ("acme/tuned", "model.cact") assert _weights_spec("acme/tuned") == ("acme/tuned", None) assert _weights_spec("acme/tuned/sub/dir/m.cact") == ("acme/tuned", "sub/dir/m.cact") def test_lib_name_for_tags(): from needle.agent.fetch import _lib_name_for assert _lib_name_for("macosx_11_0_arm64") == "libneedle.dylib" assert _lib_name_for("win_amd64") == "libneedle.dll" assert _lib_name_for("manylinux2014_aarch64") == "libneedle.so" assert _lib_name_for("musllinux_1_2_x86_64") == "libneedle.so" def test_component_platform_is_downloadable(): from needle.agent.fetch import PLATFORMS assert "wasm-component" in PLATFORMS def test_unpublished_engine_wheels_lists_every_missing_tag(monkeypatch): from needle.agent import fetch published = {} for generation, version in fetch.ENGINE_VERSIONS.items(): if generation in fetch.WEIGHTS_ONLY: continue published.setdefault(fetch.ENGINE_REPOS[generation], set()).update( fetch.engine_wheel(version, tag, generation) for tag in fetch.WHEEL_TAGS) monkeypatch.setattr("huggingface_hub.list_repo_files", lambda repo: sorted(published[repo]) + ["needle3.cact"]) assert fetch.unpublished_engine_wheels() == [] repo3 = fetch.ENGINE_REPOS[3] dropped = fetch.engine_wheel(fetch.ENGINE_VERSIONS[3], "win_arm64") published[repo3].remove(dropped) assert fetch.unpublished_engine_wheels() == [repo3 + "/" + dropped] assert fetch.WHISTLE in fetch.WEIGHTS_ONLY assert not any(fetch.ENGINE_REPOS[fetch.WHISTLE] in path for path in fetch.unpublished_engine_wheels()) def test_a_repo_that_cannot_be_listed_holds_none_of_its_wheels(monkeypatch): """The whistle repo is private until release; the gate must report, not raise.""" from needle.agent import fetch def refuse(repo): raise OSError("401 private repo") monkeypatch.setattr("huggingface_hub.list_repo_files", refuse) missing = fetch.unpublished_engine_wheels() engines = len(fetch.ENGINE_VERSIONS) - len(fetch.WEIGHTS_ONLY) assert len(missing) == engines * len(fetch.WHEEL_TAGS) def test_release_gate_runs_before_the_publish_step(): from pathlib import Path workflow = Path(__file__).parents[1] / ".github/workflows/release.yaml" text = workflow.read_text() assert text.index("unpublished_engine_wheels") < text.index("pypa/gh-action-pypi-publish") def test_fetch_library_creates_destination(tmp_path, monkeypatch): import zipfile from needle.agent import fetch wheel = tmp_path / "engine.whl" with zipfile.ZipFile(wheel, "w") as archive: archive.writestr("needle/libneedle.so", b"engine") monkeypatch.setattr(fetch, "_register_download", lambda generation: None) monkeypatch.setattr("huggingface_hub.hf_hub_download", lambda **kwargs: str(wheel)) out = fetch.fetch_library("2.0.4", tmp_path / "new", tag="manylinux2014_x86_64") assert (tmp_path / "new" / "libneedle.so").read_bytes() == b"engine" assert out == str(tmp_path / "new" / "libneedle.so") def test_engine_gate_finds_the_cache_the_runtime_loads_from(tmp_path, monkeypatch): import needle from needle.agent import fetch from conftest import _engine_available package = tmp_path / "pkg" package.mkdir() monkeypatch.setattr(needle, "__file__", str(package / "__init__.py")) monkeypatch.setenv("HOME", str(tmp_path)) monkeypatch.setenv("USERPROFILE", str(tmp_path)) monkeypatch.delenv("NEEDLE_LIB_PATH", raising=False) monkeypatch.delenv("NEEDLE3_LIB_PATH", raising=False) assert not _engine_available() cache = tmp_path / ".cache" / "cactus-needle" / "v3" / fetch.engine_version(3) cache.mkdir(parents=True) (cache / fetch._lib_name()).write_bytes(b"") assert _engine_available() def test_engine_gate_honours_the_library_override(tmp_path, monkeypatch): import needle from needle.agent import fetch from conftest import _engine_available package = tmp_path / "pkg" package.mkdir() monkeypatch.setattr(needle, "__file__", str(package / "__init__.py")) monkeypatch.setenv("HOME", str(tmp_path)) monkeypatch.setenv("USERPROFILE", str(tmp_path)) engine = tmp_path / fetch._lib_name() engine.write_bytes(b"") monkeypatch.setenv("NEEDLE3_LIB_PATH", str(engine)) assert _engine_available() monkeypatch.setenv("NEEDLE3_LIB_PATH", str(tmp_path / "gone")) assert not _engine_available() def test_musl_detected_when_libc_ver_claims_glibc(monkeypatch, tmp_path): import platform from needle.agent import fetch monkeypatch.setattr(platform, "libc_ver", lambda *a, **k: ("glibc", "2.9")) monkeypatch.setattr(sys, "platform", "linux") maps = tmp_path / "maps" maps.write_bytes(b"7f0000000000-7f0000001000 r-xp /lib/ld-musl-x86_64.so.1\n") real_open = open monkeypatch.setattr("builtins.open", lambda p, *a, **k: real_open(maps, *a, **k) if p == "/proc/self/maps" else real_open(p, *a, **k)) assert fetch._is_musl() is True def test_glibc_detected_from_maps(monkeypatch, tmp_path): import platform from needle.agent import fetch monkeypatch.setattr(platform, "libc_ver", lambda *a, **k: ("", "")) monkeypatch.setattr(sys, "platform", "linux") maps = tmp_path / "maps" maps.write_bytes(b"7f0000000000-7f0000001000 r-xp /usr/lib/x86_64-linux-gnu/libc.so.6\n") real_open = open monkeypatch.setattr("builtins.open", lambda p, *a, **k: real_open(maps, *a, **k) if p == "/proc/self/maps" else real_open(p, *a, **k)) assert fetch._is_musl() is False def test_platform_tag_names_only_the_architectures_the_engine_is_built_for(monkeypatch): import platform from needle.agent import fetch monkeypatch.setattr(fetch, "_is_musl", lambda: False) monkeypatch.setattr(fetch, "_is_android", lambda: False) for system, machine, tag in (("linux", "x86_64", "manylinux2014_x86_64"), ("linux", "aarch64", "manylinux2014_aarch64"), ("win32", "AMD64", "win_amd64"), ("win32", "ARM64", "win_arm64"), ("darwin", "arm64", "macosx_11_0_arm64")): monkeypatch.setattr(sys, "platform", system) monkeypatch.setattr(platform, "machine", lambda machine=machine: machine) assert fetch._platform_tag() == tag # Every other machine was handed the x86_64 build, which cannot load there. for system, machine in (("linux", "armv7l"), ("linux", "riscv64"), ("linux", "i686"), ("win32", "x86")): monkeypatch.setattr(sys, "platform", system) monkeypatch.setattr(platform, "machine", lambda machine=machine: machine) with pytest.raises(RuntimeError, match=f"on {machine};.*NEEDLE3_LIB_PATH"): fetch._platform_tag() def test_other_libc_tag_swaps_families(monkeypatch): from needle.agent import fetch monkeypatch.setattr(fetch, "_platform_tag", lambda: "manylinux2014_aarch64") assert fetch.other_libc_tag() == "musllinux_1_2_aarch64" monkeypatch.setattr(fetch, "_platform_tag", lambda: "musllinux_1_2_x86_64") assert fetch.other_libc_tag() == "manylinux2014_x86_64" monkeypatch.setattr(fetch, "_platform_tag", lambda: "macosx_11_0_arm64") assert fetch.other_libc_tag() is None def test_engine_load_falls_back_to_the_other_libc(monkeypatch, tmp_path): import ctypes import needle from needle.agent import fetch good = tmp_path / "musl" / "libneedle.so" good.parent.mkdir() good.write_bytes(b"x") monkeypatch.setattr(needle, "_library_path", lambda generation=2: str(tmp_path / "libneedle.so")) monkeypatch.setattr(fetch, "other_libc_tag", lambda: "musllinux_1_2_x86_64") monkeypatch.setattr(fetch, "fetch_library", lambda version=None, dest_dir=None, tag=None, generation=2: str(good)) loaded = {} def fake_cdll(path): if path.endswith("musl/libneedle.so"): loaded["path"] = path return "handle" raise OSError("Error relocating libneedle.so: strtoll_l: symbol not found") monkeypatch.setattr(ctypes, "CDLL", fake_cdll) with pytest.warns(UserWarning, match="using the musllinux"): assert needle._load_cdll(3) == "handle" assert loaded["path"].endswith("musl/libneedle.so") def test_android_is_refused_instead_of_being_handed_a_glibc_wheel(monkeypatch): """Termux reports Linux/aarch64, so it used to download a wheel Bionic cannot load.""" import platform from needle.agent import fetch monkeypatch.setattr(sys, "platform", "linux") monkeypatch.setattr(platform, "machine", lambda: "aarch64") monkeypatch.setattr(fetch, "_is_musl", lambda: False) monkeypatch.setattr(fetch, "_is_android", lambda: False) assert fetch._platform_tag() == "manylinux2014_aarch64" monkeypatch.setattr(fetch, "_is_android", lambda: True) with pytest.raises(RuntimeError, match="Android .Bionic.*libm.so.6.*NEEDLE3_LIB_PATH"): fetch._platform_tag() def test_android_is_detected_from_the_api_level_or_the_runtime_env(monkeypatch): from needle.agent import fetch monkeypatch.delenv("ANDROID_ROOT", raising=False) monkeypatch.delenv("ANDROID_DATA", raising=False) monkeypatch.delattr(sys, "getandroidapilevel", raising=False) assert fetch._is_android() is False monkeypatch.setenv("ANDROID_ROOT", "/system") assert fetch._is_android() is False, "one marker alone is not Android" monkeypatch.setenv("ANDROID_DATA", "/data") assert fetch._is_android() is True monkeypatch.delenv("ANDROID_ROOT") monkeypatch.delenv("ANDROID_DATA") monkeypatch.setattr(sys, "getandroidapilevel", lambda: 34, raising=False) assert fetch._is_android() is True def test_building_the_published_archive_needs_no_training_stack(tmp_path, monkeypatch): import needle._telemetry from needle.agent import fetch from needle.cli import main base = tmp_path / "needle3.cact" base.write_bytes(b"archive") def fake_platform(name, out_dir, generation=3, dest=None): (tmp_path / "pi").mkdir() (tmp_path / "pi" / "needle").write_bytes(b"engine") return [str(tmp_path / "pi" / "needle")] monkeypatch.setattr(needle._telemetry, "track", lambda *a, **k: None) monkeypatch.setattr(fetch, "fetch_weights", lambda generation=2, dest_dir=None, force=False: str(base)) monkeypatch.setattr(fetch, "download_platform", fake_platform) for name in ("numpy", "jax"): monkeypatch.setitem(sys.modules, name, None) monkeypatch.setattr(sys, "argv", ["needle", "build", "--platform", "linux-arm64", "--out", str(tmp_path / "pi")]) main() assert (tmp_path / "pi" / "needle3.cact").read_bytes() == b"archive"