Files
dify/api/tests/unit_tests/services/plugin/test_dispatch_identity.py
T

75 lines
3.2 KiB
Python

"""Dispatch identity remains bound to the caller across installation changes."""
import json
from typing import Never
import httpx
import pytest
from core.plugin.impl import base as plugin_client_module
from core.plugin.impl.base import BasePluginClient
from core.plugin.plugin_service import PluginService
from services.plugin.dispatch_identity import PLUGIN_IDENTIFIER_HEADER, prepare_dispatch_identity
PLUGIN_ID = "langgenius/example"
IDENTIFIER = f"{PLUGIN_ID}:1.0.0@{'a' * 32}"
def _unexpected_configuration_access(*_args: object, **_kwargs: object) -> Never:
raise AssertionError("Dispatch must not discover installations or invalidate configuration")
@pytest.mark.parametrize("stream", [False, True])
def test_dispatch_keeps_declared_version_without_refetch_or_invalidation(
monkeypatch: pytest.MonkeyPatch, stream: bool
) -> None:
monkeypatch.setattr(PluginService, "list_installations_from_ids", _unexpected_configuration_access)
monkeypatch.setattr(PluginService, "invalidate_plugin_model_providers_cache", _unexpected_configuration_access)
requests: list[httpx.Request] = []
def handle(request: httpx.Request) -> httpx.Response:
requests.append(request)
return httpx.Response(200, text='data: {"code": 0, "message": "", "data": true}\n')
with httpx.Client(transport=httpx.MockTransport(handle)) as http_client:
monkeypatch.setattr(plugin_client_module, "_httpx_client", http_client)
client = BasePluginClient(plugin_unique_identifier=IDENTIFIER)
path = "plugin/tenant-a/dispatch/tool/invoke"
headers = {"X-Plugin-ID": PLUGIN_ID, "Content-Type": "application/json"}
data = {"data": {"credentials": {"api_key": "secret"}}}
if stream:
assert len(list(client._stream_request("POST", path, headers=headers, data=data))) == 1
else:
assert client._request("POST", path, headers=headers, data=data).status_code == 200
assert len(requests) == 1
assert requests[0].headers["X-Plugin-ID"] == PLUGIN_ID
assert requests[0].headers[PLUGIN_IDENTIFIER_HEADER] == IDENTIFIER
assert json.loads(requests[0].content)["data"]["credentials"] == {"api_key": "secret"}
def test_unbound_dispatch_keeps_legacy_headers_without_refetch(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(PluginService, "list_installations_from_ids", _unexpected_configuration_access)
headers = {"X-Plugin-ID": PLUGIN_ID}
prepare_dispatch_identity("plugin/tenant-a/dispatch/oauth/get_credentials", headers, None)
assert headers == {"X-Plugin-ID": PLUGIN_ID}
def test_dispatch_rejects_cross_plugin_identity() -> None:
with pytest.raises(ValueError, match="does not match"):
prepare_dispatch_identity(
"plugin/tenant-a/dispatch/tool/invoke", {"X-Plugin-ID": "langgenius/other"}, IDENTIFIER
)
def test_pinned_dispatch_requires_plugin_id() -> None:
with pytest.raises(ValueError, match="X-Plugin-ID is required"):
prepare_dispatch_identity("plugin/tenant-a/dispatch/tool/invoke", {}, IDENTIFIER)
def test_explicit_empty_identifier_cannot_fall_back_to_legacy_dispatch() -> None:
with pytest.raises(ValueError, match="must not be empty"):
BasePluginClient(plugin_unique_identifier="")