mirror of https://github.com/razor-ai/soup.git
132 lines
3.8 KiB
Python
132 lines
3.8 KiB
Python
"""Tests for soup push command."""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from typer.testing import CliRunner
|
|
|
|
from soup_cli.cli import app
|
|
from soup_cli.commands.push import _format_size, _generate_model_card
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
# --- _format_size ---
|
|
|
|
def test_format_size_bytes():
|
|
assert _format_size(512) == "512.0 B"
|
|
|
|
|
|
def test_format_size_kb():
|
|
assert _format_size(2048) == "2.0 KB"
|
|
|
|
|
|
def test_format_size_mb():
|
|
assert _format_size(5 * 1024 * 1024) == "5.0 MB"
|
|
|
|
|
|
def test_format_size_gb():
|
|
assert _format_size(3 * 1024**3) == "3.0 GB"
|
|
|
|
|
|
def test_format_size_tb():
|
|
assert _format_size(2 * 1024**4) == "2.0 TB"
|
|
|
|
|
|
# --- _generate_model_card ---
|
|
|
|
def test_model_card_adapter(tmp_path: Path):
|
|
"""Model card for LoRA adapter should include base model info."""
|
|
adapter_dir = tmp_path / "adapter"
|
|
adapter_dir.mkdir()
|
|
config = adapter_dir / "adapter_config.json"
|
|
config.write_text(json.dumps({
|
|
"base_model_name_or_path": "meta-llama/Llama-3.1-8B",
|
|
"r": 64,
|
|
"lora_alpha": 16,
|
|
}))
|
|
|
|
card = _generate_model_card(adapter_dir, "user/my-model", is_adapter=True)
|
|
assert "my-model" in card
|
|
assert "meta-llama/Llama-3.1-8B" in card
|
|
assert "LoRA rank" in card
|
|
assert "soup-cli" in card
|
|
|
|
|
|
def test_model_card_full_model(tmp_path: Path):
|
|
"""Model card for full model should not include LoRA info."""
|
|
model_dir = tmp_path / "model"
|
|
model_dir.mkdir()
|
|
|
|
card = _generate_model_card(model_dir, "user/full-model", is_adapter=False)
|
|
assert "full-model" in card
|
|
assert "fine-tuned language model" in card
|
|
assert "soup-cli" in card
|
|
|
|
|
|
def test_model_card_adapter_bad_config(tmp_path: Path):
|
|
"""Model card should handle malformed adapter_config.json gracefully."""
|
|
adapter_dir = tmp_path / "adapter"
|
|
adapter_dir.mkdir()
|
|
config = adapter_dir / "adapter_config.json"
|
|
config.write_text("not valid json")
|
|
|
|
card = _generate_model_card(adapter_dir, "user/broken", is_adapter=True)
|
|
assert "broken" in card
|
|
|
|
|
|
def test_model_card_repo_without_slash():
|
|
"""Model card should handle repo ID without slash."""
|
|
import tempfile
|
|
with tempfile.TemporaryDirectory() as td:
|
|
card = _generate_model_card(Path(td), "my-model", is_adapter=False)
|
|
assert "my-model" in card
|
|
|
|
|
|
# --- CLI validation ---
|
|
|
|
def test_push_missing_model():
|
|
result = runner.invoke(app, ["push", "--model", "/nonexistent", "--repo", "user/model"])
|
|
assert result.exit_code == 1
|
|
|
|
|
|
def test_push_not_a_directory(tmp_path: Path):
|
|
fake_file = tmp_path / "model.bin"
|
|
fake_file.write_text("content")
|
|
result = runner.invoke(
|
|
app, ["push", "--model", str(fake_file), "--repo", "user/model"]
|
|
)
|
|
assert result.exit_code == 1
|
|
assert "directory" in result.output.lower()
|
|
|
|
|
|
def test_push_invalid_model_dir(tmp_path: Path):
|
|
"""Empty directory (no adapter_config.json or config.json) should fail."""
|
|
model_dir = tmp_path / "empty"
|
|
model_dir.mkdir()
|
|
result = runner.invoke(
|
|
app, ["push", "--model", str(model_dir), "--repo", "user/model"]
|
|
)
|
|
assert result.exit_code == 1
|
|
|
|
|
|
def test_push_no_token(tmp_path: Path, monkeypatch):
|
|
"""Should fail if no HF token is available."""
|
|
monkeypatch.delenv("HF_TOKEN", raising=False)
|
|
monkeypatch.delenv("HUGGINGFACE_HUB_TOKEN", raising=False)
|
|
# Point HOME at an empty dir so the cached-token fallback also misses.
|
|
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path / "no-creds")
|
|
# `soup push` now requires --model under cwd.
|
|
monkeypatch.chdir(tmp_path)
|
|
|
|
model_dir = tmp_path / "model"
|
|
model_dir.mkdir()
|
|
(model_dir / "adapter_config.json").write_text("{}")
|
|
(model_dir / "adapter_model.safetensors").write_text("fake")
|
|
|
|
result = runner.invoke(
|
|
app, ["push", "--model", str(model_dir), "--repo", "user/model"]
|
|
)
|
|
assert result.exit_code == 1
|
|
assert "token" in result.output.lower()
|