mirror of
https://github.com/langgenius/dify.git
synced 2026-06-23 04:11:09 +08:00
chore: add more type in test (#37609)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
e4500d2b9d
commit
bd15b8e6ce
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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__)
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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():
|
||||
"""
|
||||
|
||||
@ -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))
|
||||
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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"):
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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={
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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"}]}}
|
||||
|
||||
@ -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"})()
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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])
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user