deer-flow/backend/tests/test_knowledge_center_extension.py
王一帆 d0ec8dcc5d
feat(knowledge): add optional ingestion and status center (#7518)
* feat(knowledge): add optional ingestion and status center

* docs(knowledge): record upstream CI budget fix and regression results

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-02 10:42:21 +08:00

78 lines
3.1 KiB
Python

"""The optional page must ship real assets without installing a second backend."""
from pathlib import Path
import pytest
from deerflow.extensions.loader import ExtensionSpec, load_extensions
@pytest.fixture
def load_center(monkeypatch):
monkeypatch.syspath_prepend(str(Path(__file__).resolve().parents[2] / "examples/deerflow-extension-knowledge-center"))
def load(config):
return load_extensions([ExtensionSpec(use="deerflow_extension_knowledge_center:install", config=config)])
return load
@pytest.mark.parametrize("enabled", [False, True])
def test_optional_center_has_packaged_page_and_no_management_backend(load_center, enabled):
loaded, diagnostics = load_center({"enabled": enabled})
assert not diagnostics
((_, plugin),) = loaded.plugins
assert plugin.namespace == "knowledge-center"
assert plugin.enabled is enabled
assert not plugin.backend and not plugin.tools and not loaded.routers
assets = plugin.frontend
assert assets.entry == "static/index.mjs"
assert set(assets.files) == {"static/index.mjs", "static/center.mjs", "static/styles.css", "static/copy.mjs"}
assert b"requestBackend" in assets.files["static/center.mjs"].content
@pytest.mark.parametrize("config", [{"enabled": "true"}, {"endpoint": "https://example.invalid"}, {"enabled": True, "token": "secret"}])
def test_page_config_cannot_introduce_credentials_or_provider_configuration(load_center, config):
loaded, diagnostics = load_center(config)
assert not loaded.plugins
assert diagnostics
@pytest.mark.asyncio
@pytest.mark.parametrize("method", ["GET", "POST"])
@pytest.mark.parametrize("root_path", ["", "/proxy"])
async def test_stale_browser_viewer_never_reaches_contributed_routes(monkeypatch, method, root_path):
from types import SimpleNamespace
import httpx
from fastapi import FastAPI
from app.gateway.auth_middleware import AuthMiddleware
async def user(request):
return SimpleNamespace(id="bob", system_role="admin")
async def permissions(*args, **kwargs):
return []
monkeypatch.setattr("app.gateway.auth_middleware.is_auth_disabled", lambda: False)
monkeypatch.setattr("app.gateway.deps.get_current_user_from_request", user)
monkeypatch.setattr("app.gateway.auth_middleware.resolve_route_permissions", permissions)
app = FastAPI()
app.add_middleware(AuthMiddleware)
calls = []
@app.api_route("/api/extensions/example/documents", methods=["GET", "POST"])
async def contributed():
calls.append("provider")
return {"ok": True}
async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app, root_path=root_path), base_url="http://test", cookies={"access_token": "session-b"}) as client:
path = root_path + "/api/extensions/example/documents"
stale = await client.request(method, path, headers={"X-Deerflow-Plugin-Viewer": "alice"})
assert stale.status_code == 409
assert calls == []
for header in ({"X-Deerflow-Plugin-Viewer": "bob"}, {}):
assert (await client.request(method, path, headers=header)).status_code == 200
assert calls == ["provider", "provider"]