test: migrate agent controller sessions and ORM models to SQLite (#40600)

Co-authored-by: Byron Wang <byron@dify.ai>
This commit is contained in:
Asuka Minato
2026-08-15 15:15:28 +00:00
committed by GitHub
co-authored by Byron Wang
parent 1651bcc774
commit 8fb5dd8587
4 changed files with 134 additions and 104 deletions
@@ -64,9 +64,10 @@ from controllers.console.app.message import (
AgentMessageSuggestedQuestionApi,
)
from core.app.entities.app_invoke_entities import InvokeFrom
from models.account import Account, TenantAccountRole
from models.agent import Agent, AgentConfigDraftType, AgentScope, AgentSource, AgentStatus
from models.enums import ApiTokenType, ConversationFromSource
from models.model import ApiToken, App, AppMode, Conversation, Message
from models.model import ApiToken, App, AppMode, Conversation, IconType, Message
from services.entities.agent_entities import (
ComposerSavePayload,
ComposerSaveStrategy,
@@ -200,36 +201,39 @@ def _agent_app_composer_response() -> dict:
}
def _app_detail_obj(**overrides):
def _app_detail_obj(**overrides) -> App:
data = {
"id": "app-1",
"tenant_id": "tenant-1",
"name": "Iris",
"description": "Agent app",
"mode_compatible_with_agent": "agent",
"mode": AppMode.AGENT,
"icon_type": "emoji",
"icon": "robot",
"icon_background": "#fff",
"enable_site": False,
"enable_api": False,
"app_model_config": None,
"workflow": None,
"tracing": None,
"use_icon_as_answer_icon": False,
"created_by": "account-1",
"created_at": None,
"updated_by": "account-1",
"updated_at": None,
"access_mode": None,
"tags": [],
"api_base_url": None,
"max_active_requests": 0,
"deleted_tools": [],
"site": None,
"bound_agent_id": "00000000-0000-0000-0000-000000000001",
}
overrides.pop("bound_agent_id", None)
data.update(overrides)
return SimpleNamespace(**data)
data["icon_type"] = IconType(data["icon_type"])
return App(**data)
def _account(*, account_id: str = "account-1", privileged: bool = False, timezone: str | None = None) -> Account:
account = Account(name="Agent Controller Tester", email=f"{account_id}@example.com")
account.id = account_id
account.timezone = timezone
if privileged:
account.role = TenantAccountRole.OWNER
return account
def _candidates_response(variant: str) -> dict:
@@ -307,7 +311,7 @@ def account_id() -> str:
def test_agent_app_list_and_create_use_agent_route(
app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str
app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str, sqlite_session: Session
) -> None:
captured: dict[str, object] = {}
@@ -334,12 +338,11 @@ def test_agent_app_list_and_create_use_agent_route(
roster_controller.AgentRosterService,
"load_app_backing_agents_by_app_id",
lambda _self, **kwargs: {
"app-list": SimpleNamespace(
"app-list": Agent(
id="agent-list",
app_id="app-list",
backing_app_id=None,
role="List role",
debug_conversation_id="debug-conversation-list",
active_config_snapshot_id=None,
)
},
@@ -347,12 +350,11 @@ def test_agent_app_list_and_create_use_agent_route(
monkeypatch.setattr(
roster_controller.AgentRosterService,
"get_app_backing_agent",
lambda _self, **kwargs: SimpleNamespace(
lambda _self, **kwargs: Agent(
id="agent-created",
app_id="app-created",
backing_app_id=None,
role="Created role",
debug_conversation_id="debug-conversation-created",
active_config_snapshot_id=None,
),
)
@@ -412,7 +414,9 @@ def test_agent_app_list_and_create_use_agent_route(
with app.test_request_context(
"/console/api/agent?page=1&limit=10&mode=workflow&sort_by=recently_created&is_created_by_me=true"
):
listed = unwrap(AgentAppListApi.get)(AgentAppListApi(), MagicMock(), "tenant-1", SimpleNamespace(id=account_id))
listed = unwrap(AgentAppListApi.get)(
AgentAppListApi(), sqlite_session, "tenant-1", _account(account_id=account_id)
)
assert listed["page"] == 1
assert listed["limit"] == 10
assert listed["total"] == 1
@@ -448,9 +452,9 @@ def test_agent_app_list_and_create_use_agent_route(
AgentAppCreatePayload(
name="Iris", description="Agent app", role="Coordinator", icon_type="emoji", icon="robot"
),
MagicMock(),
sqlite_session,
"tenant-1",
SimpleNamespace(id=account_id),
_account(account_id=account_id),
)
assert status == 201
assert created["id"] == "agent-created"
@@ -499,7 +503,7 @@ def test_agent_app_create_omits_optional_role_as_empty_string(
"_serialize_agent_app_detail",
lambda _session, app_model, **_kwargs: {"id": "agent-created", "app_id": app_model.id},
)
current_user = SimpleNamespace(id=account_id)
current_user = _account(account_id=account_id)
with app.test_request_context(
"/console/api/agent",
json={"name": "No-role Iris", "description": "Agent app", "icon_type": "emoji", "icon": "robot"},
@@ -577,7 +581,8 @@ def test_agent_app_detail_update_delete_resolve_app_from_agent_id(
monkeypatch.setattr(roster_controller, "AppService", FakeAppService)
session = sqlite_session
detail = unwrap(AgentAppApi.get)(AgentAppApi(), session, tenant_id, SimpleNamespace(id=account_id), agent_id)
with app.test_request_context():
detail = unwrap(AgentAppApi.get)(AgentAppApi(), session, tenant_id, _account(account_id=account_id), agent_id)
assert detail["id"] == agent_id
assert detail["app_id"] == app_id
assert detail["debug_conversation_id"] == "debug-conversation-detail"
@@ -597,7 +602,7 @@ def test_agent_app_detail_update_delete_resolve_app_from_agent_id(
AgentAppUpdatePayload(name="Renamed", description="", role="Reviewer", icon_type="emoji", icon="R"),
session,
tenant_id,
SimpleNamespace(id=account_id),
_account(account_id=account_id),
agent_id,
)
assert updated["name"] == "Renamed"
@@ -621,7 +626,7 @@ def test_agent_app_copy_uses_agent_id_and_returns_agent_detail(
app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str
) -> None:
agent_id = "00000000-0000-0000-0000-000000000001"
current_user = SimpleNamespace(id=account_id)
current_user = _account(account_id=account_id)
copied_app = _app_detail_obj(id="copied-app", bound_agent_id="copied-agent")
captured: dict[str, object] = {}
@@ -696,7 +701,7 @@ def test_agent_debug_conversation_refresh_resets_build_for_current_user(
method="POST",
):
response = unwrap(AgentDebugConversationRefreshApi.post)(
AgentDebugConversationRefreshApi(), MagicMock(), "tenant-1", SimpleNamespace(id=account_id), agent_id
AgentDebugConversationRefreshApi(), MagicMock(), "tenant-1", _account(account_id=account_id), agent_id
)
assert response == {
"debug_conversation_id": "new-debug-conversation-id",
@@ -714,7 +719,7 @@ def test_agent_publish_and_build_draft_routes_call_composer_service(
app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str
) -> None:
agent_id = "00000000-0000-0000-0000-000000000001"
current_user = SimpleNamespace(id=account_id)
current_user = _account(account_id=account_id)
captured: dict[str, dict[str, object]] = {}
def publish_agent_app_draft(**kwargs: object) -> dict[str, object]:
@@ -842,17 +847,17 @@ def test_agent_publish_and_build_draft_routes_call_composer_service(
def test_agent_api_access_uses_agent_id_and_returns_service_api_metadata(monkeypatch: pytest.MonkeyPatch) -> None:
agent_id = "00000000-0000-0000-0000-000000000001"
app_model = SimpleNamespace(
app_model = _app_detail_obj(
id="app-1",
tenant_id="tenant-1",
enable_api=True,
api_base_url="https://api.example.test/v1",
api_rpm=60,
api_rph=600,
)
monkeypatch.setattr(roster_controller, "_resolve_agent_app_model", lambda _session, **kwargs: app_model)
monkeypatch.setattr(roster_controller, "_agent_api_key_count", lambda _session, _app: 2)
monkeypatch.setattr(roster_controller, "_agent_app_access_ready", lambda _session, _app: True)
monkeypatch.setattr("models.model.dify_config.SERVICE_API_URL", "https://api.example.test/v1")
response = unwrap(AgentApiAccessApi.get)(AgentApiAccessApi(), MagicMock(), "tenant-1", agent_id)
assert response == {
"access_ready": True,
@@ -892,11 +897,10 @@ def test_agent_api_status_and_key_routes_resolve_backing_app(
) -> None:
agent_id = "00000000-0000-0000-0000-000000000001"
api_key_id = "00000000-0000-0000-0000-000000000002"
app_model = SimpleNamespace(
app_model = _app_detail_obj(
id="app-1",
tenant_id="tenant-1",
enable_api=False,
api_base_url="https://api.example.test/v1",
api_rpm=0,
api_rph=0,
)
@@ -920,7 +924,7 @@ def test_agent_api_status_and_key_routes_resolve_backing_app(
def fake_create_api_key(self, resource_id: str, tenant_id: str, *, session: object):
captured["create_key"] = {"session": session, "resource_id": resource_id, "tenant_id": tenant_id}
return SimpleNamespace(id=api_key_id, type="app", token="app-test-token", last_used_at=None, created_at=None)
return ApiToken(id=api_key_id, type="app", token="app-test-token", last_used_at=None, created_at=None)
def fake_delete_api_key(
self,
@@ -966,7 +970,7 @@ def test_agent_api_status_and_key_routes_resolve_backing_app(
"resource_id": "app-1",
"tenant_id": "tenant-1",
}
current_user = SimpleNamespace(id="account-1", is_admin_or_owner=True)
current_user = _account(privileged=True)
deleted, delete_status = unwrap(AgentApiKeyApi.delete)(
AgentApiKeyApi(), unbound_session, "tenant-1", current_user, agent_id, api_key_id
)
@@ -986,7 +990,9 @@ def test_agent_api_status_and_key_routes_resolve_backing_app(
]
def test_agent_app_update_allows_empty_role(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
def test_agent_app_update_allows_empty_role(
app: Flask, monkeypatch: pytest.MonkeyPatch, sqlite_session: Session
) -> None:
agent_id = "00000000-0000-0000-0000-000000000001"
app_model = _app_detail_obj(id="app-1", bound_agent_id=agent_id)
captured: dict[str, object] = {}
@@ -994,12 +1000,11 @@ def test_agent_app_update_allows_empty_role(app: Flask, monkeypatch: pytest.Monk
monkeypatch.setattr(
roster_controller.AgentRosterService,
"get_app_backing_agent",
lambda _self, **kwargs: SimpleNamespace(
lambda _self, **kwargs: Agent(
id=agent_id,
app_id="app-1",
backing_app_id=None,
role="",
debug_conversation_id="debug-conversation-detail",
active_config_snapshot_id=None,
),
)
@@ -1033,9 +1038,9 @@ def test_agent_app_update_allows_empty_role(app: Flask, monkeypatch: pytest.Monk
updated = unwrap(AgentAppApi.put)(
AgentAppApi(),
AgentAppUpdatePayload(name="Renamed", description="", role="", icon_type="emoji", icon="R"),
MagicMock(),
sqlite_session,
"tenant-1",
SimpleNamespace(id="account-1"),
_account(),
agent_id,
)
assert updated["role"] == ""
@@ -1104,7 +1109,7 @@ def test_agent_versions_call_services(app: Flask, monkeypatch: pytest.MonkeyPatc
assert version_detail["id"] == version_id
assert version_detail["agent_id"] == agent_id
restored = unwrap(AgentRosterVersionRestoreApi.post)(
AgentRosterVersionRestoreApi(), MagicMock(), "tenant-1", SimpleNamespace(id="account-1"), agent_id, version_id
AgentRosterVersionRestoreApi(), MagicMock(), "tenant-1", _account(), agent_id, version_id
)
assert restored == {
"result": "success",
@@ -1124,7 +1129,7 @@ def test_agent_observability_routes_resolve_app_from_agent_id(
app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str
) -> None:
agent_id = "00000000-0000-0000-0000-000000000001"
app_model = SimpleNamespace(id="app-1")
app_model = _app_detail_obj(id="app-1")
captured: dict[str, object] = {}
class FakeObservabilityService:
@@ -1249,7 +1254,7 @@ def test_agent_observability_routes_resolve_app_from_agent_id(
monkeypatch.setattr(roster_controller, "_resolve_agent_runtime_app_model", lambda _session, **kwargs: app_model)
monkeypatch.setattr(roster_controller, "_agent_observability_service", lambda *_args: FakeObservabilityService())
account = SimpleNamespace(id=account_id, timezone="UTC")
account = _account(account_id=account_id, timezone="UTC")
with app.test_request_context(
"/console/api/agent/00000000-0000-0000-0000-000000000001/logs?page=2&limit=5&keyword=hello&statuses=success&statuses=failed&sources=webapp:app-1&sources=workflow:app-2:workflow-1:v1:node-1&sort_by=created_at&sort_order=asc"
):
@@ -1305,7 +1310,7 @@ def test_agent_observability_routes_resolve_app_from_agent_id(
def test_workflow_composer_get_put_validate_candidates_impact_and_save(
app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str
) -> None:
app_model = SimpleNamespace(id="app-1")
app_model = _app_detail_obj(id="app-1")
payload = {
"variant": ComposerVariant.WORKFLOW.value,
"save_strategy": ComposerSaveStrategy.NODE_JOB_ONLY.value,
@@ -1413,7 +1418,7 @@ def test_build_draft_apply_leaves_transaction_ownership_to_service() -> None:
def test_workflow_composer_copy_from_roster(app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str) -> None:
app_model = SimpleNamespace(id="app-1")
app_model = _app_detail_obj(id="app-1")
captured: dict[str, object] = {}
def fake_copy_from_roster(**kwargs):
@@ -1484,7 +1489,7 @@ def test_workflow_impact_returns_empty_without_version(app: Flask) -> None:
),
MagicMock(),
"tenant-1",
SimpleNamespace(id="app-1"),
_app_detail_obj(id="app-1"),
"node-1",
)
assert result == {"current_snapshot_id": None, "workflow_node_count": 0, "bindings": []}
@@ -1560,7 +1565,7 @@ def test_agent_chat_generate_and_stop_routes_resolve_app_from_agent_id(
app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str, unbound_session: Session
) -> None:
agent_id = "00000000-0000-0000-0000-000000000001"
app_model = SimpleNamespace(id="app-1", tenant_id="tenant-1", mode="agent")
app_model = _app_detail_obj(id="app-1", tenant_id="tenant-1", mode=AppMode.AGENT)
captured: dict[str, object] = {}
def resolve_agent_app_model(**kwargs: object) -> object:
@@ -1590,14 +1595,14 @@ def test_agent_chat_generate_and_stop_routes_resolve_app_from_agent_id(
monkeypatch.setattr(completion_controller, "_stop_chat_message", stop_chat_message)
with app.test_request_context(json={"inputs": {}, "query": "hello"}):
assert unwrap(AgentChatMessageApi.post)(
AgentChatMessageApi(), unbound_session, "tenant-1", SimpleNamespace(id=account_id), agent_id
AgentChatMessageApi(), unbound_session, "tenant-1", _account(account_id=account_id), agent_id
) == {"result": "generated"}
assert cast(dict[str, object], captured["resolve"]) == {"tenant_id": "tenant-1", "agent_id": agent_id}
assert captured["resolve_session"] is unbound_session
create_call = cast(dict[str, object], captured["create"])
assert create_call["session"] is unbound_session
assert create_call["app_model"] is app_model
assert cast(SimpleNamespace, create_call["current_user"]).id == account_id
assert cast(Account, create_call["current_user"]).id == account_id
assert unwrap(AgentChatMessageStopApi.post)(
AgentChatMessageStopApi(), unbound_session, "tenant-1", account_id, agent_id, "task-1"
) == ({"result": "success"}, 200)
@@ -1656,7 +1661,7 @@ def test_agent_build_chat_finalize_route_resolves_app_from_agent_id(
app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str, unbound_session: Session
) -> None:
agent_id = "00000000-0000-0000-0000-000000000001"
app_model = SimpleNamespace(id="app-1", tenant_id="tenant-1", mode="agent")
app_model = _app_detail_obj(id="app-1", tenant_id="tenant-1", mode=AppMode.AGENT)
captured: dict[str, object] = {}
def resolve_agent_app_model(**kwargs: object) -> object:
@@ -1675,7 +1680,7 @@ def test_agent_build_chat_finalize_route_resolves_app_from_agent_id(
monkeypatch.setattr(completion_controller, "_create_build_chat_finalization_message", create_finalization_message)
with app.test_request_context():
assert unwrap(AgentBuildChatFinalizeApi.post)(
AgentBuildChatFinalizeApi(), unbound_session, "tenant-1", SimpleNamespace(id=account_id), agent_id
AgentBuildChatFinalizeApi(), unbound_session, "tenant-1", _account(account_id=account_id), agent_id
) == {"result": "generated"}
assert cast(dict[str, object], captured["resolve"]) == {"tenant_id": "tenant-1", "agent_id": agent_id}
finalize_call = cast(dict[str, object], captured["finalize"])
@@ -1683,13 +1688,13 @@ def test_agent_build_chat_finalize_route_resolves_app_from_agent_id(
assert finalize_call["app_model"] is app_model
assert finalize_call["current_tenant_id"] == "tenant-1"
assert finalize_call["agent_id"] == agent_id
assert cast(SimpleNamespace, finalize_call["current_user"]).id == account_id
assert cast(Account, finalize_call["current_user"]).id == account_id
def test_build_chat_finalization_helper_forces_debug_build_and_push_prompt(
app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str, unbound_session: Session
) -> None:
app_model = SimpleNamespace(id="app-1", tenant_id="tenant-1", mode="agent")
app_model = _app_detail_obj(id="app-1", tenant_id="tenant-1", mode=AppMode.AGENT)
captured: dict[str, object] = {}
def resolve_debug_conversation(**kwargs: object) -> str:
@@ -1709,16 +1714,17 @@ def test_build_chat_finalization_helper_forces_debug_build_and_push_prompt(
with app.test_request_context(headers={"X-Trace-Id": "trace-1"}):
result = completion_controller._create_build_chat_finalization_message(
current_tenant_id="tenant-1",
current_user=SimpleNamespace(id=account_id),
current_user=_account(account_id=account_id),
app_model=app_model,
agent_id="agent-1",
session=unbound_session,
)
assert result == ({"result": "success"}, 200)
assert captured["resolve_debug_conversation"] == {
resolve_call = cast(dict[str, object], captured["resolve_debug_conversation"])
assert resolve_call.pop("current_user") is not None
assert resolve_call == {
"session": unbound_session,
"current_tenant_id": "tenant-1",
"current_user": SimpleNamespace(id=account_id),
"app_model": app_model,
"agent_id": "agent-1",
"draft_type": AgentConfigDraftType.DEBUG_BUILD,
@@ -1805,8 +1811,8 @@ def test_agent_chat_helper_resolves_scoped_conversation_and_forces_streaming(
expected_start_new: bool,
unbound_session: Session,
) -> None:
app_model = SimpleNamespace(id="app-1", tenant_id="tenant-1", mode="agent")
current_user = SimpleNamespace(id=account_id)
app_model = _app_detail_obj(id="app-1", tenant_id="tenant-1", mode=AppMode.AGENT)
current_user = _account(account_id=account_id)
captured: dict[str, object] = {}
def generate(**kwargs: object) -> dict[str, object]:
@@ -1850,8 +1856,8 @@ def test_agent_chat_helper_resolves_scoped_conversation_and_forces_streaming(
def test_agent_chat_helper_ignores_private_exit_intent_payload_key(
app: Flask, monkeypatch: pytest.MonkeyPatch, account_id: str, unbound_session: Session
) -> None:
app_model = SimpleNamespace(id="app-1", tenant_id="tenant-1", mode="agent")
current_user = SimpleNamespace(id=account_id)
app_model = _app_detail_obj(id="app-1", tenant_id="tenant-1", mode=AppMode.AGENT)
current_user = _account(account_id=account_id)
captured: dict[str, object] = {}
def generate(**kwargs: object) -> dict[str, object]:
@@ -1907,7 +1913,7 @@ def test_agent_chat_helper_rejects_foreign_debug_conversation_before_generation(
expected_draft_type: AgentConfigDraftType,
unbound_session: Session,
) -> None:
app_model = SimpleNamespace(id="app-1", tenant_id="tenant-1", mode="agent")
app_model = _app_detail_obj(id="app-1", tenant_id="tenant-1", mode=AppMode.AGENT)
generate = MagicMock()
resolve_debug_conversation = MagicMock(return_value="owned-conversation")
monkeypatch.setattr(completion_controller.AppGenerateService, "generate", generate)
@@ -1928,7 +1934,7 @@ def test_agent_chat_helper_rejects_foreign_debug_conversation_before_generation(
with pytest.raises(NotFound):
completion_controller._create_chat_message(
current_tenant_id="tenant-1",
current_user=SimpleNamespace(id=account_id),
current_user=_account(account_id=account_id),
app_model=app_model,
agent_id="agent-1",
session=unbound_session,
@@ -1964,14 +1970,14 @@ def test_resolve_current_user_agent_debug_conversation_uses_agent_or_backing_app
def get_app_backing_agent(self, **kwargs: object) -> object:
calls.append({"get_app_backing_agent": kwargs})
return SimpleNamespace(id="backing-agent")
return Agent(id="backing-agent")
monkeypatch.setattr(completion_controller, "AgentRosterService", FakeRosterService)
explicit_id = completion_controller._resolve_current_user_agent_debug_conversation_id(
session="session-1", # type: ignore[arg-type]
current_tenant_id="tenant-1",
current_user=SimpleNamespace(id="account-1"),
app_model=SimpleNamespace(id="app-1"),
current_user=_account(),
app_model=_app_detail_obj(id="app-1"),
agent_id="agent-1",
draft_type=AgentConfigDraftType.DRAFT,
start_new=True,
@@ -1979,16 +1985,16 @@ def test_resolve_current_user_agent_debug_conversation_uses_agent_or_backing_app
fallback_id = completion_controller._resolve_current_user_agent_debug_conversation_id(
session="session-1", # type: ignore[arg-type]
current_tenant_id="tenant-1",
current_user=SimpleNamespace(id="account-1"),
app_model=SimpleNamespace(id="app-1"),
current_user=_account(),
app_model=_app_detail_obj(id="app-1"),
agent_id=None,
draft_type=AgentConfigDraftType.DEBUG_BUILD,
)
fallback_preview_id = completion_controller._resolve_current_user_agent_debug_conversation_id(
session="session-1", # type: ignore[arg-type]
current_tenant_id="tenant-1",
current_user=SimpleNamespace(id="account-1"),
app_model=SimpleNamespace(id="app-1"),
current_user=_account(),
app_model=_app_detail_obj(id="app-1"),
agent_id=None,
draft_type=AgentConfigDraftType.DRAFT,
start_new=True,
@@ -1996,8 +2002,8 @@ def test_resolve_current_user_agent_debug_conversation_uses_agent_or_backing_app
current_preview_id = completion_controller._resolve_current_user_agent_debug_conversation_id(
session="session-1", # type: ignore[arg-type]
current_tenant_id="tenant-1",
current_user=SimpleNamespace(id="account-1"),
app_model=SimpleNamespace(id="app-1"),
current_user=_account(),
app_model=_app_detail_obj(id="app-1"),
agent_id="agent-1",
draft_type=AgentConfigDraftType.DRAFT,
)
@@ -2070,12 +2076,12 @@ def test_agent_chat_helper_maps_generation_errors(
expected: type[Exception],
unbound_session: Session,
) -> None:
app_model = SimpleNamespace(id="app-1", mode="chat")
app_model = _app_detail_obj(id="app-1", mode=AppMode.CHAT)
monkeypatch.setattr(completion_controller.AppGenerateService, "generate", lambda **_: (_ for _ in ()).throw(error))
with app.test_request_context(json={"inputs": {}, "query": "hello"}):
with pytest.raises(expected):
completion_controller._create_chat_message(
current_user=SimpleNamespace(id="account-1"), app_model=app_model, session=unbound_session
current_user=_account(), app_model=app_model, session=unbound_session
)
@@ -2084,8 +2090,8 @@ def test_agent_chat_message_routes_resolve_app_from_agent_id(
) -> None:
agent_id = "00000000-0000-0000-0000-000000000001"
message_id = "00000000-0000-0000-0000-000000000002"
app_model = SimpleNamespace(id="app-1", mode="agent")
current_user = SimpleNamespace(id="account-1")
app_model = _app_detail_obj(id="app-1", mode=AppMode.AGENT)
current_user = _account()
captured: dict[str, object] = {}
resolver_calls: list[dict[str, object]] = []
@@ -2195,7 +2201,7 @@ def test_list_chat_messages_supports_first_id_pagination(
f"/console/api/agent/agent-1/chat-messages?conversation_id={conversation_id}&first_id={first_message_id}&limit=1"
):
result = message_controller._list_chat_messages(
session=sqlite_session, app_model=SimpleNamespace(id=app_id, mode="chat")
session=sqlite_session, app_model=_app_detail_obj(id=app_id, mode=AppMode.CHAT)
)
assert result == {"data": [older_message_id], "limit": 1, "has_more": True}
@@ -2213,8 +2219,8 @@ def test_list_agent_chat_messages_uses_current_user_conversation(
message_id=message_id,
created_at=datetime(2025, 1, 1),
)
current_user = SimpleNamespace(id="account-1")
app_model = SimpleNamespace(id=app_id, mode="agent")
current_user = _account()
app_model = _app_detail_obj(id=app_id, mode=AppMode.AGENT)
captured: dict[str, object] = {}
class FakeMessagePaginationResponse:
@@ -2257,8 +2263,8 @@ def test_list_agent_chat_messages_rejects_foreign_conversation(
with pytest.raises(NotFound):
message_controller._list_chat_messages(
session=unbound_session,
app_model=SimpleNamespace(id="app-1", mode="agent"),
current_user=SimpleNamespace(id="account-1"),
app_model=_app_detail_obj(id="app-1", mode=AppMode.AGENT),
current_user=_account(),
)
@@ -2278,8 +2284,8 @@ def test_update_message_feedback_rejects_empty_rating_without_existing_feedback(
with pytest.raises(ValueError, match="rating cannot be None"):
message_controller._update_message_feedback(
session=sqlite_session,
current_user=SimpleNamespace(id="account-1"),
app_model=SimpleNamespace(id=app_id),
current_user=_account(),
app_model=_app_detail_obj(id=app_id),
)
assert message.admin_feedback_with_session(session=sqlite_session) is None
@@ -2319,8 +2325,8 @@ def test_get_message_suggested_questions_maps_service_errors(
with pytest.raises(expected):
message_controller._get_message_suggested_questions(
session=unbound_session,
current_user=SimpleNamespace(id="account-1"),
app_model=SimpleNamespace(id="app-1"),
current_user=_account(),
app_model=_app_detail_obj(id="app-1"),
message_id="00000000-0000-0000-0000-000000000002",
)
@@ -5,13 +5,18 @@ import pytest
from sqlalchemy.orm import Session
from controllers.console.agent import app_helpers
from models.model import App, AppMode
def _app() -> App:
return App(id="app-1", tenant_id="tenant-1", name="Agent App", mode=AppMode.AGENT)
def test_resolve_agent_app_model_reuses_caller_session(
monkeypatch: pytest.MonkeyPatch, unbound_session: Session
) -> None:
session = unbound_session
app = MagicMock()
app = _app()
service = MagicMock()
service.get_agent_app_model.return_value = app
service_factory = MagicMock(return_value=service)
@@ -35,7 +40,7 @@ def test_resolve_agent_runtime_app_model_reuses_caller_session(
monkeypatch: pytest.MonkeyPatch, unbound_session: Session
) -> None:
session = unbound_session
app = MagicMock()
app = _app()
service = MagicMock()
service.get_agent_runtime_app_model.return_value = app
service_factory = MagicMock(return_value=service)
@@ -8,6 +8,7 @@ from dify_agent.client import DifyAgentClientError, DifyAgentHTTPError, DifyAgen
from dify_agent.protocol import BindingFileListResponse, BindingFileReadResponse
from controllers.console import agent_app_sandbox as module
from models.account import Account
from models.model import App, AppMode, IconType
from services.agent_app_sandbox_service import AgentSandboxDownload, AgentSandboxInfo, AgentSandboxInspectorError
@@ -139,6 +140,12 @@ def _app_model(app_id: str = "app-1") -> App:
)
def _account() -> Account:
account = Account(name="Sandbox Tester", email="sandbox-tester@example.com")
account.id = "account-1"
return account
@pytest.mark.parametrize(
"method",
[
@@ -187,7 +194,7 @@ def test_handle_maps_sandbox_and_agent_backend_errors() -> None:
def test_agent_app_sandbox_resources_proxy_service(monkeypatch: pytest.MonkeyPatch) -> None:
service = _AgentAppService()
account = SimpleNamespace(id="account-1")
account = _account()
monkeypatch.setattr(module, "AgentAppSandboxService", lambda: service)
monkeypatch.setattr(
module,
@@ -226,7 +233,7 @@ def test_agent_app_sandbox_resource_returns_normalized_errors(monkeypatch: pytes
raise AgentSandboxInspectorError("no_active_binding", "no active binding", status_code=404)
monkeypatch.setattr(module, "AgentAppSandboxService", FailingService)
account = SimpleNamespace(id="account-1")
account = _account()
monkeypatch.setattr(
module,
"query_params_from_request",
@@ -262,7 +269,7 @@ def test_workflow_agent_sandbox_resources_proxy_service(monkeypatch: pytest.Monk
req_data = module.WorkflowAgentSandboxDownloadPayload.model_validate(
{"node_execution_id": "execution-1", "path": "download.txt"}
)
account = SimpleNamespace(id="account-1")
account = _account()
download = unwrap(module.WorkflowAgentSandboxDownloadResource.post)(
object(), req_data, "tenant-1", account, "app-1", "run-1", "agent-node"
)
@@ -10,6 +10,18 @@ from flask import Flask
from werkzeug.exceptions import BadRequest
from controllers.console.app import statistic as statistic_module
from models.account import Account
from models.model import App
def _account() -> Account:
account = Account(name="Statistics Tester", email="statistics-tester@example.com", timezone="UTC")
account.id = "account-1"
return account
def _app_model() -> App:
return App(id="app-1", tenant_id="tenant-1", name="Statistics App")
class _ConnContext:
@@ -56,8 +68,8 @@ def test_daily_message_statistic_returns_rows(app: Flask, monkeypatch: pytest.Mo
response = method(
api,
SimpleNamespace(start=None, end=None),
SimpleNamespace(timezone="UTC"),
app_model=SimpleNamespace(id="app-1"),
_account(),
app_model=_app_model(),
)
assert _json_payload(response) == {"data": [{"date": "2024-01-01", "message_count": 3}]}
@@ -75,8 +87,8 @@ def test_daily_conversation_statistic_returns_rows(app: Flask, monkeypatch: pyte
response = method(
api,
SimpleNamespace(start=None, end=None),
SimpleNamespace(timezone="UTC"),
app_model=SimpleNamespace(id="app-1"),
_account(),
app_model=_app_model(),
)
assert _json_payload(response) == {"data": [{"date": "2024-01-02", "conversation_count": 5}]}
@@ -94,8 +106,8 @@ def test_daily_token_cost_statistic_returns_rows(app: Flask, monkeypatch: pytest
response = method(
api,
SimpleNamespace(start=None, end=None),
SimpleNamespace(timezone="UTC"),
app_model=SimpleNamespace(id="app-1"),
_account(),
app_model=_app_model(),
)
data = _json_payload(response)
@@ -117,8 +129,8 @@ def test_daily_terminals_statistic_returns_rows(app: Flask, monkeypatch: pytest.
response = method(
api,
SimpleNamespace(start=None, end=None),
SimpleNamespace(timezone="UTC"),
app_model=SimpleNamespace(id="app-1"),
_account(),
app_model=_app_model(),
)
assert _json_payload(response) == {"data": [{"date": "2024-01-04", "terminal_count": 7}]}
@@ -149,8 +161,8 @@ def test_daily_message_statistic_with_invalid_time_range(app: Flask, monkeypatch
method(
api,
SimpleNamespace(start=None, end=None),
SimpleNamespace(timezone="UTC"),
app_model=SimpleNamespace(id="app-1"),
_account(),
app_model=_app_model(),
)
@@ -170,8 +182,8 @@ def test_daily_message_statistic_multiple_rows(app: Flask, monkeypatch: pytest.M
response = method(
api,
SimpleNamespace(start=None, end=None),
SimpleNamespace(timezone="UTC"),
app_model=SimpleNamespace(id="app-1"),
_account(),
app_model=_app_model(),
)
data = _json_payload(response)
@@ -189,8 +201,8 @@ def test_daily_message_statistic_empty_result(app: Flask, monkeypatch: pytest.Mo
response = method(
api,
SimpleNamespace(start=None, end=None),
SimpleNamespace(timezone="UTC"),
app_model=SimpleNamespace(id="app-1"),
_account(),
app_model=_app_model(),
)
assert _json_payload(response) == {"data": []}
@@ -213,8 +225,8 @@ def test_daily_conversation_statistic_with_time_range(app: Flask, monkeypatch: p
response = method(
api,
SimpleNamespace(start=None, end=None),
SimpleNamespace(timezone="UTC"),
app_model=SimpleNamespace(id="app-1"),
_account(),
app_model=_app_model(),
)
assert _json_payload(response) == {"data": [{"date": "2024-01-02", "conversation_count": 5}]}
@@ -235,8 +247,8 @@ def test_daily_token_cost_with_multiple_currencies(app: Flask, monkeypatch: pyte
response = method(
api,
SimpleNamespace(start=None, end=None),
SimpleNamespace(timezone="UTC"),
app_model=SimpleNamespace(id="app-1"),
_account(),
app_model=_app_model(),
)
data = _json_payload(response)