# #region Test.Api.Plugins [C:2] [TYPE Module] [SEMANTICS test,plugins,api] # @BRIEF Unit tests for plugins API route. # @RELATION BINDS_TO -> [PluginsRouter] # @TEST_EDGE: empty_list -> 200 import os os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:") os.environ.setdefault("AUTH_DATABASE_URL", "sqlite:///:memory:") os.environ.setdefault("SECRET_KEY", "test-secret-key-for-tests") import sys from pathlib import Path from unittest.mock import MagicMock, patch import pytest from fastapi import FastAPI, HTTPException from fastapi.testclient import TestClient _src = str(Path(__file__).resolve().parent.parent.parent / "src") if _src not in sys.path: sys.path.insert(0, _src) def _make_client(overrides: dict | None = None) -> TestClient: from src.api.routes.plugins import router from src.dependencies import get_current_user, has_permission from src.schemas.auth import User, RoleSchema app = FastAPI() # Plugins router has no prefix — must add it when including app.include_router(router, prefix="/api/plugins") mock_user = User( id="admin-1", username="admin", email="admin@x.com", auth_source="LOCAL", created_at=__import__("datetime").datetime.now(), roles=[RoleSchema(id="r1", name="Admin", description="", permissions=[])], ) defaults = { get_current_user: lambda: mock_user, } if overrides: defaults.update(overrides) for dep, mock_fn in defaults.items(): app.dependency_overrides[dep] = mock_fn return TestClient(app) class TestListPlugins: """GET /api/plugins""" def test_list_plugins_success(self): """Happy path: returns list of plugin configs.""" from src.core.plugin_base import PluginConfig mock_loader = MagicMock() mock_loader.get_all_plugin_configs.return_value = [ PluginConfig( id="superset-migration", name="superset-migration", version="1.0.0", description="Migration plugin", ui_route="/plugins/migration", schema={"type": "object", "properties": {}}, ), PluginConfig( id="superset-backup", name="superset-backup", version="2.0.0", description="Backup plugin", ui_route="/plugins/backup", schema={"type": "object", "properties": {}}, ), ] from src.dependencies import get_plugin_loader client = _make_client({get_plugin_loader: lambda: mock_loader}) resp = client.get("/api/plugins") assert resp.status_code == 200 data = resp.json() assert isinstance(data, list) assert len(data) == 2 def test_list_plugins_empty(self): """Empty plugin list returns 200 with empty array.""" mock_loader = MagicMock() mock_loader.get_all_plugin_configs.return_value = [] from src.dependencies import get_plugin_loader client = _make_client({get_plugin_loader: lambda: mock_loader}) resp = client.get("/api/plugins") assert resp.status_code == 200 assert resp.json() == [] def test_list_plugins_no_permission(self): """Without READ permission returns 403.""" from src.schemas.auth import User, RoleSchema from src.dependencies import get_current_user # User without Admin role and without permissions app = FastAPI() from src.api.routes.plugins import router app.include_router(router, prefix="/api/plugins") app.dependency_overrides[get_current_user] = lambda: User( id="u1", username="regular", email="u@x.com", auth_source="LOCAL", created_at=__import__("datetime").datetime.now(), roles=[], ) client = TestClient(app) resp = client.get("/api/plugins") assert resp.status_code == 403 # #endregion Test.Api.Plugins