From da0979b373bec0966a9cff333ce119c669b15c38 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Tue, 28 Jul 2026 12:20:27 +0900 Subject: [PATCH] test: use SQLite sessions in services core (#39090) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Byron.wang --- .../test_workflow_run_service_pause.py | 193 ++++-------------- 1 file changed, 36 insertions(+), 157 deletions(-) diff --git a/api/tests/unit_tests/services/test_workflow_run_service_pause.py b/api/tests/unit_tests/services/test_workflow_run_service_pause.py index 239cc83518e..5b24cfd8a66 100644 --- a/api/tests/unit_tests/services/test_workflow_run_service_pause.py +++ b/api/tests/unit_tests/services/test_workflow_run_service_pause.py @@ -1,178 +1,57 @@ -"""Comprehensive unit tests for WorkflowRunService class. +"""Tests for the session lifecycle owned by ``WorkflowRunService``.""" -This test suite covers all pause state management operations including: -- Retrieving pause state for workflow runs -- Saving pause state with file uploads -- Marking paused workflows as resumed -- Error handling and edge cases -- Database transaction management -- Repository-based approach testing -""" - -from datetime import datetime -from unittest.mock import MagicMock, create_autospec, patch +from unittest.mock import create_autospec, patch import pytest -from sqlalchemy import Engine +from sqlalchemy import Engine, text from sqlalchemy.orm import Session, sessionmaker -from graphon.enums import WorkflowExecutionStatus -from models.workflow import WorkflowPause from repositories.api_workflow_run_repository import APIWorkflowRunRepository -from repositories.sqlalchemy_api_workflow_run_repository import _PrivateWorkflowPauseEntity -from services.workflow_run_service import ( - WorkflowRunService, -) +from services.workflow_run_service import WorkflowRunService -class TestDataFactory: - """Factory class for creating test data objects.""" - - @staticmethod - def create_workflow_run_mock( - id: str = "workflow-run-123", - tenant_id: str = "tenant-456", - app_id: str = "app-789", - workflow_id: str = "workflow-101", - status: str | WorkflowExecutionStatus = "paused", - **kwargs, - ) -> MagicMock: - """Create a mock WorkflowRun object.""" - mock_run = MagicMock() - mock_run.id = id - mock_run.tenant_id = tenant_id - mock_run.app_id = app_id - mock_run.workflow_id = workflow_id - mock_run.status = status - - for key, value in kwargs.items(): - setattr(mock_run, key, value) - - return mock_run - - @staticmethod - def create_workflow_pause_mock( - id: str = "pause-123", - tenant_id: str = "tenant-456", - app_id: str = "app-789", - workflow_id: str = "workflow-101", - workflow_execution_id: str = "workflow-execution-123", - state_file_id: str = "file-456", - resumed_at: datetime | None = None, - **kwargs, - ) -> MagicMock: - """Create a mock WorkflowPauseModel object.""" - mock_pause = MagicMock(spec=WorkflowPause) - mock_pause.id = id - mock_pause.tenant_id = tenant_id - mock_pause.app_id = app_id - mock_pause.workflow_id = workflow_id - mock_pause.workflow_execution_id = workflow_execution_id - mock_pause.state_file_id = state_file_id - mock_pause.resumed_at = resumed_at - - for key, value in kwargs.items(): - setattr(mock_pause, key, value) - - return mock_pause - - @staticmethod - def create_pause_entity_mock( - pause_model: MagicMock | None = None, - ) -> _PrivateWorkflowPauseEntity: - """Create a mock _PrivateWorkflowPauseEntity object.""" - if pause_model is None: - pause_model = TestDataFactory.create_workflow_pause_mock() - - return _PrivateWorkflowPauseEntity(pause_model=pause_model, reason_models=[], human_input_form=[]) +@pytest.fixture +def sqlite_session_factory(sqlite_engine: Engine) -> sessionmaker[Session]: + """Return a real factory whose sessions are bound to the isolated SQLite engine.""" + return sessionmaker(bind=sqlite_engine, expire_on_commit=False) -class TestWorkflowRunService: - """Comprehensive unit tests for WorkflowRunService class.""" +@pytest.fixture +def workflow_run_repository(): + """Keep the repository boundary mocked while exercising real session construction.""" + return create_autospec(APIWorkflowRunRepository) - @pytest.fixture - def mock_session_factory(self): - """Create a mock session factory with proper session management.""" - mock_session = create_autospec(Session) - # Create a mock context manager for the session - mock_session_cm = MagicMock() - mock_session_cm.__enter__ = MagicMock(return_value=mock_session) - mock_session_cm.__exit__ = MagicMock(return_value=None) +def test_init_with_session_factory( + sqlite_session_factory: sessionmaker[Session], workflow_run_repository: APIWorkflowRunRepository +) -> None: + with patch("services.workflow_run_service.DifyAPIRepositoryFactory", autospec=True) as repository_factory: + repository_factory.create_api_workflow_run_repository.return_value = workflow_run_repository - # Create a mock context manager for the transaction - mock_transaction_cm = MagicMock() - mock_transaction_cm.__enter__ = MagicMock(return_value=mock_session) - mock_transaction_cm.__exit__ = MagicMock(return_value=None) + service = WorkflowRunService(sqlite_session_factory) - mock_session.begin = MagicMock(return_value=mock_transaction_cm) + assert service._session_factory is sqlite_session_factory + repository_factory.create_api_workflow_run_repository.assert_called_once_with(sqlite_session_factory) + with service._session_factory() as session: + assert session.scalar(text("SELECT 1")) == 1 - # Create mock factory that returns the context manager - mock_factory = MagicMock(spec=sessionmaker) - mock_factory.return_value = mock_session_cm - return mock_factory, mock_session +def test_init_with_engine_creates_bound_session_factory( + sqlite_engine: Engine, workflow_run_repository: APIWorkflowRunRepository +) -> None: + with patch("services.workflow_run_service.DifyAPIRepositoryFactory", autospec=True) as repository_factory: + repository_factory.create_api_workflow_run_repository.return_value = workflow_run_repository - @pytest.fixture - def mock_workflow_run_repository(self): - """Create a mock APIWorkflowRunRepository.""" - mock_repo = create_autospec(APIWorkflowRunRepository) - return mock_repo + service = WorkflowRunService(sqlite_engine) - @pytest.fixture - def workflow_run_service(self, mock_session_factory, mock_workflow_run_repository): - """Create WorkflowRunService instance with mocked dependencies.""" - session_factory, _ = mock_session_factory + assert service._session_factory.kw["bind"] is sqlite_engine + assert service._session_factory.kw["expire_on_commit"] is False + repository_factory.create_api_workflow_run_repository.assert_called_once_with(service._session_factory) + with service._session_factory() as session: + assert session.scalar(text("SELECT 1")) == 1 - with patch("services.workflow_run_service.DifyAPIRepositoryFactory", autospec=True) as mock_factory: - mock_factory.create_api_workflow_run_repository.return_value = mock_workflow_run_repository - service = WorkflowRunService(session_factory) - return service - @pytest.fixture - def workflow_run_service_with_engine(self, mock_session_factory, mock_workflow_run_repository): - """Create WorkflowRunService instance with Engine input.""" - mock_engine = create_autospec(Engine) - session_factory, _ = mock_session_factory +def test_init_with_default_repository_dependencies(sqlite_session_factory: sessionmaker[Session]) -> None: + service = WorkflowRunService(sqlite_session_factory) - with patch("services.workflow_run_service.DifyAPIRepositoryFactory", autospec=True) as mock_factory: - mock_factory.create_api_workflow_run_repository.return_value = mock_workflow_run_repository - service = WorkflowRunService(mock_engine) - return service - - # ==================== Initialization Tests ==================== - - def test_init_with_session_factory(self, mock_session_factory, mock_workflow_run_repository): - """Test WorkflowRunService initialization with session_factory.""" - session_factory, _ = mock_session_factory - - with patch("services.workflow_run_service.DifyAPIRepositoryFactory", autospec=True) as mock_factory: - mock_factory.create_api_workflow_run_repository.return_value = mock_workflow_run_repository - service = WorkflowRunService(session_factory) - - assert service._session_factory == session_factory - mock_factory.create_api_workflow_run_repository.assert_called_once_with(session_factory) - - def test_init_with_engine(self, mock_session_factory, mock_workflow_run_repository): - """Test WorkflowRunService initialization with Engine (should convert to sessionmaker).""" - mock_engine = create_autospec(Engine) - session_factory, _ = mock_session_factory - - with patch("services.workflow_run_service.DifyAPIRepositoryFactory", autospec=True) as mock_factory: - mock_factory.create_api_workflow_run_repository.return_value = mock_workflow_run_repository - with patch( - "services.workflow_run_service.sessionmaker", return_value=session_factory, autospec=True - ) as mock_sessionmaker: - service = WorkflowRunService(mock_engine) - - mock_sessionmaker.assert_called_once_with(bind=mock_engine, expire_on_commit=False) - assert service._session_factory == session_factory - mock_factory.create_api_workflow_run_repository.assert_called_once_with(session_factory) - - def test_init_with_default_dependencies(self, mock_session_factory): - """Test WorkflowRunService initialization with default dependencies.""" - session_factory, _ = mock_session_factory - - service = WorkflowRunService(session_factory) - - assert service._session_factory == session_factory + assert service._session_factory is sqlite_session_factory