mirror of
https://github.com/langgenius/dify.git
synced 2026-07-30 16:59:35 +08:00
test: use SQLite sessions in controllers console app (#39097)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
9e8733413a
commit
7c9a0ce93b
@ -6,6 +6,7 @@ from unittest.mock import MagicMock, PropertyMock, patch
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from sqlalchemy.orm import Session
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
from controllers.common import wraps as common_wraps
|
||||
@ -15,6 +16,7 @@ from controllers.console.app import ops_trace as ops_trace_module
|
||||
from controllers.console.app import wraps as app_wraps
|
||||
from libs import login as login_lib
|
||||
from models.account import Account, AccountStatus, TenantAccountRole
|
||||
from models.model import App, AppMode, IconType
|
||||
|
||||
|
||||
def _make_account(role: TenantAccountRole) -> Account:
|
||||
@ -137,11 +139,33 @@ def test_trace_config_mutations_require_rbac_permission(
|
||||
payload: dict[str, object] | None,
|
||||
service_method_name: str,
|
||||
service_result: object,
|
||||
sqlite_session: Session,
|
||||
) -> None:
|
||||
app.config.setdefault("RESTX_MASK_HEADER", "X-Fields")
|
||||
account = _make_account(TenantAccountRole.NORMAL)
|
||||
_patch_console_guards(monkeypatch, account, _make_app(), rbac_enabled=True)
|
||||
monkeypatch.setattr(common_wraps.db, "session", SimpleNamespace(scalar=lambda _stmt: "other-account"))
|
||||
owned_app = App()
|
||||
owned_app.id = "app-123"
|
||||
owned_app.tenant_id = "tenant-123"
|
||||
owned_app.name = "Trace app"
|
||||
owned_app.description = ""
|
||||
owned_app.mode = AppMode.CHAT
|
||||
owned_app.icon_type = IconType.EMOJI
|
||||
owned_app.icon = "robot"
|
||||
owned_app.icon_background = "#ffffff"
|
||||
owned_app.enable_site = False
|
||||
owned_app.enable_api = False
|
||||
owned_app.api_rpm = 0
|
||||
owned_app.api_rph = 0
|
||||
owned_app.is_demo = False
|
||||
owned_app.is_public = False
|
||||
owned_app.is_universal = False
|
||||
owned_app.max_active_requests = None
|
||||
owned_app.maintainer = "other-account"
|
||||
owned_app.use_icon_as_answer_icon = False
|
||||
sqlite_session.add(owned_app)
|
||||
sqlite_session.commit()
|
||||
monkeypatch.setattr(common_wraps.db, "session", sqlite_session)
|
||||
monkeypatch.setattr(common_wraps.RBACService.CheckAccess, "check", MagicMock(return_value=False))
|
||||
service_mock = MagicMock(return_value=service_result)
|
||||
monkeypatch.setattr(ops_trace_module.OpsService, service_method_name, service_mock)
|
||||
|
||||
@ -1,14 +1,64 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
import uuid
|
||||
from datetime import UTC, datetime
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, PropertyMock, patch
|
||||
from unittest.mock import PropertyMock, patch
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from sqlalchemy import Engine
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from controllers.console import console_ns
|
||||
from controllers.console.app import workflow_trigger as workflow_trigger_module
|
||||
from models.base import TypeBase
|
||||
from models.enums import AppTriggerStatus, AppTriggerType
|
||||
from models.model import App, AppMode, IconType
|
||||
from models.trigger import AppTrigger
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def database_session(sqlite_engine: Engine, monkeypatch: pytest.MonkeyPatch):
|
||||
models = (App, AppTrigger)
|
||||
tables = [model.metadata.tables[model.__tablename__] for model in models]
|
||||
TypeBase.metadata.create_all(sqlite_engine, tables=tables)
|
||||
monkeypatch.setattr(workflow_trigger_module, "db", SimpleNamespace(engine=sqlite_engine))
|
||||
with Session(sqlite_engine, expire_on_commit=False) as session:
|
||||
yield session
|
||||
|
||||
|
||||
def _persist_app_trigger(
|
||||
session: Session,
|
||||
*,
|
||||
tenant_id: str | None = None,
|
||||
status: AppTriggerStatus = AppTriggerStatus.ENABLED,
|
||||
) -> tuple[App, AppTrigger]:
|
||||
tenant_id = tenant_id or str(uuid.uuid4())
|
||||
app_model = App(
|
||||
id=str(uuid.uuid4()),
|
||||
tenant_id=tenant_id,
|
||||
name="Workflow App",
|
||||
mode=AppMode.WORKFLOW,
|
||||
icon_type=IconType.EMOJI,
|
||||
icon="workflow",
|
||||
icon_background="#FFFFFF",
|
||||
enable_site=False,
|
||||
enable_api=True,
|
||||
)
|
||||
trigger = AppTrigger(
|
||||
tenant_id=tenant_id,
|
||||
app_id=app_model.id,
|
||||
node_id="node-1",
|
||||
trigger_type=AppTriggerType.TRIGGER_PLUGIN,
|
||||
title="Trigger",
|
||||
provider_name="provider",
|
||||
status=status,
|
||||
)
|
||||
session.add_all([app_model, trigger])
|
||||
session.commit()
|
||||
return app_model, trigger
|
||||
|
||||
|
||||
def test_parser_models_validate():
|
||||
@ -23,22 +73,20 @@ def test_parser_models_validate():
|
||||
|
||||
def test_workflow_trigger_response_serializes_datetime():
|
||||
created_at = datetime(2026, 1, 2, 3, 4, 5, tzinfo=UTC)
|
||||
trigger = SimpleNamespace(
|
||||
id="trigger-1",
|
||||
trigger_type="trigger-plugin",
|
||||
response = workflow_trigger_module.WorkflowTriggerResponse(
|
||||
id=str(uuid.uuid4()),
|
||||
trigger_type=AppTriggerType.TRIGGER_PLUGIN,
|
||||
title="Trigger",
|
||||
node_id="node-1",
|
||||
provider_name="provider",
|
||||
icon="https://example.com/icon",
|
||||
status="enabled",
|
||||
status=AppTriggerStatus.ENABLED,
|
||||
created_at=created_at,
|
||||
updated_at=created_at,
|
||||
)
|
||||
|
||||
payload = workflow_trigger_module.WorkflowTriggerResponse.model_validate(trigger, from_attributes=True).model_dump(
|
||||
mode="json"
|
||||
)
|
||||
assert payload["id"] == "trigger-1"
|
||||
payload = response.model_dump(mode="json")
|
||||
assert payload["id"] == response.id
|
||||
assert payload["created_at"] == "2026-01-02T03:04:05Z"
|
||||
assert payload["updated_at"] == "2026-01-02T03:04:05Z"
|
||||
|
||||
@ -59,51 +107,33 @@ def test_webhook_trigger_response_serializes_datetime():
|
||||
assert payload["created_at"] == "2026-01-02T03:04:05Z"
|
||||
|
||||
|
||||
def test_app_triggers_get_uses_injected_tenant_id(app: Flask) -> None:
|
||||
trigger = SimpleNamespace(
|
||||
id="trigger-1",
|
||||
trigger_type="trigger-plugin",
|
||||
title="Trigger",
|
||||
def test_app_triggers_get_uses_injected_tenant_id(app: Flask, database_session: Session) -> None:
|
||||
app_model, trigger = _persist_app_trigger(database_session)
|
||||
other_tenant_trigger = AppTrigger(
|
||||
tenant_id=str(uuid.uuid4()),
|
||||
app_id=app_model.id,
|
||||
node_id="node-1",
|
||||
trigger_type=AppTriggerType.TRIGGER_PLUGIN,
|
||||
title="Other Tenant Trigger",
|
||||
provider_name="provider",
|
||||
icon="",
|
||||
status="enabled",
|
||||
created_at=None,
|
||||
updated_at=None,
|
||||
status=AppTriggerStatus.ENABLED,
|
||||
)
|
||||
session = MagicMock()
|
||||
session.execute.return_value.scalars.return_value.all.return_value = [trigger]
|
||||
database_session.add(other_tenant_trigger)
|
||||
database_session.commit()
|
||||
|
||||
api = workflow_trigger_module.AppTriggersApi()
|
||||
method = inspect.unwrap(api.get)
|
||||
|
||||
with (
|
||||
app.test_request_context("/"),
|
||||
patch.object(type(workflow_trigger_module.db), "engine", new_callable=PropertyMock, return_value=MagicMock()),
|
||||
patch("controllers.console.app.workflow_trigger.sessionmaker") as sessionmaker_mock,
|
||||
):
|
||||
sessionmaker_mock.return_value.begin.return_value.__enter__.return_value = session
|
||||
response = method(api, "tenant-1", SimpleNamespace(id="app-1"))
|
||||
with app.test_request_context("/"):
|
||||
response = method(api, app_model.tenant_id, app_model)
|
||||
|
||||
assert response["data"][0]["id"] == "trigger-1"
|
||||
assert [item["id"] for item in response["data"]] == [trigger.id]
|
||||
assert response["data"][0]["icon"].endswith("/provider/icon")
|
||||
|
||||
|
||||
def test_app_trigger_enable_uses_injected_tenant_id(app: Flask) -> None:
|
||||
trigger = SimpleNamespace(
|
||||
id="trigger-1",
|
||||
trigger_type="trigger-plugin",
|
||||
title="Trigger",
|
||||
node_id="node-1",
|
||||
provider_name="provider",
|
||||
icon="",
|
||||
status="disabled",
|
||||
created_at=None,
|
||||
updated_at=None,
|
||||
)
|
||||
session = MagicMock()
|
||||
session.execute.return_value.scalar_one_or_none.return_value = trigger
|
||||
payload = {"trigger_id": "trigger-1", "enable_trigger": True}
|
||||
def test_app_trigger_enable_uses_injected_tenant_id(app: Flask, database_session: Session) -> None:
|
||||
app_model, trigger = _persist_app_trigger(database_session, status=AppTriggerStatus.DISABLED)
|
||||
payload = {"trigger_id": trigger.id, "enable_trigger": True}
|
||||
|
||||
api = workflow_trigger_module.AppTriggerEnableApi()
|
||||
method = inspect.unwrap(api.post)
|
||||
@ -111,11 +141,12 @@ def test_app_trigger_enable_uses_injected_tenant_id(app: Flask) -> None:
|
||||
with (
|
||||
app.test_request_context("/", json=payload),
|
||||
patch.object(type(console_ns), "payload", new_callable=PropertyMock, return_value=payload),
|
||||
patch.object(type(workflow_trigger_module.db), "engine", new_callable=PropertyMock, return_value=MagicMock()),
|
||||
patch("controllers.console.app.workflow_trigger.sessionmaker") as sessionmaker_mock,
|
||||
):
|
||||
sessionmaker_mock.return_value.begin.return_value.__enter__.return_value = session
|
||||
response = method(api, "tenant-1", SimpleNamespace(id="app-1"))
|
||||
response = method(api, app_model.tenant_id, app_model)
|
||||
|
||||
assert response["id"] == "trigger-1"
|
||||
assert response["id"] == trigger.id
|
||||
assert response["status"] == "enabled"
|
||||
database_session.expire_all()
|
||||
persisted_trigger = database_session.get(AppTrigger, trigger.id)
|
||||
assert persisted_trigger is not None
|
||||
assert persisted_trigger.status == AppTriggerStatus.ENABLED
|
||||
|
||||
Loading…
Reference in New Issue
Block a user