test: compose real datasource credential repositories (#43216)

This commit is contained in:
Asuka Minato authored and GitHub committed 2026-10-02 13:53:36 +00:00
1 parent ba28e3ad82
commit a54589a672
5 files changed
+27 -21

No files matched your search

@@ -9,6 +9,7 @@ from flask import Flask
from sqlalchemy.orm import Session, sessionmaker
from controllers.console.datasets.data_source import DataSourceNotionListApi, DataSourceNotionListQuery
from extensions.application_services.data_sources import build_data_source_credentials
from machinery.context import RequestContext
from models import Account, Tenant, TenantAccountJoin, TenantAccountRole
from models.dataset import Dataset, Document
@@ -16,7 +17,6 @@ from models.enums import DataSourceType, DocumentCreatedFrom, IndexingStatus
from repositories.knowledge.dataset_repository import SQLAlchemyDatasetRepository
from repositories.knowledge.document_repository import SQLAlchemyDocumentRepository
from repositories.workspace.workspace_repository import WorkspaceRepository
from services.data_source.credential_gateway import ActorDatasourceCredentialResolver
from services.data_source.notion_import_adapters import PluginNotionSourceGateway
from services.data_source.notion_import_application_service import NotionImportApplicationService
from services.knowledge.dataset_access import DatasetAccessService
@@ -89,8 +89,7 @@ def test_notion_page_is_marked_bound_from_persisted_document(
sessions = sessionmaker(bind=db_session_with_containers.get_bind(), expire_on_commit=False)
datasets = SQLAlchemyDatasetRepository(session_factory=sessions)
credentials = MagicMock(spec=ActorDatasourceCredentialResolver)
credentials.resolve.return_value = {"token": "token"}
credentials = build_data_source_credentials(database_client=sessions).actor
notion_imports = NotionImportApplicationService(
dataset_access=DatasetAccessService(
datasets=datasets,
@@ -106,6 +105,7 @@ def test_notion_page_is_marked_bound_from_persisted_document(
)
with (
patch.object(credentials, "resolve", return_value={"token": "token"}) as resolve_credentials,
flask_app_with_containers.test_request_context(f"/?credential_id=c1&dataset_id={dataset_id}"),
patch(
"controllers.console.datasets.data_source.application_services",
@@ -120,7 +120,7 @@ def test_notion_page_is_marked_bound_from_persisted_document(
assert status == 200
assert response["notion_info"][0]["pages"][0]["is_bound"] is True
credentials.resolve.assert_called_once_with(
resolve_credentials.assert_called_once_with(
workspace_id=tenant_id,
actor_id=account.id,
credential_id="c1",
@@ -6,6 +6,7 @@ from unittest.mock import ANY, MagicMock, patch
import pytest
from flask import Flask
from sqlalchemy.orm import Session, sessionmaker
from werkzeug.exceptions import Forbidden, NotFound
from controllers.console import console_ns
@@ -28,10 +29,10 @@ from controllers.console.datasets.rag_pipeline.datasource_auth import (
DatasourceUpdateProviderNameApi,
)
from core.plugin.impl.oauth import OAuthHandler
from extensions.application_services.data_sources import build_data_source_credentials
from graphon.model_runtime.errors.validate import CredentialsValidateFailedError
from models.account import Account
from services.credentials.query import CredentialQuery
from services.data_source.credential_gateway import DatasourceProviderCredentialStore
from repositories.credentials.query_repository import CredentialQueryRepository
from services.data_source.provider_service import DatasourceProviderService
from services.plugin.oauth_service import OAuthProxyService
from tests.unit_tests.model_factories import make_account
@@ -40,9 +41,9 @@ _PROVIDER_ID = "langgenius/notion_datasource/notion"
@pytest.fixture(autouse=True)
def credential_query_dependency(monkeypatch: pytest.MonkeyPatch) -> None:
query = MagicMock(spec=CredentialQuery)
providers = DatasourceProviderService(credentials=MagicMock(spec=DatasourceProviderCredentialStore))
def credential_query_dependency(monkeypatch: pytest.MonkeyPatch, sqlite_session_factory: sessionmaker[Session]) -> None:
query = CredentialQueryRepository(session_factory=sqlite_session_factory)
providers = build_data_source_credentials(database_client=sqlite_session_factory).providers
monkeypatch.setattr(
import_module("controllers.console.datasets.rag_pipeline.datasource_auth"),
"application_services",
@@ -25,7 +25,6 @@ from models.account import TenantAccountRole
from models.credential_permission import CredentialPermission
from models.enums import PermissionEnum
from repositories.credentials.query_repository import CredentialQueryRepository
from services.credentials.query import CredentialQuery
if not hasattr(builtins, "MethodView"):
builtins.MethodView = MethodView # type: ignore[attr-defined]
@@ -43,7 +42,7 @@ def app() -> Flask:
@pytest.fixture
def controller_module(monkeypatch: pytest.MonkeyPatch, config_overrides: Callable[..., None]):
def controller_module(monkeypatch: pytest.MonkeyPatch, config_overrides: Callable[..., None], sqlite_session_factory):
"""
Import the controller with auth decorators neutralized only during import.
@@ -82,7 +81,9 @@ def controller_module(monkeypatch: pytest.MonkeyPatch, config_overrides: Callabl
login_module = importlib.import_module("libs.login")
monkeypatch.setattr(login_module, "check_csrf_token", lambda *args, **kwargs: None)
monkeypatch.setattr(
module, "application_services", lambda: SimpleNamespace(credential_queries=MagicMock(spec=CredentialQuery))
module,
"application_services",
lambda: SimpleNamespace(credential_queries=CredentialQueryRepository(session_factory=sqlite_session_factory)),
)
return module
@@ -32,11 +32,10 @@ from core.datasource.online_document.online_document_provider import (
from core.rag.extractor import notion_extractor as notion_extractor_module
from core.rag.extractor.notion_extractor import NotionExtractor
from core.rag.models.document import Document
from extensions.application_services.data_sources import build_data_source_credentials
from models.base import TypeBase
from models.dataset import Document as DocumentModel
from models.enums import DataSourceType, DocumentCreatedFrom
from services.data_source.credential_gateway import DatasourceProviderCredentialStore
from services.data_source.provider_service import DatasourceProviderService
@dataclass(frozen=True)
@@ -140,12 +139,14 @@ class TestNotionExtractorAuthentication:
)
@patch("services.data_source.provider_service.DatasourceProviderService.get_datasource_credentials")
def test_init_with_integration_token_fallback(self, mock_get_token, mock_document_model, config_overrides):
def test_init_with_integration_token_fallback(
self, mock_get_token, mock_document_model, config_overrides, sqlite_session_factory
):
"""Test NotionExtractor falls back to integration token when credential not found."""
# Arrange
mock_get_token.side_effect = Exception("No credential id found")
config_overrides(NOTION_INTEGRATION_TOKEN="integration-token-fallback")
providers = DatasourceProviderService(credentials=Mock(spec=DatasourceProviderCredentialStore))
providers = build_data_source_credentials(database_client=sqlite_session_factory).providers
# Act
extractor = NotionExtractor(
@@ -164,12 +165,14 @@ class TestNotionExtractorAuthentication:
assert extractor._notion_access_token == "integration-token-fallback"
@patch("services.data_source.provider_service.DatasourceProviderService.get_datasource_credentials")
def test_init_missing_credentials_raises_error(self, mock_get_token, mock_document_model, config_overrides):
def test_init_missing_credentials_raises_error(
self, mock_get_token, mock_document_model, config_overrides, sqlite_session_factory
):
"""Test NotionExtractor raises error when no credentials available."""
# Arrange
mock_get_token.side_effect = Exception("No credential id found")
config_overrides(NOTION_INTEGRATION_TOKEN=None)
providers = DatasourceProviderService(credentials=Mock(spec=DatasourceProviderCredentialStore))
providers = build_data_source_credentials(database_client=sqlite_session_factory).providers
# Act & Assert
with pytest.raises(ValueError) as exc_info:
@@ -3,17 +3,17 @@ from dataclasses import dataclass, field
from unittest.mock import Mock
import pytest
from sqlalchemy.orm import Session, sessionmaker
from core.rag.extractor.entity.datasource_type import DatasourceType
from core.rag.extractor.entity.extract_setting import UploadFileExtractionInput
from core.rag.extractor.notion_extractor import NotionExtractor
from extensions.application_services.data_sources import build_data_source_credentials
from services.data_source.credential_gateway import (
DatasourceCredentialNotFoundError,
DatasourceCredentialRefreshError,
DatasourceProviderCredentialStore,
)
from services.data_source.entities.notion_import import NotionPageType
from services.data_source.provider_service import DatasourceProviderService
from services.knowledge.indexing.adapters.sources import (
CompositeStoredSourceResolver,
FileSourceAdapter,
@@ -194,6 +194,7 @@ def test_stored_notion_resolution_uses_trusted_document_owner_chain() -> None:
)
@pytest.mark.parametrize("integration_token", ["environment-secret", None, ""])
def test_stored_notion_preview_and_indexing_use_same_integration(
sqlite_session_factory: sessionmaker[Session],
monkeypatch: pytest.MonkeyPatch,
credential_id: str | None,
credentials: dict[str, object],
@@ -205,7 +206,7 @@ def test_stored_notion_preview_and_indexing_use_same_integration(
stored = RecordingStoredCredentials(credentials, error=error)
resolver = _notion_resolver(stored=stored)
indexing_credentials = Mock(return_value=credentials, side_effect=error)
providers = DatasourceProviderService(credentials=Mock(spec=DatasourceProviderCredentialStore))
providers = build_data_source_credentials(database_client=sqlite_session_factory).providers
monkeypatch.setattr(
providers,
"get_datasource_credentials",