f33a563003
- Add ToolManifest Pydantic models with validators for ports, mounts, health checks, and traefik config - Implement in-memory ToolRegistry with YAML loading and built-in manifest scanning - Add FastAPI CRUD routes for listing, retrieving, and creating tool manifests - Include built-in manifests for runfusion and code-server - Harden web Dockerfile with unprivileged nginx and port 8080 - Add tool manifest specification documentation and architecture updates Fusion-Task-Id: FN-003
94 lines
2.6 KiB
Python
94 lines
2.6 KiB
Python
"""Tests for the FastAPI tool manifest router."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.main import app
|
|
from app.tools.registry import registry
|
|
|
|
client = TestClient(app)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _reset_registry() -> None:
|
|
registry._manifests.clear()
|
|
registry.load_builtin_manifests()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GET /api/v1/tools
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_list_tools_includes_builtins() -> None:
|
|
response = client.get("/api/v1/tools")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
ids = {item["id"] for item in data}
|
|
assert "runfusion" in ids
|
|
assert "code-server" in ids
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GET /api/v1/tools/{tool_id}
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_get_tool_runfusion() -> None:
|
|
response = client.get("/api/v1/tools/runfusion")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["id"] == "runfusion"
|
|
assert data["name"] == "RunFusion"
|
|
|
|
|
|
def test_get_tool_not_found() -> None:
|
|
response = client.get("/api/v1/tools/nonexistent")
|
|
assert response.status_code == 404
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# POST /api/v1/tools
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_create_tool_success() -> None:
|
|
payload = {
|
|
"id": "new-tool",
|
|
"name": "New Tool",
|
|
"image": "new-tool:latest",
|
|
"ports": [{"container_port": 3000, "primary": True}],
|
|
"traefik": {"enabled": False},
|
|
}
|
|
response = client.post("/api/v1/tools", json=payload)
|
|
assert response.status_code == 201
|
|
data = response.json()
|
|
assert data["id"] == "new-tool"
|
|
assert data["name"] == "New Tool"
|
|
|
|
|
|
def test_create_tool_duplicate() -> None:
|
|
payload = {
|
|
"id": "runfusion",
|
|
"name": "Duplicate",
|
|
"image": "dup:latest",
|
|
"ports": [{"container_port": 3000, "primary": True}],
|
|
"traefik": {"enabled": False},
|
|
}
|
|
response = client.post("/api/v1/tools", json=payload)
|
|
assert response.status_code == 409
|
|
|
|
|
|
def test_create_tool_invalid_id() -> None:
|
|
payload = {
|
|
"id": "Bad ID",
|
|
"name": "Bad Tool",
|
|
"image": "bad:latest",
|
|
"ports": [{"container_port": 3000, "primary": True}],
|
|
"traefik": {"enabled": False},
|
|
}
|
|
response = client.post("/api/v1/tools", json=payload)
|
|
assert response.status_code == 422
|