test: use SQLite sessions in controllers console app (#39095)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Asuka Minato 2026-07-30 12:07:43 +09:00 committed by GitHub
parent 3453fdce44
commit 9e8733413a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 120 additions and 53 deletions

View File

@ -10,6 +10,8 @@ from unittest.mock import Mock
import pytest
from flask import Flask
from pydantic import ValidationError
from sqlalchemy import Engine
from sqlalchemy.orm import Session
from werkzeug.exceptions import HTTPException, NotFound
from controllers.common.errors import InvalidArgumentError
@ -331,45 +333,34 @@ def test_restore_published_workflow_to_draft_returns_400_for_invalid_structure(
def test_get_published_workflows_serializes_items_before_session_closes(
app: Flask, monkeypatch: pytest.MonkeyPatch
app: Flask, monkeypatch: pytest.MonkeyPatch, sqlite_engine: Engine
) -> None:
api = workflow_module.PublishedAllWorkflowApi()
handler = inspect.unwrap(api.get)
session_state = {"open": False}
class _SessionContext:
def __enter__(self):
session_state["open"] = True
return object()
def __exit__(self, exc_type, exc, tb):
session_state["open"] = False
return False
class _SessionMaker:
def begin(self):
return _SessionContext()
base_workflow = _make_workflow()
class _Workflow:
def __init__(self, session: Session) -> None:
self._session = session
def __getattr__(self, name):
return getattr(base_workflow, name)
@property
def id(self):
assert session_state["open"] is True
assert self._session.in_transaction()
return "w1"
monkeypatch.setattr(workflow_module, "db", SimpleNamespace(engine=object()))
monkeypatch.setattr(workflow_module, "sessionmaker", lambda *_args, **_kwargs: _SessionMaker())
def get_all_published_workflow(*, session: Session, **_kwargs):
assert isinstance(session, Session)
return [_Workflow(session)], False
monkeypatch.setattr(workflow_module, "db", SimpleNamespace(engine=sqlite_engine))
monkeypatch.setattr(
workflow_module,
"WorkflowService",
lambda: SimpleNamespace(
get_all_published_workflow=lambda **_kwargs: ([_Workflow()], False),
),
lambda: SimpleNamespace(get_all_published_workflow=get_all_published_workflow),
)
with app.test_request_context(

View File

@ -1,19 +1,68 @@
"""Console workflow pause-detail tests backed by persisted workflow execution state."""
from __future__ import annotations
import inspect
from dataclasses import dataclass
from datetime import datetime
from types import SimpleNamespace
from unittest.mock import Mock
from uuid import uuid4
import pytest
from flask import Flask
from sqlalchemy.engine import Engine
from sqlalchemy.orm import Session
from controllers.common.errors import NotFoundError
from controllers.console.app import workflow_run as workflow_run_module
from core.workflow.nodes.human_input.entities import ParagraphInputConfig, UserActionConfig
from core.workflow.nodes.human_input.pause_reason import HumanInputRequired
from graphon.enums import WorkflowExecutionStatus
from models.workflow import WorkflowRun
from models.enums import CreatorUserRole, WorkflowRunTriggeredFrom
from models.workflow import WorkflowPause, WorkflowRun, WorkflowType
@dataclass(frozen=True)
class _Database:
engine: Engine
session: Session
def _persist_run(
session: Session,
*,
run_id: str,
tenant_id: str,
status: WorkflowExecutionStatus,
paused: bool = False,
) -> WorkflowRun:
workflow_id = str(uuid4())
workflow_run = WorkflowRun(
id=run_id,
tenant_id=tenant_id,
app_id=str(uuid4()),
workflow_id=workflow_id,
type=WorkflowType.WORKFLOW,
triggered_from=WorkflowRunTriggeredFrom.DEBUGGING,
version="draft",
graph="{}",
inputs="{}",
status=status,
created_by_role=CreatorUserRole.ACCOUNT,
created_by=str(uuid4()),
created_at=datetime(2024, 1, 1, 12, 0, 0),
)
session.add(workflow_run)
if paused:
session.add(
WorkflowPause(
workflow_id=workflow_id,
workflow_run_id=run_id,
state_object_key="workflow-pauses/state.json",
)
)
session.commit()
return workflow_run
class _PauseEntity:
@ -25,15 +74,25 @@ class _PauseEntity:
return self._reasons
def test_pause_details_returns_backstage_input_url(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
def test_pause_details_returns_backstage_input_url(
app: Flask, monkeypatch: pytest.MonkeyPatch, sqlite_session: Session
) -> None:
monkeypatch.setattr(workflow_run_module.dify_config, "APP_WEB_URL", "https://web.example.com")
workflow_run = Mock(spec=WorkflowRun)
workflow_run.tenant_id = "tenant-123"
workflow_run.status = WorkflowExecutionStatus.PAUSED
workflow_run.created_at = datetime(2024, 1, 1, 12, 0, 0)
fake_db = SimpleNamespace(engine=Mock(), session=SimpleNamespace(get=lambda *_: workflow_run))
monkeypatch.setattr(workflow_run_module, "db", fake_db)
tenant_id = str(uuid4())
run_id = str(uuid4())
_persist_run(
sqlite_session,
run_id=run_id,
tenant_id=tenant_id,
status=WorkflowExecutionStatus.PAUSED,
paused=True,
)
monkeypatch.setattr(
workflow_run_module,
"db",
_Database(engine=sqlite_session.get_bind(), session=sqlite_session),
)
reason = HumanInputRequired(
form_id="form-1",
@ -58,12 +117,12 @@ def test_pause_details_returns_backstage_input_url(app: Flask, monkeypatch: pyte
lambda _form_ids: {"form-1": "backstage-token"},
)
with app.test_request_context("/console/api/workflow/run-1/pause-details", method="GET"):
with app.test_request_context(f"/console/api/workflow/{run_id}/pause-details", method="GET"):
handler = inspect.unwrap(workflow_run_module.ConsoleWorkflowPauseDetailsApi.get)
response, status = handler(
workflow_run_module.ConsoleWorkflowPauseDetailsApi(),
"tenant-123",
workflow_run_id="run-1",
tenant_id,
workflow_run_id=run_id,
)
assert status == 200
@ -77,39 +136,56 @@ def test_pause_details_returns_backstage_input_url(app: Flask, monkeypatch: pyte
assert "pending_human_inputs" not in response
def test_pause_details_tenant_isolation(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
def test_pause_details_tenant_isolation(app: Flask, monkeypatch: pytest.MonkeyPatch, sqlite_session: Session) -> None:
monkeypatch.setattr(workflow_run_module.dify_config, "APP_WEB_URL", "https://web.example.com")
workflow_run = Mock(spec=WorkflowRun)
workflow_run.tenant_id = "tenant-456"
workflow_run.status = WorkflowExecutionStatus.PAUSED
workflow_run.created_at = datetime(2024, 1, 1, 12, 0, 0)
fake_db = SimpleNamespace(engine=Mock(), session=SimpleNamespace(get=lambda *_: workflow_run))
monkeypatch.setattr(workflow_run_module, "db", fake_db)
run_id = str(uuid4())
_persist_run(
sqlite_session,
run_id=run_id,
tenant_id=str(uuid4()),
status=WorkflowExecutionStatus.PAUSED,
paused=True,
)
monkeypatch.setattr(
workflow_run_module,
"db",
_Database(engine=sqlite_session.get_bind(), session=sqlite_session),
)
handler = inspect.unwrap(workflow_run_module.ConsoleWorkflowPauseDetailsApi.get)
with app.test_request_context("/console/api/workflow/run-1/pause-details", method="GET"):
with app.test_request_context(f"/console/api/workflow/{run_id}/pause-details", method="GET"):
with pytest.raises(NotFoundError):
handler(
workflow_run_module.ConsoleWorkflowPauseDetailsApi(),
"tenant-123",
workflow_run_id="run-1",
str(uuid4()),
workflow_run_id=run_id,
)
def test_pause_details_returns_empty_response_for_non_paused_run(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
workflow_run = Mock(spec=WorkflowRun)
workflow_run.tenant_id = "tenant-123"
workflow_run.status = WorkflowExecutionStatus.RUNNING
fake_db = SimpleNamespace(engine=Mock(), session=SimpleNamespace(get=lambda *_: workflow_run))
monkeypatch.setattr(workflow_run_module, "db", fake_db)
def test_pause_details_returns_empty_response_for_non_paused_run(
app: Flask, monkeypatch: pytest.MonkeyPatch, sqlite_session: Session
) -> None:
tenant_id = str(uuid4())
run_id = str(uuid4())
_persist_run(
sqlite_session,
run_id=run_id,
tenant_id=tenant_id,
status=WorkflowExecutionStatus.RUNNING,
)
monkeypatch.setattr(
workflow_run_module,
"db",
_Database(engine=sqlite_session.get_bind(), session=sqlite_session),
)
with app.test_request_context("/console/api/workflow/run-1/pause-details", method="GET"):
with app.test_request_context(f"/console/api/workflow/{run_id}/pause-details", method="GET"):
handler = inspect.unwrap(workflow_run_module.ConsoleWorkflowPauseDetailsApi.get)
response, status = handler(
workflow_run_module.ConsoleWorkflowPauseDetailsApi(),
"tenant-123",
workflow_run_id="run-1",
tenant_id,
workflow_run_id=run_id,
)
assert status == 200