mirror of
https://github.com/langgenius/dify.git
synced 2025-12-23 15:57:29 +00:00
Compare commits
6 Commits
test/log-r
...
fix-statel
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40f2bee979 | ||
|
|
c14f4495c1 | ||
|
|
c9eabe1612 | ||
|
|
e0fb754e80 | ||
|
|
695d89ef2d | ||
|
|
be13f79696 |
@@ -142,7 +142,7 @@ class AppMCPServerRefreshController(Resource):
|
|||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@marshal_with(app_server_fields)
|
@marshal_with(app_server_fields)
|
||||||
def get(self, server_id):
|
def post(self, server_id):
|
||||||
if not current_user.is_editor:
|
if not current_user.is_editor:
|
||||||
raise NotFound()
|
raise NotFound()
|
||||||
server = (
|
server = (
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ class MessageSuggestedQuestionApi(Resource):
|
|||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
@get_app_model(mode=[AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT])
|
@get_app_model(mode=[AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT])
|
||||||
def get(self, app_model, message_id):
|
def post(self, app_model, message_id):
|
||||||
message_id = str(message_id)
|
message_id = str(message_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ class LoginApi(Resource):
|
|||||||
@console_ns.route("/logout")
|
@console_ns.route("/logout")
|
||||||
class LogoutApi(Resource):
|
class LogoutApi(Resource):
|
||||||
@setup_required
|
@setup_required
|
||||||
def get(self):
|
def post(self):
|
||||||
account = cast(Account, flask_login.current_user)
|
account = cast(Account, flask_login.current_user)
|
||||||
if isinstance(account, flask_login.AnonymousUserMixin):
|
if isinstance(account, flask_login.AnonymousUserMixin):
|
||||||
return {"result": "success"}
|
return {"result": "success"}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ class MessageFeedbackApi(InstalledAppResource):
|
|||||||
endpoint="installed_app_more_like_this",
|
endpoint="installed_app_more_like_this",
|
||||||
)
|
)
|
||||||
class MessageMoreLikeThisApi(InstalledAppResource):
|
class MessageMoreLikeThisApi(InstalledAppResource):
|
||||||
def get(self, installed_app, message_id):
|
def post(self, installed_app, message_id):
|
||||||
app_model = installed_app.app
|
app_model = installed_app.app
|
||||||
if app_model.mode != "completion":
|
if app_model.mode != "completion":
|
||||||
raise NotCompletionAppError()
|
raise NotCompletionAppError()
|
||||||
@@ -117,7 +117,12 @@ class MessageMoreLikeThisApi(InstalledAppResource):
|
|||||||
|
|
||||||
parser = reqparse.RequestParser()
|
parser = reqparse.RequestParser()
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"response_mode", type=str, required=True, choices=["blocking", "streaming"], location="args"
|
"response_mode",
|
||||||
|
type=str,
|
||||||
|
required=False,
|
||||||
|
choices=["blocking", "streaming"],
|
||||||
|
default="blocking",
|
||||||
|
location="json",
|
||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -158,7 +163,7 @@ class MessageMoreLikeThisApi(InstalledAppResource):
|
|||||||
endpoint="installed_app_suggested_question",
|
endpoint="installed_app_suggested_question",
|
||||||
)
|
)
|
||||||
class MessageSuggestedQuestionApi(InstalledAppResource):
|
class MessageSuggestedQuestionApi(InstalledAppResource):
|
||||||
def get(self, installed_app, message_id):
|
def post(self, installed_app, message_id):
|
||||||
app_model = installed_app.app
|
app_model = installed_app.app
|
||||||
app_mode = AppMode.value_of(app_model.mode)
|
app_mode = AppMode.value_of(app_model.mode)
|
||||||
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ class AccountDeleteVerifyApi(Resource):
|
|||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@account_initialization_required
|
@account_initialization_required
|
||||||
def get(self):
|
def post(self):
|
||||||
if not isinstance(current_user, Account):
|
if not isinstance(current_user, Account):
|
||||||
raise ValueError("Invalid user account")
|
raise ValueError("Invalid user account")
|
||||||
account = current_user
|
account = current_user
|
||||||
|
|||||||
@@ -169,12 +169,6 @@ class MessageMoreLikeThisApi(WebApiResource):
|
|||||||
@web_ns.doc(
|
@web_ns.doc(
|
||||||
params={
|
params={
|
||||||
"message_id": {"description": "Message UUID", "type": "string", "required": True},
|
"message_id": {"description": "Message UUID", "type": "string", "required": True},
|
||||||
"response_mode": {
|
|
||||||
"description": "Response mode",
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["blocking", "streaming"],
|
|
||||||
"required": True,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@web_ns.doc(
|
@web_ns.doc(
|
||||||
@@ -187,7 +181,7 @@ class MessageMoreLikeThisApi(WebApiResource):
|
|||||||
500: "Internal Server Error",
|
500: "Internal Server Error",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
def get(self, app_model, end_user, message_id):
|
def post(self, app_model, end_user, message_id):
|
||||||
if app_model.mode != "completion":
|
if app_model.mode != "completion":
|
||||||
raise NotCompletionAppError()
|
raise NotCompletionAppError()
|
||||||
|
|
||||||
@@ -195,7 +189,12 @@ class MessageMoreLikeThisApi(WebApiResource):
|
|||||||
|
|
||||||
parser = reqparse.RequestParser()
|
parser = reqparse.RequestParser()
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"response_mode", type=str, required=True, choices=["blocking", "streaming"], location="args"
|
"response_mode",
|
||||||
|
type=str,
|
||||||
|
required=False,
|
||||||
|
choices=["blocking", "streaming"],
|
||||||
|
default="blocking",
|
||||||
|
location="json",
|
||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -250,7 +249,7 @@ class MessageSuggestedQuestionApi(WebApiResource):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
@marshal_with(suggested_questions_response_fields)
|
@marshal_with(suggested_questions_response_fields)
|
||||||
def get(self, app_model, end_user, message_id):
|
def post(self, app_model, end_user, message_id):
|
||||||
app_mode = AppMode.value_of(app_model.mode)
|
app_mode = AppMode.value_of(app_model.mode)
|
||||||
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
|
||||||
raise NotCompletionAppError()
|
raise NotCompletionAppError()
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import inspect
|
||||||
|
import uuid
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
from controllers.console.app import message as console_message_module
|
||||||
|
from controllers.console.app.message import MessageSuggestedQuestionApi
|
||||||
|
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||||
|
from models.account import Account
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def flask_app():
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config["TESTING"] = True
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def account_user():
|
||||||
|
user = Account(name="Tester", email="tester@example.com")
|
||||||
|
user.id = "user-id"
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
class TestConsoleAppMessageSuggestedQuestionApi:
|
||||||
|
def test_post_forwards_to_service(self, flask_app, account_user, monkeypatch):
|
||||||
|
app_model = SimpleNamespace(id="app-id", mode="chat")
|
||||||
|
questions = ["a", "b"]
|
||||||
|
service_mock = MagicMock(return_value=questions)
|
||||||
|
|
||||||
|
monkeypatch.setattr(console_message_module, "current_user", account_user, raising=False)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
console_message_module.MessageService,
|
||||||
|
"get_suggested_questions_after_answer",
|
||||||
|
service_mock,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
handler = inspect.unwrap(MessageSuggestedQuestionApi.post)
|
||||||
|
controller = MessageSuggestedQuestionApi()
|
||||||
|
message_id = uuid.uuid4()
|
||||||
|
|
||||||
|
with flask_app.test_request_context(
|
||||||
|
f"/apps/{app_model.id}/chat-messages/{message_id}/suggested-questions",
|
||||||
|
method="POST",
|
||||||
|
json={},
|
||||||
|
):
|
||||||
|
result = handler(controller, app_model, message_id)
|
||||||
|
|
||||||
|
assert result == {"data": questions}
|
||||||
|
service_mock.assert_called_once_with(
|
||||||
|
app_model=app_model,
|
||||||
|
message_id=str(message_id),
|
||||||
|
user=account_user,
|
||||||
|
invoke_from=InvokeFrom.DEBUGGER,
|
||||||
|
)
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import inspect
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from flask import Flask
|
||||||
|
from werkzeug.exceptions import NotFound
|
||||||
|
|
||||||
|
from controllers.console.app.mcp_server import AppMCPServerRefreshController
|
||||||
|
from models.account import AccountStatus
|
||||||
|
from models.model import AppMCPServer
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def configure_decorators(monkeypatch):
|
||||||
|
monkeypatch.setattr("libs.login.dify_config.LOGIN_DISABLED", True, raising=False)
|
||||||
|
monkeypatch.setattr("controllers.console.wraps.dify_config.EDITION", "CLOUD", raising=False)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_current_user(monkeypatch):
|
||||||
|
user = SimpleNamespace(
|
||||||
|
is_editor=True,
|
||||||
|
status=AccountStatus.ACTIVE,
|
||||||
|
current_tenant_id="tenant-id",
|
||||||
|
is_authenticated=True,
|
||||||
|
)
|
||||||
|
from controllers.console.app import mcp_server as mcp_module
|
||||||
|
|
||||||
|
monkeypatch.setattr(mcp_module, "current_user", user, raising=False)
|
||||||
|
monkeypatch.setattr("controllers.console.wraps.current_user", user, raising=False)
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_db_session(monkeypatch):
|
||||||
|
mock_session = MagicMock()
|
||||||
|
mock_db = SimpleNamespace(session=mock_session)
|
||||||
|
from controllers.console.app import mcp_server as mcp_module
|
||||||
|
|
||||||
|
monkeypatch.setattr(mcp_module, "db", mock_db, raising=False)
|
||||||
|
return mock_session
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def flask_app():
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config["TESTING"] = True
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
class TestAppMCPServerRefreshController:
|
||||||
|
def test_refresh_regenerates_server_code(self, flask_app, mock_current_user, mock_db_session, monkeypatch):
|
||||||
|
server = MagicMock(spec=AppMCPServer)
|
||||||
|
server.server_code = "old"
|
||||||
|
|
||||||
|
server_query = MagicMock()
|
||||||
|
server_query.where.return_value = server_query
|
||||||
|
server_query.first.return_value = server
|
||||||
|
|
||||||
|
mock_db_session.query.return_value = server_query
|
||||||
|
mock_db_session.commit = MagicMock()
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"models.model.AppMCPServer.generate_server_code", MagicMock(return_value="new"), raising=False
|
||||||
|
)
|
||||||
|
|
||||||
|
controller = AppMCPServerRefreshController()
|
||||||
|
refresh_handler = inspect.unwrap(AppMCPServerRefreshController.post)
|
||||||
|
|
||||||
|
with flask_app.test_request_context("/apps/{}/server/refresh".format("app"), method="POST"):
|
||||||
|
result = refresh_handler(controller, "server-id")
|
||||||
|
|
||||||
|
assert result is server
|
||||||
|
assert server.server_code == "new"
|
||||||
|
mock_db_session.commit.assert_called_once_with()
|
||||||
|
mock_db_session.query.assert_called_once()
|
||||||
|
|
||||||
|
def test_refresh_requires_editor(self, flask_app, mock_current_user, mock_db_session, monkeypatch):
|
||||||
|
mock_current_user.is_editor = False
|
||||||
|
|
||||||
|
mock_db_session.query.return_value = MagicMock()
|
||||||
|
mock_db_session.commit = MagicMock()
|
||||||
|
|
||||||
|
controller = AppMCPServerRefreshController()
|
||||||
|
refresh_handler = inspect.unwrap(AppMCPServerRefreshController.post)
|
||||||
|
|
||||||
|
with flask_app.test_request_context("/apps/{}/server/refresh".format("app"), method="POST"):
|
||||||
|
with pytest.raises(NotFound):
|
||||||
|
refresh_handler(controller, "server-id")
|
||||||
|
|
||||||
|
mock_db_session.commit.assert_not_called()
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import inspect
|
||||||
|
import uuid
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
from controllers.console.explore.error import NotChatAppError
|
||||||
|
from controllers.console.explore.message import MessageSuggestedQuestionApi
|
||||||
|
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||||
|
from models.account import Account
|
||||||
|
from models.model import AppMode
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def flask_app():
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config["TESTING"] = True
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def account_user():
|
||||||
|
user = Account(name="Tester", email="tester@example.com")
|
||||||
|
user.id = "user-id"
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
class TestConsoleExploreMessageSuggestedQuestionApi:
|
||||||
|
def test_post_returns_questions(self, flask_app, account_user, monkeypatch):
|
||||||
|
installed_app = SimpleNamespace(app=SimpleNamespace(mode=AppMode.CHAT.value))
|
||||||
|
questions = ["q1"]
|
||||||
|
service_mock = MagicMock(return_value=questions)
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.console.explore.message.current_user",
|
||||||
|
account_user,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.console.explore.message.MessageService.get_suggested_questions_after_answer",
|
||||||
|
service_mock,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
handler = inspect.unwrap(MessageSuggestedQuestionApi.post)
|
||||||
|
controller = MessageSuggestedQuestionApi()
|
||||||
|
message_id = uuid.uuid4()
|
||||||
|
|
||||||
|
with flask_app.test_request_context(
|
||||||
|
f"/messages/{message_id}/suggested-questions",
|
||||||
|
method="POST",
|
||||||
|
json={},
|
||||||
|
):
|
||||||
|
result = handler(controller, installed_app, message_id)
|
||||||
|
|
||||||
|
assert result == {"data": questions}
|
||||||
|
service_mock.assert_called_once_with(
|
||||||
|
app_model=installed_app.app,
|
||||||
|
user=account_user,
|
||||||
|
message_id=str(message_id),
|
||||||
|
invoke_from=InvokeFrom.EXPLORE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_non_chat_app_raises(self, flask_app, account_user, monkeypatch):
|
||||||
|
installed_app = SimpleNamespace(app=SimpleNamespace(mode=AppMode.COMPLETION.value))
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.console.explore.message.current_user",
|
||||||
|
account_user,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
handler = inspect.unwrap(MessageSuggestedQuestionApi.post)
|
||||||
|
controller = MessageSuggestedQuestionApi()
|
||||||
|
message_id = uuid.uuid4()
|
||||||
|
|
||||||
|
with flask_app.test_request_context(
|
||||||
|
f"/messages/{message_id}/suggested-questions",
|
||||||
|
method="POST",
|
||||||
|
json={},
|
||||||
|
):
|
||||||
|
with pytest.raises(NotChatAppError):
|
||||||
|
handler(controller, installed_app, message_id)
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import inspect
|
||||||
|
import uuid
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
from controllers.console.explore.error import NotCompletionAppError
|
||||||
|
from controllers.console.explore.message import MessageMoreLikeThisApi
|
||||||
|
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||||
|
from models.account import Account
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def flask_app():
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config["TESTING"] = True
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def account_user():
|
||||||
|
user = Account(name="Tester", email="tester@example.com")
|
||||||
|
user.id = "user-id"
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
class TestConsoleExploreMessageMoreLikeThisApi:
|
||||||
|
def test_post_generates_with_blocking_default(self, flask_app, account_user, monkeypatch):
|
||||||
|
installed_app = SimpleNamespace(app=SimpleNamespace(mode="completion"))
|
||||||
|
response_payload = {"answer": "ok"}
|
||||||
|
generate_mock = MagicMock(return_value=object())
|
||||||
|
compact_mock = MagicMock(return_value=response_payload)
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.console.explore.message.current_user",
|
||||||
|
account_user,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.console.explore.message.AppGenerateService.generate_more_like_this",
|
||||||
|
generate_mock,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.console.explore.message.helper.compact_generate_response",
|
||||||
|
compact_mock,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
handler = inspect.unwrap(MessageMoreLikeThisApi.post)
|
||||||
|
controller = MessageMoreLikeThisApi()
|
||||||
|
message_id = uuid.uuid4()
|
||||||
|
|
||||||
|
with flask_app.test_request_context(
|
||||||
|
f"/messages/{message_id}/more-like-this",
|
||||||
|
method="POST",
|
||||||
|
json={},
|
||||||
|
):
|
||||||
|
result = handler(controller, installed_app, message_id)
|
||||||
|
|
||||||
|
assert result == response_payload
|
||||||
|
generate_mock.assert_called_once()
|
||||||
|
call_kwargs = generate_mock.call_args.kwargs
|
||||||
|
assert call_kwargs["streaming"] is False
|
||||||
|
assert call_kwargs["invoke_from"] == InvokeFrom.EXPLORE
|
||||||
|
assert call_kwargs["message_id"] == str(message_id)
|
||||||
|
compact_mock.assert_called_once_with(generate_mock.return_value)
|
||||||
|
|
||||||
|
def test_post_allows_streaming_mode(self, flask_app, account_user, monkeypatch):
|
||||||
|
installed_app = SimpleNamespace(app=SimpleNamespace(mode="completion"))
|
||||||
|
generate_mock = MagicMock(return_value=object())
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.console.explore.message.current_user",
|
||||||
|
account_user,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.console.explore.message.AppGenerateService.generate_more_like_this",
|
||||||
|
generate_mock,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.console.explore.message.helper.compact_generate_response",
|
||||||
|
MagicMock(return_value={}),
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
handler = inspect.unwrap(MessageMoreLikeThisApi.post)
|
||||||
|
controller = MessageMoreLikeThisApi()
|
||||||
|
message_id = uuid.uuid4()
|
||||||
|
|
||||||
|
with flask_app.test_request_context(
|
||||||
|
f"/messages/{message_id}/more-like-this",
|
||||||
|
method="POST",
|
||||||
|
json={"response_mode": "streaming"},
|
||||||
|
):
|
||||||
|
handler(controller, installed_app, message_id)
|
||||||
|
|
||||||
|
generate_mock.assert_called_once()
|
||||||
|
assert generate_mock.call_args.kwargs["streaming"] is True
|
||||||
|
|
||||||
|
def test_non_completion_app_raises(self, flask_app, account_user, monkeypatch):
|
||||||
|
installed_app = SimpleNamespace(app=SimpleNamespace(mode="chat"))
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.console.explore.message.current_user",
|
||||||
|
account_user,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
handler = inspect.unwrap(MessageMoreLikeThisApi.post)
|
||||||
|
controller = MessageMoreLikeThisApi()
|
||||||
|
message_id = uuid.uuid4()
|
||||||
|
|
||||||
|
with flask_app.test_request_context(
|
||||||
|
f"/messages/{message_id}/more-like-this",
|
||||||
|
method="POST",
|
||||||
|
json={},
|
||||||
|
):
|
||||||
|
with pytest.raises(NotCompletionAppError):
|
||||||
|
handler(controller, installed_app, message_id)
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import inspect
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
from controllers.console.workspace import account as account_module
|
||||||
|
from controllers.console.workspace.account import AccountDeleteVerifyApi
|
||||||
|
from models.account import Account
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def flask_app():
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config["TESTING"] = True
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def account_user():
|
||||||
|
user = Account(name="Tester", email="tester@example.com")
|
||||||
|
user.id = "user-id"
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
class TestAccountDeleteVerifyApi:
|
||||||
|
def test_post_generates_token_and_sends_email(self, flask_app, account_user, monkeypatch):
|
||||||
|
generate_mock = MagicMock(return_value=("token", "code"))
|
||||||
|
send_mock = MagicMock()
|
||||||
|
|
||||||
|
monkeypatch.setattr(account_module, "current_user", account_user, raising=False)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
account_module.AccountService,
|
||||||
|
"generate_account_deletion_verification_code",
|
||||||
|
generate_mock,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
account_module.AccountService,
|
||||||
|
"send_account_deletion_verification_email",
|
||||||
|
send_mock,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
controller = AccountDeleteVerifyApi()
|
||||||
|
handler = inspect.unwrap(AccountDeleteVerifyApi.post)
|
||||||
|
|
||||||
|
with flask_app.test_request_context("/account/delete/verify", method="POST", json={}):
|
||||||
|
response = handler(controller)
|
||||||
|
|
||||||
|
assert response == {"result": "success", "data": "token"}
|
||||||
|
generate_mock.assert_called_once_with(account_user)
|
||||||
|
send_mock.assert_called_once_with(account_user, "code")
|
||||||
|
|
||||||
|
def test_post_requires_account_user(self, flask_app, monkeypatch):
|
||||||
|
monkeypatch.setattr(account_module, "current_user", object(), raising=False)
|
||||||
|
|
||||||
|
controller = AccountDeleteVerifyApi()
|
||||||
|
handler = inspect.unwrap(AccountDeleteVerifyApi.post)
|
||||||
|
|
||||||
|
with flask_app.test_request_context("/account/delete/verify", method="POST", json={}):
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
handler(controller)
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import inspect
|
||||||
|
import uuid
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
from controllers.web.error import NotCompletionAppError
|
||||||
|
from controllers.web.message import MessageMoreLikeThisApi
|
||||||
|
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def flask_app():
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config["TESTING"] = True
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
class TestWebMessageMoreLikeThisApi:
|
||||||
|
def test_post_uses_blocking_by_default(self, flask_app, monkeypatch):
|
||||||
|
app_model = SimpleNamespace(mode="completion")
|
||||||
|
end_user = SimpleNamespace()
|
||||||
|
response_payload = {"answer": "ok"}
|
||||||
|
|
||||||
|
generate_mock = MagicMock(return_value=object())
|
||||||
|
compact_mock = MagicMock(return_value=response_payload)
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.web.message.AppGenerateService.generate_more_like_this",
|
||||||
|
generate_mock,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.web.message.helper.compact_generate_response",
|
||||||
|
compact_mock,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
handler = inspect.unwrap(MessageMoreLikeThisApi.post)
|
||||||
|
controller = MessageMoreLikeThisApi()
|
||||||
|
message_id = uuid.uuid4()
|
||||||
|
|
||||||
|
with flask_app.test_request_context(
|
||||||
|
f"/messages/{message_id}/more-like-this",
|
||||||
|
method="POST",
|
||||||
|
json={},
|
||||||
|
):
|
||||||
|
result = handler(controller, app_model, end_user, message_id)
|
||||||
|
|
||||||
|
assert result == response_payload
|
||||||
|
generate_mock.assert_called_once()
|
||||||
|
call_kwargs = generate_mock.call_args.kwargs
|
||||||
|
assert call_kwargs["streaming"] is False
|
||||||
|
assert call_kwargs["invoke_from"] == InvokeFrom.WEB_APP
|
||||||
|
assert call_kwargs["message_id"] == str(message_id)
|
||||||
|
compact_mock.assert_called_once_with(generate_mock.return_value)
|
||||||
|
|
||||||
|
def test_post_allows_streaming_mode(self, flask_app, monkeypatch):
|
||||||
|
app_model = SimpleNamespace(mode="completion")
|
||||||
|
end_user = SimpleNamespace()
|
||||||
|
|
||||||
|
generate_mock = MagicMock(return_value=object())
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.web.message.AppGenerateService.generate_more_like_this",
|
||||||
|
generate_mock,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.web.message.helper.compact_generate_response",
|
||||||
|
MagicMock(return_value={}),
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
handler = inspect.unwrap(MessageMoreLikeThisApi.post)
|
||||||
|
controller = MessageMoreLikeThisApi()
|
||||||
|
message_id = uuid.uuid4()
|
||||||
|
|
||||||
|
with flask_app.test_request_context(
|
||||||
|
f"/messages/{message_id}/more-like-this",
|
||||||
|
method="POST",
|
||||||
|
json={"response_mode": "streaming"},
|
||||||
|
):
|
||||||
|
handler(controller, app_model, end_user, message_id)
|
||||||
|
|
||||||
|
generate_mock.assert_called_once()
|
||||||
|
assert generate_mock.call_args.kwargs["streaming"] is True
|
||||||
|
|
||||||
|
def test_non_completion_app_raises(self, flask_app):
|
||||||
|
app_model = SimpleNamespace(mode="chat")
|
||||||
|
end_user = SimpleNamespace()
|
||||||
|
handler = inspect.unwrap(MessageMoreLikeThisApi.post)
|
||||||
|
controller = MessageMoreLikeThisApi()
|
||||||
|
message_id = uuid.uuid4()
|
||||||
|
|
||||||
|
with flask_app.test_request_context(
|
||||||
|
f"/messages/{message_id}/more-like-this",
|
||||||
|
method="POST",
|
||||||
|
json={},
|
||||||
|
):
|
||||||
|
with pytest.raises(NotCompletionAppError):
|
||||||
|
handler(controller, app_model, end_user, message_id)
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import inspect
|
||||||
|
import uuid
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
from controllers.web.error import NotCompletionAppError
|
||||||
|
from controllers.web.message import MessageSuggestedQuestionApi
|
||||||
|
from core.app.entities.app_invoke_entities import InvokeFrom
|
||||||
|
from models.model import AppMode
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def flask_app():
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config["TESTING"] = True
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
class TestWebMessageSuggestedQuestionApi:
|
||||||
|
def test_post_returns_questions(self, flask_app, monkeypatch):
|
||||||
|
app_model = SimpleNamespace(mode=AppMode.CHAT.value)
|
||||||
|
end_user = SimpleNamespace()
|
||||||
|
questions = ["Q1", "Q2"]
|
||||||
|
|
||||||
|
service_mock = MagicMock(return_value=questions)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"controllers.web.message.MessageService.get_suggested_questions_after_answer",
|
||||||
|
service_mock,
|
||||||
|
raising=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
handler = inspect.unwrap(MessageSuggestedQuestionApi.post)
|
||||||
|
controller = MessageSuggestedQuestionApi()
|
||||||
|
message_id = uuid.uuid4()
|
||||||
|
|
||||||
|
with flask_app.test_request_context(
|
||||||
|
f"/messages/{message_id}/suggested-questions",
|
||||||
|
method="POST",
|
||||||
|
json={},
|
||||||
|
):
|
||||||
|
result = handler(controller, app_model, end_user, message_id)
|
||||||
|
|
||||||
|
assert result == {"data": questions}
|
||||||
|
service_mock.assert_called_once_with(
|
||||||
|
app_model=app_model,
|
||||||
|
user=end_user,
|
||||||
|
message_id=str(message_id),
|
||||||
|
invoke_from=InvokeFrom.WEB_APP,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_non_chat_app_raises(self, flask_app):
|
||||||
|
app_model = SimpleNamespace(mode=AppMode.COMPLETION.value)
|
||||||
|
end_user = SimpleNamespace()
|
||||||
|
handler = inspect.unwrap(MessageSuggestedQuestionApi.post)
|
||||||
|
controller = MessageSuggestedQuestionApi()
|
||||||
|
message_id = uuid.uuid4()
|
||||||
|
|
||||||
|
with flask_app.test_request_context(
|
||||||
|
f"/messages/{message_id}/suggested-questions",
|
||||||
|
method="POST",
|
||||||
|
json={},
|
||||||
|
):
|
||||||
|
with pytest.raises(NotCompletionAppError):
|
||||||
|
handler(controller, app_model, end_user, message_id)
|
||||||
@@ -84,8 +84,8 @@ export const updateUserProfile: Fetcher<CommonResponse, { url: string; body: Rec
|
|||||||
return post<CommonResponse>(url, { body })
|
return post<CommonResponse>(url, { body })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const logout: Fetcher<CommonResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
|
export const logout: Fetcher<CommonResponse, { url: string; body?: Record<string, any> }> = ({ url, body }) => {
|
||||||
return get<CommonResponse>(url, params)
|
return post<CommonResponse>(url, { body: body ?? {} })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchLangGeniusVersion: Fetcher<LangGeniusVersionResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
|
export const fetchLangGeniusVersion: Fetcher<LangGeniusVersionResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
|
||||||
@@ -375,7 +375,7 @@ export const verifyWebAppResetPasswordCode = (body: { email: string; code: strin
|
|||||||
post<CommonResponse & { is_valid: boolean; token: string }>('/forgot-password/validity', { body }, { isPublicAPI: true })
|
post<CommonResponse & { is_valid: boolean; token: string }>('/forgot-password/validity', { body }, { isPublicAPI: true })
|
||||||
|
|
||||||
export const sendDeleteAccountCode = () =>
|
export const sendDeleteAccountCode = () =>
|
||||||
get<CommonResponse & { data: string }>('/account/delete/verify')
|
post<CommonResponse & { data: string }>('/account/delete/verify', { body: {} })
|
||||||
|
|
||||||
export const verifyDeleteAccountCode = (body: { code: string; token: string }) =>
|
export const verifyDeleteAccountCode = (body: { code: string; token: string }) =>
|
||||||
post<CommonResponse & { is_valid: boolean }>('/account/delete', { body })
|
post<CommonResponse & { is_valid: boolean }>('/account/delete', { body })
|
||||||
|
|||||||
@@ -61,9 +61,11 @@ export const sendCompletionMessage = async (appId: string, body: Record<string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const fetchSuggestedQuestions = (appId: string, messageId: string, getAbortController?: any) => {
|
export const fetchSuggestedQuestions = (appId: string, messageId: string, getAbortController?: any) => {
|
||||||
return get(
|
return post(
|
||||||
`apps/${appId}/chat-messages/${messageId}/suggested-questions`,
|
`apps/${appId}/chat-messages/${messageId}/suggested-questions`,
|
||||||
{},
|
{
|
||||||
|
body: {},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
getAbortController,
|
getAbortController,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -251,8 +251,8 @@ export const updateFeedback = async ({ url, body }: { url: string; body: Feedbac
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const fetchMoreLikeThis = async (messageId: string, isInstalledApp: boolean, installedAppId = '') => {
|
export const fetchMoreLikeThis = async (messageId: string, isInstalledApp: boolean, installedAppId = '') => {
|
||||||
return (getAction('get', isInstalledApp))(getUrl(`/messages/${messageId}/more-like-this`, isInstalledApp, installedAppId), {
|
return (getAction('post', isInstalledApp))(getUrl(`/messages/${messageId}/more-like-this`, isInstalledApp, installedAppId), {
|
||||||
params: {
|
body: {
|
||||||
response_mode: 'blocking',
|
response_mode: 'blocking',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -271,7 +271,9 @@ export const removeMessage = (messageId: string, isInstalledApp: boolean, instal
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const fetchSuggestedQuestions = (messageId: string, isInstalledApp: boolean, installedAppId = '') => {
|
export const fetchSuggestedQuestions = (messageId: string, isInstalledApp: boolean, installedAppId = '') => {
|
||||||
return (getAction('get', isInstalledApp))(getUrl(`/messages/${messageId}/suggested-questions`, isInstalledApp, installedAppId))
|
return (getAction('post', isInstalledApp))(getUrl(`/messages/${messageId}/suggested-questions`, isInstalledApp, installedAppId), {
|
||||||
|
body: {},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const audioToText = (url: string, isPublicAPI: boolean, body: FormData) => {
|
export const audioToText = (url: string, isPublicAPI: boolean, body: FormData) => {
|
||||||
|
|||||||
@@ -249,8 +249,10 @@ export const useUpdateMCPServer = () => {
|
|||||||
export const useRefreshMCPServerCode = () => {
|
export const useRefreshMCPServerCode = () => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationKey: [NAME_SPACE, 'refresh-mcp-server-code'],
|
mutationKey: [NAME_SPACE, 'refresh-mcp-server-code'],
|
||||||
mutationFn: (appID: string) => {
|
mutationFn: (serverID: string) => {
|
||||||
return get<MCPServerDetail>(`apps/${appID}/server/refresh`)
|
return post<MCPServerDetail>(`apps/${serverID}/server/refresh`, {
|
||||||
|
body: {},
|
||||||
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user