"""Pure handler contracts for the Watchtower and one-click Gradio tabs."""
from __future__ import annotations
import importlib
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import Mock
import pytest
auto_obliterate = importlib.import_module("obliteratus.auto_obliterate")
ui_watchtower = importlib.import_module("obliteratus.ui_watchtower")
def test_component_update_delegates_to_gradio_when_available(monkeypatch):
update = Mock(return_value={"delegated": True})
monkeypatch.setattr(ui_watchtower, "gr", SimpleNamespace(update=update))
assert ui_watchtower._component_update(interactive=True) == {"delegated": True}
update.assert_called_once_with(interactive=True)
def test_trending_choices_use_watchtower_and_fall_back_on_empty_or_error(monkeypatch):
watchtower = SimpleNamespace(get_model_choices=lambda: ["org/one", "org/two"])
monkeypatch.setattr(ui_watchtower, "_get_watchtower", lambda: watchtower)
assert ui_watchtower._get_trending_choices() == ["org/one", "org/two"]
watchtower.get_model_choices = lambda: []
assert ui_watchtower._get_trending_choices()[0] == "meta-llama/Llama-3.1-8B-Instruct"
monkeypatch.setattr(
ui_watchtower,
"_get_watchtower",
Mock(side_effect=RuntimeError("offline")),
)
assert "Qwen/Qwen3-4B" in ui_watchtower._get_trending_choices()
@pytest.mark.parametrize("model_id", ["", " ", None])
def test_one_click_rejects_missing_model_without_starting_work(model_id):
outputs = list(ui_watchtower._run_one_click(model_id, 3, 5))
assert len(outputs) == 1
assert "Please enter or select" in outputs[0][0]
assert outputs[0][3]["interactive"] is False
def test_one_click_streams_progress_and_preserves_zero_refusal_rate(monkeypatch, tmp_path):
output = tmp_path / "model"
output.mkdir()
created: list[object] = []
final = SimpleNamespace(
success=True,
final_output_dir=str(output),
final_refusal_rate=0.0,
total_time_seconds=12.5,
)
class FakeObliterator:
def __init__(self, **kwargs):
created.append(kwargs)
self._result = SimpleNamespace(final_output_dir=str(output))
def run(self):
yield "running", "step", "metrics"
return final
def _format_metrics(self):
return "final metrics"
monkeypatch.setattr(auto_obliterate, "AutoObliterator", FakeObliterator)
outputs = list(ui_watchtower._run_one_click(" org/model ", 3.9, 5))
assert created == [
{
"model_id": "org/model",
"max_iterations": 3,
"target_refusal_rate": 0.05,
}
]
assert outputs[0][0] == "running"
assert outputs[0][3]["interactive"] is True
assert outputs[-1][0] == "✅ Complete!"
assert "Final refusal rate: 0.0" in outputs[-1][1]
assert "unknown" not in outputs[-1][1]
def test_one_click_converts_generator_failure_to_noninteractive_error(monkeypatch):
class FailingObliterator:
def __init__(self, **_kwargs):
self._result = SimpleNamespace(final_output_dir=None)
def run(self):
yield "starting", "", ""
raise RuntimeError("device failed")
monkeypatch.setattr(auto_obliterate, "AutoObliterator", FailingObliterator)
outputs = list(ui_watchtower._run_one_click("org/model", 1, 5))
assert outputs[0][0] == "starting"
assert "device failed" in outputs[-1][0]
assert outputs[-1][3]["interactive"] is False
def test_download_result_selects_latest_numeric_iteration(monkeypatch, tmp_path):
monkeypatch.setattr(Path, "home", classmethod(lambda cls: tmp_path))
base = tmp_path / ".obliteratus" / "auto_obliterate" / "org_model"
for name in ("iter_invalid", "iter_2", "iter_10"):
directory = base / name
directory.mkdir(parents=True)
(directory / "config.json").write_text("{}", encoding="utf-8")
assert ui_watchtower._download_result("org/model") == str(base / "iter_10")
def test_download_result_handles_empty_id_and_unreadable_iteration(monkeypatch, tmp_path):
monkeypatch.setattr(Path, "home", classmethod(lambda cls: tmp_path))
base = tmp_path / ".obliteratus" / "auto_obliterate" / "org_model"
broken = base / "iter_20"
broken.mkdir(parents=True)
(broken / "config.json").write_text("{}", encoding="utf-8")
valid = base / "iter_10"
valid.mkdir()
(valid / "config.json").write_text("{}", encoding="utf-8")
resolve = Path.resolve
def controlled_resolve(path, *args, **kwargs):
if path == broken:
raise OSError("unreadable")
return resolve(path, *args, **kwargs)
monkeypatch.setattr(Path, "resolve", controlled_resolve)
assert ui_watchtower._download_result("") is None
assert ui_watchtower._download_result("org/model") == str(valid)
def test_download_result_rejects_symlink_that_escapes_managed_root(monkeypatch, tmp_path):
monkeypatch.setattr(Path, "home", classmethod(lambda cls: tmp_path))
base = tmp_path / ".obliteratus" / "auto_obliterate" / "org_model"
valid = base / "iter_10"
valid.mkdir(parents=True)
(valid / "config.json").write_text("{}", encoding="utf-8")
outside = tmp_path / "outside"
outside.mkdir()
(outside / "config.json").write_text("{}", encoding="utf-8")
(base / "iter_99").symlink_to(outside, target_is_directory=True)
assert ui_watchtower._download_result("../../org/model") is None
assert ui_watchtower._download_result("org/model") == str(valid)
def test_scan_handler_returns_data_and_escaped_failure(monkeypatch):
watchtower = SimpleNamespace(
scan=lambda on_log: (on_log("scanned") or [SimpleNamespace()]),
get_stats=lambda: {"total_tracked": 1},
format_table=lambda: [["org/model"]],
)
monkeypatch.setattr(ui_watchtower, "_get_watchtower", lambda: watchtower)
status, table, log = ui_watchtower._scan_now()
assert "1" in status
assert table == [["org/model"]]
assert log == "scanned"
monkeypatch.setattr(
ui_watchtower,
"_get_watchtower",
Mock(side_effect=RuntimeError("")),
)
status, table, log = ui_watchtower._scan_now()
assert "