Add manifest-based addon manager
This commit is contained in:
103
tests/test_addons.py
Normal file
103
tests/test_addons.py
Normal file
@@ -0,0 +1,103 @@
|
||||
import asyncio
|
||||
import json
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from src import addons
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def addon_store(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(addons, "ADDONS_DIR", str(tmp_path / "addons"))
|
||||
monkeypatch.setattr(addons, "ADDONS_FILE", str(tmp_path / "addons.json"))
|
||||
return tmp_path
|
||||
|
||||
|
||||
def test_addon_template_is_valid(addon_store):
|
||||
manifest = addons.addon_template("demo-addon")
|
||||
saved = addons.save_addon_manifest(manifest)
|
||||
|
||||
assert saved["id"] == "demo-addon"
|
||||
assert saved["enabled"] is False
|
||||
assert saved["contributions"][0]["type"] == "agent_prompt"
|
||||
assert addons.get_addon("demo-addon")["name"] == "Demo Addon"
|
||||
|
||||
|
||||
def test_enable_addon_exposes_prompt_contribution(addon_store):
|
||||
addons.save_addon_manifest({
|
||||
"id": "prompt-addon",
|
||||
"name": "Prompt Addon",
|
||||
"contributions": [
|
||||
{"type": "agent_prompt", "text": "Prefer compact summaries."},
|
||||
{"type": "unknown", "text": "ignored"},
|
||||
],
|
||||
})
|
||||
|
||||
assert addons.get_addons_prompt() == ""
|
||||
addons.set_addon_enabled("prompt-addon", True)
|
||||
|
||||
prompt = addons.get_addons_prompt()
|
||||
assert "Prompt Addon" in prompt
|
||||
assert "Prefer compact summaries." in prompt
|
||||
assert "unknown" not in prompt
|
||||
|
||||
|
||||
def test_addon_id_rejects_path_traversal(addon_store):
|
||||
with pytest.raises(ValueError):
|
||||
addons.save_addon_manifest({"id": "../bad", "name": "Bad"})
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def addon_routes(addon_store):
|
||||
fastapi = pytest.importorskip("fastapi")
|
||||
from routes.addon_routes import setup_addon_routes
|
||||
|
||||
class _AuthManager:
|
||||
def get_username_for_token(self, token):
|
||||
return "admin" if token == "session-token" else None
|
||||
|
||||
def is_admin(self, user):
|
||||
return user == "admin"
|
||||
|
||||
router = setup_addon_routes(_AuthManager())
|
||||
|
||||
def endpoint(path, method):
|
||||
for route in router.routes:
|
||||
if getattr(route, "path", "") == path and method in getattr(route, "methods", set()):
|
||||
return route.endpoint
|
||||
raise AssertionError(f"{method} {path} route not registered")
|
||||
|
||||
return endpoint, fastapi.HTTPException
|
||||
|
||||
|
||||
class _Request(SimpleNamespace):
|
||||
def __init__(self, token="session-token"):
|
||||
super().__init__(cookies={"odysseus_session": token})
|
||||
|
||||
|
||||
def test_addon_routes_require_admin(addon_routes):
|
||||
endpoint, http_exception = addon_routes
|
||||
list_all = endpoint("/api/addons", "GET")
|
||||
|
||||
with pytest.raises(http_exception) as exc:
|
||||
asyncio.run(list_all(_Request(token="bad")))
|
||||
|
||||
assert exc.value.status_code == 401
|
||||
|
||||
|
||||
def test_addon_routes_create_and_enable(addon_routes):
|
||||
endpoint, _ = addon_routes
|
||||
create = endpoint("/api/addons", "POST")
|
||||
set_enabled = endpoint("/api/addons/{addon_id}/enabled", "POST")
|
||||
list_all = endpoint("/api/addons", "GET")
|
||||
|
||||
body = SimpleNamespace(manifest={"id": "route-addon", "name": "Route Addon"})
|
||||
result = asyncio.run(create(body, _Request()))
|
||||
assert result["addon"]["id"] == "route-addon"
|
||||
|
||||
enabled_body = SimpleNamespace(enabled=True)
|
||||
asyncio.run(set_enabled("route-addon", enabled_body, _Request()))
|
||||
listed = asyncio.run(list_all(_Request()))
|
||||
assert listed["addons"][0]["enabled"] is True
|
||||
assert json.loads((addons._addon_dir("route-addon") / "addon.json").read_text())["enabled"] is True
|
||||
Reference in New Issue
Block a user