116 lines
3.2 KiB
Python
116 lines
3.2 KiB
Python
"""Tests: Plugin-Loader laedt alle Stubs, Lifecycle-Hooks, Entry-Points."""
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
from redaktionskern.contracts import BasePlugin, NavEntry
|
|
from redaktionskern.plugin_loader import PluginError, PluginRegistry
|
|
|
|
ERWARTETE_STUBS = {
|
|
"dedup",
|
|
"planung",
|
|
"archiv",
|
|
"erinnerung",
|
|
"benachrichtigung",
|
|
"audit-log",
|
|
"export",
|
|
}
|
|
|
|
# Voll implementierte Plugins (keine Stubs mehr):
|
|
ERWARTETE_PLUGINS = ERWARTETE_STUBS | {"neuheiten", "dedup", "planung"}
|
|
|
|
|
|
def test_alle_plugins_werden_geladen(app):
|
|
namen = {p.name for p in app.state.registry.all()}
|
|
assert ERWARTETE_PLUGINS <= namen
|
|
|
|
|
|
def test_on_load_wurde_aufgerufen(app):
|
|
for plugin in app.state.registry.all():
|
|
if plugin.name in ERWARTETE_PLUGINS:
|
|
assert plugin.loaded, f"Plugin {plugin.name} ist nicht geladen."
|
|
assert plugin.context is not None
|
|
|
|
|
|
def test_plugins_haben_navigation(app):
|
|
for name in ERWARTETE_PLUGINS:
|
|
plugin = app.state.registry.get(name)
|
|
assert len(plugin.navigation()) == 1
|
|
|
|
|
|
# Voll implementierte Plugins tragen keinen Platzhalter-Text mehr auf ihrer
|
|
# Seite; die Prüfung läuft weiter für alle übrigen Platzhalter.
|
|
VOLL_IMPLEMENTIERT = {
|
|
"audit-log", "neuheiten", "benachrichtigung", "dedup", "planung", "archiv", "erinnerung",
|
|
}
|
|
PLATZHALTER_STUBS = ERWARTETE_STUBS - VOLL_IMPLEMENTIERT
|
|
|
|
|
|
def test_plugin_seiten_erreichbar(client):
|
|
from tests.conftest import melde_an
|
|
|
|
melde_an(client)
|
|
for name in sorted(PLATZHALTER_STUBS):
|
|
antwort = client.get(f"/{name}")
|
|
assert antwort.status_code == 200, name
|
|
assert "ist geladen" in antwort.text
|
|
|
|
|
|
def test_audit_log_seite_erreichbar_fuer_admin(client):
|
|
from tests.conftest import melde_an
|
|
|
|
melde_an(client)
|
|
antwort = client.get("/audit-log")
|
|
assert antwort.status_code == 200
|
|
assert "Audit-Log" in antwort.text
|
|
|
|
|
|
def test_on_unload_beim_herunterfahren(app):
|
|
plugin = app.state.registry.get("neuheiten")
|
|
with TestClient(app):
|
|
assert plugin.loaded
|
|
assert not plugin.loaded
|
|
|
|
|
|
def test_doppelte_plugin_namen_abgelehnt():
|
|
class A(BasePlugin):
|
|
name = "a"
|
|
|
|
registry = PluginRegistry()
|
|
registry.register(A())
|
|
with pytest.raises(PluginError):
|
|
registry.register(A())
|
|
|
|
|
|
def test_entry_point_plugins_werden_geladen(monkeypatch, settings_ohne_plugins):
|
|
"""Entry-Point-Erkennung via importlib.metadata (hier simuliert)."""
|
|
import redaktionskern.plugin_loader as loader
|
|
|
|
externes_plugin = loader._als_plugin(ExternesPlugin())
|
|
|
|
class FakeEntryPoint:
|
|
name = "extern"
|
|
|
|
def load(self):
|
|
return externes_plugin
|
|
|
|
monkeypatch.setattr(loader, "entry_points", lambda group: [FakeEntryPoint()])
|
|
from redaktionskern.app import create_app
|
|
|
|
app = create_app(settings_ohne_plugins)
|
|
geladen = app.state.registry.get("extern")
|
|
assert geladen is externes_plugin
|
|
assert geladen.loaded
|
|
|
|
|
|
class ExternesPlugin(BasePlugin):
|
|
"""Simuliert ein als Paket installiertes Plugin (Entry-Point)."""
|
|
|
|
name = "extern"
|
|
title = "Externes Plugin"
|
|
description = "Test-Plugin via Entry-Point."
|
|
|
|
def navigation(self) -> list[NavEntry]:
|
|
return []
|