mirror of
https://github.com/langgenius/dify.git
synced 2026-11-13 06:41:07 +08:00
82 lines
3.3 KiB
Python
82 lines
3.3 KiB
Python
from collections.abc import Callable
|
|
from unittest.mock import MagicMock, create_autospec
|
|
|
|
import pytest
|
|
|
|
from machinery.context import RequestContext
|
|
from services.knowledge.dataset_access import AccessibleDataset, DatasetAccess, DatasetAccessDeniedError
|
|
from services.knowledge.external.application import (
|
|
ExternalHitTestingError,
|
|
ExternalKnowledgeApplicationService,
|
|
ExternalKnowledgeOperations,
|
|
)
|
|
from services.knowledge.resource_scope import DatasetRef
|
|
|
|
CONTEXT = RequestContext("request", None, "actor", "tenant")
|
|
|
|
|
|
@pytest.fixture
|
|
def operations() -> MagicMock:
|
|
return create_autospec(ExternalKnowledgeOperations, instance=True, spec_set=True)
|
|
|
|
|
|
@pytest.fixture
|
|
def access() -> MagicMock:
|
|
result = create_autospec(DatasetAccess, instance=True, spec_set=True)
|
|
result.require_accessible.return_value = AccessibleDataset("dataset", "tenant")
|
|
return result
|
|
|
|
|
|
@pytest.fixture
|
|
def service(operations: MagicMock, access: MagicMock) -> ExternalKnowledgeApplicationService:
|
|
return ExternalKnowledgeApplicationService(dataset_access=access, operations=operations)
|
|
|
|
|
|
@pytest.mark.parametrize("settings", [{}, {"endpoint": "https://example"}, {"api_key": "secret"}])
|
|
@pytest.mark.parametrize(
|
|
"method", [ExternalKnowledgeApplicationService.create_template, ExternalKnowledgeApplicationService.update_template]
|
|
)
|
|
def test_template_validation_prevents_io(
|
|
service: ExternalKnowledgeApplicationService,
|
|
operations: MagicMock,
|
|
settings: dict[str, str],
|
|
method: Callable[..., object],
|
|
) -> None:
|
|
extra: dict[str, str] = (
|
|
{"template_id": "template"} if method is ExternalKnowledgeApplicationService.update_template else {}
|
|
)
|
|
with pytest.raises(ValueError, match="(?:api list is empty|api_key is required|endpoint is required)"):
|
|
method(service, CONTEXT, name="API", settings=settings, **extra)
|
|
assert operations.mock_calls == []
|
|
|
|
|
|
def test_retrieval_authorization_precedes_validation_and_network(
|
|
service: ExternalKnowledgeApplicationService, access: MagicMock, operations: MagicMock
|
|
) -> None:
|
|
access.require_accessible.side_effect = DatasetAccessDeniedError()
|
|
with pytest.raises(DatasetAccessDeniedError):
|
|
service.hit_testing(CONTEXT, dataset_id="dataset", query="", retrieval_model=None, metadata_filters=None)
|
|
assert operations.mock_calls == []
|
|
|
|
|
|
@pytest.mark.parametrize("query", ["", "a" * 251])
|
|
def test_invalid_query_prevents_retrieval(
|
|
service: ExternalKnowledgeApplicationService, operations: MagicMock, query: str
|
|
) -> None:
|
|
with pytest.raises(ValueError, match="(?:Query cannot exceed 250 characters|Query or attachment_ids is required)"):
|
|
service.hit_testing(CONTEXT, dataset_id="dataset", query=query, retrieval_model=None, metadata_filters=None)
|
|
operations.retrieve.assert_not_called()
|
|
|
|
|
|
def test_retrieval_failure_is_neutral_and_receives_owned_ref(
|
|
service: ExternalKnowledgeApplicationService, operations: MagicMock
|
|
) -> None:
|
|
operations.retrieve.side_effect = RuntimeError("unavailable")
|
|
with pytest.raises(ExternalHitTestingError, match="unavailable"):
|
|
service.hit_testing(
|
|
CONTEXT, dataset_id="dataset", query="query", retrieval_model={"top_k": 2}, metadata_filters=None
|
|
)
|
|
operations.retrieve.assert_called_once_with(
|
|
CONTEXT, DatasetRef("tenant", "dataset"), query="query", retrieval_model={"top_k": 2}, metadata_filters=None
|
|
)
|