mirror of
https://github.com/langgenius/dify.git
synced 2026-10-04 13:47:55 +08:00
test: use real HTTP responses and plugin tool clients (#43364)
This commit is contained in:
1 parent
1cbb6dc385
commit
16bbfc50bd
3 files changed
+50
-36
No files matched your search
@@ -1,9 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
import json
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
import core.plugin.impl.base as plugin_client_module
|
||||
from core.tools.entities.common_entities import I18nObject
|
||||
from core.tools.entities.tool_entities import (
|
||||
ToolEntity,
|
||||
@@ -63,27 +65,29 @@ def test_plugin_tool_provider_controller_basic_behaviors():
|
||||
controller.get_tool("missing")
|
||||
|
||||
|
||||
def test_validate_credentials_success():
|
||||
@pytest.mark.parametrize("valid", [True, False])
|
||||
def test_validate_credentials(monkeypatch: pytest.MonkeyPatch, valid: bool):
|
||||
controller = _build_controller()
|
||||
manager = Mock()
|
||||
manager.validate_provider_credentials.return_value = True
|
||||
requests: list[httpx.Request] = []
|
||||
|
||||
with patch("core.tools.plugin_tool.provider.PluginToolManager", return_value=manager):
|
||||
controller._validate_credentials(user_id="u1", credentials={"api_key": "x"})
|
||||
def handle_request(request: httpx.Request) -> httpx.Response:
|
||||
requests.append(request)
|
||||
return httpx.Response(200, json={"code": 0, "message": "", "data": {"result": valid}})
|
||||
|
||||
manager.validate_provider_credentials.assert_called_once_with(
|
||||
tenant_id="tenant-1",
|
||||
user_id="u1",
|
||||
provider="provider-a",
|
||||
credentials={"api_key": "x"},
|
||||
)
|
||||
|
||||
|
||||
def test_validate_credentials_failure():
|
||||
controller = _build_controller()
|
||||
manager = Mock()
|
||||
manager.validate_provider_credentials.return_value = False
|
||||
|
||||
with patch("core.tools.plugin_tool.provider.PluginToolManager", return_value=manager):
|
||||
with pytest.raises(ToolProviderCredentialValidationError, match="Invalid credentials"):
|
||||
with httpx.Client(transport=httpx.MockTransport(handle_request)) as client:
|
||||
monkeypatch.setattr(plugin_client_module, "_httpx_client", client)
|
||||
if valid:
|
||||
controller._validate_credentials(user_id="u1", credentials={"api_key": "x"})
|
||||
else:
|
||||
with pytest.raises(ToolProviderCredentialValidationError, match="Invalid credentials"):
|
||||
controller._validate_credentials(user_id="u1", credentials={"api_key": "x"})
|
||||
|
||||
assert len(requests) == 1
|
||||
request = requests[0]
|
||||
assert request.method == "POST"
|
||||
assert request.url.path.endswith("/plugin/tenant-1/dispatch/tool/validate_credentials")
|
||||
assert request.headers["X-Plugin-ID"] == "langgenius/provider-a"
|
||||
assert json.loads(request.content) == {
|
||||
"user_id": "u1",
|
||||
"data": {"provider": "provider-a", "credentials": {"api_key": "x"}},
|
||||
}
|
||||
@@ -7,7 +7,7 @@ remote HTTP remain mocked because they are external I/O boundaries.
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterator
|
||||
from unittest.mock import Mock, patch
|
||||
from unittest.mock import patch
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
import httpx
|
||||
@@ -119,10 +119,12 @@ def test_create_file_by_raw_prefers_filename_extension_over_mimetype(
|
||||
def test_create_file_by_url_downloads_and_persists_record(sqlite_tool_file_session: Session) -> None:
|
||||
manager = ToolFileManager()
|
||||
tenant_id = str(uuid4())
|
||||
response = Mock()
|
||||
response.content = b"binary"
|
||||
response.headers = {"Content-Type": "application/octet-stream"}
|
||||
response.raise_for_status.return_value = None
|
||||
response = httpx.Response(
|
||||
200,
|
||||
content=b"binary",
|
||||
headers={"Content-Type": "application/octet-stream"},
|
||||
request=httpx.Request("GET", "https://example.com/f.bin"),
|
||||
)
|
||||
|
||||
with (
|
||||
patch("core.tools.tool_file_manager.storage") as storage,
|
||||
@@ -143,10 +145,12 @@ def test_create_file_by_url_prefers_url_extension_over_mimetype(
|
||||
) -> None:
|
||||
manager = ToolFileManager()
|
||||
tenant_id = str(uuid4())
|
||||
response = Mock()
|
||||
response.content = b"docx"
|
||||
response.headers = {"Content-Type": "application/octet-stream"}
|
||||
response.raise_for_status.return_value = None
|
||||
response = httpx.Response(
|
||||
200,
|
||||
content=b"docx",
|
||||
headers={"Content-Type": "application/octet-stream"},
|
||||
request=httpx.Request("GET", "https://example.com/report.docx?download=1"),
|
||||
)
|
||||
|
||||
with (
|
||||
patch("core.tools.tool_file_manager.storage") as storage,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from json.decoder import JSONDecodeError
|
||||
from typing import Any
|
||||
from unittest.mock import Mock, patch
|
||||
from unittest.mock import patch
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from yaml import YAMLError
|
||||
@@ -326,16 +327,20 @@ def test_parse_openai_plugin_json_branches(app):
|
||||
|
||||
def test_parse_openai_plugin_json_http_branches(app):
|
||||
with app.test_request_context():
|
||||
response = type("Resp", (), {"status_code": 500, "text": "", "close": Mock()})()
|
||||
response = httpx.Response(500, stream=httpx.ByteStream(b""))
|
||||
assert not response.is_closed
|
||||
with patch("core.tools.utils.parser.ssrf_proxy.get", return_value=response):
|
||||
with pytest.raises(ToolProviderNotFoundError, match="cannot get openapi yaml"):
|
||||
ApiBasedToolSchemaParser.parse_openai_plugin_json_to_tool_bundle(
|
||||
'{"api": {"url": "https://x", "type": "openapi"}}'
|
||||
)
|
||||
response.close.assert_called_once()
|
||||
assert response.is_closed
|
||||
|
||||
success_response = type("Resp", (), {"status_code": 200, "text": "openapi: 3.0.0", "close": Mock()})()
|
||||
with patch("core.tools.utils.parser.ssrf_proxy.get", return_value=success_response):
|
||||
success_response = httpx.Response(200, text="openapi: 3.0.0")
|
||||
with (
|
||||
patch("core.tools.utils.parser.ssrf_proxy.get", return_value=success_response),
|
||||
patch.object(success_response, "close", wraps=success_response.close) as close,
|
||||
):
|
||||
with patch(
|
||||
"core.tools.utils.parser.ApiBasedToolSchemaParser.parse_openapi_yaml_to_tool_bundle",
|
||||
return_value=["bundle"],
|
||||
@@ -345,7 +350,8 @@ def test_parse_openai_plugin_json_http_branches(app):
|
||||
)
|
||||
assert bundles == ["bundle"]
|
||||
mock_parse.assert_called_once()
|
||||
success_response.close.assert_called_once()
|
||||
close.assert_called_once_with()
|
||||
assert success_response.is_closed
|
||||
|
||||
|
||||
def test_auto_parse_json_yaml_failure():
|
||||
|
||||
Reference in new issue
Block a user