Feat add testcontainers test for workflow draft variable service (#23466)

This commit is contained in:
NeatGuyCoding
2025-08-06 09:47:56 +08:00
committed by GitHub
parent 2575eaf1d6
commit ad622cea9e

View File

@@ -0,0 +1,739 @@
import pytest
from faker import Faker
from core.variables.segments import StringSegment
from core.workflow.constants import CONVERSATION_VARIABLE_NODE_ID, SYSTEM_VARIABLE_NODE_ID
from models import App, Workflow
from models.enums import DraftVariableType
from models.workflow import WorkflowDraftVariable
from services.workflow_draft_variable_service import (
UpdateNotSupportedError,
WorkflowDraftVariableService,
)
class TestWorkflowDraftVariableService:
"""
Comprehensive integration tests for WorkflowDraftVariableService using testcontainers.
This test class covers all major functionality of the WorkflowDraftVariableService:
- CRUD operations for workflow draft variables (Create, Read, Update, Delete)
- Variable listing and filtering by type (conversation, system, node)
- Variable updates and resets with proper validation
- Variable deletion operations at different scopes
- Special functionality like prefill and conversation ID retrieval
- Error handling for various edge cases and invalid operations
All tests use the testcontainers infrastructure to ensure proper database isolation
and realistic testing environment with actual database interactions.
"""
@pytest.fixture
def mock_external_service_dependencies(self):
"""
Mock setup for external service dependencies.
WorkflowDraftVariableService doesn't have external dependencies that need mocking,
so this fixture returns an empty dictionary to maintain consistency with other test classes.
This ensures the test structure remains consistent across different service test files.
"""
# WorkflowDraftVariableService doesn't have external dependencies that need mocking
return {}
def _create_test_app(self, db_session_with_containers, mock_external_service_dependencies, fake=None):
"""
Helper method to create a test app with realistic data for testing.
This method creates a complete App instance with all required fields populated
using Faker for generating realistic test data. The app is configured for
workflow mode to support workflow draft variable testing.
Args:
db_session_with_containers: Database session from testcontainers infrastructure
mock_external_service_dependencies: Mock dependencies (unused in this service)
fake: Faker instance for generating test data, creates new instance if not provided
Returns:
App: Created test app instance with all required fields populated
"""
fake = fake or Faker()
app = App()
app.id = fake.uuid4()
app.tenant_id = fake.uuid4()
app.name = fake.company()
app.description = fake.text()
app.mode = "workflow"
app.icon_type = "emoji"
app.icon = "🤖"
app.icon_background = "#FFEAD5"
app.enable_site = True
app.enable_api = True
app.created_by = fake.uuid4()
app.updated_by = app.created_by
from extensions.ext_database import db
db.session.add(app)
db.session.commit()
return app
def _create_test_workflow(self, db_session_with_containers, app, fake=None):
"""
Helper method to create a test workflow associated with an app.
This method creates a Workflow instance using the proper factory method
to ensure all required fields are set correctly. The workflow is configured
as a draft version with basic graph structure for testing workflow variables.
Args:
db_session_with_containers: Database session from testcontainers infrastructure
app: The app to associate the workflow with
fake: Faker instance for generating test data, creates new instance if not provided
Returns:
Workflow: Created test workflow instance with proper configuration
"""
fake = fake or Faker()
workflow = Workflow.new(
tenant_id=app.tenant_id,
app_id=app.id,
type="workflow",
version="draft",
graph='{"nodes": [], "edges": []}',
features="{}",
created_by=app.created_by,
environment_variables=[],
conversation_variables=[],
)
from extensions.ext_database import db
db.session.add(workflow)
db.session.commit()
return workflow
def _create_test_variable(
self, db_session_with_containers, app_id, node_id, name, value, variable_type="conversation", fake=None
):
"""
Helper method to create a test workflow draft variable with proper configuration.
This method creates different types of variables (conversation, system, node) using
the appropriate factory methods to ensure proper initialization. Each variable type
has specific requirements and this method handles the creation logic for all types.
Args:
db_session_with_containers: Database session from testcontainers infrastructure
app_id: ID of the app to associate the variable with
node_id: ID of the node (or special constants like CONVERSATION_VARIABLE_NODE_ID)
name: Name of the variable for identification
value: StringSegment value for the variable content
variable_type: Type of variable ("conversation", "system", "node") determining creation method
fake: Faker instance for generating test data, creates new instance if not provided
Returns:
WorkflowDraftVariable: Created test variable instance with proper type configuration
"""
fake = fake or Faker()
if variable_type == "conversation":
# Create conversation variable using the appropriate factory method
variable = WorkflowDraftVariable.new_conversation_variable(
app_id=app_id,
name=name,
value=value,
description=fake.text(max_nb_chars=20),
)
elif variable_type == "system":
# Create system variable with editable flag and execution context
variable = WorkflowDraftVariable.new_sys_variable(
app_id=app_id,
name=name,
value=value,
node_execution_id=fake.uuid4(),
editable=True,
)
else: # node variable
# Create node variable with visibility and editability settings
variable = WorkflowDraftVariable.new_node_variable(
app_id=app_id,
node_id=node_id,
name=name,
value=value,
node_execution_id=fake.uuid4(),
visible=True,
editable=True,
)
from extensions.ext_database import db
db.session.add(variable)
db.session.commit()
return variable
def test_get_variable_success(self, db_session_with_containers, mock_external_service_dependencies):
"""
Test getting a single variable by ID successfully.
This test verifies that the service can retrieve a specific variable
by its ID and that the returned variable contains the correct data.
It ensures the basic CRUD read operation works correctly for workflow draft variables.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
test_value = StringSegment(value=fake.word())
variable = self._create_test_variable(
db_session_with_containers, app.id, CONVERSATION_VARIABLE_NODE_ID, "test_var", test_value, fake=fake
)
service = WorkflowDraftVariableService(db_session_with_containers)
retrieved_variable = service.get_variable(variable.id)
assert retrieved_variable is not None
assert retrieved_variable.id == variable.id
assert retrieved_variable.name == "test_var"
assert retrieved_variable.app_id == app.id
assert retrieved_variable.get_value().value == test_value.value
def test_get_variable_not_found(self, db_session_with_containers, mock_external_service_dependencies):
"""
Test getting a variable that doesn't exist.
This test verifies that the service returns None when trying to
retrieve a variable with a non-existent ID. This ensures proper
handling of missing data scenarios.
"""
fake = Faker()
non_existent_id = fake.uuid4()
service = WorkflowDraftVariableService(db_session_with_containers)
retrieved_variable = service.get_variable(non_existent_id)
assert retrieved_variable is None
def test_get_draft_variables_by_selectors_success(
self, db_session_with_containers, mock_external_service_dependencies
):
"""
Test getting variables by selectors successfully.
This test verifies that the service can retrieve multiple variables
using selector pairs (node_id, variable_name) and returns the correct
variables for each selector. This is useful for bulk variable retrieval
operations in workflow execution contexts.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
var1_value = StringSegment(value=fake.word())
var2_value = StringSegment(value=fake.word())
var3_value = StringSegment(value=fake.word())
var1 = self._create_test_variable(
db_session_with_containers, app.id, CONVERSATION_VARIABLE_NODE_ID, "var1", var1_value, fake=fake
)
var2 = self._create_test_variable(
db_session_with_containers, app.id, CONVERSATION_VARIABLE_NODE_ID, "var2", var2_value, fake=fake
)
var3 = self._create_test_variable(
db_session_with_containers, app.id, "test_node_1", "var3", var3_value, "node", fake=fake
)
selectors = [
[CONVERSATION_VARIABLE_NODE_ID, "var1"],
[CONVERSATION_VARIABLE_NODE_ID, "var2"],
["test_node_1", "var3"],
]
service = WorkflowDraftVariableService(db_session_with_containers)
retrieved_variables = service.get_draft_variables_by_selectors(app.id, selectors)
assert len(retrieved_variables) == 3
var_names = [var.name for var in retrieved_variables]
assert "var1" in var_names
assert "var2" in var_names
assert "var3" in var_names
for var in retrieved_variables:
if var.name == "var1":
assert var.get_value().value == var1_value.value
elif var.name == "var2":
assert var.get_value().value == var2_value.value
elif var.name == "var3":
assert var.get_value().value == var3_value.value
def test_list_variables_without_values_success(
self, db_session_with_containers, mock_external_service_dependencies
):
"""
Test listing variables without values successfully with pagination.
This test verifies that the service can list variables with pagination
and that the returned variables don't include their values (for performance).
This is important for scenarios where only variable metadata is needed
without loading the actual content.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
for i in range(5):
test_value = StringSegment(value=fake.numerify("value##"))
self._create_test_variable(
db_session_with_containers, app.id, CONVERSATION_VARIABLE_NODE_ID, fake.word(), test_value, fake=fake
)
service = WorkflowDraftVariableService(db_session_with_containers)
result = service.list_variables_without_values(app.id, page=1, limit=3)
assert result.total == 5
assert len(result.variables) == 3
assert result.variables[0].created_at >= result.variables[1].created_at
assert result.variables[1].created_at >= result.variables[2].created_at
for var in result.variables:
assert var.name is not None
assert var.app_id == app.id
def test_list_node_variables_success(self, db_session_with_containers, mock_external_service_dependencies):
"""
Test listing variables for a specific node successfully.
This test verifies that the service can filter and return only
variables associated with a specific node ID. This is crucial for
workflow execution where variables need to be scoped to specific nodes.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
node_id = fake.word()
var1_value = StringSegment(value=fake.word())
var2_value = StringSegment(value=fake.word())
var3_value = StringSegment(value=fake.word())
self._create_test_variable(db_session_with_containers, app.id, node_id, "var1", var1_value, "node", fake=fake)
self._create_test_variable(db_session_with_containers, app.id, node_id, "var2", var3_value, "node", fake=fake)
self._create_test_variable(
db_session_with_containers, app.id, "other_node", "var3", var2_value, "node", fake=fake
)
service = WorkflowDraftVariableService(db_session_with_containers)
result = service.list_node_variables(app.id, node_id)
assert len(result.variables) == 2
for var in result.variables:
assert var.node_id == node_id
assert var.app_id == app.id
var_names = [var.name for var in result.variables]
assert "var1" in var_names
assert "var2" in var_names
assert "var3" not in var_names
def test_list_conversation_variables_success(self, db_session_with_containers, mock_external_service_dependencies):
"""
Test listing conversation variables successfully.
This test verifies that the service can filter and return only
conversation variables, excluding system and node variables.
Conversation variables are user-facing variables that can be
modified during conversation flows.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
conv_var1_value = StringSegment(value=fake.word())
conv_var2_value = StringSegment(value=fake.word())
conv_var1 = self._create_test_variable(
db_session_with_containers, app.id, CONVERSATION_VARIABLE_NODE_ID, "conv_var1", conv_var1_value, fake=fake
)
conv_var2 = self._create_test_variable(
db_session_with_containers, app.id, CONVERSATION_VARIABLE_NODE_ID, "conv_var2", conv_var2_value, fake=fake
)
sys_var_value = StringSegment(value=fake.word())
self._create_test_variable(
db_session_with_containers, app.id, SYSTEM_VARIABLE_NODE_ID, "sys_var", sys_var_value, "system", fake=fake
)
service = WorkflowDraftVariableService(db_session_with_containers)
result = service.list_conversation_variables(app.id)
assert len(result.variables) == 2
for var in result.variables:
assert var.node_id == CONVERSATION_VARIABLE_NODE_ID
assert var.app_id == app.id
assert var.get_variable_type() == DraftVariableType.CONVERSATION
var_names = [var.name for var in result.variables]
assert "conv_var1" in var_names
assert "conv_var2" in var_names
assert "sys_var" not in var_names
def test_update_variable_success(self, db_session_with_containers, mock_external_service_dependencies):
"""
Test updating a variable's name and value successfully.
This test verifies that the service can update both the name and value
of an editable variable and that the changes are persisted correctly.
It also checks that the last_edited_at timestamp is updated appropriately.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
original_value = StringSegment(value=fake.word())
new_value = StringSegment(value=fake.word())
variable = self._create_test_variable(
db_session_with_containers,
app.id,
CONVERSATION_VARIABLE_NODE_ID,
"original_name",
original_value,
fake=fake,
)
service = WorkflowDraftVariableService(db_session_with_containers)
updated_variable = service.update_variable(variable, name="new_name", value=new_value)
assert updated_variable.name == "new_name"
assert updated_variable.get_value().value == new_value.value
assert updated_variable.last_edited_at is not None
from extensions.ext_database import db
db.session.refresh(variable)
assert variable.name == "new_name"
assert variable.get_value().value == new_value.value
assert variable.last_edited_at is not None
def test_update_variable_not_editable(self, db_session_with_containers, mock_external_service_dependencies):
"""
Test that updating a non-editable variable raises an exception.
This test verifies that the service properly prevents updates to
variables that are not marked as editable. This is important for
maintaining data integrity and preventing unauthorized modifications
to system-controlled variables.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
original_value = StringSegment(value=fake.word())
new_value = StringSegment(value=fake.word())
variable = WorkflowDraftVariable.new_sys_variable(
app_id=app.id,
name=fake.word(), # This is typically not editable
value=original_value,
node_execution_id=fake.uuid4(),
editable=False, # Set as non-editable
)
from extensions.ext_database import db
db.session.add(variable)
db.session.commit()
service = WorkflowDraftVariableService(db_session_with_containers)
with pytest.raises(UpdateNotSupportedError) as exc_info:
service.update_variable(variable, name="new_name", value=new_value)
assert "variable not support updating" in str(exc_info.value)
assert variable.id in str(exc_info.value)
def test_reset_conversation_variable_success(self, db_session_with_containers, mock_external_service_dependencies):
"""
Test resetting conversation variable successfully.
This test verifies that the service can reset a conversation variable
to its default value and clear the last_edited_at timestamp.
This functionality is useful for reverting user modifications
back to the original workflow configuration.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
workflow = self._create_test_workflow(db_session_with_containers, app, fake=fake)
from core.variables.variables import StringVariable
conv_var = StringVariable(
id=fake.uuid4(),
name="test_conv_var",
value="default_value",
selector=[CONVERSATION_VARIABLE_NODE_ID, "test_conv_var"],
)
workflow.conversation_variables = [conv_var]
from extensions.ext_database import db
db.session.commit()
modified_value = StringSegment(value=fake.word())
variable = self._create_test_variable(
db_session_with_containers,
app.id,
CONVERSATION_VARIABLE_NODE_ID,
"test_conv_var",
modified_value,
fake=fake,
)
variable.last_edited_at = fake.date_time()
db.session.commit()
service = WorkflowDraftVariableService(db_session_with_containers)
reset_variable = service.reset_variable(workflow, variable)
assert reset_variable is not None
assert reset_variable.get_value().value == "default_value"
assert reset_variable.last_edited_at is None
db.session.refresh(variable)
assert variable.get_value().value == "default_value"
assert variable.last_edited_at is None
def test_delete_variable_success(self, db_session_with_containers, mock_external_service_dependencies):
"""
Test deleting a single variable successfully.
This test verifies that the service can delete a specific variable
and that it's properly removed from the database. It ensures that
the deletion operation is atomic and complete.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
test_value = StringSegment(value=fake.word())
variable = self._create_test_variable(
db_session_with_containers, app.id, CONVERSATION_VARIABLE_NODE_ID, "test_var", test_value, fake=fake
)
from extensions.ext_database import db
assert db.session.query(WorkflowDraftVariable).filter_by(id=variable.id).first() is not None
service = WorkflowDraftVariableService(db_session_with_containers)
service.delete_variable(variable)
assert db.session.query(WorkflowDraftVariable).filter_by(id=variable.id).first() is None
def test_delete_workflow_variables_success(self, db_session_with_containers, mock_external_service_dependencies):
"""
Test deleting all variables for a workflow successfully.
This test verifies that the service can delete all variables
associated with a specific app/workflow. This is useful for
cleanup operations when workflows are deleted or reset.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
for i in range(3):
test_value = StringSegment(value=fake.numerify("value##"))
self._create_test_variable(
db_session_with_containers, app.id, CONVERSATION_VARIABLE_NODE_ID, fake.word(), test_value, fake=fake
)
other_app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
other_value = StringSegment(value=fake.word())
self._create_test_variable(
db_session_with_containers, other_app.id, CONVERSATION_VARIABLE_NODE_ID, fake.word(), other_value, fake=fake
)
from extensions.ext_database import db
app_variables = db.session.query(WorkflowDraftVariable).filter_by(app_id=app.id).all()
other_app_variables = db.session.query(WorkflowDraftVariable).filter_by(app_id=other_app.id).all()
assert len(app_variables) == 3
assert len(other_app_variables) == 1
service = WorkflowDraftVariableService(db_session_with_containers)
service.delete_workflow_variables(app.id)
app_variables_after = db.session.query(WorkflowDraftVariable).filter_by(app_id=app.id).all()
other_app_variables_after = db.session.query(WorkflowDraftVariable).filter_by(app_id=other_app.id).all()
assert len(app_variables_after) == 0
assert len(other_app_variables_after) == 1
def test_delete_node_variables_success(self, db_session_with_containers, mock_external_service_dependencies):
"""
Test deleting all variables for a specific node successfully.
This test verifies that the service can delete all variables
associated with a specific node while preserving variables
for other nodes and conversation variables. This is important
for node-specific cleanup operations in workflow management.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
node_id = fake.word()
for i in range(2):
test_value = StringSegment(value=fake.numerify("node_value##"))
self._create_test_variable(
db_session_with_containers, app.id, node_id, fake.word(), test_value, "node", fake=fake
)
other_node_value = StringSegment(value=fake.word())
self._create_test_variable(
db_session_with_containers, app.id, "other_node", fake.word(), other_node_value, "node", fake=fake
)
conv_value = StringSegment(value=fake.word())
self._create_test_variable(
db_session_with_containers, app.id, CONVERSATION_VARIABLE_NODE_ID, fake.word(), conv_value, fake=fake
)
from extensions.ext_database import db
target_node_variables = db.session.query(WorkflowDraftVariable).filter_by(app_id=app.id, node_id=node_id).all()
other_node_variables = (
db.session.query(WorkflowDraftVariable).filter_by(app_id=app.id, node_id="other_node").all()
)
conv_variables = (
db.session.query(WorkflowDraftVariable)
.filter_by(app_id=app.id, node_id=CONVERSATION_VARIABLE_NODE_ID)
.all()
)
assert len(target_node_variables) == 2
assert len(other_node_variables) == 1
assert len(conv_variables) == 1
service = WorkflowDraftVariableService(db_session_with_containers)
service.delete_node_variables(app.id, node_id)
target_node_variables_after = (
db.session.query(WorkflowDraftVariable).filter_by(app_id=app.id, node_id=node_id).all()
)
other_node_variables_after = (
db.session.query(WorkflowDraftVariable).filter_by(app_id=app.id, node_id="other_node").all()
)
conv_variables_after = (
db.session.query(WorkflowDraftVariable)
.filter_by(app_id=app.id, node_id=CONVERSATION_VARIABLE_NODE_ID)
.all()
)
assert len(target_node_variables_after) == 0
assert len(other_node_variables_after) == 1
assert len(conv_variables_after) == 1
def test_prefill_conversation_variable_default_values_success(
self, db_session_with_containers, mock_external_service_dependencies
):
"""
Test prefill conversation variable default values successfully.
This test verifies that the service can automatically create
conversation variables with default values based on the workflow
configuration when none exist. This is important for initializing
workflow variables with proper defaults from the workflow definition.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
workflow = self._create_test_workflow(db_session_with_containers, app, fake=fake)
from core.variables.variables import StringVariable
conv_var1 = StringVariable(
id=fake.uuid4(),
name="conv_var1",
value="default_value1",
selector=[CONVERSATION_VARIABLE_NODE_ID, "conv_var1"],
)
conv_var2 = StringVariable(
id=fake.uuid4(),
name="conv_var2",
value="default_value2",
selector=[CONVERSATION_VARIABLE_NODE_ID, "conv_var2"],
)
workflow.conversation_variables = [conv_var1, conv_var2]
from extensions.ext_database import db
db.session.commit()
service = WorkflowDraftVariableService(db_session_with_containers)
service.prefill_conversation_variable_default_values(workflow)
draft_variables = (
db.session.query(WorkflowDraftVariable)
.filter_by(app_id=app.id, node_id=CONVERSATION_VARIABLE_NODE_ID)
.all()
)
assert len(draft_variables) == 2
var_names = [var.name for var in draft_variables]
assert "conv_var1" in var_names
assert "conv_var2" in var_names
for var in draft_variables:
assert var.app_id == app.id
assert var.node_id == CONVERSATION_VARIABLE_NODE_ID
assert var.editable is True
assert var.get_variable_type() == DraftVariableType.CONVERSATION
def test_get_conversation_id_from_draft_variable_success(
self, db_session_with_containers, mock_external_service_dependencies
):
"""
Test getting conversation ID from draft variable successfully.
This test verifies that the service can extract the conversation ID
from a system variable named "conversation_id". This is important
for maintaining conversation context across workflow executions.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
conversation_id = fake.uuid4()
conv_id_value = StringSegment(value=conversation_id)
self._create_test_variable(
db_session_with_containers,
app.id,
SYSTEM_VARIABLE_NODE_ID,
"conversation_id",
conv_id_value,
"system",
fake=fake,
)
service = WorkflowDraftVariableService(db_session_with_containers)
retrieved_conv_id = service._get_conversation_id_from_draft_variable(app.id)
assert retrieved_conv_id == conversation_id
def test_get_conversation_id_from_draft_variable_not_found(
self, db_session_with_containers, mock_external_service_dependencies
):
"""
Test getting conversation ID when it doesn't exist.
This test verifies that the service returns None when no
conversation_id variable exists for the app. This ensures
proper handling of missing conversation context scenarios.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
service = WorkflowDraftVariableService(db_session_with_containers)
retrieved_conv_id = service._get_conversation_id_from_draft_variable(app.id)
assert retrieved_conv_id is None
def test_list_system_variables_success(self, db_session_with_containers, mock_external_service_dependencies):
"""
Test listing system variables successfully.
This test verifies that the service can filter and return only
system variables, excluding conversation and node variables.
System variables are internal variables used by the workflow
engine for maintaining state and context.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
sys_var1_value = StringSegment(value=fake.word())
sys_var2_value = StringSegment(value=fake.word())
sys_var1 = self._create_test_variable(
db_session_with_containers, app.id, SYSTEM_VARIABLE_NODE_ID, "sys_var1", sys_var1_value, "system", fake=fake
)
sys_var2 = self._create_test_variable(
db_session_with_containers, app.id, SYSTEM_VARIABLE_NODE_ID, "sys_var2", sys_var2_value, "system", fake=fake
)
conv_var_value = StringSegment(value=fake.word())
self._create_test_variable(
db_session_with_containers, app.id, CONVERSATION_VARIABLE_NODE_ID, "conv_var", conv_var_value, fake=fake
)
service = WorkflowDraftVariableService(db_session_with_containers)
result = service.list_system_variables(app.id)
assert len(result.variables) == 2
for var in result.variables:
assert var.node_id == SYSTEM_VARIABLE_NODE_ID
assert var.app_id == app.id
assert var.get_variable_type() == DraftVariableType.SYS
var_names = [var.name for var in result.variables]
assert "sys_var1" in var_names
assert "sys_var2" in var_names
assert "conv_var" not in var_names
def test_get_variable_by_name_success(self, db_session_with_containers, mock_external_service_dependencies):
"""
Test getting variables by name successfully for different types.
This test verifies that the service can retrieve variables by name
for different variable types (conversation, system, node). This
functionality is important for variable lookup operations during
workflow execution and user interactions.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
test_value = StringSegment(value=fake.word())
conv_var = self._create_test_variable(
db_session_with_containers, app.id, CONVERSATION_VARIABLE_NODE_ID, "test_conv_var", test_value, fake=fake
)
sys_var = self._create_test_variable(
db_session_with_containers, app.id, SYSTEM_VARIABLE_NODE_ID, "test_sys_var", test_value, "system", fake=fake
)
node_var = self._create_test_variable(
db_session_with_containers, app.id, "test_node", "test_node_var", test_value, "node", fake=fake
)
service = WorkflowDraftVariableService(db_session_with_containers)
retrieved_conv_var = service.get_conversation_variable(app.id, "test_conv_var")
assert retrieved_conv_var is not None
assert retrieved_conv_var.name == "test_conv_var"
assert retrieved_conv_var.node_id == CONVERSATION_VARIABLE_NODE_ID
retrieved_sys_var = service.get_system_variable(app.id, "test_sys_var")
assert retrieved_sys_var is not None
assert retrieved_sys_var.name == "test_sys_var"
assert retrieved_sys_var.node_id == SYSTEM_VARIABLE_NODE_ID
retrieved_node_var = service.get_node_variable(app.id, "test_node", "test_node_var")
assert retrieved_node_var is not None
assert retrieved_node_var.name == "test_node_var"
assert retrieved_node_var.node_id == "test_node"
def test_get_variable_by_name_not_found(self, db_session_with_containers, mock_external_service_dependencies):
"""
Test getting variables by name when they don't exist.
This test verifies that the service returns None when trying to
retrieve variables by name that don't exist. This ensures proper
handling of missing variable scenarios for all variable types.
"""
fake = Faker()
app = self._create_test_app(db_session_with_containers, mock_external_service_dependencies, fake=fake)
service = WorkflowDraftVariableService(db_session_with_containers)
retrieved_conv_var = service.get_conversation_variable(app.id, "non_existent_conv_var")
assert retrieved_conv_var is None
retrieved_sys_var = service.get_system_variable(app.id, "non_existent_sys_var")
assert retrieved_sys_var is None
retrieved_node_var = service.get_node_variable(app.id, "test_node", "non_existent_node_var")
assert retrieved_node_var is None