選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

test_offload.py 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import uuid
  2. import pytest
  3. from sqlalchemy.orm import Session, joinedload, selectinload
  4. from extensions.ext_database import db
  5. from libs.datetime_utils import naive_utc_now
  6. from libs.uuid_utils import uuidv7
  7. from models.enums import CreatorUserRole
  8. from models.model import UploadFile
  9. from models.workflow import WorkflowNodeExecutionModel, WorkflowNodeExecutionOffload, WorkflowNodeExecutionTriggeredFrom
  10. @pytest.fixture
  11. def session(flask_req_ctx):
  12. with Session(bind=db.engine, expire_on_commit=False) as session:
  13. yield session
  14. def test_offload(session, setup_account):
  15. tenant_id = str(uuid.uuid4())
  16. app_id = str(uuid.uuid4())
  17. # step 1: create a UploadFile
  18. input_upload_file = UploadFile(
  19. tenant_id=tenant_id,
  20. storage_type="local",
  21. key="fake_storage_key",
  22. name="test_file.txt",
  23. size=1024,
  24. extension="txt",
  25. mime_type="text/plain",
  26. created_by_role=CreatorUserRole.ACCOUNT,
  27. created_by=setup_account.id,
  28. created_at=naive_utc_now(),
  29. used=False,
  30. )
  31. output_upload_file = UploadFile(
  32. tenant_id=tenant_id,
  33. storage_type="local",
  34. key="fake_storage_key",
  35. name="test_file.txt",
  36. size=1024,
  37. extension="txt",
  38. mime_type="text/plain",
  39. created_by_role=CreatorUserRole.ACCOUNT,
  40. created_by=setup_account.id,
  41. created_at=naive_utc_now(),
  42. used=False,
  43. )
  44. session.add(input_upload_file)
  45. session.add(output_upload_file)
  46. session.flush()
  47. # step 2: create a WorkflowNodeExecutionModel
  48. node_execution = WorkflowNodeExecutionModel(
  49. id=str(uuid.uuid4()),
  50. tenant_id=tenant_id,
  51. app_id=app_id,
  52. workflow_id=str(uuid.uuid4()),
  53. triggered_from=WorkflowNodeExecutionTriggeredFrom.WORKFLOW_RUN,
  54. index=1,
  55. node_id="test_node_id",
  56. node_type="test",
  57. title="Test Node",
  58. status="succeeded",
  59. created_by_role=CreatorUserRole.ACCOUNT.value,
  60. created_by=setup_account.id,
  61. )
  62. session.add(node_execution)
  63. session.flush()
  64. # step 3: create a WorkflowNodeExecutionOffload
  65. offload = WorkflowNodeExecutionOffload(
  66. id=uuidv7(),
  67. tenant_id=tenant_id,
  68. app_id=app_id,
  69. node_execution_id=node_execution.id,
  70. inputs_file_id=input_upload_file.id,
  71. outputs_file_id=output_upload_file.id,
  72. )
  73. session.add(offload)
  74. session.flush()
  75. # Test preloading - this should work without raising LazyLoadError
  76. result = (
  77. session.query(WorkflowNodeExecutionModel)
  78. .options(
  79. selectinload(WorkflowNodeExecutionModel.offload_data).options(
  80. joinedload(
  81. WorkflowNodeExecutionOffload.inputs_file,
  82. ),
  83. joinedload(
  84. WorkflowNodeExecutionOffload.outputs_file,
  85. ),
  86. )
  87. )
  88. .filter(WorkflowNodeExecutionModel.id == node_execution.id)
  89. .first()
  90. )
  91. # Verify the relationships are properly loaded
  92. assert result is not None
  93. assert result.offload_data is not None
  94. assert result.offload_data.inputs_file is not None
  95. assert result.offload_data.inputs_file.id == input_upload_file.id
  96. assert result.offload_data.inputs_file.name == "test_file.txt"
  97. # Test the computed properties
  98. assert result.inputs_truncated is True
  99. assert result.outputs_truncated is False
  100. assert False
  101. def _test_offload_save(session, setup_account):
  102. tenant_id = str(uuid.uuid4())
  103. app_id = str(uuid.uuid4())
  104. # step 1: create a UploadFile
  105. input_upload_file = UploadFile(
  106. tenant_id=tenant_id,
  107. storage_type="local",
  108. key="fake_storage_key",
  109. name="test_file.txt",
  110. size=1024,
  111. extension="txt",
  112. mime_type="text/plain",
  113. created_by_role=CreatorUserRole.ACCOUNT,
  114. created_by=setup_account.id,
  115. created_at=naive_utc_now(),
  116. used=False,
  117. )
  118. output_upload_file = UploadFile(
  119. tenant_id=tenant_id,
  120. storage_type="local",
  121. key="fake_storage_key",
  122. name="test_file.txt",
  123. size=1024,
  124. extension="txt",
  125. mime_type="text/plain",
  126. created_by_role=CreatorUserRole.ACCOUNT,
  127. created_by=setup_account.id,
  128. created_at=naive_utc_now(),
  129. used=False,
  130. )
  131. node_execution_id = id = str(uuid.uuid4())
  132. # step 3: create a WorkflowNodeExecutionOffload
  133. offload = WorkflowNodeExecutionOffload(
  134. id=uuidv7(),
  135. tenant_id=tenant_id,
  136. app_id=app_id,
  137. node_execution_id=node_execution_id,
  138. )
  139. offload.inputs_file = input_upload_file
  140. offload.outputs_file = output_upload_file
  141. # step 2: create a WorkflowNodeExecutionModel
  142. node_execution = WorkflowNodeExecutionModel(
  143. id=str(uuid.uuid4()),
  144. tenant_id=tenant_id,
  145. app_id=app_id,
  146. workflow_id=str(uuid.uuid4()),
  147. triggered_from=WorkflowNodeExecutionTriggeredFrom.WORKFLOW_RUN,
  148. index=1,
  149. node_id="test_node_id",
  150. node_type="test",
  151. title="Test Node",
  152. status="succeeded",
  153. created_by_role=CreatorUserRole.ACCOUNT.value,
  154. created_by=setup_account.id,
  155. )
  156. node_execution.offload_data = offload
  157. session.add(node_execution)
  158. session.flush()
  159. assert False
  160. """
  161. 2025-08-21 15:34:49,570 INFO sqlalchemy.engine.Engine BEGIN (implicit)
  162. 2025-08-21 15:34:49,572 INFO sqlalchemy.engine.Engine INSERT INTO upload_files (id, tenant_id, storage_type, key, name, size, extension, mime_type, created_by_role, created_by, created_at, used, used_by, used_at, hash, source_url) VALUES (%(id__0)s::UUID, %(tenant_id__0)s::UUID, %(storage_type__0)s, %(k ... 410 characters truncated ... (created_at__1)s, %(used__1)s, %(used_by__1)s::UUID, %(used_at__1)s, %(hash__1)s, %(source_url__1)s)
  163. 2025-08-21 15:34:49,572 INFO sqlalchemy.engine.Engine [generated in 0.00009s (insertmanyvalues) 1/1 (unordered)] {'created_at__0': datetime.datetime(2025, 8, 21, 15, 34, 49, 570482), 'id__0': '366621fa-4326-403e-8709-62e4d0de7367', 'storage_type__0': 'local', 'extension__0': 'txt', 'created_by__0': 'ccc7657c-fb48-46bd-8f42-c837b14eab18', 'used_at__0': None, 'used_by__0': None, 'source_url__0': '', 'mime_type__0': 'text/plain', 'created_by_role__0': 'account', 'used__0': False, 'size__0': 1024, 'tenant_id__0': '4c1bbfc9-a28b-4d93-8987-45db78e3269c', 'hash__0': None, 'key__0': 'fake_storage_key', 'name__0': 'test_file.txt', 'created_at__1': datetime.datetime(2025, 8, 21, 15, 34, 49, 570563), 'id__1': '3cdec641-a452-4df0-a9af-4a1a30c27ea5', 'storage_type__1': 'local', 'extension__1': 'txt', 'created_by__1': 'ccc7657c-fb48-46bd-8f42-c837b14eab18', 'used_at__1': None, 'used_by__1': None, 'source_url__1': '', 'mime_type__1': 'text/plain', 'created_by_role__1': 'account', 'used__1': False, 'size__1': 1024, 'tenant_id__1': '4c1bbfc9-a28b-4d93-8987-45db78e3269c', 'hash__1': None, 'key__1': 'fake_storage_key', 'name__1': 'test_file.txt'}
  164. 2025-08-21 15:34:49,576 INFO sqlalchemy.engine.Engine INSERT INTO workflow_node_executions (id, tenant_id, app_id, workflow_id, triggered_from, workflow_run_id, index, predecessor_node_id, node_execution_id, node_id, node_type, title, inputs, process_data, outputs, status, error, execution_metadata, created_by_role, created_by, finished_at) VALUES (%(id)s::UUID, %(tenant_id)s::UUID, %(app_id)s::UUID, %(workflow_id)s::UUID, %(triggered_from)s, %(workflow_run_id)s::UUID, %(index)s, %(predecessor_node_id)s, %(node_execution_id)s, %(node_id)s, %(node_type)s, %(title)s, %(inputs)s, %(process_data)s, %(outputs)s, %(status)s, %(error)s, %(execution_metadata)s, %(created_by_role)s, %(created_by)s::UUID, %(finished_at)s) RETURNING workflow_node_executions.elapsed_time, workflow_node_executions.created_at
  165. 2025-08-21 15:34:49,576 INFO sqlalchemy.engine.Engine [generated in 0.00019s] {'id': '9aac28b6-b6fc-4aea-abdf-21da3227e621', 'tenant_id': '4c1bbfc9-a28b-4d93-8987-45db78e3269c', 'app_id': '79fa81c7-2760-40db-af54-74cb2fea2ce7', 'workflow_id': '95d341e3-381c-4c54-a383-f685a9741053', 'triggered_from': <WorkflowNodeExecutionTriggeredFrom.WORKFLOW_RUN: 'workflow-run'>, 'workflow_run_id': None, 'index': 1, 'predecessor_node_id': None, 'node_execution_id': None, 'node_id': 'test_node_id', 'node_type': 'test', 'title': 'Test Node', 'inputs': None, 'process_data': None, 'outputs': None, 'status': 'succeeded', 'error': None, 'execution_metadata': None, 'created_by_role': 'account', 'created_by': 'ccc7657c-fb48-46bd-8f42-c837b14eab18', 'finished_at': None}
  166. 2025-08-21 15:34:49,579 INFO sqlalchemy.engine.Engine INSERT INTO workflow_node_execution_offload (id, created_at, tenant_id, app_id, node_execution_id, inputs_file_id, outputs_file_id) VALUES (%(id)s::UUID, %(created_at)s, %(tenant_id)s::UUID, %(app_id)s::UUID, %(node_execution_id)s::UUID, %(inputs_file_id)s::UUID, %(outputs_file_id)s::UUID)
  167. 2025-08-21 15:34:49,579 INFO sqlalchemy.engine.Engine [generated in 0.00016s] {'id': '0198cd44-b7ea-724b-9e1b-5f062a2ef45b', 'created_at': datetime.datetime(2025, 8, 21, 15, 34, 49, 579072), 'tenant_id': '4c1bbfc9-a28b-4d93-8987-45db78e3269c', 'app_id': '79fa81c7-2760-40db-af54-74cb2fea2ce7', 'node_execution_id': '9aac28b6-b6fc-4aea-abdf-21da3227e621', 'inputs_file_id': '366621fa-4326-403e-8709-62e4d0de7367', 'outputs_file_id': '3cdec641-a452-4df0-a9af-4a1a30c27ea5'}
  168. 2025-08-21 15:34:49,581 INFO sqlalchemy.engine.Engine SELECT workflow_node_executions.id AS workflow_node_executions_id, workflow_node_executions.tenant_id AS workflow_node_executions_tenant_id, workflow_node_executions.app_id AS workflow_node_executions_app_id, workflow_node_executions.workflow_id AS workflow_node_executions_workflow_id, workflow_node_executions.triggered_from AS workflow_node_executions_triggered_from, workflow_node_executions.workflow_run_id AS workflow_node_executions_workflow_run_id, workflow_node_executions.index AS workflow_node_executions_index, workflow_node_executions.predecessor_node_id AS workflow_node_executions_predecessor_node_id, workflow_node_executions.node_execution_id AS workflow_node_executions_node_execution_id, workflow_node_executions.node_id AS workflow_node_executions_node_id, workflow_node_executions.node_type AS workflow_node_executions_node_type, workflow_node_executions.title AS workflow_node_executions_title, workflow_node_executions.inputs AS workflow_node_executions_inputs, workflow_node_executions.process_data AS workflow_node_executions_process_data, workflow_node_executions.outputs AS workflow_node_executions_outputs, workflow_node_executions.status AS workflow_node_executions_status, workflow_node_executions.error AS workflow_node_executions_error, workflow_node_executions.elapsed_time AS workflow_node_executions_elapsed_time, workflow_node_executions.execution_metadata AS workflow_node_executions_execution_metadata, workflow_node_executions.created_at AS workflow_node_executions_created_at, workflow_node_executions.created_by_role AS workflow_node_executions_created_by_role, workflow_node_executions.created_by AS workflow_node_executions_created_by, workflow_node_executions.finished_at AS workflow_node_executions_finished_at
  169. FROM workflow_node_executions
  170. WHERE workflow_node_executions.id = %(id_1)s::UUID
  171. LIMIT %(param_1)s
  172. 2025-08-21 15:34:49,581 INFO sqlalchemy.engine.Engine [generated in 0.00009s] {'id_1': '9aac28b6-b6fc-4aea-abdf-21da3227e621', 'param_1': 1}
  173. 2025-08-21 15:34:49,585 INFO sqlalchemy.engine.Engine SELECT workflow_node_execution_offload.node_execution_id AS workflow_node_execution_offload_node_execution_id, workflow_node_execution_offload.id AS workflow_node_execution_offload_id, workflow_node_execution_offload.created_at AS workflow_node_execution_offload_created_at, workflow_node_execution_offload.tenant_id AS workflow_node_execution_offload_tenant_id, workflow_node_execution_offload.app_id AS workflow_node_execution_offload_app_id, workflow_node_execution_offload.inputs_file_id AS workflow_node_execution_offload_inputs_file_id, workflow_node_execution_offload.outputs_file_id AS workflow_node_execution_offload_outputs_file_id
  174. FROM workflow_node_execution_offload
  175. WHERE workflow_node_execution_offload.node_execution_id IN (%(primary_keys_1)s::UUID)
  176. 2025-08-21 15:34:49,585 INFO sqlalchemy.engine.Engine [generated in 0.00021s] {'primary_keys_1': '9aac28b6-b6fc-4aea-abdf-21da3227e621'}
  177. 2025-08-21 15:34:49,587 INFO sqlalchemy.engine.Engine SELECT upload_files.id AS upload_files_id, upload_files.tenant_id AS upload_files_tenant_id, upload_files.storage_type AS upload_files_storage_type, upload_files.key AS upload_files_key, upload_files.name AS upload_files_name, upload_files.size AS upload_files_size, upload_files.extension AS upload_files_extension, upload_files.mime_type AS upload_files_mime_type, upload_files.created_by_role AS upload_files_created_by_role, upload_files.created_by AS upload_files_created_by, upload_files.created_at AS upload_files_created_at, upload_files.used AS upload_files_used, upload_files.used_by AS upload_files_used_by, upload_files.used_at AS upload_files_used_at, upload_files.hash AS upload_files_hash, upload_files.source_url AS upload_files_source_url
  178. FROM upload_files
  179. WHERE upload_files.id IN (%(primary_keys_1)s::UUID)
  180. 2025-08-21 15:34:49,587 INFO sqlalchemy.engine.Engine [generated in 0.00012s] {'primary_keys_1': '3cdec641-a452-4df0-a9af-4a1a30c27ea5'}
  181. 2025-08-21 15:34:49,588 INFO sqlalchemy.engine.Engine SELECT upload_files.id AS upload_files_id, upload_files.tenant_id AS upload_files_tenant_id, upload_files.storage_type AS upload_files_storage_type, upload_files.key AS upload_files_key, upload_files.name AS upload_files_name, upload_files.size AS upload_files_size, upload_files.extension AS upload_files_extension, upload_files.mime_type AS upload_files_mime_type, upload_files.created_by_role AS upload_files_created_by_role, upload_files.created_by AS upload_files_created_by, upload_files.created_at AS upload_files_created_at, upload_files.used AS upload_files_used, upload_files.used_by AS upload_files_used_by, upload_files.used_at AS upload_files_used_at, upload_files.hash AS upload_files_hash, upload_files.source_url AS upload_files_source_url
  182. FROM upload_files
  183. WHERE upload_files.id IN (%(primary_keys_1)s::UUID)
  184. 2025-08-21 15:34:49,588 INFO sqlalchemy.engine.Engine [generated in 0.00010s] {'primary_keys_1': '366621fa-4326-403e-8709-62e4d0de7367'}
  185. """
  186. """
  187. upload_file_id: 366621fa-4326-403e-8709-62e4d0de7367 3cdec641-a452-4df0-a9af-4a1a30c27ea5
  188. workflow_node_executions_id: 9aac28b6-b6fc-4aea-abdf-21da3227e621
  189. offload_id: 0198cd44-b7ea-724b-9e1b-5f062a2ef45b
  190. """