mirror of
https://github.com/langgenius/dify.git
synced 2026-08-30 17:11:50 +08:00
162 lines
7.5 KiB
Python
162 lines
7.5 KiB
Python
"""Unit tests for controllers.web.app endpoints."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from types import SimpleNamespace
|
|
from unittest.mock import ANY, MagicMock, patch
|
|
|
|
import pytest
|
|
from flask import Flask
|
|
|
|
from controllers.web.app import AppAccessMode, AppMeta, AppParameterApi, AppWebAuthPermission
|
|
from controllers.web.error import AgentNotPublishedError, AppUnavailableError
|
|
from core.app.app_config.common.parameters_mapping import get_parameters_from_feature_dict
|
|
from services.app_definition_query_service import AppDefinitionNotPublishedError, AppDefinitionUnavailableError
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# AppParameterApi
|
|
# ---------------------------------------------------------------------------
|
|
class TestAppParameterApi:
|
|
@patch("controllers.web.app.application_services")
|
|
def test_get_returns_public_parameters(self, application_services: MagicMock, app: Flask) -> None:
|
|
app_definitions = MagicMock()
|
|
app_definitions.get_public_parameters.return_value = get_parameters_from_feature_dict(
|
|
features_dict={"opening_statement": "Hello"},
|
|
user_input_form=[],
|
|
)
|
|
application_services.return_value = SimpleNamespace(app_definitions=app_definitions)
|
|
app_model = SimpleNamespace(id="app-1")
|
|
|
|
with app.test_request_context("/parameters"):
|
|
result = AppParameterApi().get(app_model, SimpleNamespace())
|
|
|
|
assert result["opening_statement"] == "Hello"
|
|
app_definitions.get_public_parameters.assert_called_once_with("app-1")
|
|
|
|
@pytest.mark.parametrize(
|
|
("service_error", "http_error"),
|
|
[
|
|
pytest.param(AppDefinitionNotPublishedError(), AgentNotPublishedError, id="not-published"),
|
|
pytest.param(AppDefinitionUnavailableError(), AppUnavailableError, id="unavailable"),
|
|
],
|
|
)
|
|
@patch("controllers.web.app.application_services")
|
|
def test_get_maps_query_errors(
|
|
self,
|
|
application_services: MagicMock,
|
|
service_error: Exception,
|
|
http_error: type[Exception],
|
|
app: Flask,
|
|
) -> None:
|
|
app_definitions = MagicMock()
|
|
app_definitions.get_public_parameters.side_effect = service_error
|
|
application_services.return_value = SimpleNamespace(app_definitions=app_definitions)
|
|
|
|
with app.test_request_context("/parameters"):
|
|
with pytest.raises(http_error):
|
|
AppParameterApi().get(SimpleNamespace(id="app-1"), SimpleNamespace())
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# AppMeta
|
|
# ---------------------------------------------------------------------------
|
|
class TestAppMeta:
|
|
@patch("controllers.web.app.application_services")
|
|
def test_get_returns_meta(self, application_services: MagicMock, app: Flask) -> None:
|
|
app_definitions = MagicMock()
|
|
app_definitions.get_tool_icons.return_value = {}
|
|
application_services.return_value = SimpleNamespace(app_definitions=app_definitions)
|
|
app_model = SimpleNamespace(id="app-1")
|
|
|
|
with app.test_request_context("/meta"):
|
|
result = AppMeta().get(app_model, SimpleNamespace())
|
|
|
|
assert result == {"tool_icons": {}}
|
|
app_definitions.get_tool_icons.assert_called_once_with("app-1")
|
|
|
|
@patch("controllers.web.app.application_services")
|
|
def test_maps_unavailable_definition_to_app_unavailable(self, application_services: MagicMock, app: Flask) -> None:
|
|
app_definitions = MagicMock()
|
|
app_definitions.get_tool_icons.side_effect = AppDefinitionUnavailableError
|
|
application_services.return_value = SimpleNamespace(app_definitions=app_definitions)
|
|
|
|
with app.test_request_context("/meta"):
|
|
with pytest.raises(AppUnavailableError) as raised:
|
|
AppMeta().get(SimpleNamespace(id="app-1"), SimpleNamespace())
|
|
|
|
assert raised.value.data == {
|
|
"code": "app_unavailable",
|
|
"message": "App unavailable, please check your app configurations.",
|
|
"status": 400,
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# AppAccessMode
|
|
# ---------------------------------------------------------------------------
|
|
class TestAppAccessMode:
|
|
@patch("controllers.web.app.FeatureService.get_system_features")
|
|
def test_returns_public_when_webapp_auth_disabled(self, mock_features: MagicMock, app: Flask) -> None:
|
|
mock_features.return_value = SimpleNamespace(webapp_auth=SimpleNamespace(enabled=False))
|
|
|
|
with app.test_request_context("/webapp/access-mode?appId=app-1"):
|
|
result = AppAccessMode().get()
|
|
|
|
assert result == {"accessMode": "public"}
|
|
|
|
@patch("controllers.web.app.EnterpriseService.WebAppAuth.get_app_access_mode_by_id")
|
|
@patch("controllers.web.app.FeatureService.get_system_features")
|
|
def test_returns_access_mode_with_app_id(
|
|
self, mock_features: MagicMock, mock_access: MagicMock, app: Flask
|
|
) -> None:
|
|
mock_features.return_value = SimpleNamespace(webapp_auth=SimpleNamespace(enabled=True))
|
|
mock_access.return_value = SimpleNamespace(access_mode="internal")
|
|
|
|
with app.test_request_context("/webapp/access-mode?appId=app-1"):
|
|
result = AppAccessMode().get()
|
|
|
|
assert result == {"accessMode": "internal"}
|
|
mock_access.assert_called_once_with("app-1")
|
|
|
|
@patch("controllers.web.app.AppService.get_app_id_by_code", return_value="resolved-id")
|
|
@patch("controllers.web.app.EnterpriseService.WebAppAuth.get_app_access_mode_by_id")
|
|
@patch("controllers.web.app.FeatureService.get_system_features")
|
|
def test_resolves_app_code_to_id(
|
|
self, mock_features: MagicMock, mock_access: MagicMock, mock_resolve: MagicMock, app: Flask
|
|
) -> None:
|
|
mock_features.return_value = SimpleNamespace(webapp_auth=SimpleNamespace(enabled=True))
|
|
mock_access.return_value = SimpleNamespace(access_mode="external")
|
|
|
|
with app.test_request_context("/webapp/access-mode?appCode=code1"):
|
|
result = AppAccessMode().get()
|
|
|
|
mock_resolve.assert_called_once_with("code1", session=ANY)
|
|
mock_access.assert_called_once_with("resolved-id")
|
|
assert result == {"accessMode": "external"}
|
|
|
|
@patch("controllers.web.app.FeatureService.get_system_features")
|
|
def test_raises_when_no_app_id_or_code(self, mock_features: MagicMock, app: Flask) -> None:
|
|
mock_features.return_value = SimpleNamespace(webapp_auth=SimpleNamespace(enabled=True))
|
|
|
|
with app.test_request_context("/webapp/access-mode"):
|
|
with pytest.raises(ValueError, match="appId or appCode"):
|
|
AppAccessMode().get()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# AppWebAuthPermission
|
|
# ---------------------------------------------------------------------------
|
|
class TestAppWebAuthPermission:
|
|
@patch("controllers.web.app.WebAppAuthService.is_app_require_permission_check", return_value=False)
|
|
def test_returns_true_when_no_permission_check_required(self, mock_check: MagicMock, app: Flask) -> None:
|
|
with app.test_request_context("/webapp/permission?appId=app-1", headers={"X-App-Code": "code1"}):
|
|
result = AppWebAuthPermission().get()
|
|
|
|
assert result == {"result": True}
|
|
|
|
def test_raises_when_missing_app_id(self, app: Flask) -> None:
|
|
with app.test_request_context("/webapp/permission", headers={"X-App-Code": "code1"}):
|
|
with pytest.raises(ValueError, match="appId"):
|
|
AppWebAuthPermission().get()
|