diff --git a/api/tests/test_containers_integration_tests/controllers/mcp/test_mcp.py b/api/tests/test_containers_integration_tests/controllers/mcp/test_mcp.py index 21b395a04c6..c281f071560 100644 --- a/api/tests/test_containers_integration_tests/controllers/mcp/test_mcp.py +++ b/api/tests/test_containers_integration_tests/controllers/mcp/test_mcp.py @@ -3,6 +3,7 @@ from __future__ import annotations import types +from inspect import unwrap from unittest.mock import MagicMock, patch from uuid import uuid4 @@ -13,12 +14,6 @@ from pydantic import ValidationError import controllers.mcp.mcp as module -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - @pytest.fixture(autouse=True) def mock_mcp_ns(): fake_ns = types.SimpleNamespace() diff --git a/api/tests/unit_tests/controllers/console/app/test_mcp_server_response.py b/api/tests/unit_tests/controllers/console/app/test_mcp_server_response.py index c7dba152162..aa248180bca 100644 --- a/api/tests/unit_tests/controllers/console/app/test_mcp_server_response.py +++ b/api/tests/unit_tests/controllers/console/app/test_mcp_server_response.py @@ -1,4 +1,5 @@ import datetime +from inspect import unwrap from types import SimpleNamespace from unittest.mock import PropertyMock, patch @@ -8,12 +9,6 @@ from controllers.console import console_ns from controllers.console.app.mcp_server import AppMCPServerController, AppMCPServerResponse -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - class _ValidatedResponse: def __init__(self, payload): self._payload = payload diff --git a/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_datasource_content_preview.py b/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_datasource_content_preview.py index 2cf8947014b..eccf5ed56d1 100644 --- a/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_datasource_content_preview.py +++ b/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_datasource_content_preview.py @@ -1,3 +1,4 @@ +from inspect import unwrap from unittest.mock import MagicMock, patch import pytest @@ -11,12 +12,6 @@ from models import Account from models.dataset import Pipeline -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - def make_account() -> Account: account = Account(name="Test User", email="user@example.com") account.id = "account-1" diff --git a/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_rag_pipeline_draft_variable.py b/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_rag_pipeline_draft_variable.py index f51b1ae1da4..0d18120b715 100644 --- a/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_rag_pipeline_draft_variable.py +++ b/api/tests/unit_tests/controllers/console/datasets/rag_pipeline/test_rag_pipeline_draft_variable.py @@ -1,3 +1,4 @@ +from inspect import unwrap from unittest.mock import MagicMock, patch import pytest @@ -19,12 +20,6 @@ from graphon.variables.types import SegmentType from models.account import Account, TenantAccountRole -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - @pytest.fixture def fake_db(): db = MagicMock() diff --git a/api/tests/unit_tests/controllers/console/explore/test_banner.py b/api/tests/unit_tests/controllers/console/explore/test_banner.py index d1cb6b6a03d..552dc7d8217 100644 --- a/api/tests/unit_tests/controllers/console/explore/test_banner.py +++ b/api/tests/unit_tests/controllers/console/explore/test_banner.py @@ -1,4 +1,5 @@ from datetime import datetime +from inspect import unwrap from unittest.mock import MagicMock, patch from flask import Flask @@ -7,12 +8,6 @@ import controllers.console.explore.banner as banner_module from models.enums import BannerStatus -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - class TestBannerApi: def test_get_banners_with_requested_language(self, app: Flask): api = banner_module.BannerApi() diff --git a/api/tests/unit_tests/controllers/console/explore/test_installed_app.py b/api/tests/unit_tests/controllers/console/explore/test_installed_app.py index be85082ecd0..c5780e46ede 100644 --- a/api/tests/unit_tests/controllers/console/explore/test_installed_app.py +++ b/api/tests/unit_tests/controllers/console/explore/test_installed_app.py @@ -13,10 +13,7 @@ type Payload = dict[str, object] type PayloadPatch = Callable[[Payload], AbstractContextManager[object]] -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func +from inspect import unwrap @pytest.fixture diff --git a/api/tests/unit_tests/controllers/console/explore/test_parameter.py b/api/tests/unit_tests/controllers/console/explore/test_parameter.py index 7aaecbff14f..9ee9403baaf 100644 --- a/api/tests/unit_tests/controllers/console/explore/test_parameter.py +++ b/api/tests/unit_tests/controllers/console/explore/test_parameter.py @@ -1,3 +1,4 @@ +from inspect import unwrap from unittest.mock import MagicMock, patch import pytest @@ -7,12 +8,6 @@ from controllers.console.app.error import AppUnavailableError from models.model import AppMode -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - class TestAppParameterApi: def test_get_app_none(self): api = module.AppParameterApi() diff --git a/api/tests/unit_tests/controllers/console/explore/test_recommended_app.py b/api/tests/unit_tests/controllers/console/explore/test_recommended_app.py index ef08aa1b36a..8a2e14cce9b 100644 --- a/api/tests/unit_tests/controllers/console/explore/test_recommended_app.py +++ b/api/tests/unit_tests/controllers/console/explore/test_recommended_app.py @@ -1,3 +1,4 @@ +from inspect import unwrap from unittest.mock import ANY, patch from flask import Flask @@ -7,12 +8,6 @@ from models import Account from models.model import AppMode, IconType -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - def make_account(interface_language: str | None) -> Account: account = Account(name="Test User", email="user@example.com") account.id = "account-1" diff --git a/api/tests/unit_tests/controllers/console/explore/test_saved_message.py b/api/tests/unit_tests/controllers/console/explore/test_saved_message.py index 07e674afad6..f210d0d5d04 100644 --- a/api/tests/unit_tests/controllers/console/explore/test_saved_message.py +++ b/api/tests/unit_tests/controllers/console/explore/test_saved_message.py @@ -1,3 +1,4 @@ +from inspect import unwrap from unittest.mock import MagicMock, PropertyMock, patch from uuid import uuid4 @@ -10,12 +11,6 @@ from controllers.console.explore.error import NotCompletionAppError from services.errors.message import MessageNotExistsError -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - def make_saved_message(): msg = MagicMock() msg.id = str(uuid4()) diff --git a/api/tests/unit_tests/controllers/console/explore/test_workflow.py b/api/tests/unit_tests/controllers/console/explore/test_workflow.py index c5b2f0bd9ba..83cfcadd093 100644 --- a/api/tests/unit_tests/controllers/console/explore/test_workflow.py +++ b/api/tests/unit_tests/controllers/console/explore/test_workflow.py @@ -1,3 +1,4 @@ +from inspect import unwrap from unittest.mock import MagicMock, patch import pytest @@ -14,12 +15,6 @@ from models.model import AppMode from services.errors.llm import InvokeRateLimitError -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - @pytest.fixture def app(): app = Flask(__name__) diff --git a/api/tests/unit_tests/controllers/console/explore/test_wraps.py b/api/tests/unit_tests/controllers/console/explore/test_wraps.py index 2c1acfc3d65..69c380487ed 100644 --- a/api/tests/unit_tests/controllers/console/explore/test_wraps.py +++ b/api/tests/unit_tests/controllers/console/explore/test_wraps.py @@ -18,12 +18,6 @@ from controllers.console.explore.wraps import ( ) -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - def test_installed_app_required_not_found(): @installed_app_required def view(installed_app): diff --git a/api/tests/unit_tests/controllers/console/snippets/test_snippet_workflow_draft_variable.py b/api/tests/unit_tests/controllers/console/snippets/test_snippet_workflow_draft_variable.py index b885b9d601f..9a3637d2f43 100644 --- a/api/tests/unit_tests/controllers/console/snippets/test_snippet_workflow_draft_variable.py +++ b/api/tests/unit_tests/controllers/console/snippets/test_snippet_workflow_draft_variable.py @@ -84,7 +84,7 @@ def test_system_variables_returns_empty_list(app): assert result == WorkflowDraftVariableList(variables=[]) -def test_delete_variable_collection_deletes_current_user_variables(app, monkeypatch): +def test_delete_variable_collection_deletes_current_user_variables(app: Flask, monkeypatch: pytest.MonkeyPatch): draft_var_service = SimpleNamespace(delete_user_workflow_variables=Mock()) monkeypatch.setattr(module, "WorkflowDraftVariableService", Mock(return_value=draft_var_service)) db_session = Mock() @@ -101,7 +101,7 @@ def test_delete_variable_collection_deletes_current_user_variables(app, monkeypa db_session.commit.assert_called_once() -def test_variable_collection_get_raises_when_draft_workflow_missing(app, monkeypatch): +def test_variable_collection_get_raises_when_draft_workflow_missing(app: Flask, monkeypatch: pytest.MonkeyPatch): monkeypatch.setattr( module, "SnippetService", @@ -116,7 +116,7 @@ def test_variable_collection_get_raises_when_draft_workflow_missing(app, monkeyp handler(api, _make_account(), snippet=SimpleNamespace(id="snippet-1")) -def test_node_variable_collection_get_lists_node_variables(app, monkeypatch): +def test_node_variable_collection_get_lists_node_variables(app: Flask, monkeypatch: pytest.MonkeyPatch): variables = WorkflowDraftVariableList(variables=[SimpleNamespace(id="var-1")]) list_node_variables = Mock(return_value=variables) @@ -149,7 +149,7 @@ def test_node_variable_collection_get_lists_node_variables(app, monkeypatch): list_node_variables.assert_called_once_with("snippet-1", "llm-1", user_id="user-1") -def test_node_variable_collection_delete_deletes_node_variables(app, monkeypatch): +def test_node_variable_collection_delete_deletes_node_variables(app: Flask, monkeypatch: pytest.MonkeyPatch): delete_node_variables = Mock() draft_var_service = SimpleNamespace(delete_node_variables=delete_node_variables) monkeypatch.setattr(module, "WorkflowDraftVariableService", Mock(return_value=draft_var_service)) @@ -168,7 +168,7 @@ def test_node_variable_collection_delete_deletes_node_variables(app, monkeypatch db_session.commit.assert_called_once() -def test_variable_patch_returns_variable_when_no_changes(app, monkeypatch): +def test_variable_patch_returns_variable_when_no_changes(app: Flask, monkeypatch: pytest.MonkeyPatch): variable = SimpleNamespace(id="var-1", app_id="snippet-1", user_id="user-1", node_id="llm-1") draft_var_service = SimpleNamespace(get_variable=Mock(return_value=variable), update_variable=Mock()) db_session = Mock() @@ -192,7 +192,7 @@ def test_variable_patch_returns_variable_when_no_changes(app, monkeypatch): db_session.commit.assert_not_called() -def test_variable_delete_deletes_variable(app, monkeypatch): +def test_variable_delete_deletes_variable(app: Flask, monkeypatch: pytest.MonkeyPatch): variable = SimpleNamespace(id="var-1", app_id="snippet-1", user_id="user-1", node_id="llm-1") delete_variable = Mock() draft_var_service = SimpleNamespace(get_variable=Mock(return_value=variable), delete_variable=delete_variable) @@ -212,7 +212,7 @@ def test_variable_delete_deletes_variable(app, monkeypatch): db_session.commit.assert_called_once() -def test_variable_reset_returns_no_content_when_reset_result_is_none(app, monkeypatch): +def test_variable_reset_returns_no_content_when_reset_result_is_none(app: Flask, monkeypatch: pytest.MonkeyPatch): variable = SimpleNamespace(id="var-1", app_id="snippet-1", user_id="user-1", node_id="llm-1") draft_workflow = SimpleNamespace(id="workflow-1") draft_var_service = SimpleNamespace( @@ -240,7 +240,7 @@ def test_variable_reset_returns_no_content_when_reset_result_is_none(app, monkey db_session.commit.assert_called_once() -def test_environment_variables_returns_workflow_environment_variables(app, monkeypatch): +def test_environment_variables_returns_workflow_environment_variables(app: Flask, monkeypatch: pytest.MonkeyPatch): env_var = SimpleNamespace( id="env-1", name="API_KEY", diff --git a/api/tests/unit_tests/controllers/console/test_spec.py b/api/tests/unit_tests/controllers/console/test_spec.py index 05a4befaa83..84c2004ec70 100644 --- a/api/tests/unit_tests/controllers/console/test_spec.py +++ b/api/tests/unit_tests/controllers/console/test_spec.py @@ -1,14 +1,9 @@ +from inspect import unwrap from unittest.mock import patch import controllers.console.spec as spec_module -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - class TestSpecSchemaDefinitionsApi: def test_get_success(self): api = spec_module.SpecSchemaDefinitionsApi() diff --git a/api/tests/unit_tests/controllers/console/workspace/test_agent_providers.py b/api/tests/unit_tests/controllers/console/workspace/test_agent_providers.py index f70450955a4..f7310b5fec8 100644 --- a/api/tests/unit_tests/controllers/console/workspace/test_agent_providers.py +++ b/api/tests/unit_tests/controllers/console/workspace/test_agent_providers.py @@ -1,3 +1,4 @@ +from inspect import unwrap from unittest.mock import MagicMock, patch from flask import Flask @@ -8,12 +9,6 @@ from controllers.console.workspace.agent_providers import ( ) -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - class TestAgentProviderListApi: def test_get_success(self, app: Flask): api = AgentProviderListApi() diff --git a/api/tests/unit_tests/controllers/console/workspace/test_members.py b/api/tests/unit_tests/controllers/console/workspace/test_members.py index 2048e3717d9..321e2a68f39 100644 --- a/api/tests/unit_tests/controllers/console/workspace/test_members.py +++ b/api/tests/unit_tests/controllers/console/workspace/test_members.py @@ -1,4 +1,5 @@ from contextlib import nullcontext +from inspect import unwrap from types import SimpleNamespace from unittest.mock import MagicMock, patch @@ -26,12 +27,6 @@ from controllers.console.workspace.members import ( from services.errors.account import AccountAlreadyInTenantError -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - class TestMemberListApi: def test_get_success(self, app: Flask): api = MemberListApi() diff --git a/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py b/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py index d938558806f..21055390362 100644 --- a/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py +++ b/api/tests/unit_tests/controllers/console/workspace/test_model_providers.py @@ -20,10 +20,7 @@ VALID_UUID = "123e4567-e89b-12d3-a456-426614174000" INVALID_UUID = "123" -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func +from inspect import unwrap class TestModelProviderListApi: diff --git a/api/tests/unit_tests/controllers/console/workspace/test_models.py b/api/tests/unit_tests/controllers/console/workspace/test_models.py index 00977e6d7b6..3374c49caff 100644 --- a/api/tests/unit_tests/controllers/console/workspace/test_models.py +++ b/api/tests/unit_tests/controllers/console/workspace/test_models.py @@ -1,3 +1,4 @@ +from inspect import unwrap from types import SimpleNamespace from unittest.mock import patch @@ -19,12 +20,6 @@ from graphon.model_runtime.entities.model_entities import ModelType from graphon.model_runtime.errors.validate import CredentialsValidateFailedError -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - class TestDefaultModelApi: def test_get_success(self, app: Flask): api = DefaultModelApi() diff --git a/api/tests/unit_tests/controllers/files/test_image_preview.py b/api/tests/unit_tests/controllers/files/test_image_preview.py index 49846b89ee9..9ad7120c30f 100644 --- a/api/tests/unit_tests/controllers/files/test_image_preview.py +++ b/api/tests/unit_tests/controllers/files/test_image_preview.py @@ -1,4 +1,5 @@ import types +from inspect import unwrap from unittest.mock import patch import pytest @@ -7,12 +8,6 @@ from werkzeug.exceptions import NotFound import controllers.files.image_preview as module -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - @pytest.fixture(autouse=True) def mock_db(): """ diff --git a/api/tests/unit_tests/controllers/files/test_tool_files.py b/api/tests/unit_tests/controllers/files/test_tool_files.py index edb91c3f262..a41e2a056b1 100644 --- a/api/tests/unit_tests/controllers/files/test_tool_files.py +++ b/api/tests/unit_tests/controllers/files/test_tool_files.py @@ -1,4 +1,5 @@ import types +from inspect import unwrap from unittest.mock import patch import pytest @@ -7,12 +8,6 @@ from werkzeug.exceptions import Forbidden, NotFound import controllers.files.tool_files as module -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - def fake_request(args: dict): return types.SimpleNamespace(args=types.SimpleNamespace(to_dict=lambda flat=True: args)) diff --git a/api/tests/unit_tests/controllers/files/test_upload.py b/api/tests/unit_tests/controllers/files/test_upload.py index 7c98b088ce3..5281dcf0645 100644 --- a/api/tests/unit_tests/controllers/files/test_upload.py +++ b/api/tests/unit_tests/controllers/files/test_upload.py @@ -1,5 +1,6 @@ import io import types +from inspect import unwrap from unittest.mock import patch import pytest @@ -9,12 +10,6 @@ import controllers.files.upload as module from core.workflow.file_reference import build_file_reference -def unwrap(func): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - return func - - def fake_request(args: dict, file=None): return types.SimpleNamespace( args=types.SimpleNamespace(to_dict=lambda flat=True: args), diff --git a/api/tests/unit_tests/core/agent/test_base_agent_runner.py b/api/tests/unit_tests/core/agent/test_base_agent_runner.py index f5e4b099936..a1af0a87a55 100644 --- a/api/tests/unit_tests/core/agent/test_base_agent_runner.py +++ b/api/tests/unit_tests/core/agent/test_base_agent_runner.py @@ -21,7 +21,7 @@ def mock_db_session(mocker: MockerFixture): @pytest.fixture -def runner(mocker, mock_db_session): +def runner(mocker: MockerFixture, mock_db_session): r = BaseAgentRunner.__new__(BaseAgentRunner) r.tenant_id = "tenant" r.user_id = "user" diff --git a/api/tests/unit_tests/core/agent/test_cot_completion_agent_runner.py b/api/tests/unit_tests/core/agent/test_cot_completion_agent_runner.py index 0d949c357dc..e79ea549a74 100644 --- a/api/tests/unit_tests/core/agent/test_cot_completion_agent_runner.py +++ b/api/tests/unit_tests/core/agent/test_cot_completion_agent_runner.py @@ -17,7 +17,7 @@ from graphon.model_runtime.entities.message_entities import ( @pytest.fixture -def runner(mocker, dummy_tool_factory): +def runner(mocker: MockerFixture, dummy_tool_factory): runner = CotCompletionAgentRunner.__new__(CotCompletionAgentRunner) runner._instruction = "Test instruction" diff --git a/api/tests/unit_tests/core/app/app_config/easy_ui_based_app/test_dataset_manager.py b/api/tests/unit_tests/core/app/app_config/easy_ui_based_app/test_dataset_manager.py index 3a239eac0e7..d5305d2fc0b 100644 --- a/api/tests/unit_tests/core/app/app_config/easy_ui_based_app/test_dataset_manager.py +++ b/api/tests/unit_tests/core/app/app_config/easy_ui_based_app/test_dataset_manager.py @@ -32,7 +32,7 @@ def base_config(valid_uuid): @pytest.fixture -def mock_dataset_service(mocker, valid_uuid): +def mock_dataset_service(mocker: MockerFixture, valid_uuid): mock_dataset = MagicMock() mock_dataset.tenant_id = "tenant1" diff --git a/api/tests/unit_tests/core/app/apps/agent_app/test_input_guards.py b/api/tests/unit_tests/core/app/apps/agent_app/test_input_guards.py index 31bc4a10806..24dd61cb951 100644 --- a/api/tests/unit_tests/core/app/apps/agent_app/test_input_guards.py +++ b/api/tests/unit_tests/core/app/apps/agent_app/test_input_guards.py @@ -11,6 +11,8 @@ from __future__ import annotations from types import SimpleNamespace from typing import Any +import pytest + import core.app.features.annotation_reply.annotation_reply as annotation_mod import core.moderation.input_moderation as input_moderation_mod from core.app.apps.agent_app.app_generator import AgentAppGenerator @@ -42,7 +44,7 @@ def _make_entity(query: str = "hello") -> SimpleNamespace: ) -def _patch_moderation(monkeypatch, *, returns=None, raises: Exception | None = None) -> None: +def _patch_moderation(monkeypatch: pytest.MonkeyPatch, *, returns=None, raises: Exception | None = None) -> None: class _FakeModeration: def check(self, **kwargs: Any): if raises is not None: @@ -52,7 +54,7 @@ def _patch_moderation(monkeypatch, *, returns=None, raises: Exception | None = N monkeypatch.setattr(input_moderation_mod, "InputModeration", _FakeModeration) -def _patch_annotation(monkeypatch, *, reply=None) -> None: +def _patch_annotation(monkeypatch: pytest.MonkeyPatch, *, reply=None) -> None: class _FakeAnnotation: def query(self, **kwargs: Any): return reply @@ -73,7 +75,7 @@ def _saved_user_query(events: list[Any]) -> str: class TestRunInputGuards: - def test_no_guards_passes_through(self, monkeypatch): + def test_no_guards_passes_through(self, monkeypatch: pytest.MonkeyPatch): _patch_moderation(monkeypatch, returns=(False, {}, "hello")) _patch_annotation(monkeypatch, reply=None) qm = _FakeQueueManager() @@ -89,7 +91,7 @@ class TestRunInputGuards: assert query == "hello" assert qm.events == [] - def test_moderation_override_sanitizes_query(self, monkeypatch): + def test_moderation_override_sanitizes_query(self, monkeypatch: pytest.MonkeyPatch): _patch_moderation(monkeypatch, returns=(True, {}, "[redacted]")) _patch_annotation(monkeypatch, reply=None) qm = _FakeQueueManager() @@ -105,7 +107,7 @@ class TestRunInputGuards: assert query == "[redacted]" assert qm.events == [] - def test_moderation_block_short_circuits(self, monkeypatch): + def test_moderation_block_short_circuits(self, monkeypatch: pytest.MonkeyPatch): _patch_moderation(monkeypatch, raises=ModerationError("blocked preset answer")) _patch_annotation(monkeypatch, reply=None) qm = _FakeQueueManager() @@ -122,7 +124,7 @@ class TestRunInputGuards: assert _answer_text(qm.events) == "blocked preset answer" assert _saved_user_query(qm.events) == "forbidden" - def test_annotation_hit_short_circuits(self, monkeypatch): + def test_annotation_hit_short_circuits(self, monkeypatch: pytest.MonkeyPatch): _patch_moderation(monkeypatch, returns=(False, {}, "what is your name")) _patch_annotation(monkeypatch, reply=SimpleNamespace(id="anno-1", content="I am the annotated Iris.")) qm = _FakeQueueManager() diff --git a/api/tests/unit_tests/core/app/apps/agent_app/test_resolve_agent.py b/api/tests/unit_tests/core/app/apps/agent_app/test_resolve_agent.py index 637c0fad5d2..c0a226ad575 100644 --- a/api/tests/unit_tests/core/app/apps/agent_app/test_resolve_agent.py +++ b/api/tests/unit_tests/core/app/apps/agent_app/test_resolve_agent.py @@ -44,7 +44,7 @@ def _snapshot() -> SimpleNamespace: class TestResolveAgentById: - def test_success_returns_agent_snapshot_soul(self, monkeypatch): + def test_success_returns_agent_snapshot_soul(self, monkeypatch: pytest.MonkeyPatch): agent = SimpleNamespace(id="agent-1") snapshot = _snapshot() _patch_session(monkeypatch, [agent, snapshot]) @@ -59,24 +59,24 @@ class TestResolveAgentById: assert soul.model is not None assert soul.model.model == "gpt-4o-mini" - def test_agent_missing_raises(self, monkeypatch): + def test_agent_missing_raises(self, monkeypatch: pytest.MonkeyPatch): _patch_session(monkeypatch, [None]) with pytest.raises(AgentAppGeneratorError, match="Agent not found"): AgentAppGenerator._resolve_agent_by_id(tenant_id="t1", agent_id="x", snapshot_id="snap-1") - def test_no_published_version_raises(self, monkeypatch): + def test_no_published_version_raises(self, monkeypatch: pytest.MonkeyPatch): _patch_session(monkeypatch, [SimpleNamespace(id="agent-1")]) with pytest.raises(AgentAppGeneratorError, match="no published version"): AgentAppGenerator._resolve_agent_by_id(tenant_id="t1", agent_id="agent-1", snapshot_id=None) - def test_snapshot_missing_raises(self, monkeypatch): + def test_snapshot_missing_raises(self, monkeypatch: pytest.MonkeyPatch): _patch_session(monkeypatch, [SimpleNamespace(id="agent-1"), None]) with pytest.raises(AgentAppGeneratorError, match="published version not found"): AgentAppGenerator._resolve_agent_by_id(tenant_id="t1", agent_id="agent-1", snapshot_id="snap-1") class TestResolveAgent: - def test_success_chains_to_resolve_by_id(self, monkeypatch): + def test_success_chains_to_resolve_by_id(self, monkeypatch: pytest.MonkeyPatch): bound_agent = SimpleNamespace(id="agent-1", active_config_snapshot_id="snap-1") inner_agent = SimpleNamespace(id="agent-1") snapshot = _snapshot() @@ -90,7 +90,7 @@ class TestResolveAgent: assert snap is snapshot assert soul.model is not None - def test_unbound_app_raises(self, monkeypatch): + def test_unbound_app_raises(self, monkeypatch: pytest.MonkeyPatch): _patch_session(monkeypatch, [None]) app_model = SimpleNamespace(id="app-1", tenant_id="t1") with pytest.raises(AgentAppGeneratorError, match="has no bound Agent"): diff --git a/api/tests/unit_tests/core/app/apps/test_pause_resume.py b/api/tests/unit_tests/core/app/apps/test_pause_resume.py index 5f13c6aff64..30961051a75 100644 --- a/api/tests/unit_tests/core/app/apps/test_pause_resume.py +++ b/api/tests/unit_tests/core/app/apps/test_pause_resume.py @@ -288,7 +288,7 @@ def test_advanced_chat_pause_resume_matches_baseline(mocker: MockerFixture): assert resumed_state.outputs == baseline_outputs -def test_resume_emits_resumption_start_reason(mocker) -> None: +def test_resume_emits_resumption_start_reason(mocker: MockerFixture) -> None: _patch_tool_node(mocker) paused_state = _build_runtime_state("resume-reason") diff --git a/api/tests/unit_tests/core/app/workflow/test_node_factory.py b/api/tests/unit_tests/core/app/workflow/test_node_factory.py index addce649d56..f74c2d48814 100644 --- a/api/tests/unit_tests/core/app/workflow/test_node_factory.py +++ b/api/tests/unit_tests/core/app/workflow/test_node_factory.py @@ -40,7 +40,7 @@ class DummyDocumentExtractorNode(DummyNode): class TestDifyNodeFactory: @staticmethod - def _stub_node_resolution(monkeypatch, node_class): + def _stub_node_resolution(monkeypatch: pytest.MonkeyPatch, node_class): monkeypatch.setattr( "core.workflow.node_factory.resolve_workflow_node_class", lambda **_kwargs: node_class, diff --git a/api/tests/unit_tests/core/datasource/test_datasource_manager.py b/api/tests/unit_tests/core/datasource/test_datasource_manager.py index 8842d678c7a..baf51489dfb 100644 --- a/api/tests/unit_tests/core/datasource/test_datasource_manager.py +++ b/api/tests/unit_tests/core/datasource/test_datasource_manager.py @@ -89,7 +89,9 @@ def test_get_datasource_runtime_delegates_to_provider_controller(mocker: MockerF ), ], ) -def test_get_datasource_plugin_provider_creates_controller_and_caches(mocker, datasource_type, controller_path): +def test_get_datasource_plugin_provider_creates_controller_and_caches( + mocker: MockerFixture, datasource_type, controller_path +): _invalidate_recyclable_contextvars() provider_entity = types.SimpleNamespace(declaration=object(), plugin_id="plugin", plugin_unique_identifier="uniq") diff --git a/api/tests/unit_tests/core/file/test_remote_fetcher.py b/api/tests/unit_tests/core/file/test_remote_fetcher.py index ec377e8dc43..c9612a062ac 100644 --- a/api/tests/unit_tests/core/file/test_remote_fetcher.py +++ b/api/tests/unit_tests/core/file/test_remote_fetcher.py @@ -36,7 +36,7 @@ def _signed_url(*, base_url: str, path: str, payload: str, secret: str = "test-s return f"{base_url}{path}?{query}" -def _patch_file_fetcher_config(monkeypatch): +def _patch_file_fetcher_config(monkeypatch: pytest.MonkeyPatch): monkeypatch.setattr(remote_fetcher.dify_config, "FILES_URL", "http://localhost:5001") monkeypatch.setattr(remote_fetcher.dify_config, "INTERNAL_FILES_URL", "http://api:5001") monkeypatch.setattr(remote_fetcher.dify_config, "SECRET_KEY", "test-secret") @@ -44,7 +44,7 @@ def _patch_file_fetcher_config(monkeypatch): monkeypatch.setattr(remote_fetcher.time, "time", lambda: 1700000100) -def _patch_session(monkeypatch): +def _patch_session(monkeypatch: pytest.MonkeyPatch): session = MagicMock() session_cm = MagicMock() session_cm.__enter__.return_value = session @@ -53,19 +53,19 @@ def _patch_session(monkeypatch): return session -def _patch_ssrf_make_request(monkeypatch, response=None): +def _patch_ssrf_make_request(monkeypatch: pytest.MonkeyPatch, response=None): make_request = MagicMock(return_value=response) if response is not None else MagicMock() monkeypatch.setattr(remote_fetcher.ssrf_proxy, "make_request", make_request) return make_request -def _patch_signer_times(monkeypatch): +def _patch_signer_times(monkeypatch: pytest.MonkeyPatch): monkeypatch.setattr("core.datasource.datasource_file_manager.time.time", lambda: 1700000000) monkeypatch.setattr("core.tools.signature.time.time", lambda: 1700000000) monkeypatch.setattr("core.tools.tool_file_manager.time.time", lambda: 1700000000) -def test_get_signed_upload_file_url_reads_storage_without_ssrf(monkeypatch): +def test_get_signed_upload_file_url_reads_storage_without_ssrf(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) session = _patch_session(monkeypatch) upload_file = SimpleNamespace( @@ -102,7 +102,7 @@ def test_get_signed_upload_file_url_reads_storage_without_ssrf(monkeypatch): ssrf_make_request.assert_not_called() -def test_make_request_resolves_upload_preview_url_generated_by_signer(monkeypatch): +def test_make_request_resolves_upload_preview_url_generated_by_signer(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) _patch_signer_times(monkeypatch) session = _patch_session(monkeypatch) @@ -132,7 +132,7 @@ def test_make_request_resolves_upload_preview_url_generated_by_signer(monkeypatc ssrf_make_request.assert_not_called() -def test_make_request_resolves_sign_tool_file_url_with_empty_extension(monkeypatch): +def test_make_request_resolves_sign_tool_file_url_with_empty_extension(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) _patch_signer_times(monkeypatch) session = _patch_session(monkeypatch) @@ -161,7 +161,7 @@ def test_make_request_resolves_sign_tool_file_url_with_empty_extension(monkeypat ssrf_make_request.assert_not_called() -def test_make_request_resolves_tool_manager_url_with_empty_extension(monkeypatch): +def test_make_request_resolves_tool_manager_url_with_empty_extension(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) _patch_signer_times(monkeypatch) session = _patch_session(monkeypatch) @@ -189,7 +189,7 @@ def test_make_request_resolves_tool_manager_url_with_empty_extension(monkeypatch ssrf_make_request.assert_not_called() -def test_make_request_resolves_datasource_manager_url_with_empty_extension(monkeypatch): +def test_make_request_resolves_datasource_manager_url_with_empty_extension(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) _patch_signer_times(monkeypatch) _patch_session(monkeypatch) @@ -222,7 +222,7 @@ def test_make_request_resolves_datasource_manager_url_with_empty_extension(monke ssrf_make_request.assert_not_called() -def test_head_signed_upload_file_url_returns_metadata_without_storage_content(monkeypatch): +def test_head_signed_upload_file_url_returns_metadata_without_storage_content(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) session = _patch_session(monkeypatch) upload_file = SimpleNamespace( @@ -259,7 +259,7 @@ def test_head_signed_upload_file_url_returns_metadata_without_storage_content(mo ssrf_make_request.assert_not_called() -def test_make_request_get_signed_upload_file_url_reads_storage_without_ssrf(monkeypatch): +def test_make_request_get_signed_upload_file_url_reads_storage_without_ssrf(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) _patch_session(monkeypatch) upload_file = SimpleNamespace( @@ -291,7 +291,7 @@ def test_make_request_get_signed_upload_file_url_reads_storage_without_ssrf(monk ssrf_make_request.assert_not_called() -def test_make_request_head_signed_upload_file_url_returns_metadata_without_ssrf(monkeypatch): +def test_make_request_head_signed_upload_file_url_returns_metadata_without_ssrf(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) _patch_session(monkeypatch) upload_file = SimpleNamespace( @@ -325,7 +325,7 @@ def test_make_request_head_signed_upload_file_url_returns_metadata_without_ssrf( ssrf_make_request.assert_not_called() -def test_make_request_get_unsigned_dify_url_delegates_to_ssrf_proxy(monkeypatch): +def test_make_request_get_unsigned_dify_url_delegates_to_ssrf_proxy(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) get_upload_file = MagicMock() monkeypatch.setattr(remote_fetcher._file_access_controller, "get_upload_file", get_upload_file) @@ -345,7 +345,7 @@ def test_make_request_get_unsigned_dify_url_delegates_to_ssrf_proxy(monkeypatch) ) -def test_make_request_post_signed_upload_file_url_delegates_to_ssrf_proxy(monkeypatch): +def test_make_request_post_signed_upload_file_url_delegates_to_ssrf_proxy(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) get_upload_file = MagicMock() monkeypatch.setattr(remote_fetcher._file_access_controller, "get_upload_file", get_upload_file) @@ -369,7 +369,7 @@ def test_make_request_post_signed_upload_file_url_delegates_to_ssrf_proxy(monkey ) -def test_get_signed_image_preview_url_uses_image_preview_signature(monkeypatch): +def test_get_signed_image_preview_url_uses_image_preview_signature(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) _patch_session(monkeypatch) upload_file = SimpleNamespace( @@ -401,7 +401,7 @@ def test_get_signed_image_preview_url_uses_image_preview_signature(monkeypatch): ssrf_make_request.assert_not_called() -def test_image_preview_url_with_file_preview_signature_delegates_to_ssrf_proxy(monkeypatch): +def test_image_preview_url_with_file_preview_signature_delegates_to_ssrf_proxy(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) proxy_response = httpx.Response(403, request=httpx.Request("GET", "http://localhost:5001/bad")) ssrf_make_request = _patch_ssrf_make_request(monkeypatch, proxy_response) @@ -421,7 +421,7 @@ def test_image_preview_url_with_file_preview_signature_delegates_to_ssrf_proxy(m ) -def test_duplicate_signature_query_value_delegates_to_ssrf_proxy(monkeypatch): +def test_duplicate_signature_query_value_delegates_to_ssrf_proxy(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) url = ( _signed_url( @@ -444,7 +444,7 @@ def test_duplicate_signature_query_value_delegates_to_ssrf_proxy(monkeypatch): ) -def test_malformed_timestamp_delegates_to_ssrf_proxy(monkeypatch): +def test_malformed_timestamp_delegates_to_ssrf_proxy(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) url = _signed_url( base_url="http://localhost:5001", @@ -464,7 +464,7 @@ def test_malformed_timestamp_delegates_to_ssrf_proxy(monkeypatch): ) -def test_expired_signature_delegates_to_ssrf_proxy(monkeypatch): +def test_expired_signature_delegates_to_ssrf_proxy(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) monkeypatch.setattr(remote_fetcher.time, "time", lambda: 1700004001) url = _signed_url( @@ -485,7 +485,7 @@ def test_expired_signature_delegates_to_ssrf_proxy(monkeypatch): ) -def test_invalid_signature_delegates_to_ssrf_proxy(monkeypatch): +def test_invalid_signature_delegates_to_ssrf_proxy(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) proxy_response = httpx.Response(403, request=httpx.Request("GET", "http://localhost:5001/bad")) ssrf_make_request = _patch_ssrf_make_request(monkeypatch, proxy_response) @@ -502,7 +502,7 @@ def test_invalid_signature_delegates_to_ssrf_proxy(monkeypatch): ) -def test_host_mismatch_delegates_to_ssrf_proxy(monkeypatch): +def test_host_mismatch_delegates_to_ssrf_proxy(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) url = _signed_url( base_url="http://example.com", @@ -522,7 +522,7 @@ def test_host_mismatch_delegates_to_ssrf_proxy(monkeypatch): ) -def test_unsupported_dify_path_delegates_to_ssrf_proxy(monkeypatch): +def test_unsupported_dify_path_delegates_to_ssrf_proxy(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) url = _signed_url( base_url="http://localhost:5001", @@ -543,7 +543,7 @@ def test_unsupported_dify_path_delegates_to_ssrf_proxy(monkeypatch): ) -def test_invalid_url_scheme_delegates_to_ssrf_proxy(monkeypatch): +def test_invalid_url_scheme_delegates_to_ssrf_proxy(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) url = f"file:///tmp/files/{UPLOAD_FILE_ID}/file-preview?timestamp=1700000000&nonce=nonce&sign=ignored" proxy_response = httpx.Response(403, request=httpx.Request("GET", url)) @@ -559,7 +559,7 @@ def test_invalid_url_scheme_delegates_to_ssrf_proxy(monkeypatch): ) -def test_invalid_url_port_delegates_to_ssrf_proxy(monkeypatch): +def test_invalid_url_port_delegates_to_ssrf_proxy(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) url = f"http://localhost:invalid/files/{UPLOAD_FILE_ID}/file-preview?timestamp=1700000000&nonce=nonce&sign=ignored" proxy_response = httpx.Response(403, request=httpx.Request("GET", "http://proxy.example/fallback")) @@ -575,7 +575,7 @@ def test_invalid_url_port_delegates_to_ssrf_proxy(monkeypatch): ) -def test_invalid_configured_file_origin_delegates_to_ssrf_proxy(monkeypatch): +def test_invalid_configured_file_origin_delegates_to_ssrf_proxy(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) monkeypatch.setattr(remote_fetcher.dify_config, "FILES_URL", "") monkeypatch.setattr(remote_fetcher.dify_config, "INTERNAL_FILES_URL", "file:///tmp/files") @@ -597,7 +597,7 @@ def test_invalid_configured_file_origin_delegates_to_ssrf_proxy(monkeypatch): ) -def test_signed_upload_file_url_returns_404_when_record_missing(monkeypatch): +def test_signed_upload_file_url_returns_404_when_record_missing(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) _patch_session(monkeypatch) get_upload_file = MagicMock(return_value=None) @@ -617,7 +617,7 @@ def test_signed_upload_file_url_returns_404_when_record_missing(monkeypatch): ssrf_make_request.assert_not_called() -def test_get_signed_tool_file_url_reads_storage_without_ssrf(monkeypatch): +def test_get_signed_tool_file_url_reads_storage_without_ssrf(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) session = _patch_session(monkeypatch) tool_file = SimpleNamespace( @@ -651,7 +651,7 @@ def test_get_signed_tool_file_url_reads_storage_without_ssrf(monkeypatch): ssrf_make_request.assert_not_called() -def test_signed_tool_file_url_returns_404_when_record_missing(monkeypatch): +def test_signed_tool_file_url_returns_404_when_record_missing(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) _patch_session(monkeypatch) get_tool_file = MagicMock(return_value=None) @@ -671,7 +671,7 @@ def test_signed_tool_file_url_returns_404_when_record_missing(monkeypatch): ssrf_make_request.assert_not_called() -def test_get_signed_datasource_file_url_reads_upload_storage_without_ssrf(monkeypatch): +def test_get_signed_datasource_file_url_reads_upload_storage_without_ssrf(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) _patch_session(monkeypatch) upload_file = SimpleNamespace( @@ -705,7 +705,7 @@ def test_get_signed_datasource_file_url_reads_upload_storage_without_ssrf(monkey ssrf_make_request.assert_not_called() -def test_get_signed_datasource_file_url_reads_tool_storage_when_upload_missing(monkeypatch): +def test_get_signed_datasource_file_url_reads_tool_storage_when_upload_missing(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) _patch_session(monkeypatch) tool_file = SimpleNamespace( @@ -740,7 +740,7 @@ def test_get_signed_datasource_file_url_reads_tool_storage_when_upload_missing(m ssrf_make_request.assert_not_called() -def test_signed_datasource_file_url_returns_404_when_records_missing(monkeypatch): +def test_signed_datasource_file_url_returns_404_when_records_missing(monkeypatch: pytest.MonkeyPatch): _patch_file_fetcher_config(monkeypatch) _patch_session(monkeypatch) get_upload_file = MagicMock(return_value=None) diff --git a/api/tests/unit_tests/core/rag/datasource/keyword/jieba/test_jieba.py b/api/tests/unit_tests/core/rag/datasource/keyword/jieba/test_jieba.py index e233bd2ef07..9224d6c88e8 100644 --- a/api/tests/unit_tests/core/rag/datasource/keyword/jieba/test_jieba.py +++ b/api/tests/unit_tests/core/rag/datasource/keyword/jieba/test_jieba.py @@ -81,7 +81,7 @@ def patched_runtime(monkeypatch: pytest.MonkeyPatch): return SimpleNamespace(session=session, storage=storage, lock=lock) -def test_create_indexes_documents_and_returns_self(monkeypatch, patched_runtime): +def test_create_indexes_documents_and_returns_self(monkeypatch: pytest.MonkeyPatch, patched_runtime): dataset = _dataset(_dataset_keyword_table(), keyword_number=2) keyword = Jieba(dataset) handler = MagicMock() @@ -111,7 +111,7 @@ def test_create_indexes_documents_and_returns_self(monkeypatch, patched_runtime) patched_runtime.lock.assert_called_once_with("keyword_indexing_lock_dataset-1", timeout=600) -def test_add_texts_supports_keywords_list_and_extract_fallback(monkeypatch, patched_runtime): +def test_add_texts_supports_keywords_list_and_extract_fallback(monkeypatch: pytest.MonkeyPatch, patched_runtime): keyword = Jieba(_dataset(_dataset_keyword_table(), keyword_number=3)) handler = MagicMock() handler.extract_keywords.return_value = {"auto"} @@ -135,7 +135,7 @@ def test_add_texts_supports_keywords_list_and_extract_fallback(monkeypatch, patc keyword._save_dataset_keyword_table.assert_called_once() -def test_add_texts_without_keywords_list_always_uses_extractor(monkeypatch, patched_runtime): +def test_add_texts_without_keywords_list_always_uses_extractor(monkeypatch: pytest.MonkeyPatch, patched_runtime): keyword = Jieba(_dataset(_dataset_keyword_table(), keyword_number=1)) handler = MagicMock() handler.extract_keywords.return_value = {"from-extractor"} @@ -162,7 +162,7 @@ def test_text_exists_handles_missing_and_existing_keyword_table(monkeypatch: pyt assert keyword.text_exists("node-x") is False -def test_delete_by_ids_updates_table_when_present(monkeypatch, patched_runtime): +def test_delete_by_ids_updates_table_when_present(monkeypatch: pytest.MonkeyPatch, patched_runtime): keyword = Jieba(_dataset(_dataset_keyword_table())) monkeypatch.setattr(keyword, "_get_dataset_keyword_table", MagicMock(return_value={"k": {"node-1", "node-2"}})) monkeypatch.setattr(keyword, "_delete_ids_from_keyword_table", MagicMock(return_value={"k": {"node-2"}})) @@ -174,7 +174,7 @@ def test_delete_by_ids_updates_table_when_present(monkeypatch, patched_runtime): keyword._save_dataset_keyword_table.assert_called_once_with({"k": {"node-2"}}) -def test_delete_by_ids_saves_none_when_keyword_table_is_missing(monkeypatch, patched_runtime): +def test_delete_by_ids_saves_none_when_keyword_table_is_missing(monkeypatch: pytest.MonkeyPatch, patched_runtime): keyword = Jieba(_dataset(_dataset_keyword_table())) monkeypatch.setattr(keyword, "_get_dataset_keyword_table", MagicMock(return_value=None)) monkeypatch.setattr(keyword, "_delete_ids_from_keyword_table", MagicMock()) @@ -186,7 +186,7 @@ def test_delete_by_ids_saves_none_when_keyword_table_is_missing(monkeypatch, pat keyword._save_dataset_keyword_table.assert_called_once_with(None) -def test_search_returns_documents_in_rank_order_and_applies_filter(monkeypatch, patched_runtime): +def test_search_returns_documents_in_rank_order_and_applies_filter(monkeypatch: pytest.MonkeyPatch, patched_runtime): class _FakeDocumentSegment: dataset_id = _Field("dataset_id") index_node_id = _Field("index_node_id") @@ -216,7 +216,7 @@ def test_search_returns_documents_in_rank_order_and_applies_filter(monkeypatch, assert documents[0].metadata["doc_hash"] == "hash-2" -def test_delete_removes_keyword_table_and_optional_file(monkeypatch, patched_runtime): +def test_delete_removes_keyword_table_and_optional_file(monkeypatch: pytest.MonkeyPatch, patched_runtime): db_keyword = _dataset_keyword_table(data_source_type="database") file_keyword = _dataset_keyword_table(data_source_type="object_storage") @@ -232,7 +232,7 @@ def test_delete_removes_keyword_table_and_optional_file(monkeypatch, patched_run assert patched_runtime.session.commit.call_count == 2 -def test_save_dataset_keyword_table_to_database(monkeypatch, patched_runtime): +def test_save_dataset_keyword_table_to_database(monkeypatch: pytest.MonkeyPatch, patched_runtime): dataset_keyword_table = _dataset_keyword_table(data_source_type="database") keyword = Jieba(_dataset(dataset_keyword_table)) @@ -243,7 +243,7 @@ def test_save_dataset_keyword_table_to_database(monkeypatch, patched_runtime): patched_runtime.session.commit.assert_called_once() -def test_save_dataset_keyword_table_to_file_storage(monkeypatch, patched_runtime): +def test_save_dataset_keyword_table_to_file_storage(monkeypatch: pytest.MonkeyPatch, patched_runtime): dataset_keyword_table = _dataset_keyword_table(data_source_type="file") keyword = Jieba(_dataset(dataset_keyword_table)) patched_runtime.storage.exists.return_value = True @@ -257,7 +257,7 @@ def test_save_dataset_keyword_table_to_file_storage(monkeypatch, patched_runtime assert isinstance(save_args[1], bytes) -def test_get_dataset_keyword_table_returns_existing_table_data(monkeypatch, patched_runtime): +def test_get_dataset_keyword_table_returns_existing_table_data(monkeypatch: pytest.MonkeyPatch, patched_runtime): existing = _dataset_keyword_table( keyword_table_dict={"__type__": "keyword_table", "__data__": {"table": {"kw": ["node-1"]}}} ) @@ -269,7 +269,7 @@ def test_get_dataset_keyword_table_returns_existing_table_data(monkeypatch, patc assert keyword_with_missing_payload._get_dataset_keyword_table() == {} -def test_get_dataset_keyword_table_creates_table_when_missing(monkeypatch, patched_runtime): +def test_get_dataset_keyword_table_creates_table_when_missing(monkeypatch: pytest.MonkeyPatch, patched_runtime): created_tables: list[SimpleNamespace] = [] def _fake_dataset_keyword_table(**kwargs): @@ -323,7 +323,7 @@ def test_retrieve_ids_by_query_ranks_by_keyword_frequency(monkeypatch: pytest.Mo assert ranked_ids == ["node-2"] -def test_update_segment_keywords_updates_when_segment_exists(monkeypatch, patched_runtime): +def test_update_segment_keywords_updates_when_segment_exists(monkeypatch: pytest.MonkeyPatch, patched_runtime): class _FakeDocumentSegment: dataset_id = _Field("dataset_id") index_node_id = _Field("index_node_id") diff --git a/api/tests/unit_tests/core/rag/datasource/vdb/test_vector_factory.py b/api/tests/unit_tests/core/rag/datasource/vdb/test_vector_factory.py index 067159398d2..d86ffd26478 100644 --- a/api/tests/unit_tests/core/rag/datasource/vdb/test_vector_factory.py +++ b/api/tests/unit_tests/core/rag/datasource/vdb/test_vector_factory.py @@ -9,7 +9,7 @@ import pytest from core.rag.models.document import Document -def _register_fake_factory_module(monkeypatch, module_path: str, class_name: str): +def _register_fake_factory_module(monkeypatch: pytest.MonkeyPatch, module_path: str, class_name: str): fake_module = types.ModuleType(module_path) fake_cls = type(class_name, (), {}) setattr(fake_module, class_name, fake_cls) diff --git a/api/tests/unit_tests/events/event_handlers/test_delete_tool_parameters_cache_when_sync_draft_workflow.py b/api/tests/unit_tests/events/event_handlers/test_delete_tool_parameters_cache_when_sync_draft_workflow.py index 0aba4d49908..27f162de75e 100644 --- a/api/tests/unit_tests/events/event_handlers/test_delete_tool_parameters_cache_when_sync_draft_workflow.py +++ b/api/tests/unit_tests/events/event_handlers/test_delete_tool_parameters_cache_when_sync_draft_workflow.py @@ -7,7 +7,10 @@ from core.tools.errors import ToolProviderNotFoundError from events.event_handlers import delete_tool_parameters_cache_when_sync_draft_workflow as handler_module -def test_missing_tool_provider_does_not_log_error_traceback(monkeypatch, caplog: pytest.LogCaptureFixture): +def test_missing_tool_provider_does_not_log_error_traceback( + monkeypatch: pytest.MonkeyPatch, + caplog: pytest.LogCaptureFixture, +): app = SimpleNamespace(id="workflow-id", tenant_id="tenant-id") workflow = SimpleNamespace( graph_dict={ diff --git a/api/tests/unit_tests/libs/test_archive_storage.py b/api/tests/unit_tests/libs/test_archive_storage.py index 4363c235716..966e75f1f9e 100644 --- a/api/tests/unit_tests/libs/test_archive_storage.py +++ b/api/tests/unit_tests/libs/test_archive_storage.py @@ -16,7 +16,7 @@ from libs.archive_storage import ( BUCKET_NAME = "archive-bucket" -def _configure_storage(monkeypatch, **overrides): +def _configure_storage(monkeypatch: pytest.MonkeyPatch, **overrides): defaults = { "ARCHIVE_STORAGE_ENABLED": True, "ARCHIVE_STORAGE_ENDPOINT": "https://storage.example.com", diff --git a/api/tests/unit_tests/oss/__mock/aliyun_oss.py b/api/tests/unit_tests/oss/__mock/aliyun_oss.py index 27e1c0ad850..59434e072fa 100644 --- a/api/tests/unit_tests/oss/__mock/aliyun_oss.py +++ b/api/tests/unit_tests/oss/__mock/aliyun_oss.py @@ -3,7 +3,6 @@ import posixpath from unittest.mock import MagicMock import pytest -from _pytest.monkeypatch import MonkeyPatch from oss2 import Bucket from oss2.models import GetObjectResult, PutObjectResult @@ -85,7 +84,7 @@ MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true" @pytest.fixture -def setup_aliyun_oss_mock(monkeypatch: MonkeyPatch): +def setup_aliyun_oss_mock(monkeypatch: pytest.MonkeyPatch): if MOCK: monkeypatch.setattr(Bucket, "__init__", MockAliyunOssClass.__init__) monkeypatch.setattr(Bucket, "put_object", MockAliyunOssClass.put_object) diff --git a/api/tests/unit_tests/oss/__mock/baidu_obs.py b/api/tests/unit_tests/oss/__mock/baidu_obs.py index d70a7c2eaab..49c0830cdfb 100644 --- a/api/tests/unit_tests/oss/__mock/baidu_obs.py +++ b/api/tests/unit_tests/oss/__mock/baidu_obs.py @@ -5,7 +5,6 @@ from io import BytesIO from types import SimpleNamespace import pytest -from _pytest.monkeypatch import MonkeyPatch from baidubce.services.bos.bos_client import BosClient from tests.unit_tests.oss.__mock.base import ( @@ -55,7 +54,7 @@ MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true" @pytest.fixture -def setup_baidu_obs_mock(monkeypatch: MonkeyPatch): +def setup_baidu_obs_mock(monkeypatch: pytest.MonkeyPatch): if MOCK: monkeypatch.setattr(BosClient, "__init__", MockBaiduObsClass.__init__) monkeypatch.setattr(BosClient, "put_object", MockBaiduObsClass.put_object) diff --git a/api/tests/unit_tests/oss/__mock/local.py b/api/tests/unit_tests/oss/__mock/local.py index 95cc06958c6..ee01ab2a6ae 100644 --- a/api/tests/unit_tests/oss/__mock/local.py +++ b/api/tests/unit_tests/oss/__mock/local.py @@ -4,7 +4,6 @@ from pathlib import Path from unittest.mock import MagicMock, mock_open, patch import pytest -from _pytest.monkeypatch import MonkeyPatch from tests.unit_tests.oss.__mock.base import ( get_example_data, @@ -40,7 +39,7 @@ MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true" @pytest.fixture -def setup_local_fs_mock(monkeypatch: MonkeyPatch): +def setup_local_fs_mock(monkeypatch: pytest.MonkeyPatch): if MOCK: monkeypatch.setattr(Path, "write_bytes", MockLocalFSClass.write_bytes) monkeypatch.setattr(Path, "read_bytes", MockLocalFSClass.read_bytes) diff --git a/api/tests/unit_tests/oss/__mock/tencent_cos.py b/api/tests/unit_tests/oss/__mock/tencent_cos.py index 5189b68e871..65b5bf31329 100644 --- a/api/tests/unit_tests/oss/__mock/tencent_cos.py +++ b/api/tests/unit_tests/oss/__mock/tencent_cos.py @@ -2,7 +2,6 @@ import os from unittest.mock import MagicMock import pytest -from _pytest.monkeypatch import MonkeyPatch from qcloud_cos import CosS3Client from qcloud_cos.streambody import StreamBody @@ -67,7 +66,7 @@ MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true" @pytest.fixture -def setup_tencent_cos_mock(monkeypatch: MonkeyPatch): +def setup_tencent_cos_mock(monkeypatch: pytest.MonkeyPatch): if MOCK: monkeypatch.setattr(CosS3Client, "__init__", MockTencentCosClass.__init__) monkeypatch.setattr(CosS3Client, "put_object", MockTencentCosClass.put_object) diff --git a/api/tests/unit_tests/oss/__mock/volcengine_tos.py b/api/tests/unit_tests/oss/__mock/volcengine_tos.py index 649d93a2026..af35fdeb2d0 100644 --- a/api/tests/unit_tests/oss/__mock/volcengine_tos.py +++ b/api/tests/unit_tests/oss/__mock/volcengine_tos.py @@ -3,7 +3,6 @@ from collections import UserDict from unittest.mock import MagicMock import pytest -from _pytest.monkeypatch import MonkeyPatch from tos import TosClientV2 from tos.clientv2 import DeleteObjectOutput, GetObjectOutput, HeadObjectOutput, PutObjectOutput @@ -76,7 +75,7 @@ MOCK = os.getenv("MOCK_SWITCH", "false").lower() == "true" @pytest.fixture -def setup_volcengine_tos_mock(monkeypatch: MonkeyPatch): +def setup_volcengine_tos_mock(monkeypatch: pytest.MonkeyPatch): if MOCK: monkeypatch.setattr(TosClientV2, "__init__", MockVolcengineTosClass.__init__) monkeypatch.setattr(TosClientV2, "put_object", MockVolcengineTosClass.put_object) diff --git a/api/tests/unit_tests/services/agent/test_agent_services.py b/api/tests/unit_tests/services/agent/test_agent_services.py index 43fce6a5135..816dc39ed79 100644 --- a/api/tests/unit_tests/services/agent/test_agent_services.py +++ b/api/tests/unit_tests/services/agent/test_agent_services.py @@ -460,7 +460,7 @@ def test_composer_save_helpers_create_and_rebind_agents(monkeypatch: pytest.Monk assert new_version_binding.current_snapshot_id == "new-version-1" -def test_node_job_only_updates_inline_agent_soul(monkeypatch): +def test_node_job_only_updates_inline_agent_soul(monkeypatch: pytest.MonkeyPatch): fake_session = FakeSession() monkeypatch.setattr(composer_service.db, "session", fake_session) inline_agent = SimpleNamespace( @@ -532,7 +532,7 @@ def test_node_job_only_updates_inline_agent_soul(monkeypatch): assert inline_agent.updated_by == "account-1" -def test_node_job_only_rejects_inline_binding_pointing_to_roster_agent(monkeypatch): +def test_node_job_only_rejects_inline_binding_pointing_to_roster_agent(monkeypatch: pytest.MonkeyPatch): fake_session = FakeSession() monkeypatch.setattr(composer_service.db, "session", fake_session) current_snapshot = AgentConfigSnapshot( @@ -579,7 +579,7 @@ def test_node_job_only_rejects_inline_binding_pointing_to_roster_agent(monkeypat ) -def test_composer_create_agents_syncs_active_config_has_model(monkeypatch): +def test_composer_create_agents_syncs_active_config_has_model(monkeypatch: pytest.MonkeyPatch): fake_session = FakeSession() monkeypatch.setattr(composer_service.db, "session", fake_session) monkeypatch.setattr( @@ -2570,7 +2570,7 @@ def test_save_workflow_composer_guards_drive_refs_for_existing_agent_strategies( assert guarded["agent_id"] == "agent-1" -def test_save_workflow_composer_guards_drive_refs_for_inline_node_job_only(monkeypatch): +def test_save_workflow_composer_guards_drive_refs_for_inline_node_job_only(monkeypatch: pytest.MonkeyPatch): payload = ComposerSavePayload.model_validate( { "variant": "workflow", @@ -2627,7 +2627,7 @@ def test_save_workflow_composer_guards_drive_refs_for_inline_node_job_only(monke assert guarded == {"tenant_id": "t-1", "agent_id": "agent-1"} -def test_save_workflow_composer_skips_drive_refs_for_roster_node_job_only(monkeypatch): +def test_save_workflow_composer_skips_drive_refs_for_roster_node_job_only(monkeypatch: pytest.MonkeyPatch): payload = ComposerSavePayload.model_validate( { "variant": "workflow", @@ -2681,7 +2681,7 @@ def test_save_workflow_composer_skips_drive_refs_for_roster_node_job_only(monkey assert result == {"state": "ok", "validation": {"warnings": []}} -def test_remove_drive_refs_noop_when_skill_slug_unmatched(monkeypatch): +def test_remove_drive_refs_noop_when_skill_slug_unmatched(monkeypatch: pytest.MonkeyPatch): soul_dict = {"skills_files": {"skills": [{"name": "Other", "skill_md_key": "other/SKILL.md"}], "files": []}} _, captured, committed = _patch_remove_drive_refs_env(monkeypatch, soul_dict=soul_dict) assert ( diff --git a/api/tests/unit_tests/services/agent/test_skill_tool_inference_service.py b/api/tests/unit_tests/services/agent/test_skill_tool_inference_service.py index cd708127ae7..c0b6e6490f4 100644 --- a/api/tests/unit_tests/services/agent/test_skill_tool_inference_service.py +++ b/api/tests/unit_tests/services/agent/test_skill_tool_inference_service.py @@ -33,7 +33,7 @@ def _patch_soul_files(monkeypatch, files): monkeypatch.setattr(SkillToolInferenceService, "_manifest_files_from_soul", staticmethod(lambda **kwargs: files)) -def test_infer_returns_suggestions_with_inferred_from(monkeypatch): +def test_infer_returns_suggestions_with_inferred_from(monkeypatch: pytest.MonkeyPatch): service, drive = _service() _patch_soul_files(monkeypatch, ["SKILL.md", "scripts/transcribe.sh"]) raw = ( @@ -53,7 +53,7 @@ def test_infer_returns_suggestions_with_inferred_from(monkeypatch): drive.preview.assert_called_once_with(tenant_id="t-1", agent_id="a-1", key="audio-transcribe/SKILL.md") -def test_infer_threads_manifest_files_into_the_prompt(monkeypatch): +def test_infer_threads_manifest_files_into_the_prompt(monkeypatch: pytest.MonkeyPatch): service, _ = _service() _patch_soul_files(monkeypatch, ["scripts/run.sh"]) captured: dict[str, str] = {} @@ -69,7 +69,7 @@ def test_infer_threads_manifest_files_into_the_prompt(monkeypatch): assert "ffmpeg" in captured["prompt"] # SKILL.md body present -def test_infer_not_inferable_passes_reason_through(monkeypatch): +def test_infer_not_inferable_passes_reason_through(monkeypatch: pytest.MonkeyPatch): service, _ = _service() _patch_soul_files(monkeypatch, []) raw = '{"inferable": false, "cli_tools": [], "reason": "SKILL.md 未描述任何外部命令依赖"}' @@ -78,7 +78,7 @@ def test_infer_not_inferable_passes_reason_through(monkeypatch): assert result == {"inferable": False, "cli_tools": [], "reason": "SKILL.md 未描述任何外部命令依赖"} -def test_infer_retries_once_then_422(monkeypatch): +def test_infer_retries_once_then_422(monkeypatch: pytest.MonkeyPatch): service, _ = _service() _patch_soul_files(monkeypatch, []) calls: list[int] = [] @@ -96,7 +96,7 @@ def test_infer_retries_once_then_422(monkeypatch): assert exc_info.value.status_code == 422 -def test_infer_repairs_slightly_malformed_json(monkeypatch): +def test_infer_repairs_slightly_malformed_json(monkeypatch: pytest.MonkeyPatch): service, _ = _service() _patch_soul_files(monkeypatch, []) raw = 'Here you go: {"inferable": true, "cli_tools": [], "reason": null,}' @@ -126,7 +126,7 @@ def test_binary_skill_md_maps_to_404(): # ── real-path coverage: _invoke / _manifest_files_from_soul / passthrough ──── -def test_invoke_maps_missing_default_model_to_400(monkeypatch): +def test_invoke_maps_missing_default_model_to_400(monkeypatch: pytest.MonkeyPatch): import services.agent.skill_tool_inference_service as module from core.errors.error import ProviderTokenNotInitError @@ -140,7 +140,7 @@ def test_invoke_maps_missing_default_model_to_400(monkeypatch): assert exc_info.value.status_code == 400 -def test_invoke_maps_model_failure_to_422_and_success_returns_text(monkeypatch): +def test_invoke_maps_model_failure_to_422_and_success_returns_text(monkeypatch: pytest.MonkeyPatch): import services.agent.skill_tool_inference_service as module fake_manager = MagicMock() @@ -173,7 +173,7 @@ def test_load_skill_md_passes_through_non_missing_drive_errors(): assert exc_info.value.code == "agent_not_found" -def _patch_inference_db(monkeypatch, *, agent, snapshot): +def _patch_inference_db(monkeypatch: pytest.MonkeyPatch, *, agent, snapshot): from types import SimpleNamespace import services.agent.skill_tool_inference_service as module @@ -182,7 +182,7 @@ def _patch_inference_db(monkeypatch, *, agent, snapshot): monkeypatch.setattr(module.db, "session", SimpleNamespace(scalar=lambda stmt: next(results))) -def test_manifest_files_from_soul_reads_active_snapshot(monkeypatch): +def test_manifest_files_from_soul_reads_active_snapshot(monkeypatch: pytest.MonkeyPatch): from types import SimpleNamespace soul_dict = { @@ -203,7 +203,7 @@ def test_manifest_files_from_soul_reads_active_snapshot(monkeypatch): assert files == ["scripts/a.sh"] -def test_manifest_files_from_soul_degrades_when_agent_or_snapshot_missing(monkeypatch): +def test_manifest_files_from_soul_degrades_when_agent_or_snapshot_missing(monkeypatch: pytest.MonkeyPatch): _patch_inference_db(monkeypatch, agent=None, snapshot=None) assert SkillToolInferenceService._manifest_files_from_soul(tenant_id="t", agent_id="a", slug="s") == [] @@ -213,7 +213,7 @@ def test_manifest_files_from_soul_degrades_when_agent_or_snapshot_missing(monkey assert SkillToolInferenceService._manifest_files_from_soul(tenant_id="t", agent_id="a", slug="s") == [] -def test_manifest_files_from_soul_empty_when_slug_not_in_soul(monkeypatch): +def test_manifest_files_from_soul_empty_when_slug_not_in_soul(monkeypatch: pytest.MonkeyPatch): from types import SimpleNamespace soul_dict = {"skills_files": {"skills": [{"name": "Other", "skill_md_key": "other/SKILL.md"}]}} diff --git a/api/tests/unit_tests/services/data_migration/test_import_service.py b/api/tests/unit_tests/services/data_migration/test_import_service.py index 3fcc6c7ef88..bd130cdd78b 100644 --- a/api/tests/unit_tests/services/data_migration/test_import_service.py +++ b/api/tests/unit_tests/services/data_migration/test_import_service.py @@ -385,7 +385,7 @@ def test_workflow_tool_import_publishes_referenced_app_before_create(monkeypatch (IdStrategy.GENERATE_NEW_ID, ""), ], ) -def test_workflow_tool_import_id_follows_id_strategy(monkeypatch, id_strategy, expected_import_id): +def test_workflow_tool_import_id_follows_id_strategy(monkeypatch: pytest.MonkeyPatch, id_strategy, expected_import_id): created_kwargs = [] target_provider = type("WorkflowToolProvider", (), {"id": "target-workflow-tool-id"})() account = type("Account", (), {"id": "account-1"})() diff --git a/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_built_in_retrieval.py b/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_built_in_retrieval.py index 1928958ea4a..441a914ee62 100644 --- a/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_built_in_retrieval.py +++ b/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_built_in_retrieval.py @@ -1,3 +1,5 @@ +from pytest_mock import MockerFixture + from services.rag_pipeline.pipeline_template.built_in.built_in_retrieval import BuiltInPipelineTemplateRetrieval from services.rag_pipeline.pipeline_template.pipeline_template_type import PipelineTemplateType @@ -8,7 +10,7 @@ def test_get_type() -> None: assert retrieval.get_type() == PipelineTemplateType.BUILTIN -def test_get_pipeline_templates(mocker) -> None: +def test_get_pipeline_templates(mocker: MockerFixture) -> None: mocker.patch.object( BuiltInPipelineTemplateRetrieval, "_get_builtin_data", @@ -26,7 +28,7 @@ def test_get_pipeline_templates(mocker) -> None: assert templates == {"pipeline_templates": [{"id": "tpl-1"}]} -def test_get_pipeline_template_detail(mocker) -> None: +def test_get_pipeline_template_detail(mocker: MockerFixture) -> None: mocker.patch.object( BuiltInPipelineTemplateRetrieval, "_get_builtin_data", @@ -43,7 +45,7 @@ def test_get_pipeline_template_detail(mocker) -> None: assert detail == {"id": "tpl-1", "name": "Template 1"} -def test_get_pipeline_templates_missing_language_returns_empty_dict(mocker) -> None: +def test_get_pipeline_templates_missing_language_returns_empty_dict(mocker: MockerFixture) -> None: mocker.patch.object( BuiltInPipelineTemplateRetrieval, "_get_builtin_data", @@ -56,7 +58,7 @@ def test_get_pipeline_templates_missing_language_returns_empty_dict(mocker) -> N assert result == {} -def test_get_pipeline_template_detail_returns_none_for_unknown_id(mocker) -> None: +def test_get_pipeline_template_detail_returns_none_for_unknown_id(mocker: MockerFixture) -> None: mocker.patch.object( BuiltInPipelineTemplateRetrieval, "_get_builtin_data", @@ -69,7 +71,7 @@ def test_get_pipeline_template_detail_returns_none_for_unknown_id(mocker) -> Non assert result is None -def test_get_builtin_data_reads_from_file_and_caches(mocker) -> None: +def test_get_builtin_data_reads_from_file_and_caches(mocker: MockerFixture) -> None: import json # Ensure no cached data @@ -98,7 +100,7 @@ def test_get_builtin_data_reads_from_file_and_caches(mocker) -> None: BuiltInPipelineTemplateRetrieval.builtin_data = None -def test_get_builtin_data_returns_cache_on_second_call(mocker) -> None: +def test_get_builtin_data_returns_cache_on_second_call(mocker: MockerFixture) -> None: cached_data = {"pipeline_templates": {"en-US": {}}} BuiltInPipelineTemplateRetrieval.builtin_data = cached_data diff --git a/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_customized_retrieval.py b/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_customized_retrieval.py index 106b959a78b..168ec8fce3c 100644 --- a/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_customized_retrieval.py +++ b/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_customized_retrieval.py @@ -1,10 +1,12 @@ from types import SimpleNamespace +from pytest_mock import MockerFixture + from services.rag_pipeline.pipeline_template.customized.customized_retrieval import CustomizedPipelineTemplateRetrieval from services.rag_pipeline.pipeline_template.pipeline_template_type import PipelineTemplateType -def test_get_pipeline_templates(mocker) -> None: +def test_get_pipeline_templates(mocker: MockerFixture) -> None: customized_template = SimpleNamespace( id="tpl-1", name="Custom Template", @@ -40,7 +42,7 @@ def test_get_pipeline_templates(mocker) -> None: } -def test_get_pipeline_template_detail_returns_detail(mocker) -> None: +def test_get_pipeline_template_detail_returns_detail(mocker: MockerFixture) -> None: session_mock = mocker.Mock() session_mock.get.return_value = SimpleNamespace( id="tpl-1", @@ -71,7 +73,7 @@ def test_get_pipeline_template_detail_returns_detail(mocker) -> None: } -def test_get_pipeline_template_detail_returns_none_when_not_found(mocker) -> None: +def test_get_pipeline_template_detail_returns_none_when_not_found(mocker: MockerFixture) -> None: session_mock = mocker.Mock() session_mock.get.return_value = None mocker.patch( diff --git a/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_database_retrieval.py b/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_database_retrieval.py index 0175f66808b..41f60e45755 100644 --- a/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_database_retrieval.py +++ b/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_database_retrieval.py @@ -1,10 +1,12 @@ from types import SimpleNamespace +from pytest_mock import MockerFixture + from services.rag_pipeline.pipeline_template.database.database_retrieval import DatabasePipelineTemplateRetrieval from services.rag_pipeline.pipeline_template.pipeline_template_type import PipelineTemplateType -def test_get_pipeline_templates(mocker) -> None: +def test_get_pipeline_templates(mocker: MockerFixture) -> None: built_in_template = SimpleNamespace( id="tpl-1", name="Template 1", @@ -44,7 +46,7 @@ def test_get_pipeline_templates(mocker) -> None: } -def test_get_pipeline_template_detail_returns_detail(mocker) -> None: +def test_get_pipeline_template_detail_returns_detail(mocker: MockerFixture) -> None: session_mock = mocker.Mock() session_mock.get.return_value = SimpleNamespace( id="tpl-1", @@ -73,7 +75,7 @@ def test_get_pipeline_template_detail_returns_detail(mocker) -> None: } -def test_get_pipeline_template_detail_returns_none_when_not_found(mocker) -> None: +def test_get_pipeline_template_detail_returns_none_when_not_found(mocker: MockerFixture) -> None: session_mock = mocker.Mock() session_mock.get.return_value = None mocker.patch( diff --git a/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_remote_retrieval.py b/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_remote_retrieval.py index 10b5bc7cf67..5da6684926c 100644 --- a/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_remote_retrieval.py +++ b/api/tests/unit_tests/services/rag_pipeline/pipeline_template/test_remote_retrieval.py @@ -1,11 +1,12 @@ import pytest +from pytest_mock import MockerFixture from services.rag_pipeline.pipeline_template.database.database_retrieval import DatabasePipelineTemplateRetrieval from services.rag_pipeline.pipeline_template.pipeline_template_type import PipelineTemplateType from services.rag_pipeline.pipeline_template.remote.remote_retrieval import RemotePipelineTemplateRetrieval -def test_get_pipeline_templates_fallbacks_to_database_on_error(mocker) -> None: +def test_get_pipeline_templates_fallbacks_to_database_on_error(mocker: MockerFixture) -> None: fetch_mock = mocker.patch.object( RemotePipelineTemplateRetrieval, "fetch_pipeline_templates_from_dify_official", @@ -26,7 +27,7 @@ def test_get_pipeline_templates_fallbacks_to_database_on_error(mocker) -> None: fallback_mock.assert_called_once_with("en-US") -def test_get_pipeline_template_detail_fallbacks_to_database_on_error(mocker) -> None: +def test_get_pipeline_template_detail_fallbacks_to_database_on_error(mocker: MockerFixture) -> None: fetch_mock = mocker.patch.object( RemotePipelineTemplateRetrieval, "fetch_pipeline_template_detail_from_dify_official", @@ -46,7 +47,7 @@ def test_get_pipeline_template_detail_fallbacks_to_database_on_error(mocker) -> fallback_mock.assert_called_once_with("tpl-1") -def test_fetch_pipeline_templates_from_dify_official(mocker) -> None: +def test_fetch_pipeline_templates_from_dify_official(mocker: MockerFixture) -> None: mocker.patch( "services.rag_pipeline.pipeline_template.remote.remote_retrieval" ".dify_config.HOSTED_FETCH_PIPELINE_TEMPLATES_REMOTE_DOMAIN", @@ -72,7 +73,7 @@ def test_fetch_pipeline_templates_from_dify_official(mocker) -> None: assert http_get_mock.call_count == 2 -def test_fetch_pipeline_template_detail_from_dify_official(mocker) -> None: +def test_fetch_pipeline_template_detail_from_dify_official(mocker: MockerFixture) -> None: mocker.patch( "services.rag_pipeline.pipeline_template.remote.remote_retrieval" ".dify_config.HOSTED_FETCH_PIPELINE_TEMPLATES_REMOTE_DOMAIN", diff --git a/api/tests/unit_tests/services/rag_pipeline/test_pipeline_generate_service.py b/api/tests/unit_tests/services/rag_pipeline/test_pipeline_generate_service.py index 82a5598b13d..178f4595359 100644 --- a/api/tests/unit_tests/services/rag_pipeline/test_pipeline_generate_service.py +++ b/api/tests/unit_tests/services/rag_pipeline/test_pipeline_generate_service.py @@ -2,6 +2,7 @@ from types import SimpleNamespace from typing import cast import pytest +from pytest_mock import MockerFixture from core.app.entities.app_invoke_entities import InvokeFrom from models.dataset import Pipeline @@ -9,7 +10,7 @@ from models.model import Account, App, EndUser from services.rag_pipeline.pipeline_generate_service import PipelineGenerateService -def test_get_max_active_requests_uses_smallest_non_zero_limit(mocker) -> None: +def test_get_max_active_requests_uses_smallest_non_zero_limit(mocker: MockerFixture) -> None: mocker.patch("services.rag_pipeline.pipeline_generate_service.dify_config.APP_DEFAULT_ACTIVE_REQUESTS", 5) mocker.patch("services.rag_pipeline.pipeline_generate_service.dify_config.APP_MAX_ACTIVE_REQUESTS", 3) @@ -20,7 +21,7 @@ def test_get_max_active_requests_uses_smallest_non_zero_limit(mocker) -> None: assert result == 3 -def test_get_max_active_requests_returns_zero_when_all_unlimited(mocker) -> None: +def test_get_max_active_requests_returns_zero_when_all_unlimited(mocker: MockerFixture) -> None: mocker.patch("services.rag_pipeline.pipeline_generate_service.dify_config.APP_DEFAULT_ACTIVE_REQUESTS", 0) mocker.patch("services.rag_pipeline.pipeline_generate_service.dify_config.APP_MAX_ACTIVE_REQUESTS", 0) @@ -39,7 +40,7 @@ def test_get_max_active_requests_returns_zero_when_all_unlimited(mocker) -> None (InvokeFrom.DEBUGGER, SimpleNamespace(id="wf-1"), None), ], ) -def test_get_workflow(mocker, invoke_from, workflow, expected_error) -> None: +def test_get_workflow(mocker: MockerFixture, invoke_from, workflow, expected_error) -> None: rag_pipeline_service_cls = mocker.patch("services.rag_pipeline.pipeline_generate_service.RagPipelineService") rag_pipeline_service = rag_pipeline_service_cls.return_value rag_pipeline_service.get_draft_workflow.return_value = workflow @@ -55,7 +56,7 @@ def test_get_workflow(mocker, invoke_from, workflow, expected_error) -> None: assert result == workflow -def test_generate_updates_document_status_and_returns_event_stream(mocker) -> None: +def test_generate_updates_document_status_and_returns_event_stream(mocker: MockerFixture) -> None: pipeline = cast(Pipeline, SimpleNamespace(id="pipeline-1")) user = cast(Account | EndUser, SimpleNamespace(id="user-1")) args = {"original_document_id": "doc-1", "query": "hello"} @@ -80,7 +81,7 @@ def test_generate_updates_document_status_and_returns_event_stream(mocker) -> No update_status_mock.assert_called_once_with("doc-1") -def test_update_document_status_updates_existing_document(mocker) -> None: +def test_update_document_status_updates_existing_document(mocker: MockerFixture) -> None: document = SimpleNamespace(indexing_status="completed") session_mock = mocker.Mock() @@ -99,7 +100,7 @@ def test_update_document_status_updates_existing_document(mocker) -> None: commit_mock.assert_called_once() -def test_update_document_status_skips_when_document_missing(mocker) -> None: +def test_update_document_status_skips_when_document_missing(mocker: MockerFixture) -> None: session_mock = mocker.Mock() session_mock.get.return_value = None add_mock = session_mock.add @@ -118,7 +119,7 @@ def test_update_document_status_skips_when_document_missing(mocker) -> None: # --- generate_single_iteration --- -def test_generate_single_iteration_delegates(mocker) -> None: +def test_generate_single_iteration_delegates(mocker: MockerFixture) -> None: mocker.patch.object(PipelineGenerateService, "_get_workflow", return_value=SimpleNamespace(id="wf-1")) generator_cls = mocker.patch("services.rag_pipeline.pipeline_generate_service.PipelineGenerator") @@ -138,7 +139,7 @@ def test_generate_single_iteration_delegates(mocker) -> None: # --- generate_single_loop --- -def test_generate_single_loop_delegates(mocker) -> None: +def test_generate_single_loop_delegates(mocker: MockerFixture) -> None: mocker.patch.object(PipelineGenerateService, "_get_workflow", return_value=SimpleNamespace(id="wf-1")) generator_cls = mocker.patch("services.rag_pipeline.pipeline_generate_service.PipelineGenerator") 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 dba5711795f..55ae8144d55 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 @@ -4,6 +4,7 @@ from unittest.mock import MagicMock, Mock import pytest import yaml +from pytest_mock import MockerFixture from sqlalchemy.orm import Session from core.workflow.nodes.knowledge_index import KNOWLEDGE_INDEX_NODE_TYPE @@ -55,7 +56,7 @@ def test_get_leaked_dependencies_returns_empty_list_for_empty_input() -> None: assert result == [] -def test_get_leaked_dependencies_delegates_to_analysis_service(mocker) -> None: +def test_get_leaked_dependencies_delegates_to_analysis_service(mocker: MockerFixture) -> None: expected = [Mock()] get_leaked_mock = mocker.patch( "services.rag_pipeline.rag_pipeline_dsl_service.DependenciesAnalysisService.get_leaked_dependencies", @@ -72,7 +73,7 @@ def test_get_leaked_dependencies_delegates_to_analysis_service(mocker) -> None: # --- check_dependencies --- -def test_check_dependencies_returns_empty_when_no_redis_data(mocker) -> None: +def test_check_dependencies_returns_empty_when_no_redis_data(mocker: MockerFixture) -> None: mocker.patch( "services.rag_pipeline.rag_pipeline_dsl_service.redis_client.get", return_value=None, @@ -85,7 +86,7 @@ def test_check_dependencies_returns_empty_when_no_redis_data(mocker) -> None: assert result.leaked_dependencies == [] -def test_check_dependencies_returns_leaked_deps_from_redis(mocker) -> None: +def test_check_dependencies_returns_leaked_deps_from_redis(mocker: MockerFixture) -> None: from core.plugin.entities.plugin import PluginDependency from services.rag_pipeline.rag_pipeline_dsl_service import CheckDependenciesPendingData @@ -117,7 +118,7 @@ def test_check_dependencies_returns_leaked_deps_from_redis(mocker) -> None: # --- _extract_dependencies_from_model_config --- -def test_extract_dependencies_from_model_config_extracts_model(mocker) -> None: +def test_extract_dependencies_from_model_config_extracts_model(mocker: MockerFixture) -> None: analyze_mock = mocker.patch( "services.rag_pipeline.rag_pipeline_dsl_service.DependenciesAnalysisService.analyze_model_provider_dependency", return_value="langgenius/openai", @@ -130,7 +131,7 @@ def test_extract_dependencies_from_model_config_extracts_model(mocker) -> None: analyze_mock.assert_called_with("openai") -def test_extract_dependencies_from_model_config_extracts_tools(mocker) -> None: +def test_extract_dependencies_from_model_config_extracts_tools(mocker: MockerFixture) -> None: mocker.patch( "services.rag_pipeline.rag_pipeline_dsl_service.DependenciesAnalysisService.analyze_model_provider_dependency", return_value="x", @@ -159,7 +160,7 @@ def test_extract_dependencies_from_model_config_empty_config() -> None: # --- _extract_dependencies_from_workflow_graph --- -def test_extract_dependencies_from_workflow_graph_ignores_unknown_types(mocker) -> None: +def test_extract_dependencies_from_workflow_graph_ignores_unknown_types(mocker: MockerFixture) -> None: service = RagPipelineDslService(session=Mock()) graph = {"nodes": [{"data": {"type": "some-unknown-type"}}]} @@ -176,7 +177,7 @@ def test_extract_dependencies_from_workflow_graph_handles_empty_graph() -> None: assert result == [] -def test_extract_dependencies_from_workflow_graph_handles_malformed_node(mocker) -> None: +def test_extract_dependencies_from_workflow_graph_handles_malformed_node(mocker: MockerFixture) -> None: service = RagPipelineDslService(session=Mock()) # Node with TOOL type but invalid data should be caught by exception handler from graphon.enums import BuiltinNodeTypes @@ -205,7 +206,7 @@ def test_export_rag_pipeline_dsl_raises_when_dataset_missing() -> None: # --- import_rag_pipeline --- -def test_import_rag_pipeline_url_fetch_error(mocker) -> None: +def test_import_rag_pipeline_url_fetch_error(mocker: MockerFixture) -> None: mocker.patch( "services.rag_pipeline.rag_pipeline_dsl_service.remote_fetcher.make_request", side_effect=Exception("fetch failed"), @@ -221,7 +222,7 @@ def test_import_rag_pipeline_url_fetch_error(mocker) -> None: assert "fetch failed" in result.error -def test_import_rag_pipeline_yaml_content_success(mocker) -> None: +def test_import_rag_pipeline_yaml_content_success(mocker: MockerFixture) -> None: yaml_content = """ version: 0.1.0 kind: rag_pipeline @@ -269,7 +270,7 @@ workflow: session.flush.assert_called() -def test_import_rag_pipeline_flushes_new_collection_binding_without_commit(mocker) -> None: +def test_import_rag_pipeline_flushes_new_collection_binding_without_commit(mocker: MockerFixture) -> None: yaml_content = """ version: 0.1.0 kind: rag_pipeline @@ -321,7 +322,7 @@ workflow: assert session.flush.call_count >= 2 -def test_import_rag_pipeline_pending_version(mocker) -> None: +def test_import_rag_pipeline_pending_version(mocker: MockerFixture) -> None: yaml_content = "version: 1.0.0\nkind: rag_pipeline\nrag_pipeline: {name: x}" mocker.patch("services.rag_pipeline.rag_pipeline_dsl_service.redis_client.setex") service = RagPipelineDslService(session=Mock()) @@ -336,7 +337,7 @@ def test_import_rag_pipeline_pending_version(mocker) -> None: # --- confirm_import --- -def test_confirm_import_success(mocker) -> None: +def test_confirm_import_success(mocker: MockerFixture) -> None: from services.rag_pipeline.rag_pipeline_dsl_service import RagPipelinePendingData yaml_content = """ @@ -398,7 +399,7 @@ workflow: assert result.dataset_id == "d1" -def test_confirm_import_flushes_new_collection_binding_without_commit(mocker) -> None: +def test_confirm_import_flushes_new_collection_binding_without_commit(mocker: MockerFixture) -> None: from services.rag_pipeline.rag_pipeline_dsl_service import RagPipelinePendingData yaml_content = """ @@ -518,7 +519,7 @@ def test_extract_dependencies_from_workflow_graph_types(mocker, node_type) -> No # --- _create_or_update_pipeline --- -def test_create_or_update_pipeline_create_new(mocker) -> None: +def test_create_or_update_pipeline_create_new(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) account = Mock(current_tenant_id="t1", id="u1") @@ -549,7 +550,7 @@ def test_create_or_update_pipeline_create_new(mocker) -> None: # --- export_rag_pipeline_dsl comprehensive --- -def test_export_rag_pipeline_dsl_with_workflow(mocker) -> None: +def test_export_rag_pipeline_dsl_with_workflow(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) pipeline = Mock() @@ -591,7 +592,7 @@ def test_export_rag_pipeline_dsl_with_workflow(mocker) -> None: # --- _extract_dependencies_from_workflow_graph more types --- -def test_extract_dependencies_from_workflow_graph_datasource(mocker) -> None: +def test_extract_dependencies_from_workflow_graph_datasource(mocker: MockerFixture) -> None: mocker.patch( "services.rag_pipeline.rag_pipeline_dsl_service.DatasourceNodeData.model_validate", return_value=Mock(provider_type="online", plugin_id="ds1"), @@ -642,7 +643,7 @@ def test_import_rag_pipeline_yaml_content_requires_mapping() -> None: assert "content must be a mapping" in result.error -def test_confirm_import_returns_failed_when_pending_data_is_invalid_type(mocker) -> None: +def test_confirm_import_returns_failed_when_pending_data_is_invalid_type(mocker: MockerFixture) -> None: mocker.patch("services.rag_pipeline.rag_pipeline_dsl_service.redis_client.get", return_value=object()) service = RagPipelineDslService(session=Mock()) account = Mock(current_tenant_id="t1") @@ -653,7 +654,7 @@ def test_confirm_import_returns_failed_when_pending_data_is_invalid_type(mocker) assert "Invalid import information" in result.error -def test_append_workflow_export_data_filters_credentials(mocker) -> None: +def test_append_workflow_export_data_filters_credentials(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) workflow = Mock() @@ -691,7 +692,7 @@ def test_append_workflow_export_data_filters_credentials(mocker) -> None: assert "credential_id" not in nodes[1]["data"]["agent_parameters"]["tools"]["value"][0] -def test_create_rag_pipeline_dataset_raises_when_name_conflicts(mocker) -> None: +def test_create_rag_pipeline_dataset_raises_when_name_conflicts(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) session.scalar.return_value = Mock() @@ -707,7 +708,7 @@ def test_create_rag_pipeline_dataset_raises_when_name_conflicts(mocker) -> None: service.create_rag_pipeline_dataset("tenant-1", create_entity) -def test_create_rag_pipeline_dataset_generates_name_when_missing(mocker) -> None: +def test_create_rag_pipeline_dataset_generates_name_when_missing(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) session.scalar.return_value = None @@ -741,7 +742,7 @@ def test_create_rag_pipeline_dataset_generates_name_when_missing(mocker) -> None assert result["status"] == ImportStatus.COMPLETED -def test_append_workflow_export_data_encrypts_knowledge_retrieval_dataset_ids(mocker) -> None: +def test_append_workflow_export_data_encrypts_knowledge_retrieval_dataset_ids(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) workflow = Mock() @@ -773,7 +774,7 @@ def test_append_workflow_export_data_encrypts_knowledge_retrieval_dataset_ids(mo assert ids == ["enc-d1", "enc-d2"] -def test_confirm_import_updates_existing_dataset(mocker) -> None: +def test_confirm_import_updates_existing_dataset(mocker: MockerFixture) -> None: from services.rag_pipeline.rag_pipeline_dsl_service import RagPipelinePendingData yaml_content = ( @@ -812,7 +813,7 @@ def test_confirm_import_updates_existing_dataset(mocker) -> None: assert dataset.indexing_technique == "economy" -def test_import_rag_pipeline_yaml_url_handles_empty_content_after_github_rewrite(mocker) -> None: +def test_import_rag_pipeline_yaml_url_handles_empty_content_after_github_rewrite(mocker: MockerFixture) -> None: response = Mock() response.raise_for_status.return_value = None response.content = b"" @@ -835,7 +836,7 @@ def test_import_rag_pipeline_yaml_url_handles_empty_content_after_github_rewrite assert "raw.githubusercontent.com" in called_url -def test_create_or_update_pipeline_decrypts_knowledge_retrieval_dataset_ids(mocker) -> None: +def test_create_or_update_pipeline_decrypts_knowledge_retrieval_dataset_ids(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) account = Mock(id="u1", current_tenant_id="t1") @@ -866,7 +867,7 @@ def test_create_or_update_pipeline_decrypts_knowledge_retrieval_dataset_ids(mock assert draft_workflow.graph is not None -def test_create_or_update_pipeline_creates_draft_when_missing(mocker) -> None: +def test_create_or_update_pipeline_creates_draft_when_missing(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) account = Mock(id="u1", current_tenant_id="t1") @@ -882,7 +883,7 @@ def test_create_or_update_pipeline_creates_draft_when_missing(mocker) -> None: assert pipeline.workflow_id == "wf-new" -def test_import_rag_pipeline_url_size_exceeds_limit(mocker) -> None: +def test_import_rag_pipeline_url_size_exceeds_limit(mocker: MockerFixture) -> None: response = Mock() response.raise_for_status.return_value = None response.content = b"x" * (10 * 1024 * 1024 + 1) @@ -953,7 +954,7 @@ def test_append_workflow_export_data_raises_when_draft_workflow_missing() -> Non service._append_workflow_export_data(export_data={}, pipeline=Mock(tenant_id="t1"), include_secret=False) -def test_append_workflow_export_data_keeps_secret_fields_when_include_secret_true(mocker) -> None: +def test_append_workflow_export_data_keeps_secret_fields_when_include_secret_true(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) workflow = Mock() @@ -992,7 +993,7 @@ def test_append_workflow_export_data_keeps_secret_fields_when_include_secret_tru assert tool_values[0]["credential_id"] == "agent-secret" -def test_extract_dependencies_from_workflow_graph_skips_local_file_datasource(mocker) -> None: +def test_extract_dependencies_from_workflow_graph_skips_local_file_datasource(mocker: MockerFixture) -> None: mocker.patch( "services.rag_pipeline.rag_pipeline_dsl_service.DatasourceNodeData.model_validate", return_value=Mock(provider_type="local_file", plugin_id="plugin-x"), @@ -1006,7 +1007,7 @@ def test_extract_dependencies_from_workflow_graph_skips_local_file_datasource(mo assert result == [] -def test_extract_dependencies_from_workflow_graph_knowledge_index_reranking(mocker) -> None: +def test_extract_dependencies_from_workflow_graph_knowledge_index_reranking(mocker: MockerFixture) -> None: analyze = mocker.patch( "services.rag_pipeline.rag_pipeline_dsl_service.DependenciesAnalysisService.analyze_model_provider_dependency", side_effect=lambda provider: f"dep:{provider}", @@ -1031,7 +1032,7 @@ def test_extract_dependencies_from_workflow_graph_knowledge_index_reranking(mock assert analyze.call_count == 2 -def test_extract_dependencies_from_workflow_graph_multiple_retrieval_weighted_score(mocker) -> None: +def test_extract_dependencies_from_workflow_graph_multiple_retrieval_weighted_score(mocker: MockerFixture) -> None: mocker.patch( "services.rag_pipeline.rag_pipeline_dsl_service.DependenciesAnalysisService.analyze_model_provider_dependency", return_value="dep:weighted", @@ -1053,7 +1054,7 @@ def test_extract_dependencies_from_workflow_graph_multiple_retrieval_weighted_sc assert result == ["dep:weighted"] -def test_extract_dependencies_from_workflow_graph_multiple_retrieval_reranking_model(mocker) -> None: +def test_extract_dependencies_from_workflow_graph_multiple_retrieval_reranking_model(mocker: MockerFixture) -> None: mocker.patch( "services.rag_pipeline.rag_pipeline_dsl_service.DependenciesAnalysisService.analyze_model_provider_dependency", return_value="dep:rerank", @@ -1075,7 +1076,7 @@ def test_extract_dependencies_from_workflow_graph_multiple_retrieval_reranking_m assert result == ["dep:rerank"] -def test_extract_dependencies_from_model_config_includes_dataset_reranking_and_tools(mocker) -> None: +def test_extract_dependencies_from_model_config_includes_dataset_reranking_and_tools(mocker: MockerFixture) -> None: model_analyze = mocker.patch( "services.rag_pipeline.rag_pipeline_dsl_service.DependenciesAnalysisService.analyze_model_provider_dependency", side_effect=["dep:model", "dep:rerank"], @@ -1107,7 +1108,7 @@ def test_extract_dependencies_from_model_config_includes_dataset_reranking_and_t tool_analyze.assert_called_once_with("google") -def test_check_version_compatibility_hits_major_older_branch(mocker) -> None: +def test_check_version_compatibility_hits_major_older_branch(mocker: MockerFixture) -> None: mocker.patch("services.rag_pipeline.rag_pipeline_dsl_service.CURRENT_DSL_VERSION", "1.0.0") status = check_version_compatibility("0.9.0", rag_pipeline_dsl_service.CURRENT_DSL_VERSION) @@ -1115,7 +1116,7 @@ def test_check_version_compatibility_hits_major_older_branch(mocker) -> None: assert status == ImportStatus.PENDING -def test_import_rag_pipeline_sets_default_version_and_kind(mocker) -> None: +def test_import_rag_pipeline_sets_default_version_and_kind(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) account = Mock(current_tenant_id="t1") @@ -1147,7 +1148,7 @@ def test_import_rag_pipeline_sets_default_version_and_kind(mocker) -> None: assert result.imported_dsl_version == "0.1.0" -def test_import_rag_pipeline_creates_pending_for_dependencies(mocker) -> None: +def test_import_rag_pipeline_creates_pending_for_dependencies(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) account = Mock(current_tenant_id="t1") @@ -1169,7 +1170,7 @@ workflow: {graph: {nodes: []}} setex.assert_called_once() -def test_confirm_import_returns_failed_when_pending_pipeline_missing(mocker) -> None: +def test_confirm_import_returns_failed_when_pending_pipeline_missing(mocker: MockerFixture) -> None: from services.rag_pipeline.rag_pipeline_dsl_service import RagPipelinePendingData pending = RagPipelinePendingData(import_mode="yaml-content", yaml_content="version: 0.1.0", pipeline_id="p1") @@ -1186,7 +1187,7 @@ def test_confirm_import_returns_failed_when_pending_pipeline_missing(mocker) -> assert result.status == ImportStatus.FAILED -def test_append_workflow_export_data_skips_empty_node_data(mocker) -> None: +def test_append_workflow_export_data_skips_empty_node_data(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) workflow = Mock() @@ -1204,7 +1205,7 @@ def test_append_workflow_export_data_skips_empty_node_data(mocker) -> None: assert "workflow" in export_data -def test_extract_dependencies_from_workflow_graph_multiple_config_none(mocker) -> None: +def test_extract_dependencies_from_workflow_graph_multiple_config_none(mocker: MockerFixture) -> None: retrieval = Mock() retrieval.retrieval_mode = "multiple" retrieval.multiple_retrieval_config = None @@ -1221,7 +1222,7 @@ def test_extract_dependencies_from_workflow_graph_multiple_config_none(mocker) - assert result == [] -def test_extract_dependencies_from_workflow_graph_single_config_none(mocker) -> None: +def test_extract_dependencies_from_workflow_graph_single_config_none(mocker: MockerFixture) -> None: retrieval = Mock() retrieval.retrieval_mode = "single" retrieval.single_retrieval_config = None @@ -1246,7 +1247,7 @@ def test_create_or_update_pipeline_raises_when_workflow_missing() -> None: service._create_or_update_pipeline(pipeline=None, data={"rag_pipeline": {"name": "x"}}, account=account) -def test_import_rag_pipeline_with_pipeline_id_uses_existing_dataset(mocker) -> None: +def test_import_rag_pipeline_with_pipeline_id_uses_existing_dataset(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) existing_dataset = Mock(id="d1", chunk_structure="text_model") @@ -1282,7 +1283,7 @@ def test_import_rag_pipeline_with_pipeline_id_uses_existing_dataset(mocker) -> N assert result.dataset_id == "d1" -def test_import_rag_pipeline_raises_for_chunk_structure_mismatch_on_published(mocker) -> None: +def test_import_rag_pipeline_raises_for_chunk_structure_mismatch_on_published(mocker: MockerFixture) -> None: session = cast(MagicMock, Mock()) service = RagPipelineDslService(session=cast(Session, session)) existing_dataset = Mock(id="d1", chunk_structure="hierarchical_model") @@ -1318,7 +1319,7 @@ def test_import_rag_pipeline_raises_for_chunk_structure_mismatch_on_published(mo assert "Chunk structure is not compatible" in result.error -def test_import_rag_pipeline_fails_when_no_knowledge_index_node(mocker) -> None: +def test_import_rag_pipeline_fails_when_no_knowledge_index_node(mocker: MockerFixture) -> None: service = RagPipelineDslService(session=Mock()) pipeline = Mock(id="p1", name="P", description="D", is_published=False) mocker.patch.object(service, "_create_or_update_pipeline", return_value=pipeline) @@ -1340,7 +1341,7 @@ def test_import_rag_pipeline_fails_when_no_knowledge_index_node(mocker) -> None: assert "Knowledge Index node" in result.error -def test_confirm_import_fails_when_no_knowledge_index_node(mocker) -> None: +def test_confirm_import_fails_when_no_knowledge_index_node(mocker: MockerFixture) -> None: from services.rag_pipeline.rag_pipeline_dsl_service import RagPipelinePendingData yaml_content = ( @@ -1369,7 +1370,7 @@ def test_confirm_import_fails_when_no_knowledge_index_node(mocker) -> None: assert "Knowledge Index node" in result.error -def test_create_or_update_pipeline_saves_dependencies_to_redis(mocker) -> None: +def test_create_or_update_pipeline_saves_dependencies_to_redis(mocker: MockerFixture) -> None: from core.plugin.entities.plugin import PluginDependency session = cast(MagicMock, Mock()) @@ -1399,7 +1400,9 @@ def test_create_or_update_pipeline_saves_dependencies_to_redis(mocker) -> None: setex.assert_called_once() -def test_extract_dependencies_from_workflow_graph_knowledge_index_without_embedding_provider(mocker) -> None: +def test_extract_dependencies_from_workflow_graph_knowledge_index_without_embedding_provider( + mocker: MockerFixture, +) -> None: mocker.patch( "services.rag_pipeline.rag_pipeline_dsl_service.DependenciesAnalysisService.analyze_model_provider_dependency", return_value="dep", @@ -1421,7 +1424,7 @@ def test_extract_dependencies_from_workflow_graph_knowledge_index_without_embedd assert result == [] -def test_extract_dependencies_from_workflow_graph_multiple_reranking_without_model(mocker) -> None: +def test_extract_dependencies_from_workflow_graph_multiple_reranking_without_model(mocker: MockerFixture) -> None: retrieval = Mock() retrieval.retrieval_mode = "multiple" retrieval.multiple_retrieval_config.reranking_mode = "reranking_model" @@ -1439,7 +1442,7 @@ def test_extract_dependencies_from_workflow_graph_multiple_reranking_without_mod assert result == [] -def test_extract_dependencies_from_workflow_graph_multiple_weighted_without_weights(mocker) -> None: +def test_extract_dependencies_from_workflow_graph_multiple_weighted_without_weights(mocker: MockerFixture) -> None: retrieval = Mock() retrieval.retrieval_mode = "multiple" retrieval.multiple_retrieval_config.reranking_mode = "weighted_score" diff --git a/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_manage_service.py b/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_manage_service.py index bd75e699dc8..faf93f0ce8e 100644 --- a/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_manage_service.py +++ b/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_manage_service.py @@ -1,9 +1,11 @@ from types import SimpleNamespace +from pytest_mock import MockerFixture + from services.rag_pipeline.rag_pipeline_manage_service import RagPipelineManageService -def test_list_rag_pipeline_datasources_marks_authorized(mocker) -> None: +def test_list_rag_pipeline_datasources_marks_authorized(mocker: MockerFixture) -> None: datasource_1 = SimpleNamespace(provider="notion", plugin_id="plugin-1", is_authorized=False) datasource_2 = SimpleNamespace(provider="jina", plugin_id="plugin-2", is_authorized=False) diff --git a/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_service.py b/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_service.py index b255595047d..669794ac6d4 100644 --- a/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_service.py +++ b/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_service.py @@ -4,6 +4,7 @@ from datetime import datetime from types import SimpleNamespace import pytest +from pytest_mock import MockerFixture from sqlalchemy.orm import sessionmaker from models import Account, Tenant @@ -113,7 +114,7 @@ def _make_recommended_plugin(plugin_id: str) -> PipelineRecommendedPlugin: return PipelineRecommendedPlugin(plugin_id=plugin_id, provider_name=plugin_id, type="tool", position=0, active=True) -def test_get_pipeline_templates_fallbacks_to_builtin_for_non_english_empty_result(mocker) -> None: +def test_get_pipeline_templates_fallbacks_to_builtin_for_non_english_empty_result(mocker: MockerFixture) -> None: mocker.patch("services.rag_pipeline.rag_pipeline.dify_config.HOSTED_FETCH_PIPELINE_TEMPLATES_MODE", "remote") remote_retrieval = mocker.Mock() @@ -132,7 +133,7 @@ def test_get_pipeline_templates_fallbacks_to_builtin_for_non_english_empty_resul builtin_retrieval.fetch_pipeline_templates_from_builtin.assert_called_once_with("en-US") -def test_get_pipeline_templates_customized_mode_uses_customized_factory(mocker) -> None: +def test_get_pipeline_templates_customized_mode_uses_customized_factory(mocker: MockerFixture) -> None: retrieval = mocker.Mock() retrieval.get_pipeline_templates.return_value = {"pipeline_templates": [{"id": "custom-1"}]} @@ -146,7 +147,7 @@ def test_get_pipeline_templates_customized_mode_uses_customized_factory(mocker) @pytest.mark.parametrize("template_type", ["built-in", "customized"]) -def test_get_pipeline_template_detail_uses_expected_mode(mocker, template_type: str) -> None: +def test_get_pipeline_template_detail_uses_expected_mode(mocker: MockerFixture, template_type: str) -> None: mocker.patch("services.rag_pipeline.rag_pipeline.dify_config.HOSTED_FETCH_PIPELINE_TEMPLATES_MODE", "remote") retrieval = mocker.Mock() retrieval.get_pipeline_template_detail.return_value = {"id": "tpl-1"} @@ -161,7 +162,9 @@ def test_get_pipeline_template_detail_uses_expected_mode(mocker, template_type: factory_mock.get_pipeline_template_factory.assert_called_with(expected_mode) -def test_get_published_workflow_returns_none_when_pipeline_has_no_workflow_id(rag_pipeline_service) -> None: +def test_get_published_workflow_returns_none_when_pipeline_has_no_workflow_id( + rag_pipeline_service: RagPipelineService, +) -> None: pipeline = _make_pipeline(workflow_id=None) result = rag_pipeline_service.get_published_workflow(pipeline) @@ -169,7 +172,9 @@ def test_get_published_workflow_returns_none_when_pipeline_has_no_workflow_id(ra assert result is None -def test_get_all_published_workflow_returns_empty_for_unpublished_pipeline(rag_pipeline_service) -> None: +def test_get_all_published_workflow_returns_empty_for_unpublished_pipeline( + rag_pipeline_service: RagPipelineService, +) -> None: pipeline = _make_pipeline(workflow_id=None) session = SimpleNamespace() @@ -186,7 +191,7 @@ def test_get_all_published_workflow_returns_empty_for_unpublished_pipeline(rag_p assert has_more is False -def test_get_all_published_workflow_applies_limit_and_has_more(rag_pipeline_service) -> None: +def test_get_all_published_workflow_applies_limit_and_has_more(rag_pipeline_service: RagPipelineService) -> None: scalars_result = SimpleNamespace(all=lambda: ["wf1", "wf2", "wf3"]) session = SimpleNamespace(scalars=lambda stmt: scalars_result) pipeline = _make_pipeline(pipeline_id="pipeline-1", workflow_id="wf-live") @@ -207,7 +212,9 @@ def test_get_all_published_workflow_applies_limit_and_has_more(rag_pipeline_serv # --- sync_draft_workflow --- -def test_sync_draft_workflow_creates_new_when_none_exists(mocker, rag_pipeline_service) -> None: +def test_sync_draft_workflow_creates_new_when_none_exists( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: mocker.patch.object(rag_pipeline_service, "get_draft_workflow", return_value=None) class FakeWorkflow: @@ -238,7 +245,9 @@ def test_sync_draft_workflow_creates_new_when_none_exists(mocker, rag_pipeline_s assert pipeline.workflow_id == "wf-new" -def test_sync_draft_workflow_raises_on_hash_mismatch(mocker, rag_pipeline_service) -> None: +def test_sync_draft_workflow_raises_on_hash_mismatch( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from services.errors.app import WorkflowHashNotEqualError existing_wf = _make_workflow(graph={"nodes": [{"id": "old"}]}) @@ -259,7 +268,7 @@ def test_sync_draft_workflow_raises_on_hash_mismatch(mocker, rag_pipeline_servic ) -def test_sync_draft_workflow_updates_existing(mocker, rag_pipeline_service) -> None: +def test_sync_draft_workflow_updates_existing(mocker: MockerFixture, rag_pipeline_service: RagPipelineService) -> None: existing_wf = SimpleNamespace( unique_hash="hash-1", graph=None, @@ -293,7 +302,9 @@ def test_sync_draft_workflow_updates_existing(mocker, rag_pipeline_service) -> N # --- get_default_block_config --- -def test_get_default_block_config_returns_config_for_valid_type(mocker, rag_pipeline_service) -> None: +def test_get_default_block_config_returns_config_for_valid_type( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: fake_node_class = mocker.Mock() fake_node_class.get_default_config.return_value = {"type": "start", "config": {}} @@ -311,14 +322,16 @@ def test_get_default_block_config_returns_config_for_valid_type(mocker, rag_pipe assert result == {"type": "start", "config": {}} -def test_get_default_block_config_returns_none_for_unmapped_type(rag_pipeline_service) -> None: +def test_get_default_block_config_returns_none_for_unmapped_type(rag_pipeline_service: RagPipelineService) -> None: assert rag_pipeline_service.get_default_block_config("nonexistent-type") is None # --- update_workflow --- -def test_update_workflow_updates_allowed_fields(mocker, rag_pipeline_service) -> None: +def test_update_workflow_updates_allowed_fields( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: workflow = SimpleNamespace( id="wf-1", marked_name="", marked_comment="", updated_by=None, updated_at=None, disallowed="original" ) @@ -339,7 +352,9 @@ def test_update_workflow_updates_allowed_fields(mocker, rag_pipeline_service) -> assert result.updated_by == "u1" -def test_update_workflow_returns_none_when_not_found(mocker, rag_pipeline_service) -> None: +def test_update_workflow_returns_none_when_not_found( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: session = mocker.Mock() session.scalar.return_value = None @@ -357,7 +372,9 @@ def test_update_workflow_returns_none_when_not_found(mocker, rag_pipeline_servic # --- get_rag_pipeline_paginate_workflow_runs --- -def test_get_rag_pipeline_paginate_workflow_runs_delegates(mocker, rag_pipeline_service) -> None: +def test_get_rag_pipeline_paginate_workflow_runs_delegates( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: expected = mocker.Mock() repo_mock = mocker.Mock() repo_mock.get_paginated_workflow_runs.return_value = expected @@ -379,7 +396,9 @@ def test_get_rag_pipeline_paginate_workflow_runs_delegates(mocker, rag_pipeline_ # --- get_rag_pipeline_workflow_run --- -def test_get_rag_pipeline_workflow_run_delegates(mocker, rag_pipeline_service) -> None: +def test_get_rag_pipeline_workflow_run_delegates( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: expected = mocker.Mock() repo_mock = mocker.Mock() repo_mock.get_workflow_run_by_id.return_value = expected @@ -395,14 +414,18 @@ def test_get_rag_pipeline_workflow_run_delegates(mocker, rag_pipeline_service) - # --- is_workflow_exist --- -def test_is_workflow_exist_returns_true_when_draft_exists(mocker, rag_pipeline_service) -> None: +def test_is_workflow_exist_returns_true_when_draft_exists( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: mocker.patch("services.rag_pipeline.rag_pipeline.db.session.scalar", return_value=1) pipeline = _make_pipeline() assert rag_pipeline_service.is_workflow_exist(pipeline) is True -def test_is_workflow_exist_returns_false_when_no_draft(mocker, rag_pipeline_service) -> None: +def test_is_workflow_exist_returns_false_when_no_draft( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: mocker.patch("services.rag_pipeline.rag_pipeline.db.session.scalar", return_value=0) pipeline = _make_pipeline() @@ -412,7 +435,7 @@ def test_is_workflow_exist_returns_false_when_no_draft(mocker, rag_pipeline_serv # --- publish_workflow --- -def test_publish_workflow_success(mocker, rag_pipeline_service) -> None: +def test_publish_workflow_success(mocker: MockerFixture, rag_pipeline_service: RagPipelineService) -> None: # Don't import Workflow from rag_pipeline to avoid confusion during patching # 1. Mock select to bypass SQLAlchemy validation @@ -483,7 +506,9 @@ def test_publish_workflow_success(mocker, rag_pipeline_service) -> None: # --- run_datasource_workflow_node --- -def test_run_datasource_workflow_node_website_crawl(mocker, rag_pipeline_service) -> None: +def test_run_datasource_workflow_node_website_crawl( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from core.datasource.entities.datasource_entities import DatasourceProviderType # 1. Setup workflow and node @@ -559,7 +584,9 @@ def test_run_datasource_workflow_node_website_crawl(mocker, rag_pipeline_service # --- run_datasource_node_preview --- -def test_run_datasource_node_preview_online_document(mocker, rag_pipeline_service) -> None: +def test_run_datasource_node_preview_online_document( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from core.datasource.entities.datasource_entities import DatasourceMessage, DatasourceProviderType # 1. Setup workflow and node @@ -632,7 +659,7 @@ def test_run_datasource_node_preview_online_document(mocker, rag_pipeline_servic # --- _handle_node_run_result --- -def test_handle_node_run_result_success(mocker, rag_pipeline_service) -> None: +def test_handle_node_run_result_success(mocker: MockerFixture, rag_pipeline_service: RagPipelineService) -> None: from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus from graphon.graph_events import NodeRunSucceededEvent from graphon.node_events.base import NodeRunResult @@ -676,7 +703,7 @@ def test_handle_node_run_result_success(mocker, rag_pipeline_service) -> None: # --- get_first_step_parameters / get_second_step_parameters --- -def test_get_first_step_parameters_success(mocker, rag_pipeline_service) -> None: +def test_get_first_step_parameters_success(mocker: MockerFixture, rag_pipeline_service: RagPipelineService) -> None: # 1. Setup mock workflow pipeline = mocker.Mock() workflow = mocker.Mock() @@ -694,7 +721,7 @@ def test_get_first_step_parameters_success(mocker, rag_pipeline_service) -> None assert result[0]["variable"] == "url" -def test_get_second_step_parameters_success(mocker, rag_pipeline_service) -> None: +def test_get_second_step_parameters_success(mocker: MockerFixture, rag_pipeline_service: RagPipelineService) -> None: # 1. Setup mock workflow pipeline = mocker.Mock() workflow = mocker.Mock() @@ -722,7 +749,9 @@ def test_get_second_step_parameters_success(mocker, rag_pipeline_service) -> Non # --- publish_customized_pipeline_template --- -def test_publish_customized_pipeline_template_success(mocker, rag_pipeline_service) -> None: +def test_publish_customized_pipeline_template_success( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: # 1. Setup mocks pipeline = _make_pipeline(workflow_id="wf-1", is_published=True) @@ -765,7 +794,7 @@ def test_publish_customized_pipeline_template_success(mocker, rag_pipeline_servi # --- get_datasource_plugins --- -def test_get_datasource_plugins_success(mocker, rag_pipeline_service) -> None: +def test_get_datasource_plugins_success(mocker: MockerFixture, rag_pipeline_service: RagPipelineService) -> None: # 1. Setup mocks dataset = _make_dataset() @@ -812,7 +841,7 @@ def test_get_datasource_plugins_success(mocker, rag_pipeline_service) -> None: # --- retry_error_document --- -def test_retry_error_document_success(mocker, rag_pipeline_service) -> None: +def test_retry_error_document_success(mocker: MockerFixture, rag_pipeline_service: RagPipelineService) -> None: from models.dataset import Document, DocumentPipelineExecutionLog, Pipeline # 1. Setup mocks @@ -850,7 +879,7 @@ def test_retry_error_document_success(mocker, rag_pipeline_service) -> None: # --- set_datasource_variables --- -def test_set_datasource_variables_success(mocker, rag_pipeline_service) -> None: +def test_set_datasource_variables_success(mocker: MockerFixture, rag_pipeline_service: RagPipelineService) -> None: from graphon.entities.workflow_node_execution import WorkflowNodeExecution from models.dataset import Pipeline @@ -909,7 +938,7 @@ def test_set_datasource_variables_success(mocker, rag_pipeline_service) -> None: # --- Utility Methods --- -def test_get_draft_workflow_success(mocker, rag_pipeline_service) -> None: +def test_get_draft_workflow_success(mocker: MockerFixture, rag_pipeline_service: RagPipelineService) -> None: # 1. Setup mocks pipeline = _make_pipeline() @@ -925,7 +954,7 @@ def test_get_draft_workflow_success(mocker, rag_pipeline_service) -> None: assert result == workflow -def test_get_published_workflow_success(mocker, rag_pipeline_service) -> None: +def test_get_published_workflow_success(mocker: MockerFixture, rag_pipeline_service: RagPipelineService) -> None: # 1. Setup mocks pipeline = _make_pipeline(workflow_id="wf-pub") @@ -941,7 +970,7 @@ def test_get_published_workflow_success(mocker, rag_pipeline_service) -> None: assert result == workflow -def test_get_default_block_configs_success(rag_pipeline_service) -> None: +def test_get_default_block_configs_success(rag_pipeline_service: RagPipelineService) -> None: # This calls static methods on node classes, should be safe with default mocks or as-is # unless they access db. result = rag_pipeline_service.get_default_block_configs() @@ -949,7 +978,7 @@ def test_get_default_block_configs_success(rag_pipeline_service) -> None: assert len(result) > 0 -def test_get_default_block_config_success(rag_pipeline_service) -> None: +def test_get_default_block_config_success(rag_pipeline_service: RagPipelineService) -> None: from graphon.enums import BuiltinNodeTypes result = rag_pipeline_service.get_default_block_config(BuiltinNodeTypes.LLM) @@ -957,7 +986,9 @@ def test_get_default_block_config_success(rag_pipeline_service) -> None: assert result["type"] == "llm" -def test_publish_workflow_raises_when_draft_workflow_missing(mocker, rag_pipeline_service) -> None: +def test_publish_workflow_raises_when_draft_workflow_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: session = mocker.Mock() session.scalar.return_value = None pipeline = _make_pipeline() @@ -967,7 +998,9 @@ def test_publish_workflow_raises_when_draft_workflow_missing(mocker, rag_pipelin rag_pipeline_service.publish_workflow(session=session, pipeline=pipeline, account=account) -def test_get_default_block_config_returns_none_when_mapped_type_missing(mocker, rag_pipeline_service) -> None: +def test_get_default_block_config_returns_none_when_mapped_type_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from graphon.enums import BuiltinNodeTypes mocker.patch("services.rag_pipeline.rag_pipeline.get_node_type_classes_mapping", return_value={}) @@ -975,7 +1008,9 @@ def test_get_default_block_config_returns_none_when_mapped_type_missing(mocker, assert rag_pipeline_service.get_default_block_config(BuiltinNodeTypes.START) is None -def test_get_default_block_config_injects_http_request_filter(mocker, rag_pipeline_service) -> None: +def test_get_default_block_config_injects_http_request_filter( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from graphon.enums import BuiltinNodeTypes fake_node_cls = mocker.Mock() @@ -992,7 +1027,9 @@ def test_get_default_block_config_injects_http_request_filter(mocker, rag_pipeli assert "http_request_config" in called_filters -def test_run_draft_workflow_node_raises_when_workflow_missing(mocker, rag_pipeline_service) -> None: +def test_run_draft_workflow_node_raises_when_workflow_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: pipeline = _make_pipeline() account = _make_account() mocker.patch.object(rag_pipeline_service, "get_draft_workflow", return_value=None) @@ -1001,7 +1038,9 @@ def test_run_draft_workflow_node_raises_when_workflow_missing(mocker, rag_pipeli rag_pipeline_service.run_draft_workflow_node(pipeline, "node-1", {}, account) -def test_run_draft_workflow_node_saves_execution_and_variables(mocker, rag_pipeline_service) -> None: +def test_run_draft_workflow_node_saves_execution_and_variables( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: mocker.patch("services.rag_pipeline.rag_pipeline.db", mocker.Mock(engine=mocker.Mock())) pipeline = _make_pipeline() account = _make_account() @@ -1035,7 +1074,9 @@ def test_run_draft_workflow_node_saves_execution_and_variables(mocker, rag_pipel saver.save.assert_called_once() -def test_run_datasource_workflow_node_returns_error_when_workflow_missing(mocker, rag_pipeline_service) -> None: +def test_run_datasource_workflow_node_returns_error_when_workflow_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: pipeline = SimpleNamespace(id="p1", tenant_id="t1") mocker.patch.object(rag_pipeline_service, "get_draft_workflow", return_value=None) @@ -1053,7 +1094,9 @@ def test_run_datasource_workflow_node_returns_error_when_workflow_missing(mocker assert events[0]["event"] == "datasource_error" -def test_run_datasource_workflow_node_online_document_success(mocker, rag_pipeline_service) -> None: +def test_run_datasource_workflow_node_online_document_success( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from core.datasource.entities.datasource_entities import DatasourceProviderType pipeline = SimpleNamespace(id="p1", tenant_id="t1") @@ -1099,7 +1142,9 @@ def test_run_datasource_workflow_node_online_document_success(mocker, rag_pipeli assert events[1]["event"] == "datasource_completed" -def test_run_datasource_workflow_node_online_drive_success(mocker, rag_pipeline_service) -> None: +def test_run_datasource_workflow_node_online_drive_success( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from core.datasource.entities.datasource_entities import DatasourceProviderType pipeline = SimpleNamespace(id="p1", tenant_id="t1") @@ -1145,7 +1190,9 @@ def test_run_datasource_workflow_node_online_drive_success(mocker, rag_pipeline_ assert events[1]["event"] == "datasource_completed" -def test_handle_node_run_result_default_value_strategy(mocker, rag_pipeline_service) -> None: +def test_handle_node_run_result_default_value_strategy( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from datetime import datetime from graphon.enums import BuiltinNodeTypes, ErrorStrategy, WorkflowNodeExecutionStatus @@ -1190,7 +1237,9 @@ def test_handle_node_run_result_default_value_strategy(mocker, rag_pipeline_serv assert result.outputs["fallback"] == "ok" -def test_get_first_step_parameters_raises_when_datasource_node_missing(mocker, rag_pipeline_service) -> None: +def test_get_first_step_parameters_raises_when_datasource_node_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: workflow = SimpleNamespace(graph_dict={"nodes": []}, rag_pipeline_variables=[{"variable": "url"}]) mocker.patch.object(rag_pipeline_service, "get_published_workflow", return_value=workflow) @@ -1198,7 +1247,9 @@ def test_get_first_step_parameters_raises_when_datasource_node_missing(mocker, r rag_pipeline_service.get_first_step_parameters(SimpleNamespace(), "missing-node") -def test_get_second_step_parameters_handles_string_and_list_variable_references(mocker, rag_pipeline_service) -> None: +def test_get_second_step_parameters_handles_string_and_list_variable_references( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: workflow = SimpleNamespace( rag_pipeline_variables=[ {"variable": "url", "belong_to_node_id": "node-1"}, @@ -1226,7 +1277,9 @@ def test_get_second_step_parameters_handles_string_and_list_variable_references( assert result == [{"variable": "keep", "belong_to_node_id": "node-1"}] -def test_get_rag_pipeline_workflow_run_node_executions_empty_when_run_missing(mocker, rag_pipeline_service) -> None: +def test_get_rag_pipeline_workflow_run_node_executions_empty_when_run_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: pipeline = _make_pipeline() mocker.patch.object(rag_pipeline_service, "get_rag_pipeline_workflow_run", return_value=None) @@ -1237,7 +1290,9 @@ def test_get_rag_pipeline_workflow_run_node_executions_empty_when_run_missing(mo assert result == [] -def test_get_rag_pipeline_workflow_run_node_executions_returns_sorted_executions(mocker, rag_pipeline_service) -> None: +def test_get_rag_pipeline_workflow_run_node_executions_returns_sorted_executions( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: mocker.patch("services.rag_pipeline.rag_pipeline.db", mocker.Mock(engine=mocker.Mock())) pipeline = _make_pipeline() mocker.patch.object(rag_pipeline_service, "get_rag_pipeline_workflow_run", return_value=SimpleNamespace(id="run-1")) @@ -1252,7 +1307,9 @@ def test_get_rag_pipeline_workflow_run_node_executions_returns_sorted_executions assert result == ["n1", "n2"] -def test_get_recommended_plugins_returns_empty_when_no_active_plugins(mocker, rag_pipeline_service) -> None: +def test_get_recommended_plugins_returns_empty_when_no_active_plugins( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: mock_db = mocker.patch("services.rag_pipeline.rag_pipeline.db") mock_db.session.scalars.return_value.all.return_value = [] @@ -1264,7 +1321,9 @@ def test_get_recommended_plugins_returns_empty_when_no_active_plugins(mocker, ra } -def test_get_recommended_plugins_returns_installed_and_uninstalled(mocker, rag_pipeline_service) -> None: +def test_get_recommended_plugins_returns_installed_and_uninstalled( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: plugin_a = _make_recommended_plugin("plugin-a") plugin_b = _make_recommended_plugin("plugin-b") mock_db = mocker.patch("services.rag_pipeline.rag_pipeline.db") @@ -1284,7 +1343,9 @@ def test_get_recommended_plugins_returns_installed_and_uninstalled(mocker, rag_p assert result["uninstalled_recommended_plugins"] == [{"plugin_id": "plugin-b", "name": "Plugin B"}] -def test_get_node_last_run_delegates_to_repository(mocker, rag_pipeline_service) -> None: +def test_get_node_last_run_delegates_to_repository( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: mocker.patch("services.rag_pipeline.rag_pipeline.db", mocker.Mock(engine=mocker.Mock())) repo = mocker.Mock() repo.get_node_last_execution.return_value = "node-exec" @@ -1300,7 +1361,9 @@ def test_get_node_last_run_delegates_to_repository(mocker, rag_pipeline_service) assert result == "node-exec" -def test_set_datasource_variables_raises_when_node_id_missing(mocker, rag_pipeline_service) -> None: +def test_set_datasource_variables_raises_when_node_id_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: pipeline = SimpleNamespace(id="p1", tenant_id="t1") workflow = mocker.Mock() mocker.patch.object(rag_pipeline_service, "get_draft_workflow", return_value=workflow) @@ -1309,7 +1372,9 @@ def test_set_datasource_variables_raises_when_node_id_missing(mocker, rag_pipeli rag_pipeline_service.set_datasource_variables(pipeline, {"start_node_id": ""}, SimpleNamespace(id="u1")) -def test_get_default_block_configs_skips_empty_configs(mocker, rag_pipeline_service) -> None: +def test_get_default_block_configs_skips_empty_configs( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from graphon.enums import BuiltinNodeTypes http_node = mocker.Mock() @@ -1333,7 +1398,9 @@ def test_get_default_block_configs_skips_empty_configs(mocker, rag_pipeline_serv empty_node.get_default_config.assert_called_once() -def test_run_datasource_workflow_node_returns_error_when_node_missing(mocker, rag_pipeline_service) -> None: +def test_run_datasource_workflow_node_returns_error_when_node_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: pipeline = SimpleNamespace(id="p1", tenant_id="t1") workflow = SimpleNamespace(graph_dict={"nodes": []}) mocker.patch.object(rag_pipeline_service, "get_published_workflow", return_value=workflow) @@ -1353,7 +1420,9 @@ def test_run_datasource_workflow_node_returns_error_when_node_missing(mocker, ra assert "Datasource node data not found" in events[0]["error"] -def test_run_datasource_workflow_node_online_document_exception(mocker, rag_pipeline_service) -> None: +def test_run_datasource_workflow_node_online_document_exception( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: pipeline = SimpleNamespace(id="p1", tenant_id="t1") workflow = SimpleNamespace( graph_dict={ @@ -1405,7 +1474,9 @@ def test_run_datasource_workflow_node_online_document_exception(mocker, rag_pipe assert "doc failed" in events[1]["error"] -def test_run_datasource_node_preview_raises_for_stream_non_string(mocker, rag_pipeline_service) -> None: +def test_run_datasource_node_preview_raises_for_stream_non_string( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from core.datasource.entities.datasource_entities import DatasourceMessage pipeline = SimpleNamespace(id="p1", tenant_id="t1") @@ -1453,7 +1524,9 @@ def test_run_datasource_node_preview_raises_for_stream_non_string(mocker, rag_pi ) -def test_get_first_step_parameters_returns_empty_when_no_rag_variables(mocker, rag_pipeline_service) -> None: +def test_get_first_step_parameters_returns_empty_when_no_rag_variables( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: workflow = SimpleNamespace( graph_dict={"nodes": [{"id": "node-1", "data": {"datasource_parameters": {"url": {"value": "literal"}}}}]}, rag_pipeline_variables=[], @@ -1465,7 +1538,9 @@ def test_get_first_step_parameters_returns_empty_when_no_rag_variables(mocker, r assert result == [] -def test_get_second_step_parameters_filters_first_step_variables(mocker, rag_pipeline_service) -> None: +def test_get_second_step_parameters_filters_first_step_variables( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: workflow = SimpleNamespace( graph_dict={ "nodes": [ @@ -1494,7 +1569,9 @@ def test_get_second_step_parameters_filters_first_step_variables(mocker, rag_pip assert result == [{"variable": "keep", "belong_to_node_id": "shared"}] -def test_retry_error_document_raises_when_execution_log_not_found(mocker, rag_pipeline_service) -> None: +def test_retry_error_document_raises_when_execution_log_not_found( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: mocker.patch("services.rag_pipeline.rag_pipeline.db.session.scalar", return_value=None) with pytest.raises(ValueError, match="Document pipeline execution log not found"): @@ -1503,7 +1580,9 @@ def test_retry_error_document_raises_when_execution_log_not_found(mocker, rag_pi ) -def test_get_datasource_plugins_raises_when_workflow_not_found(mocker, rag_pipeline_service) -> None: +def test_get_datasource_plugins_raises_when_workflow_not_found( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: dataset = SimpleNamespace(pipeline_id="p1") pipeline = SimpleNamespace(id="p1", tenant_id="t1") mocker.patch("services.rag_pipeline.rag_pipeline.db.session.scalar", side_effect=[dataset, pipeline]) @@ -1513,7 +1592,9 @@ def test_get_datasource_plugins_raises_when_workflow_not_found(mocker, rag_pipel rag_pipeline_service.get_datasource_plugins("t1", "d1", True) -def test_handle_node_run_result_raises_when_no_terminal_event(mocker, rag_pipeline_service) -> None: +def test_handle_node_run_result_raises_when_no_terminal_event( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: node_instance = SimpleNamespace( workflow_id="wf-1", node_type="start", @@ -1534,7 +1615,9 @@ def test_handle_node_run_result_raises_when_no_terminal_event(mocker, rag_pipeli ) -def test_handle_node_run_result_marks_document_error_for_published_invoke(mocker, rag_pipeline_service) -> None: +def test_handle_node_run_result_marks_document_error_for_published_invoke( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from core.app.entities.app_invoke_entities import InvokeFrom from graphon.enums import WorkflowNodeExecutionStatus from graphon.graph_events import NodeRunFailedEvent @@ -1595,7 +1678,9 @@ def test_handle_node_run_result_marks_document_error_for_published_invoke(mocker commit_mock.assert_called_once() -def test_run_datasource_node_preview_raises_for_unsupported_provider(mocker, rag_pipeline_service) -> None: +def test_run_datasource_node_preview_raises_for_unsupported_provider( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: pipeline = SimpleNamespace(id="p1", tenant_id="t1") workflow = SimpleNamespace( graph_dict={ @@ -1631,14 +1716,18 @@ def test_run_datasource_node_preview_raises_for_unsupported_provider(mocker, rag ) -def test_publish_customized_pipeline_template_raises_for_missing_pipeline(mocker, rag_pipeline_service) -> None: +def test_publish_customized_pipeline_template_raises_for_missing_pipeline( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: mocker.patch("services.rag_pipeline.rag_pipeline.db.session.get", return_value=None) with pytest.raises(ValueError, match="Pipeline not found"): rag_pipeline_service.publish_customized_pipeline_template("p1", {}, _make_account(), "t1") -def test_publish_customized_pipeline_template_raises_for_missing_workflow_id(mocker, rag_pipeline_service) -> None: +def test_publish_customized_pipeline_template_raises_for_missing_workflow_id( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: pipeline = _make_pipeline(workflow_id=None) mocker.patch("services.rag_pipeline.rag_pipeline.db.session.get", return_value=pipeline) @@ -1648,14 +1737,18 @@ def test_publish_customized_pipeline_template_raises_for_missing_workflow_id(moc ) -def test_get_pipeline_raises_when_dataset_missing(mocker, rag_pipeline_service) -> None: +def test_get_pipeline_raises_when_dataset_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: mocker.patch("services.rag_pipeline.rag_pipeline.db.session.scalar", return_value=None) with pytest.raises(ValueError, match="Dataset not found"): rag_pipeline_service.get_pipeline("t1", "d1") -def test_get_pipeline_raises_when_pipeline_missing(mocker, rag_pipeline_service) -> None: +def test_get_pipeline_raises_when_pipeline_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: dataset = SimpleNamespace(pipeline_id="p1") mocker.patch("services.rag_pipeline.rag_pipeline.db.session.scalar", side_effect=[dataset, None]) @@ -1663,7 +1756,7 @@ def test_get_pipeline_raises_when_pipeline_missing(mocker, rag_pipeline_service) rag_pipeline_service.get_pipeline("t1", "d1") -def test_init_uses_default_sessionmaker_when_none(mocker) -> None: +def test_init_uses_default_sessionmaker_when_none(mocker: MockerFixture) -> None: default_session_maker = mocker.Mock() mocker.patch("services.rag_pipeline.rag_pipeline.sessionmaker", return_value=default_session_maker) mocker.patch("services.rag_pipeline.rag_pipeline.db", SimpleNamespace(engine=mocker.Mock())) @@ -1680,7 +1773,7 @@ def test_init_uses_default_sessionmaker_when_none(mocker) -> None: create_run_repo.assert_called_once_with(default_session_maker) -def test_get_pipeline_templates_builtin_en_us_no_fallback(mocker) -> None: +def test_get_pipeline_templates_builtin_en_us_no_fallback(mocker: MockerFixture) -> None: mocker.patch("services.rag_pipeline.rag_pipeline.dify_config.HOSTED_FETCH_PIPELINE_TEMPLATES_MODE", "remote") retrieval = mocker.Mock() retrieval.get_pipeline_templates.return_value = {"pipeline_templates": []} @@ -1694,7 +1787,7 @@ def test_get_pipeline_templates_builtin_en_us_no_fallback(mocker) -> None: builtin.fetch_pipeline_templates_from_builtin.assert_not_called() -def test_update_customized_pipeline_template_commits_when_name_empty(mocker) -> None: +def test_update_customized_pipeline_template_commits_when_name_empty(mocker: MockerFixture) -> None: template = _make_customized_template() mocker.patch("services.rag_pipeline.rag_pipeline.db.session.scalar", return_value=template) commit = mocker.patch("services.rag_pipeline.rag_pipeline.db.session.commit") @@ -1706,7 +1799,7 @@ def test_update_customized_pipeline_template_commits_when_name_empty(mocker) -> commit.assert_called_once() -def test_get_all_published_workflow_without_filters_has_no_more(rag_pipeline_service) -> None: +def test_get_all_published_workflow_without_filters_has_no_more(rag_pipeline_service: RagPipelineService) -> None: session = SimpleNamespace(scalars=lambda stmt: SimpleNamespace(all=lambda: ["wf1"])) pipeline = _make_pipeline(workflow_id="wf-live") @@ -1723,7 +1816,9 @@ def test_get_all_published_workflow_without_filters_has_no_more(rag_pipeline_ser assert has_more is False -def test_publish_workflow_skips_dataset_update_for_non_knowledge_nodes(mocker, rag_pipeline_service) -> None: +def test_publish_workflow_skips_dataset_update_for_non_knowledge_nodes( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: draft = SimpleNamespace( type="workflow", graph={"nodes": [{"data": {"type": "start"}}]}, @@ -1747,7 +1842,9 @@ def test_publish_workflow_skips_dataset_update_for_non_knowledge_nodes(mocker, r assert result is published -def test_get_default_block_config_returns_none_when_default_empty(mocker, rag_pipeline_service) -> None: +def test_get_default_block_config_returns_none_when_default_empty( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from graphon.enums import BuiltinNodeTypes node_cls = mocker.Mock() @@ -1761,7 +1858,9 @@ def test_get_default_block_config_returns_none_when_default_empty(mocker, rag_pi assert rag_pipeline_service.get_default_block_config("start") is None -def test_run_datasource_workflow_node_handles_variable_parameter_types(mocker, rag_pipeline_service) -> None: +def test_run_datasource_workflow_node_handles_variable_parameter_types( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from core.datasource.entities.datasource_entities import DatasourceProviderType workflow = SimpleNamespace( @@ -1811,7 +1910,9 @@ def test_run_datasource_workflow_node_handles_variable_parameter_types(mocker, r assert events[0]["data"] == [] -def test_run_datasource_workflow_node_online_drive_branch(mocker, rag_pipeline_service) -> None: +def test_run_datasource_workflow_node_online_drive_branch( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from core.datasource.entities.datasource_entities import DatasourceProviderType workflow = SimpleNamespace( @@ -1857,7 +1958,9 @@ def test_run_datasource_workflow_node_online_drive_branch(mocker, rag_pipeline_s assert events[1]["data"] == {"items": [1]} -def test_run_datasource_node_preview_not_published_uses_draft(mocker, rag_pipeline_service) -> None: +def test_run_datasource_node_preview_not_published_uses_draft( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from core.datasource.entities.datasource_entities import DatasourceMessage workflow = SimpleNamespace( @@ -1903,7 +2006,9 @@ def test_run_datasource_node_preview_not_published_uses_draft(mocker, rag_pipeli get_draft.assert_called_once() -def test_run_free_workflow_node_delegates_to_handle_result(mocker, rag_pipeline_service) -> None: +def test_run_free_workflow_node_delegates_to_handle_result( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: expected = SimpleNamespace(id="exec-1") handle = mocker.patch.object(rag_pipeline_service, "_handle_node_run_result", return_value=expected) @@ -1919,7 +2024,9 @@ def test_run_free_workflow_node_delegates_to_handle_result(mocker, rag_pipeline_ handle.assert_called_once() -def test_publish_customized_pipeline_template_raises_when_workflow_missing(mocker, rag_pipeline_service) -> None: +def test_publish_customized_pipeline_template_raises_when_workflow_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: pipeline = _make_pipeline(workflow_id="wf-1") mocker.patch("services.rag_pipeline.rag_pipeline.db.session.get", side_effect=[pipeline, None]) @@ -1927,7 +2034,9 @@ def test_publish_customized_pipeline_template_raises_when_workflow_missing(mocke rag_pipeline_service.publish_customized_pipeline_template("p1", {}, _make_account(), "t1") -def test_publish_customized_pipeline_template_raises_when_dataset_missing(mocker, rag_pipeline_service) -> None: +def test_publish_customized_pipeline_template_raises_when_dataset_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: pipeline = _make_pipeline(workflow_id="wf-1") workflow = _make_workflow(workflow_id="wf-1") mock_db = mocker.patch("services.rag_pipeline.rag_pipeline.db") @@ -1940,7 +2049,9 @@ def test_publish_customized_pipeline_template_raises_when_dataset_missing(mocker rag_pipeline_service.publish_customized_pipeline_template("p1", {}, _make_account(), "t1") -def test_get_recommended_plugins_skips_manifest_when_missing(mocker, rag_pipeline_service) -> None: +def test_get_recommended_plugins_skips_manifest_when_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: plugin = _make_recommended_plugin("plugin-a") mock_db = mocker.patch("services.rag_pipeline.rag_pipeline.db") mock_db.session.scalars.return_value.all.return_value = [plugin] @@ -1953,7 +2064,9 @@ def test_get_recommended_plugins_skips_manifest_when_missing(mocker, rag_pipelin assert result["uninstalled_recommended_plugins"] == [] -def test_retry_error_document_raises_when_pipeline_missing(mocker, rag_pipeline_service) -> None: +def test_retry_error_document_raises_when_pipeline_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: exec_log = SimpleNamespace(pipeline_id="p1") mocker.patch("services.rag_pipeline.rag_pipeline.db.session.scalar", return_value=exec_log) mocker.patch("services.rag_pipeline.rag_pipeline.db.session.get", return_value=None) @@ -1964,7 +2077,9 @@ def test_retry_error_document_raises_when_pipeline_missing(mocker, rag_pipeline_ ) -def test_retry_error_document_raises_when_workflow_missing(mocker, rag_pipeline_service) -> None: +def test_retry_error_document_raises_when_workflow_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: exec_log = SimpleNamespace(pipeline_id="p1") pipeline = SimpleNamespace(id="p1") mocker.patch("services.rag_pipeline.rag_pipeline.db.session.scalar", return_value=exec_log) @@ -1977,7 +2092,9 @@ def test_retry_error_document_raises_when_workflow_missing(mocker, rag_pipeline_ ) -def test_get_datasource_plugins_returns_empty_for_non_datasource_nodes(mocker, rag_pipeline_service) -> None: +def test_get_datasource_plugins_returns_empty_for_non_datasource_nodes( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: dataset = _make_dataset() pipeline = _make_pipeline() workflow = SimpleNamespace( @@ -1989,7 +2106,9 @@ def test_get_datasource_plugins_returns_empty_for_non_datasource_nodes(mocker, r assert rag_pipeline_service.get_datasource_plugins("t1", "d1", True) == [] -def test_publish_workflow_raises_when_knowledge_index_dataset_missing(mocker, rag_pipeline_service) -> None: +def test_publish_workflow_raises_when_knowledge_index_dataset_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: draft = SimpleNamespace( type="workflow", graph={"nodes": [{"data": {"type": "knowledge-index"}}]}, @@ -2012,7 +2131,9 @@ def test_publish_workflow_raises_when_knowledge_index_dataset_missing(mocker, ra rag_pipeline_service.publish_workflow(session=session, pipeline=pipeline, account=SimpleNamespace(id="u1")) -def test_run_datasource_node_preview_raises_when_workflow_missing(mocker, rag_pipeline_service) -> None: +def test_run_datasource_node_preview_raises_when_workflow_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: mocker.patch.object(rag_pipeline_service, "get_published_workflow", return_value=None) with pytest.raises(RuntimeError, match="Workflow not initialized"): @@ -2026,7 +2147,9 @@ def test_run_datasource_node_preview_raises_when_workflow_missing(mocker, rag_pi ) -def test_run_datasource_node_preview_raises_when_node_missing(mocker, rag_pipeline_service) -> None: +def test_run_datasource_node_preview_raises_when_node_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: mocker.patch.object( rag_pipeline_service, "get_published_workflow", return_value=SimpleNamespace(graph_dict={"nodes": []}) ) @@ -2042,7 +2165,9 @@ def test_run_datasource_node_preview_raises_when_node_missing(mocker, rag_pipeli ) -def test_run_datasource_node_preview_keeps_existing_user_input(mocker, rag_pipeline_service) -> None: +def test_run_datasource_node_preview_keeps_existing_user_input( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: from core.datasource.entities.datasource_entities import DatasourceMessage workflow = SimpleNamespace( @@ -2088,7 +2213,9 @@ def test_run_datasource_node_preview_keeps_existing_user_input(mocker, rag_pipel assert result == {"ok": "1"} -def test_run_datasource_node_preview_ignores_non_variable_messages(mocker, rag_pipeline_service) -> None: +def test_run_datasource_node_preview_ignores_non_variable_messages( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: workflow = SimpleNamespace( graph_dict={ "nodes": [ @@ -2127,7 +2254,9 @@ def test_run_datasource_node_preview_ignores_non_variable_messages(mocker, rag_p assert result == {} -def test_set_datasource_variables_raises_when_workflow_missing(mocker, rag_pipeline_service) -> None: +def test_set_datasource_variables_raises_when_workflow_missing( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: mocker.patch.object(rag_pipeline_service, "get_draft_workflow", return_value=None) with pytest.raises(ValueError, match="Workflow not initialized"): @@ -2138,7 +2267,9 @@ def test_set_datasource_variables_raises_when_workflow_missing(mocker, rag_pipel ) -def test_get_datasource_plugins_handles_empty_datasource_data_and_non_published(mocker, rag_pipeline_service) -> None: +def test_get_datasource_plugins_handles_empty_datasource_data_and_non_published( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: dataset = _make_dataset() pipeline = _make_pipeline() workflow = SimpleNamespace( @@ -2156,7 +2287,9 @@ def test_get_datasource_plugins_handles_empty_datasource_data_and_non_published( assert len(result) == 1 -def test_get_datasource_plugins_extracts_user_inputs_and_credentials(mocker, rag_pipeline_service) -> None: +def test_get_datasource_plugins_extracts_user_inputs_and_credentials( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: dataset = _make_dataset() pipeline = _make_pipeline() workflow = SimpleNamespace( @@ -2198,7 +2331,9 @@ def test_get_datasource_plugins_extracts_user_inputs_and_credentials(mocker, rag assert result[0]["credentials"][0]["id"] == "c1" -def test_get_pipeline_returns_pipeline_when_found(mocker, rag_pipeline_service) -> None: +def test_get_pipeline_returns_pipeline_when_found( + mocker: MockerFixture, rag_pipeline_service: RagPipelineService +) -> None: dataset = _make_dataset() pipeline = _make_pipeline() mocker.patch("services.rag_pipeline.rag_pipeline.db.session.scalar", side_effect=[dataset, pipeline]) diff --git a/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_task_proxy.py b/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_task_proxy.py index 287391c24c4..a05930c73ce 100644 --- a/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_task_proxy.py +++ b/api/tests/unit_tests/services/rag_pipeline/test_rag_pipeline_task_proxy.py @@ -23,7 +23,7 @@ def proxy(mocker: MockerFixture): # --- delay --- -def test_delay_with_empty_entities_logs_warning_and_returns(mocker) -> None: +def test_delay_with_empty_entities_logs_warning_and_returns(mocker: MockerFixture) -> None: mocker.patch("services.rag_pipeline.rag_pipeline_task_proxy.TenantIsolatedTaskQueue") proxy = RagPipelineTaskProxy( dataset_tenant_id="tenant-1", @@ -37,7 +37,7 @@ def test_delay_with_empty_entities_logs_warning_and_returns(mocker) -> None: dispatch_mock.assert_not_called() -def test_delay_with_entities_calls_dispatch(mocker, proxy) -> None: +def test_delay_with_entities_calls_dispatch(mocker: MockerFixture, proxy) -> None: dispatch_mock = mocker.patch.object(proxy, "_dispatch") proxy.delay() @@ -48,7 +48,7 @@ def test_delay_with_entities_calls_dispatch(mocker, proxy) -> None: # --- _dispatch --- -def test_dispatch_billing_sandbox_uses_default_tenant_queue(mocker, proxy) -> None: +def test_dispatch_billing_sandbox_uses_default_tenant_queue(mocker: MockerFixture, proxy) -> None: upload_mock = mocker.patch.object(proxy, "_upload_invoke_entities", return_value="file-1") send_mock = mocker.patch.object(proxy, "_send_to_default_tenant_queue") @@ -65,7 +65,7 @@ def test_dispatch_billing_sandbox_uses_default_tenant_queue(mocker, proxy) -> No send_mock.assert_called_once_with("file-1") -def test_dispatch_billing_non_sandbox_uses_priority_tenant_queue(mocker, proxy) -> None: +def test_dispatch_billing_non_sandbox_uses_priority_tenant_queue(mocker: MockerFixture, proxy) -> None: upload_mock = mocker.patch.object(proxy, "_upload_invoke_entities", return_value="file-1") send_mock = mocker.patch.object(proxy, "_send_to_priority_tenant_queue") @@ -82,7 +82,7 @@ def test_dispatch_billing_non_sandbox_uses_priority_tenant_queue(mocker, proxy) send_mock.assert_called_once_with("file-1") -def test_dispatch_no_billing_uses_priority_direct_queue(mocker, proxy) -> None: +def test_dispatch_no_billing_uses_priority_direct_queue(mocker: MockerFixture, proxy) -> None: upload_mock = mocker.patch.object(proxy, "_upload_invoke_entities", return_value="file-1") send_mock = mocker.patch.object(proxy, "_send_to_priority_direct_queue") @@ -95,7 +95,7 @@ def test_dispatch_no_billing_uses_priority_direct_queue(mocker, proxy) -> None: send_mock.assert_called_once_with("file-1") -def test_dispatch_raises_on_empty_upload_file_id(mocker, proxy) -> None: +def test_dispatch_raises_on_empty_upload_file_id(mocker: MockerFixture, proxy) -> None: mocker.patch.object(proxy, "_upload_invoke_entities", return_value="") features = SimpleNamespace(billing=SimpleNamespace(enabled=False, subscription=SimpleNamespace(plan="free"))) @@ -108,7 +108,7 @@ def test_dispatch_raises_on_empty_upload_file_id(mocker, proxy) -> None: # --- _send_to_direct_queue --- -def test_send_to_direct_queue_calls_task_func_delay(mocker, proxy) -> None: +def test_send_to_direct_queue_calls_task_func_delay(mocker: MockerFixture, proxy) -> None: task_func = Mock() proxy._send_to_direct_queue("file-1", task_func) @@ -122,7 +122,7 @@ def test_send_to_direct_queue_calls_task_func_delay(mocker, proxy) -> None: # --- _send_to_tenant_queue --- -def test_send_to_tenant_queue_pushes_when_task_key_exists(mocker, proxy) -> None: +def test_send_to_tenant_queue_pushes_when_task_key_exists(mocker: MockerFixture, proxy) -> None: proxy._tenant_isolated_task_queue.get_task_key.return_value = "existing-key" task_func = Mock() @@ -132,7 +132,7 @@ def test_send_to_tenant_queue_pushes_when_task_key_exists(mocker, proxy) -> None task_func.delay.assert_not_called() -def test_send_to_tenant_queue_sets_waiting_time_and_calls_delay(mocker, proxy) -> None: +def test_send_to_tenant_queue_sets_waiting_time_and_calls_delay(mocker: MockerFixture, proxy) -> None: proxy._tenant_isolated_task_queue.get_task_key.return_value = None task_func = Mock() @@ -148,7 +148,7 @@ def test_send_to_tenant_queue_sets_waiting_time_and_calls_delay(mocker, proxy) - # --- _upload_invoke_entities --- -def test_upload_invoke_entities_returns_file_id(mocker, proxy) -> None: +def test_upload_invoke_entities_returns_file_id(mocker: MockerFixture, proxy) -> None: upload_file = SimpleNamespace(id="uploaded-file-1") file_service_cls = mocker.patch("services.rag_pipeline.rag_pipeline_task_proxy.FileService") file_service_cls.return_value.upload_text.return_value = upload_file diff --git a/api/tests/unit_tests/services/test_human_input_service.py b/api/tests/unit_tests/services/test_human_input_service.py index 01d918cd897..6995410c5e6 100644 --- a/api/tests/unit_tests/services/test_human_input_service.py +++ b/api/tests/unit_tests/services/test_human_input_service.py @@ -81,7 +81,7 @@ def sample_form_record(): ) -def test_enqueue_resume_dispatches_task_for_workflow(mocker, mock_session_factory): +def test_enqueue_resume_dispatches_task_for_workflow(mocker: MockerFixture, mock_session_factory): session_factory, session = mock_session_factory service = HumanInputService(session_factory) @@ -108,7 +108,9 @@ def test_enqueue_resume_dispatches_task_for_workflow(mocker, mock_session_factor assert call_kwargs["kwargs"]["payload"]["workflow_run_id"] == "workflow-run-id" -def test_ensure_form_active_respects_global_timeout(monkeypatch, sample_form_record, mock_session_factory): +def test_ensure_form_active_respects_global_timeout( + monkeypatch, sample_form_record: HumanInputFormRecord, mock_session_factory +): session_factory, _ = mock_session_factory service = HumanInputService(session_factory) expired_record = dataclasses.replace( @@ -122,7 +124,7 @@ def test_ensure_form_active_respects_global_timeout(monkeypatch, sample_form_rec service.ensure_form_active(Form(expired_record)) -def test_enqueue_resume_dispatches_task_for_advanced_chat(mocker, mock_session_factory): +def test_enqueue_resume_dispatches_task_for_advanced_chat(mocker: MockerFixture, mock_session_factory): session_factory, session = mock_session_factory service = HumanInputService(session_factory) @@ -149,7 +151,7 @@ def test_enqueue_resume_dispatches_task_for_advanced_chat(mocker, mock_session_f assert call_kwargs["kwargs"]["payload"]["workflow_run_id"] == "workflow-run-id" -def test_enqueue_resume_skips_unsupported_app_mode(mocker, mock_session_factory): +def test_enqueue_resume_skips_unsupported_app_mode(mocker: MockerFixture, mock_session_factory): session_factory, session = mock_session_factory service = HumanInputService(session_factory) @@ -174,7 +176,9 @@ def test_enqueue_resume_skips_unsupported_app_mode(mocker, mock_session_factory) resume_task.apply_async.assert_not_called() -def test_get_form_definition_by_token_for_console_uses_repository(sample_form_record, mock_session_factory): +def test_get_form_definition_by_token_for_console_uses_repository( + sample_form_record: HumanInputFormRecord, mock_session_factory +): session_factory, _ = mock_session_factory repo = MagicMock(spec=HumanInputFormSubmissionRepository) console_record = dataclasses.replace(sample_form_record, recipient_type=RecipientType.CONSOLE) @@ -215,7 +219,9 @@ def _build_resumption_context_state(*, options: list[str], workflow_run_id: str) return context.dumps().encode() -def test_resolve_form_inputs_uses_runtime_select_options(sample_form_record, mock_session_factory, mocker): +def test_resolve_form_inputs_uses_runtime_select_options( + sample_form_record: HumanInputFormRecord, mock_session_factory, mocker: MockerFixture +): session_factory, _ = mock_session_factory configured_input = SelectInputConfig( output_variable_name="decision", @@ -360,7 +366,7 @@ def test_submit_form_by_token_passes_submission_user_id( enqueue_spy.assert_called_once_with(sample_form_record.workflow_run_id) -def test_submit_form_by_token_invalid_action(sample_form_record, mock_session_factory): +def test_submit_form_by_token_invalid_action(sample_form_record: HumanInputFormRecord, mock_session_factory): session_factory, _ = mock_session_factory repo = MagicMock(spec=HumanInputFormSubmissionRepository) repo.get_by_token.return_value = dataclasses.replace(sample_form_record) @@ -378,7 +384,7 @@ def test_submit_form_by_token_invalid_action(sample_form_record, mock_session_fa repo.mark_submitted.assert_not_called() -def test_submit_form_by_token_missing_inputs(sample_form_record, mock_session_factory): +def test_submit_form_by_token_missing_inputs(sample_form_record: HumanInputFormRecord, mock_session_factory): session_factory, _ = mock_session_factory repo = MagicMock(spec=HumanInputFormSubmissionRepository) @@ -559,7 +565,7 @@ def test_get_form_by_token_none(mock_session_factory): assert service.get_form_by_token("invalid") is None -def test_get_form_definition_by_token_mismatch(sample_form_record, mock_session_factory): +def test_get_form_definition_by_token_mismatch(sample_form_record: HumanInputFormRecord, mock_session_factory): session_factory, _ = mock_session_factory repo = MagicMock(spec=HumanInputFormSubmissionRepository) repo.get_by_token.return_value = sample_form_record @@ -569,7 +575,7 @@ def test_get_form_definition_by_token_mismatch(sample_form_record, mock_session_ assert service.get_form_definition_by_token(RecipientType.CONSOLE, "token") is None -def test_get_form_definition_by_token_success(sample_form_record, mock_session_factory): +def test_get_form_definition_by_token_success(sample_form_record: HumanInputFormRecord, mock_session_factory): session_factory, _ = mock_session_factory repo = MagicMock(spec=HumanInputFormSubmissionRepository) repo.get_by_token.return_value = sample_form_record @@ -580,7 +586,9 @@ def test_get_form_definition_by_token_success(sample_form_record, mock_session_f assert form.id == sample_form_record.form_id -def test_get_form_definition_by_token_for_console_mismatch(sample_form_record, mock_session_factory): +def test_get_form_definition_by_token_for_console_mismatch( + sample_form_record: HumanInputFormRecord, mock_session_factory +): session_factory, _ = mock_session_factory repo = MagicMock(spec=HumanInputFormSubmissionRepository) repo.get_by_token.return_value = sample_form_record # is STANDALONE_WEB_APP @@ -599,7 +607,9 @@ def test_submit_form_by_token_delivery_not_enabled(mock_session_factory): service.submit_form_by_token(RecipientType.STANDALONE_WEB_APP, "token", "action", {}) -def test_submit_form_by_token_no_workflow_run_id(sample_form_record, mock_session_factory, mocker: MockerFixture): +def test_submit_form_by_token_no_workflow_run_id( + sample_form_record: HumanInputFormRecord, mock_session_factory, mocker: MockerFixture +): session_factory, _ = mock_session_factory repo = MagicMock(spec=HumanInputFormSubmissionRepository) repo.get_by_token.return_value = sample_form_record @@ -615,7 +625,7 @@ def test_submit_form_by_token_no_workflow_run_id(sample_form_record, mock_sessio enqueue_spy.assert_not_called() -def test_ensure_form_active_errors(sample_form_record, mock_session_factory): +def test_ensure_form_active_errors(sample_form_record: HumanInputFormRecord, mock_session_factory): session_factory, _ = mock_session_factory service = HumanInputService(session_factory) @@ -637,7 +647,7 @@ def test_ensure_form_active_errors(sample_form_record, mock_session_factory): service.ensure_form_active(Form(expired_time_record)) -def test_ensure_not_submitted_raises(sample_form_record, mock_session_factory): +def test_ensure_not_submitted_raises(sample_form_record: HumanInputFormRecord, mock_session_factory): session_factory, _ = mock_session_factory service = HumanInputService(session_factory) submitted_record = dataclasses.replace(sample_form_record, submitted_at=naive_utc_now()) @@ -646,7 +656,7 @@ def test_ensure_not_submitted_raises(sample_form_record, mock_session_factory): service._ensure_not_submitted(Form(submitted_record)) -def test_enqueue_resume_workflow_not_found(mocker, mock_session_factory): +def test_enqueue_resume_workflow_not_found(mocker: MockerFixture, mock_session_factory): session_factory, _ = mock_session_factory service = HumanInputService(session_factory) @@ -662,7 +672,7 @@ def test_enqueue_resume_workflow_not_found(mocker, mock_session_factory): assert "WorkflowRun not found" in str(excinfo.value) -def test_enqueue_resume_app_not_found(mocker, mock_session_factory): +def test_enqueue_resume_app_not_found(mocker: MockerFixture, mock_session_factory): session_factory, session = mock_session_factory service = HumanInputService(session_factory) @@ -683,7 +693,9 @@ def test_enqueue_resume_app_not_found(mocker, mock_session_factory): logger_spy.error.assert_called_once() -def test_is_globally_expired_zero_timeout(monkeypatch, sample_form_record, mock_session_factory): +def test_is_globally_expired_zero_timeout( + monkeypatch: pytest.MonkeyPatch, sample_form_record: HumanInputFormRecord, mock_session_factory +): session_factory, _ = mock_session_factory service = HumanInputService(session_factory) @@ -691,7 +703,9 @@ def test_is_globally_expired_zero_timeout(monkeypatch, sample_form_record, mock_ assert service._is_globally_expired(Form(sample_form_record)) is False -def test_submit_form_by_token_normalizes_select_and_files(sample_form_record, mock_session_factory, mocker) -> None: +def test_submit_form_by_token_normalizes_select_and_files( + sample_form_record: HumanInputFormRecord, mock_session_factory, mocker: MockerFixture +) -> None: session_factory, _ = mock_session_factory repo = MagicMock(spec=HumanInputFormSubmissionRepository) definition = FormDefinition( @@ -772,7 +786,9 @@ def test_submit_form_by_token_normalizes_select_and_files(sample_form_record, mo enqueue_spy.assert_called_once_with(sample_form_record.workflow_run_id) -def test_submit_form_by_token_invalid_select_value(sample_form_record, mock_session_factory) -> None: +def test_submit_form_by_token_invalid_select_value( + sample_form_record: HumanInputFormRecord, mock_session_factory +) -> None: session_factory, _ = mock_session_factory repo = MagicMock(spec=HumanInputFormSubmissionRepository) definition = FormDefinition( @@ -799,7 +815,9 @@ def test_submit_form_by_token_invalid_select_value(sample_form_record, mock_sess ) -def test_submit_form_by_token_invalid_file_list_item(sample_form_record, mock_session_factory) -> None: +def test_submit_form_by_token_invalid_file_list_item( + sample_form_record: HumanInputFormRecord, mock_session_factory +) -> None: session_factory, _ = mock_session_factory repo = MagicMock(spec=HumanInputFormSubmissionRepository) definition = FormDefinition( @@ -824,7 +842,9 @@ def test_submit_form_by_token_invalid_file_list_item(sample_form_record, mock_se ) -def test_submit_form_by_token_rejects_cross_tenant_file(sample_form_record, mock_session_factory, mocker) -> None: +def test_submit_form_by_token_rejects_cross_tenant_file( + sample_form_record: HumanInputFormRecord, mock_session_factory, mocker: MockerFixture +) -> None: session_factory, _ = mock_session_factory repo = MagicMock(spec=HumanInputFormSubmissionRepository) definition = FormDefinition( @@ -855,7 +875,9 @@ def test_submit_form_by_token_rejects_cross_tenant_file(sample_form_record, mock repo.mark_submitted.assert_not_called() -def test_submit_form_by_token_rejects_cross_tenant_file_list(sample_form_record, mock_session_factory, mocker) -> None: +def test_submit_form_by_token_rejects_cross_tenant_file_list( + sample_form_record: HumanInputFormRecord, mock_session_factory, mocker: MockerFixture +) -> None: session_factory, _ = mock_session_factory repo = MagicMock(spec=HumanInputFormSubmissionRepository) definition = FormDefinition(