mirror of
https://github.com/langgenius/dify.git
synced 2026-09-19 02:07:44 +08:00
test: migrate residual namespace ORM entities to real models (#40633)
This commit is contained in:
@@ -10,7 +10,7 @@ from sqlalchemy.orm import Session
|
||||
import core.rag.datasource.keyword.jieba.jieba as jieba_module
|
||||
from core.rag.datasource.keyword.jieba.jieba import Jieba, dumps_with_sets, set_orjson_default
|
||||
from core.rag.models.document import Document
|
||||
from models.dataset import DatasetKeywordTable, DocumentSegment
|
||||
from models.dataset import Dataset, DatasetKeywordTable, DocumentSegment
|
||||
|
||||
|
||||
class _DummyLock:
|
||||
@@ -21,21 +21,26 @@ class _DummyLock:
|
||||
return False
|
||||
|
||||
|
||||
def _dataset_keyword_table(data_source_type: str = "database", keyword_table_dict: dict[str, Any] | None = None):
|
||||
return SimpleNamespace(
|
||||
def _dataset_keyword_table(
|
||||
data_source_type: str = "database", keyword_table_dict: dict[str, Any] | None = None
|
||||
) -> DatasetKeywordTable:
|
||||
keyword_table = DatasetKeywordTable(
|
||||
dataset_id="dataset-1",
|
||||
data_source_type=data_source_type,
|
||||
get_keyword_table_dict=MagicMock(return_value=keyword_table_dict),
|
||||
keyword_table="",
|
||||
)
|
||||
keyword_table.get_keyword_table_dict = MagicMock(return_value=keyword_table_dict)
|
||||
return keyword_table
|
||||
|
||||
|
||||
def _dataset(dataset_keyword_table=None, keyword_number=None):
|
||||
return SimpleNamespace(
|
||||
def _dataset(dataset_keyword_table: DatasetKeywordTable | None = None, keyword_number: int | None = None) -> Dataset:
|
||||
dataset = Dataset(
|
||||
id="dataset-1",
|
||||
tenant_id="tenant-1",
|
||||
keyword_number=keyword_number,
|
||||
get_dataset_keyword_table=MagicMock(return_value=dataset_keyword_table),
|
||||
)
|
||||
dataset.get_dataset_keyword_table = MagicMock(return_value=dataset_keyword_table)
|
||||
return dataset
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -368,8 +373,12 @@ def test_multi_create_segment_keywords_uses_provided_and_extracted_keywords(
|
||||
monkeypatch.setattr(keyword, "_get_dataset_keyword_table", MagicMock(return_value={}))
|
||||
monkeypatch.setattr(keyword, "_save_dataset_keyword_table", MagicMock())
|
||||
|
||||
first_segment = SimpleNamespace(index_node_id="node-1", content="first content", keywords=None)
|
||||
second_segment = SimpleNamespace(index_node_id="node-2", content="second content", keywords=None)
|
||||
first_segment = _segment(index_node_id="node-1")
|
||||
first_segment.content = "first content"
|
||||
first_segment.keywords = None
|
||||
second_segment = _segment(index_node_id="node-2")
|
||||
second_segment.content = "second content"
|
||||
second_segment.keywords = None
|
||||
|
||||
keyword.multi_create_segment_keywords(
|
||||
[
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
from collections import UserString
|
||||
from datetime import datetime
|
||||
from types import SimpleNamespace
|
||||
@@ -19,12 +20,27 @@ from graphon.node_events import NodeRunResult
|
||||
from graphon.nodes import BuiltinNodeTypes
|
||||
from graphon.runtime import VariablePool
|
||||
from graphon.variables.variables import StringVariable
|
||||
from models.workflow import Workflow, WorkflowType
|
||||
|
||||
|
||||
def _build_typed_node_config(node_type: NodeType):
|
||||
return {"id": "node-id", "data": BaseNodeData(type=node_type)}
|
||||
|
||||
|
||||
def _workflow() -> Workflow:
|
||||
"""Build a real transient workflow for single-step orchestration tests."""
|
||||
return Workflow(
|
||||
id="workflow-id",
|
||||
tenant_id="tenant-id",
|
||||
app_id="app-id",
|
||||
type=WorkflowType.WORKFLOW,
|
||||
version=Workflow.VERSION_DRAFT,
|
||||
graph=json.dumps({"nodes": [], "edges": []}),
|
||||
_features="{}",
|
||||
created_by="user-id",
|
||||
)
|
||||
|
||||
|
||||
def _build_minimal_workflow_entry(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
*,
|
||||
@@ -222,11 +238,12 @@ class TestWorkflowEntryRun:
|
||||
class TestWorkflowEntrySingleStepRun:
|
||||
@pytest.mark.parametrize("node_type", [BuiltinNodeTypes.LOOP, BuiltinNodeTypes.ITERATION])
|
||||
def test_rejects_container_nodes(self, node_type):
|
||||
workflow = SimpleNamespace(
|
||||
get_node_config_by_id=lambda _node_id: _build_typed_node_config(node_type),
|
||||
)
|
||||
workflow = _workflow()
|
||||
|
||||
with pytest.raises(ValueError, match="engine-backed debug endpoints"):
|
||||
with (
|
||||
patch.object(workflow, "get_node_config_by_id", return_value=_build_typed_node_config(node_type)),
|
||||
pytest.raises(ValueError, match="engine-backed debug endpoints"),
|
||||
):
|
||||
workflow_entry.WorkflowEntry.single_step_run(
|
||||
workflow=workflow,
|
||||
node_id="node-id",
|
||||
@@ -258,8 +275,14 @@ class TestWorkflowEntrySingleStepRun:
|
||||
selector=["sys", "conversation_id"],
|
||||
)
|
||||
]
|
||||
workflow = _workflow()
|
||||
node_config = {
|
||||
"id": "node-id",
|
||||
"data": BaseNodeData(type=BuiltinNodeTypes.LLM, version="1", memory=object()),
|
||||
}
|
||||
|
||||
with (
|
||||
patch.object(workflow, "get_node_config_by_id", return_value=node_config),
|
||||
patch.object(workflow_entry, "DifyGraphInitContext", return_value=sentinel.graph_init_context),
|
||||
patch.object(
|
||||
workflow_entry,
|
||||
@@ -284,17 +307,6 @@ class TestWorkflowEntrySingleStepRun:
|
||||
return FakeLLMNode()
|
||||
|
||||
dify_node_factory.return_value.create_node.side_effect = _create_node
|
||||
workflow = SimpleNamespace(
|
||||
tenant_id="tenant-id",
|
||||
app_id="app-id",
|
||||
id="workflow-id",
|
||||
graph_dict={"nodes": [], "edges": []},
|
||||
get_node_config_by_id=lambda _node_id: {
|
||||
"id": "node-id",
|
||||
"data": SimpleNamespace(type=BuiltinNodeTypes.LLM, version="1", memory=object()),
|
||||
},
|
||||
)
|
||||
|
||||
node, generator = workflow_entry.WorkflowEntry.single_step_run(
|
||||
workflow=workflow,
|
||||
node_id="node-id",
|
||||
@@ -322,7 +334,13 @@ class TestWorkflowEntrySingleStepRun:
|
||||
def extract_variable_selector_to_variable_mapping(**_kwargs):
|
||||
raise NotImplementedError
|
||||
|
||||
workflow = _workflow()
|
||||
with (
|
||||
patch.object(
|
||||
workflow,
|
||||
"get_node_config_by_id",
|
||||
return_value=_build_typed_node_config(BuiltinNodeTypes.START),
|
||||
),
|
||||
patch.object(workflow_entry, "DifyGraphInitContext", return_value=sentinel.graph_init_context),
|
||||
patch.object(workflow_entry, "GraphRuntimeState", return_value=sentinel.graph_runtime_state),
|
||||
patch.object(workflow_entry, "build_dify_run_context", return_value={"_dify": "context"}),
|
||||
@@ -342,14 +360,6 @@ class TestWorkflowEntrySingleStepRun:
|
||||
),
|
||||
):
|
||||
dify_node_factory.return_value.create_node.return_value = FakeNode()
|
||||
workflow = SimpleNamespace(
|
||||
tenant_id="tenant-id",
|
||||
app_id="app-id",
|
||||
id="workflow-id",
|
||||
graph_dict={"nodes": [], "edges": []},
|
||||
get_node_config_by_id=lambda _node_id: _build_typed_node_config(BuiltinNodeTypes.START),
|
||||
)
|
||||
|
||||
node, generator = workflow_entry.WorkflowEntry.single_step_run(
|
||||
workflow=workflow,
|
||||
node_id="node-id",
|
||||
@@ -391,7 +401,13 @@ class TestWorkflowEntrySingleStepRun:
|
||||
def extract_variable_selector_to_variable_mapping(**_kwargs):
|
||||
return {"question": ["node", "question"]}
|
||||
|
||||
workflow = _workflow()
|
||||
with (
|
||||
patch.object(
|
||||
workflow,
|
||||
"get_node_config_by_id",
|
||||
return_value=_build_typed_node_config(BuiltinNodeTypes.DATASOURCE),
|
||||
),
|
||||
patch.object(workflow_entry, "DifyGraphInitContext", return_value=sentinel.graph_init_context),
|
||||
patch.object(workflow_entry, "GraphRuntimeState", return_value=sentinel.graph_runtime_state),
|
||||
patch.object(workflow_entry, "build_dify_run_context", return_value={"_dify": "context"}),
|
||||
@@ -411,14 +427,6 @@ class TestWorkflowEntrySingleStepRun:
|
||||
),
|
||||
):
|
||||
dify_node_factory.return_value.create_node.return_value = FakeDatasourceNode()
|
||||
workflow = SimpleNamespace(
|
||||
tenant_id="tenant-id",
|
||||
app_id="app-id",
|
||||
id="workflow-id",
|
||||
graph_dict={"nodes": [], "edges": []},
|
||||
get_node_config_by_id=lambda _node_id: _build_typed_node_config(BuiltinNodeTypes.DATASOURCE),
|
||||
)
|
||||
|
||||
node, generator = workflow_entry.WorkflowEntry.single_step_run(
|
||||
workflow=workflow,
|
||||
node_id="node-id",
|
||||
@@ -451,7 +459,13 @@ class TestWorkflowEntrySingleStepRun:
|
||||
def version():
|
||||
return "1"
|
||||
|
||||
workflow = _workflow()
|
||||
with (
|
||||
patch.object(
|
||||
workflow,
|
||||
"get_node_config_by_id",
|
||||
return_value=_build_typed_node_config(BuiltinNodeTypes.START),
|
||||
),
|
||||
patch.object(workflow_entry, "DifyGraphInitContext", return_value=sentinel.graph_init_context),
|
||||
patch.object(workflow_entry, "GraphRuntimeState", return_value=sentinel.graph_runtime_state),
|
||||
patch.object(workflow_entry, "build_dify_run_context", return_value={"_dify": "context"}),
|
||||
@@ -468,14 +482,6 @@ class TestWorkflowEntrySingleStepRun:
|
||||
),
|
||||
):
|
||||
dify_node_factory.return_value.create_node.return_value = FakeNode()
|
||||
workflow = SimpleNamespace(
|
||||
tenant_id="tenant-id",
|
||||
app_id="app-id",
|
||||
id="workflow-id",
|
||||
graph_dict={"nodes": [], "edges": []},
|
||||
get_node_config_by_id=lambda _node_id: _build_typed_node_config(BuiltinNodeTypes.START),
|
||||
)
|
||||
|
||||
with pytest.raises(WorkflowNodeRunFailedError):
|
||||
workflow_entry.WorkflowEntry.single_step_run(
|
||||
workflow=workflow,
|
||||
|
||||
@@ -9,7 +9,6 @@ from __future__ import annotations
|
||||
import json
|
||||
from collections.abc import Sequence
|
||||
from datetime import UTC, datetime
|
||||
from types import SimpleNamespace
|
||||
from typing import Any, Protocol
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
@@ -17,13 +16,26 @@ import pytest
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.workflow.file_reference import build_file_reference
|
||||
from core.workflow.nodes.agent_v2.binding_resolver import WorkflowAgentBindingBundle
|
||||
from graphon.enums import WorkflowExecutionStatus, WorkflowNodeExecutionStatus
|
||||
from models.agent import (
|
||||
Agent,
|
||||
AgentConfigSnapshot,
|
||||
AgentScope,
|
||||
AgentSource,
|
||||
AgentStatus,
|
||||
WorkflowAgentBindingType,
|
||||
WorkflowAgentNodeBinding,
|
||||
)
|
||||
from models.agent_config_entities import (
|
||||
AgentSoulConfig,
|
||||
DeclaredArrayItem,
|
||||
DeclaredOutputConfig,
|
||||
DeclaredOutputType,
|
||||
WorkflowNodeJobConfig,
|
||||
)
|
||||
from models.enums import CreatorUserRole, WorkflowRunTriggeredFrom
|
||||
from models.model import App
|
||||
from models.workflow import (
|
||||
WorkflowNodeExecutionModel,
|
||||
WorkflowNodeExecutionTriggeredFrom,
|
||||
@@ -43,8 +55,8 @@ from services.workflow.node_output_inspector_service import (
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _app_model(*, tenant_id: str = "tenant-1", app_id: str = "app-1"):
|
||||
return SimpleNamespace(tenant_id=tenant_id, id=app_id)
|
||||
def _app_model(*, tenant_id: str = "tenant-1", app_id: str = "app-1") -> App:
|
||||
return App(id=app_id, tenant_id=tenant_id)
|
||||
|
||||
|
||||
def _workflow_run(
|
||||
@@ -168,16 +180,35 @@ def session_for(sqlite_session: Session) -> SessionFor:
|
||||
def _stub_binding_resolver(*, declared_outputs: list[DeclaredOutputConfig]):
|
||||
"""Build a fake ``WorkflowAgentBindingResolver`` whose ``.resolve`` returns
|
||||
a binding with ``node_job_config_dict.declared_outputs``."""
|
||||
binding = SimpleNamespace(
|
||||
binding = WorkflowAgentNodeBinding(
|
||||
id="binding-1",
|
||||
node_job_config_dict={
|
||||
"workflow_prompt": "stub",
|
||||
"declared_outputs": [o.model_dump() for o in declared_outputs],
|
||||
},
|
||||
tenant_id="tenant-1",
|
||||
app_id="app-1",
|
||||
workflow_id="workflow-1",
|
||||
workflow_version="draft",
|
||||
node_id="agent-node-1",
|
||||
binding_type=WorkflowAgentBindingType.INLINE_AGENT,
|
||||
agent_id="agent-1",
|
||||
current_snapshot_id="snapshot-1",
|
||||
node_job_config=WorkflowNodeJobConfig(workflow_prompt="stub", declared_outputs=declared_outputs),
|
||||
)
|
||||
agent = Agent(
|
||||
id="agent-1",
|
||||
tenant_id="tenant-1",
|
||||
name="Agent",
|
||||
scope=AgentScope.WORKFLOW_ONLY,
|
||||
source=AgentSource.WORKFLOW,
|
||||
status=AgentStatus.ACTIVE,
|
||||
)
|
||||
snapshot = AgentConfigSnapshot(
|
||||
id="snapshot-1",
|
||||
tenant_id="tenant-1",
|
||||
agent_id="agent-1",
|
||||
version=1,
|
||||
config_snapshot=AgentSoulConfig(),
|
||||
)
|
||||
bundle = SimpleNamespace(binding=binding, agent=None, snapshot=None)
|
||||
resolver = MagicMock()
|
||||
resolver.resolve.return_value = bundle
|
||||
resolver.resolve.return_value = WorkflowAgentBindingBundle(binding=binding, agent=agent, snapshot=snapshot)
|
||||
return resolver
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user