diff --git a/api/services/plugin/plugin_migration.py b/api/services/plugin/plugin_migration.py index d6f154df812..5c2ddb77e0f 100644 --- a/api/services/plugin/plugin_migration.py +++ b/api/services/plugin/plugin_migration.py @@ -416,10 +416,9 @@ class PluginMigration: data = _tenant_plugin_adapter.validate_json(line) tenant_id = data["tenant_id"] plugin_ids = data["plugins"] - plugin_not_exist: list[str] = [] - for plugin_id in plugin_ids: - if plugin_id not in package_identifier_by_plugin_id: - plugin_not_exist.append(plugin_id) + plugin_not_exist = [ + plugin_id for plugin_id in plugin_ids if plugin_id not in package_identifier_by_plugin_id + ] if plugin_not_exist: not_installed.append( diff --git a/api/tests/unit_tests/services/plugin/test_plugin_migration.py b/api/tests/unit_tests/services/plugin/test_plugin_migration.py index fa10d775aa6..d94ab540dfd 100644 --- a/api/tests/unit_tests/services/plugin/test_plugin_migration.py +++ b/api/tests/unit_tests/services/plugin/test_plugin_migration.py @@ -148,5 +148,35 @@ class TestHandlePluginInstanceInstall: PluginMigration.install_plugins(str(extracted_plugins), str(output_file), workers=1) assert json.loads(output_file.read_text())["not_installed"] == [ - {"tenant_id": "tenant1", "plugin_not_exist": ["langgenius/missing"]} + { + "tenant_id": "tenant1", + "plugin_not_exist": ["langgenius/missing"], + } ] + mock_installer.install_from_identifiers.assert_called_once() + + def test_install_plugins_skips_unresolved_plugins(self, tmp_path) -> None: + extracted_plugins = tmp_path / "plugins.jsonl" + output_file = tmp_path / "output.json" + extracted_plugins.write_text('{"tenant_id":"tenant1","plugins":["langgenius/missing"]}\n') + + with ( + patch( + f"{MIGRATION_MODULE}.PluginMigration.extract_unique_plugins", + return_value={ + "plugins": {}, + "plugin_not_exist": ["langgenius/missing"], + }, + ), + patch(f"{MIGRATION_MODULE}.PluginMigration.handle_plugin_instance_install", return_value={}), + patch(f"{MIGRATION_MODULE}.PluginInstaller") as mock_installer_cls, + ): + mock_installer = MagicMock() + mock_installer.list_plugins.return_value = [] + mock_installer_cls.return_value = mock_installer + + PluginMigration.install_plugins(str(extracted_plugins), str(output_file), workers=1) + + output = json.loads(output_file.read_text()) + assert output["not_installed"] == [{"tenant_id": "tenant1", "plugin_not_exist": ["langgenius/missing"]}] + mock_installer.install_from_identifiers.assert_not_called() diff --git a/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_dsl_service.py b/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_dsl_service.py index 93884d07c5c..5cdb2afd093 100644 --- a/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_dsl_service.py +++ b/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_dsl_service.py @@ -633,6 +633,19 @@ def test_import_rag_pipeline_yaml_content_requires_content() -> None: assert "yaml_content is required" in result.error +def test_import_rag_pipeline_rejects_oversized_yaml_content_before_parsing( + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setattr("services.rag_pipeline.rag_pipeline_dsl_service.DSL_MAX_SIZE", 3) + service = RagPipelineDslService(session=Mock()) + account = Mock(current_tenant_id="t1") + + result = service.import_rag_pipeline(account=account, import_mode="yaml-content", yaml_content="你你") + + assert result.status == ImportStatus.FAILED + assert result.error == "File size exceeds the limit of 10MB" + + def test_import_rag_pipeline_yaml_content_requires_mapping() -> None: service = RagPipelineDslService(session=Mock()) account = Mock(current_tenant_id="t1") diff --git a/api/tests/unit_tests/services/test_app_dsl_service.py b/api/tests/unit_tests/services/test_app_dsl_service.py index 64236ea5a90..ae621bdcf52 100644 --- a/api/tests/unit_tests/services/test_app_dsl_service.py +++ b/api/tests/unit_tests/services/test_app_dsl_service.py @@ -1,50 +1,49 @@ -from types import SimpleNamespace from unittest.mock import Mock -from services.app_dsl_service import AppDslService, ImportStatus +import pytest + +from services.app_dsl_service import AppDslService +from services.entities.dsl_entities import ImportStatus -def test_import_app_rejects_oversized_yaml_content_by_bytes(monkeypatch) -> None: - monkeypatch.setattr("services.app_dsl_service.DSL_MAX_SIZE", 1) - service = AppDslService(session=SimpleNamespace()) +def test_import_app_rejects_oversized_yaml_content_before_parsing(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr("services.app_dsl_service.DSL_MAX_SIZE", 3) + service = AppDslService(session=Mock()) + account = Mock(current_tenant_id="tenant-1") - result = service.import_app( - account=SimpleNamespace(current_tenant_id="tenant-1"), - import_mode="yaml-content", - yaml_content="é", - ) + result = service.import_app(account=account, import_mode="yaml-content", yaml_content="你你") assert result.status == ImportStatus.FAILED - assert "10MB" in result.error + assert result.error == "File size exceeds the limit of 10MB" -def test_import_app_rejects_oversized_yaml_url_bytes_before_decode(monkeypatch) -> None: +def test_import_app_rejects_oversized_yaml_url_bytes_before_decode(monkeypatch: pytest.MonkeyPatch) -> 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=SimpleNamespace()) + service = AppDslService(session=Mock()) result = service.import_app( - account=SimpleNamespace(current_tenant_id="tenant-1"), + account=Mock(current_tenant_id="tenant-1"), import_mode="yaml-url", yaml_url="https://example.com/app.yaml", ) assert result.status == ImportStatus.FAILED - assert "10MB" in result.error + assert result.error == "File size exceeds the limit of 10MB" -def test_import_app_returns_decode_error_for_invalid_yaml_url_bytes(monkeypatch) -> None: +def test_import_app_returns_decode_error_for_invalid_yaml_url_bytes(monkeypatch: pytest.MonkeyPatch) -> 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=SimpleNamespace()) + service = AppDslService(session=Mock()) result = service.import_app( - account=SimpleNamespace(current_tenant_id="tenant-1"), + account=Mock(current_tenant_id="tenant-1"), import_mode="yaml-url", yaml_url="https://example.com/app.yaml", )