mirror of
https://github.com/langgenius/dify.git
synced 2026-07-21 18:58:35 +08:00
169 lines
5.9 KiB
Python
169 lines
5.9 KiB
Python
from types import SimpleNamespace
|
|
from typing import cast
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
from sqlalchemy.orm import Session
|
|
|
|
from models import App, AppMode
|
|
from models.model import AppModelConfig, IconType
|
|
from services.app_dsl_service import AppDslService
|
|
from services.entities.dsl_entities import ImportStatus
|
|
|
|
|
|
@pytest.mark.parametrize("sqlite_session", [()], indirect=True)
|
|
def test_import_app_rejects_oversized_yaml_content_before_parsing(
|
|
monkeypatch: pytest.MonkeyPatch, sqlite_session: Session
|
|
) -> None:
|
|
monkeypatch.setattr("services.app_dsl_service.DSL_MAX_SIZE", 3)
|
|
service = AppDslService(session=sqlite_session)
|
|
account = Mock(current_tenant_id="tenant-1")
|
|
|
|
result = service.import_app(account=account, import_mode="yaml-content", yaml_content="你你")
|
|
|
|
assert result.status == ImportStatus.FAILED
|
|
assert result.error == "File size exceeds the limit of 10MB"
|
|
assert not sqlite_session.in_transaction()
|
|
|
|
|
|
@pytest.mark.parametrize("sqlite_session", [()], indirect=True)
|
|
def test_import_app_rejects_oversized_yaml_url_bytes_before_decode(
|
|
monkeypatch: pytest.MonkeyPatch, sqlite_session: Session
|
|
) -> None:
|
|
monkeypatch.setattr("services.app_dsl_service.DSL_MAX_SIZE", 1)
|
|
response = Mock()
|
|
response.raise_for_status.return_value = None
|
|
response.content = b"\xff\xff"
|
|
monkeypatch.setattr("services.app_dsl_service.remote_fetcher.make_request", Mock(return_value=response))
|
|
service = AppDslService(session=sqlite_session)
|
|
|
|
result = service.import_app(
|
|
account=Mock(current_tenant_id="tenant-1"),
|
|
import_mode="yaml-url",
|
|
yaml_url="https://example.com/app.yaml",
|
|
)
|
|
|
|
assert result.status == ImportStatus.FAILED
|
|
assert result.error == "File size exceeds the limit of 10MB"
|
|
assert not sqlite_session.in_transaction()
|
|
|
|
|
|
@pytest.mark.parametrize("sqlite_session", [()], indirect=True)
|
|
def test_import_app_returns_decode_error_for_invalid_yaml_url_bytes(
|
|
monkeypatch: pytest.MonkeyPatch, sqlite_session: Session
|
|
) -> None:
|
|
response = Mock()
|
|
response.raise_for_status.return_value = None
|
|
response.content = b"\xff"
|
|
monkeypatch.setattr("services.app_dsl_service.remote_fetcher.make_request", Mock(return_value=response))
|
|
service = AppDslService(session=sqlite_session)
|
|
|
|
result = service.import_app(
|
|
account=Mock(current_tenant_id="tenant-1"),
|
|
import_mode="yaml-url",
|
|
yaml_url="https://example.com/app.yaml",
|
|
)
|
|
|
|
assert result.status == ImportStatus.FAILED
|
|
assert "utf-8" in result.error
|
|
assert not sqlite_session.in_transaction()
|
|
|
|
|
|
def test_create_or_update_app_loads_existing_model_config_with_service_session() -> None:
|
|
session = Mock()
|
|
session.get.return_value = Mock()
|
|
service = AppDslService(session=session)
|
|
app = cast(
|
|
App,
|
|
SimpleNamespace(
|
|
id="app-1",
|
|
tenant_id="tenant-1",
|
|
app_model_config_id="config-1",
|
|
name="Existing app",
|
|
description="",
|
|
icon_type=IconType.EMOJI,
|
|
icon="robot",
|
|
icon_background="#FFFFFF",
|
|
),
|
|
)
|
|
|
|
result = service._create_or_update_app(
|
|
app=app,
|
|
data={"app": {"mode": AppMode.CHAT}, "model_config": {"model": {}}},
|
|
account=Mock(id="account-1"),
|
|
)
|
|
|
|
assert result is app
|
|
session.get.assert_called_once_with(AppModelConfig, "config-1")
|
|
|
|
|
|
def test_create_or_update_app_flushes_new_model_config_before_signal(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
events: list[str] = []
|
|
session = Mock()
|
|
session.add.side_effect = lambda _config: events.append("add")
|
|
session.flush.side_effect = lambda: events.append("flush")
|
|
signal = Mock()
|
|
signal.send.side_effect = lambda *_args, **_kwargs: events.append("signal")
|
|
monkeypatch.setattr("services.app_dsl_service.app_model_config_was_updated", signal)
|
|
app = cast(
|
|
App,
|
|
SimpleNamespace(
|
|
id="app-1",
|
|
tenant_id="tenant-1",
|
|
app_model_config_id=None,
|
|
name="Existing app",
|
|
description="",
|
|
icon_type=IconType.EMOJI,
|
|
icon="robot",
|
|
icon_background="#FFFFFF",
|
|
),
|
|
)
|
|
|
|
AppDslService(session=session)._create_or_update_app(
|
|
app=app,
|
|
data={"app": {"mode": AppMode.CHAT}, "model_config": {"model": {}}},
|
|
account=Mock(id="account-1"),
|
|
)
|
|
|
|
assert events == ["add", "flush", "signal"]
|
|
assert signal.send.call_args.kwargs["session"] is session
|
|
session.commit.assert_not_called()
|
|
|
|
|
|
def test_export_dsl_loads_model_config_and_annotation_reply_with_request_session(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
model_config = {"model": {}, "agent_mode": {"tools": []}}
|
|
app_model_config = Mock(app_id="app-1")
|
|
app_model_config.to_dict.return_value = model_config
|
|
session = Mock()
|
|
session.get.return_value = app_model_config
|
|
annotation_reply = {"enabled": False}
|
|
load_annotation_reply_config = Mock(return_value=annotation_reply)
|
|
monkeypatch.setattr("services.app_dsl_service.load_annotation_reply_config", load_annotation_reply_config)
|
|
monkeypatch.setattr(
|
|
"services.app_dsl_service.DependenciesAnalysisService.generate_dependencies",
|
|
Mock(return_value=[]),
|
|
)
|
|
app = cast(
|
|
App,
|
|
SimpleNamespace(
|
|
id="app-1",
|
|
tenant_id="tenant-1",
|
|
app_model_config_id="config-1",
|
|
mode=AppMode.CHAT,
|
|
name="Chat app",
|
|
icon_type=IconType.EMOJI,
|
|
icon="robot",
|
|
icon_background="#FFFFFF",
|
|
description="",
|
|
use_icon_as_answer_icon=False,
|
|
),
|
|
)
|
|
|
|
AppDslService.export_dsl(app, session=session)
|
|
|
|
session.get.assert_called_once_with(AppModelConfig, "config-1")
|
|
load_annotation_reply_config.assert_called_once_with(session, "app-1")
|
|
app_model_config.to_dict.assert_called_once_with(annotation_reply=annotation_reply)
|