from hermes_cli import aiturk_plugin_catalog as adapter from hermes_cli import plugin_catalog as catalog from hermes_cli import plugins_cmd SHA = "a" * 40 def entry(**extra): return catalog.entry_from_mapping(dict(name="demo", repo="https://github.com/owner/demo", sha=SHA, description="test", maintainer="owner", tier="community", **extra), "test") def test_install_uses_catalog_pin(monkeypatch): monkeypatch.setattr(catalog, "get_live_catalog_entry", lambda name: entry()) monkeypatch.setattr(catalog, "resolved_removed_entries", lambda: []) seen = [] monkeypatch.setattr(plugins_cmd, "dashboard_install_plugin", lambda *args, **kwargs: seen.append((args, kwargs)) or {"ok": True}) assert adapter.install_catalog_plugin("demo")["ok"] assert seen == [(("https://github.com/owner/demo",), {"force": False, "enable": True, "ref": SHA})] def test_missing_or_incompatible_catalog_entry_is_rejected(monkeypatch): monkeypatch.setattr(catalog, "get_live_catalog_entry", lambda name: None) assert not adapter.install_catalog_plugin("missing")["ok"] monkeypatch.setattr(catalog, "resolved_removed_entries", lambda: []) monkeypatch.setattr(catalog, "get_live_catalog_entry", lambda name: entry(requires_hermes=">=999.0")) assert not adapter.install_catalog_plugin("demo")["ok"] monkeypatch.setattr(catalog, "get_live_catalog_entry", lambda name: entry(platforms=["nonexistent"])) assert not adapter.install_catalog_plugin("demo")["ok"] def test_payload_tracks_installed_revision(monkeypatch): monkeypatch.setattr(catalog, "load_catalog_live", lambda: [entry()]) monkeypatch.setattr(catalog, "resolved_removed_entries", lambda: []) monkeypatch.setattr(plugins_cmd, "_discover_all_plugins", lambda: [("demo",)]) monkeypatch.setattr(plugins_cmd, "_read_install_metadata", lambda: {"demo": {"revision": "b" * 40}}) row = adapter.catalog_payload()["entries"][0] assert row["installed"] and row["update_available"] and row["sha"] == SHA def test_catalog_http_auth_and_install_contract(monkeypatch): from fastapi.testclient import TestClient from hermes_cli import web_server as web client = TestClient(web.app) assert client.get("/api/dashboard/plugins/catalog").status_code in (401, 403) client.headers[web._SESSION_HEADER_NAME] = web._SESSION_TOKEN monkeypatch.setattr(adapter, "catalog_payload", lambda: {"entries": [entry().to_dict()], "removed": []}) response = client.get("/api/dashboard/plugins/catalog") assert response.status_code == 200 and response.json()["entries"][0]["sha"] == SHA seen = [] monkeypatch.setattr(adapter, "install_catalog_plugin", lambda name, **kwargs: seen.append(name) or {"ok": True, "after_install_path": "private"}) monkeypatch.setattr(web, "_get_dashboard_plugins", lambda **kwargs: []) monkeypatch.setattr(web, "_invalidate_plugins_hub_cache", lambda: None) response = client.post("/api/dashboard/agent-plugins/install", json={"catalog_name": "demo", "identifier": "https://untrusted.invalid/repo"}) assert response.status_code == 200 and seen == ["demo"] assert "after_install_path" not in response.json()