@open-mercato/core 0.4.2-canary-5d2c419a9b → 0.4.2-canary-9e0237de8e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/dist/generated/entities/workflow_event_trigger/index.js +33 -0
  2. package/dist/generated/entities/workflow_event_trigger/index.js.map +7 -0
  3. package/dist/generated/entities.ids.generated.js +1 -0
  4. package/dist/generated/entities.ids.generated.js.map +2 -2
  5. package/dist/generated/entity-fields-registry.js +2 -0
  6. package/dist/generated/entity-fields-registry.js.map +2 -2
  7. package/dist/modules/auth/events.js +30 -0
  8. package/dist/modules/auth/events.js.map +7 -0
  9. package/dist/modules/business_rules/api/execute/[ruleId]/route.js +145 -0
  10. package/dist/modules/business_rules/api/execute/[ruleId]/route.js.map +7 -0
  11. package/dist/modules/business_rules/data/validators.js +34 -0
  12. package/dist/modules/business_rules/data/validators.js.map +2 -2
  13. package/dist/modules/business_rules/index.js +21 -1
  14. package/dist/modules/business_rules/index.js.map +2 -2
  15. package/dist/modules/business_rules/lib/rule-engine.js +182 -1
  16. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  17. package/dist/modules/catalog/events.js +34 -0
  18. package/dist/modules/catalog/events.js.map +7 -0
  19. package/dist/modules/customers/events.js +49 -0
  20. package/dist/modules/customers/events.js.map +7 -0
  21. package/dist/modules/directory/events.js +23 -0
  22. package/dist/modules/directory/events.js.map +7 -0
  23. package/dist/modules/sales/acl.js +1 -0
  24. package/dist/modules/sales/acl.js.map +2 -2
  25. package/dist/modules/sales/backend/sales/documents/[id]/page.js +12 -0
  26. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  27. package/dist/modules/sales/commands/documents.js +62 -0
  28. package/dist/modules/sales/commands/documents.js.map +2 -2
  29. package/dist/modules/sales/events.js +63 -0
  30. package/dist/modules/sales/events.js.map +7 -0
  31. package/dist/modules/sales/lib/dictionaries.js +3 -0
  32. package/dist/modules/sales/lib/dictionaries.js.map +2 -2
  33. package/dist/modules/sales/lib/frontend/documentDataEvents.js +25 -0
  34. package/dist/modules/sales/lib/frontend/documentDataEvents.js.map +7 -0
  35. package/dist/modules/workflows/acl.js +2 -0
  36. package/dist/modules/workflows/acl.js.map +2 -2
  37. package/dist/modules/workflows/api/instances/route.js +18 -6
  38. package/dist/modules/workflows/api/instances/route.js.map +2 -2
  39. package/dist/modules/workflows/api/tasks/route.js +6 -1
  40. package/dist/modules/workflows/api/tasks/route.js.map +2 -2
  41. package/dist/modules/workflows/backend/definitions/[id]/page.js +9 -1
  42. package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
  43. package/dist/modules/workflows/backend/definitions/[id]/page.meta.js +1 -1
  44. package/dist/modules/workflows/backend/definitions/[id]/page.meta.js.map +2 -2
  45. package/dist/modules/workflows/backend/definitions/create/page.js +24 -15
  46. package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
  47. package/dist/modules/workflows/backend/definitions/create/page.meta.js +1 -1
  48. package/dist/modules/workflows/backend/definitions/create/page.meta.js.map +2 -2
  49. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +150 -132
  50. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  51. package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js +1 -1
  52. package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js.map +2 -2
  53. package/dist/modules/workflows/backend/events/[id]/page.js +1 -1
  54. package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
  55. package/dist/modules/workflows/backend/events/[id]/page.meta.js +2 -2
  56. package/dist/modules/workflows/backend/events/[id]/page.meta.js.map +2 -2
  57. package/dist/modules/workflows/backend/instances/[id]/page.meta.js +2 -2
  58. package/dist/modules/workflows/backend/instances/[id]/page.meta.js.map +2 -2
  59. package/dist/modules/workflows/backend/tasks/[id]/page.js +1 -1
  60. package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
  61. package/dist/modules/workflows/backend/tasks/[id]/page.meta.js +2 -2
  62. package/dist/modules/workflows/backend/tasks/[id]/page.meta.js.map +2 -2
  63. package/dist/modules/workflows/backend/tasks/page.js +5 -6
  64. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  65. package/dist/modules/workflows/cli.js +81 -3
  66. package/dist/modules/workflows/cli.js.map +3 -3
  67. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +481 -0
  68. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +7 -0
  69. package/dist/modules/workflows/components/EventTriggersEditor.js +553 -0
  70. package/dist/modules/workflows/components/EventTriggersEditor.js.map +7 -0
  71. package/dist/modules/workflows/data/entities.js +64 -1
  72. package/dist/modules/workflows/data/entities.js.map +2 -2
  73. package/dist/modules/workflows/data/validators.js +115 -0
  74. package/dist/modules/workflows/data/validators.js.map +2 -2
  75. package/dist/modules/workflows/events.js +38 -0
  76. package/dist/modules/workflows/events.js.map +7 -0
  77. package/dist/modules/workflows/examples/checkout-demo-definition.json +1 -5
  78. package/dist/modules/workflows/examples/order-approval-definition.json +257 -0
  79. package/dist/modules/workflows/examples/order-approval-guard-rules.json +32 -0
  80. package/dist/modules/workflows/lib/activity-executor.js +75 -13
  81. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  82. package/dist/modules/workflows/lib/event-trigger-service.js +308 -0
  83. package/dist/modules/workflows/lib/event-trigger-service.js.map +7 -0
  84. package/dist/modules/workflows/lib/graph-utils.js +71 -2
  85. package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
  86. package/dist/modules/workflows/lib/seeds.js +17 -4
  87. package/dist/modules/workflows/lib/seeds.js.map +2 -2
  88. package/dist/modules/workflows/lib/start-validator.js +33 -23
  89. package/dist/modules/workflows/lib/start-validator.js.map +2 -2
  90. package/dist/modules/workflows/lib/transition-handler.js +157 -45
  91. package/dist/modules/workflows/lib/transition-handler.js.map +3 -3
  92. package/dist/modules/workflows/migrations/Migration20260123143500.js +36 -0
  93. package/dist/modules/workflows/migrations/Migration20260123143500.js.map +7 -0
  94. package/dist/modules/workflows/subscribers/event-trigger.js +78 -0
  95. package/dist/modules/workflows/subscribers/event-trigger.js.map +7 -0
  96. package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js +323 -0
  97. package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js.map +7 -0
  98. package/dist/modules/workflows/widgets/injection/order-approval/widget.js +17 -0
  99. package/dist/modules/workflows/widgets/injection/order-approval/widget.js.map +7 -0
  100. package/dist/modules/workflows/widgets/injection-table.js +19 -0
  101. package/dist/modules/workflows/widgets/injection-table.js.map +7 -0
  102. package/generated/entities/workflow_event_trigger/index.ts +15 -0
  103. package/generated/entities.ids.generated.ts +1 -0
  104. package/generated/entity-fields-registry.ts +2 -0
  105. package/package.json +2 -2
  106. package/src/modules/auth/events.ts +39 -0
  107. package/src/modules/business_rules/api/execute/[ruleId]/route.ts +163 -0
  108. package/src/modules/business_rules/data/validators.ts +40 -0
  109. package/src/modules/business_rules/index.ts +25 -0
  110. package/src/modules/business_rules/lib/rule-engine.ts +281 -1
  111. package/src/modules/catalog/events.ts +45 -0
  112. package/src/modules/customers/events.ts +63 -0
  113. package/src/modules/directory/events.ts +31 -0
  114. package/src/modules/sales/acl.ts +1 -0
  115. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +16 -0
  116. package/src/modules/sales/commands/documents.ts +75 -1
  117. package/src/modules/sales/events.ts +82 -0
  118. package/src/modules/sales/lib/dictionaries.ts +3 -0
  119. package/src/modules/sales/lib/frontend/documentDataEvents.ts +28 -0
  120. package/src/modules/workflows/acl.ts +2 -0
  121. package/src/modules/workflows/api/__tests__/instances.route.test.ts +5 -2
  122. package/src/modules/workflows/api/instances/route.ts +21 -7
  123. package/src/modules/workflows/api/tasks/route.ts +7 -1
  124. package/src/modules/workflows/backend/definitions/[id]/page.meta.ts +1 -1
  125. package/src/modules/workflows/backend/definitions/[id]/page.tsx +9 -0
  126. package/src/modules/workflows/backend/definitions/create/page.meta.ts +1 -1
  127. package/src/modules/workflows/backend/definitions/create/page.tsx +9 -0
  128. package/src/modules/workflows/backend/definitions/visual-editor/page.meta.ts +1 -1
  129. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +21 -3
  130. package/src/modules/workflows/backend/events/[id]/page.meta.ts +2 -2
  131. package/src/modules/workflows/backend/events/[id]/page.tsx +1 -1
  132. package/src/modules/workflows/backend/instances/[id]/page.meta.ts +2 -2
  133. package/src/modules/workflows/backend/tasks/[id]/page.meta.ts +2 -2
  134. package/src/modules/workflows/backend/tasks/[id]/page.tsx +1 -1
  135. package/src/modules/workflows/backend/tasks/page.tsx +5 -6
  136. package/src/modules/workflows/cli.ts +111 -0
  137. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +581 -0
  138. package/src/modules/workflows/components/EventTriggersEditor.tsx +664 -0
  139. package/src/modules/workflows/data/entities.ts +124 -0
  140. package/src/modules/workflows/data/validators.ts +138 -0
  141. package/src/modules/workflows/events.ts +49 -0
  142. package/src/modules/workflows/examples/checkout-demo-definition.json +1 -5
  143. package/src/modules/workflows/examples/order-approval-definition.json +257 -0
  144. package/src/modules/workflows/examples/order-approval-guard-rules.json +32 -0
  145. package/src/modules/workflows/i18n/en.json +71 -0
  146. package/src/modules/workflows/lib/__tests__/activity-executor.test.ts +43 -36
  147. package/src/modules/workflows/lib/__tests__/transition-handler.test.ts +170 -90
  148. package/src/modules/workflows/lib/activity-executor.ts +129 -16
  149. package/src/modules/workflows/lib/event-trigger-service.ts +557 -0
  150. package/src/modules/workflows/lib/graph-utils.ts +117 -2
  151. package/src/modules/workflows/lib/seeds.ts +29 -8
  152. package/src/modules/workflows/lib/start-validator.ts +38 -28
  153. package/src/modules/workflows/lib/transition-handler.ts +208 -55
  154. package/src/modules/workflows/migrations/Migration20260123143500.ts +38 -0
  155. package/src/modules/workflows/subscribers/event-trigger.ts +109 -0
  156. package/src/modules/workflows/widgets/injection/order-approval/widget.client.tsx +446 -0
  157. package/src/modules/workflows/widgets/injection/order-approval/widget.ts +16 -0
  158. package/src/modules/workflows/widgets/injection-table.ts +21 -0
@@ -161,6 +161,7 @@
161
161
  "title": "Workflow Instances",
162
162
  "singular": "Workflow Instance",
163
163
  "plural": "Workflow Instances",
164
+ "duration": "Duration",
164
165
  "fields": {
165
166
  "instanceId": "Instance ID",
166
167
  "workflowId": "Workflow ID",
@@ -563,6 +564,55 @@
563
564
  "CompensationCompleted": "Compensation Completed"
564
565
  }
565
566
  },
567
+ "triggers": {
568
+ "title": "Event Triggers",
569
+ "add": "Add Trigger",
570
+ "description": "Configure events that automatically start this workflow. When a matching event occurs in the system, a new workflow instance will be created with the mapped context.",
571
+ "empty": {
572
+ "title": "No triggers configured",
573
+ "description": "Click \"Add Trigger\" to create an event trigger that automatically starts this workflow."
574
+ },
575
+ "dialog": {
576
+ "create": {
577
+ "title": "Create Event Trigger"
578
+ },
579
+ "edit": {
580
+ "title": "Edit Event Trigger"
581
+ },
582
+ "description": "Configure when this workflow should be automatically started based on system events."
583
+ },
584
+ "delete": {
585
+ "title": "Delete Event Trigger?",
586
+ "description": "This will remove the event trigger. The change will take effect when you save the workflow definition."
587
+ },
588
+ "fields": {
589
+ "name": "Name",
590
+ "description": "Description",
591
+ "eventPattern": "Event Pattern",
592
+ "enabled": "Enabled",
593
+ "priority": "Priority",
594
+ "filterConditions": "Filter Conditions",
595
+ "contextMapping": "Context Mapping",
596
+ "debounceMs": "Debounce (ms)",
597
+ "maxConcurrent": "Max Concurrent Instances"
598
+ },
599
+ "placeholders": {
600
+ "name": "Order Created Trigger",
601
+ "description": "Describe when this trigger should fire...",
602
+ "quickSelect": "Quick select...",
603
+ "unlimited": "Unlimited"
604
+ },
605
+ "hints": {
606
+ "priority": "Higher priority triggers execute first",
607
+ "eventPattern": "Use * as wildcard: \"sales.orders.*\" matches any order event",
608
+ "filterConditions": "Only trigger when the event payload matches these conditions (all must match)",
609
+ "contextMapping": "Map values from the event payload to the workflow's initial context",
610
+ "debounce": "Delay to prevent rapid re-triggers",
611
+ "maxConcurrent": "Limit simultaneous workflow instances"
612
+ },
613
+ "addCondition": "Add Condition",
614
+ "addMapping": "Add Mapping"
615
+ },
566
616
  "common": {
567
617
  "save": "Save",
568
618
  "cancel": "Cancel",
@@ -629,6 +679,27 @@
629
679
  "duplicateNotYetImplemented": "Duplicate functionality is not yet implemented"
630
680
  }
631
681
  },
682
+ "orderApproval": {
683
+ "groupLabel": "Order Approval",
684
+ "groupDescription": "Review and approve or reject this order",
685
+ "pendingTitle": "Pending Approval",
686
+ "pendingDescription": "This order requires approval before processing",
687
+ "decisionLabel": "Decision",
688
+ "approveButton": "Approve",
689
+ "rejectButton": "Reject",
690
+ "commentsLabel": "Comments",
691
+ "commentsPlaceholder": "Add optional comments...",
692
+ "approvedStatus": "Approved",
693
+ "rejectedStatus": "Rejected",
694
+ "requestApproval": "Request Approval",
695
+ "submitDecision": "Submit Decision",
696
+ "noWorkflowActive": "No approval workflow is active for this order.",
697
+ "processing": "Workflow is processing...",
698
+ "completed": "Approval workflow completed.",
699
+ "startError": "Failed to start approval workflow",
700
+ "completeError": "Failed to complete approval task",
701
+ "missingStatuses": "Missing order status entries. Please ensure pending_approval, approved, and rejected statuses exist in the sales.order_status dictionary."
702
+ },
632
703
  "common": {
633
704
  "all": "All",
634
705
  "enabled": "Enabled",
@@ -318,22 +318,24 @@ describe('Activity Executor (Unit Tests)', () => {
318
318
 
319
319
  describe('UPDATE_ENTITY activity', () => {
320
320
  test('should execute UPDATE_ENTITY activity successfully', async () => {
321
- const mockQueryEngine = {
322
- update: jest.fn().mockResolvedValue({ updated: 1 }),
321
+ const mockCommandBus = {
322
+ execute: jest.fn().mockResolvedValue({
323
+ result: { id: 'order-123', status: 'confirmed' },
324
+ logEntry: { id: 'log-123' },
325
+ }),
323
326
  }
324
327
 
325
- mockContainer.resolve.mockReturnValue(mockQueryEngine)
328
+ mockContainer.resolve.mockReturnValue(mockCommandBus)
326
329
 
327
330
  const activity: ActivityDefinition = {
328
331
  activityId: 'activity-8',
329
332
  activityName: 'Update Order Status',
330
333
  activityType: 'UPDATE_ENTITY',
331
334
  config: {
332
- entityType: 'orders',
333
- entityId: 'order-123',
334
- updates: {
335
- status: 'confirmed',
336
- confirmedAt: new Date().toISOString(),
335
+ commandId: 'sales.orders.update',
336
+ input: {
337
+ id: 'order-123',
338
+ statusEntryId: 'status-confirmed-id',
337
339
  },
338
340
  },
339
341
  }
@@ -346,21 +348,22 @@ describe('Activity Executor (Unit Tests)', () => {
346
348
  )
347
349
 
348
350
  expect(result.success).toBe(true)
349
- expect(result.output.updated).toBe(true)
350
- expect(mockQueryEngine.update).toHaveBeenCalledWith({
351
- entity: 'orders',
352
- where: { id: 'order-123' },
353
- data: expect.objectContaining({
354
- status: 'confirmed',
355
- }),
356
- tenantId: testTenantId,
357
- organizationId: testOrgId,
358
- })
351
+ expect(result.output.executed).toBe(true)
352
+ expect(result.output.commandId).toBe('sales.orders.update')
353
+ expect(mockCommandBus.execute).toHaveBeenCalledWith(
354
+ 'sales.orders.update',
355
+ expect.objectContaining({
356
+ input: expect.objectContaining({
357
+ id: 'order-123',
358
+ statusEntryId: 'status-confirmed-id',
359
+ }),
360
+ })
361
+ )
359
362
  })
360
363
 
361
- test('should fail UPDATE_ENTITY if query engine not available', async () => {
364
+ test('should fail UPDATE_ENTITY if command bus not available', async () => {
362
365
  mockContainer.resolve.mockImplementation(() => {
363
- throw new Error('queryEngine not registered')
366
+ throw new Error('commandBus not registered')
364
367
  })
365
368
 
366
369
  const activity: ActivityDefinition = {
@@ -368,9 +371,8 @@ describe('Activity Executor (Unit Tests)', () => {
368
371
  activityName: 'Test Update',
369
372
  activityType: 'UPDATE_ENTITY',
370
373
  config: {
371
- entityType: 'orders',
372
- entityId: 'order-123',
373
- updates: { status: 'confirmed' },
374
+ commandId: 'sales.orders.update',
375
+ input: { id: 'order-123', status: 'confirmed' },
374
376
  },
375
377
  }
376
378
 
@@ -382,23 +384,23 @@ describe('Activity Executor (Unit Tests)', () => {
382
384
  )
383
385
 
384
386
  expect(result.success).toBe(false)
385
- expect(result.error).toContain('queryEngine not registered')
387
+ expect(result.error).toContain('commandBus not registered')
386
388
  })
387
389
 
388
390
  test('should fail UPDATE_ENTITY if missing required fields', async () => {
389
- const mockQueryEngine = {
390
- update: jest.fn().mockResolvedValue({ updated: 1 }),
391
+ const mockCommandBus = {
392
+ execute: jest.fn().mockResolvedValue({ result: {} }),
391
393
  }
392
394
 
393
- mockContainer.resolve.mockReturnValue(mockQueryEngine)
395
+ mockContainer.resolve.mockReturnValue(mockCommandBus)
394
396
 
395
397
  const activity: ActivityDefinition = {
396
398
  activityId: 'activity-10',
397
399
  activityName: 'Invalid Update',
398
400
  activityType: 'UPDATE_ENTITY',
399
401
  config: {
400
- entityType: 'orders',
401
- // Missing entityId and updates
402
+ // Missing commandId
403
+ input: { id: 'order-123' },
402
404
  },
403
405
  }
404
406
 
@@ -410,7 +412,7 @@ describe('Activity Executor (Unit Tests)', () => {
410
412
  )
411
413
 
412
414
  expect(result.success).toBe(false)
413
- expect(result.error).toContain('requires "entityType", "entityId", and "updates"')
415
+ expect(result.error).toContain('requires "commandId"')
414
416
  })
415
417
  })
416
418
 
@@ -1095,13 +1097,16 @@ describe('Activity Executor (Unit Tests)', () => {
1095
1097
  emitEvent: jest.fn().mockResolvedValue(undefined),
1096
1098
  }
1097
1099
 
1098
- const mockQueryEngine = {
1099
- update: jest.fn().mockResolvedValue({ updated: 1 }),
1100
+ const mockCommandBus = {
1101
+ execute: jest.fn().mockResolvedValue({
1102
+ result: { id: 'order-123', status: 'confirmed' },
1103
+ logEntry: { id: 'log-123' },
1104
+ }),
1100
1105
  }
1101
1106
 
1102
1107
  mockContainer.resolve
1103
1108
  .mockReturnValueOnce(mockEventBus) // First activity
1104
- .mockReturnValueOnce(mockQueryEngine) // Second activity
1109
+ .mockReturnValueOnce(mockCommandBus) // Second activity
1105
1110
 
1106
1111
  const activities: ActivityDefinition[] = [
1107
1112
  {
@@ -1118,9 +1123,11 @@ describe('Activity Executor (Unit Tests)', () => {
1118
1123
  activityName: 'Update Entity',
1119
1124
  activityType: 'UPDATE_ENTITY',
1120
1125
  config: {
1121
- entityType: 'orders',
1122
- entityId: 'order-123',
1123
- updates: { status: 'confirmed' },
1126
+ commandId: 'sales.orders.update',
1127
+ input: {
1128
+ id: 'order-123',
1129
+ statusEntryId: 'status-confirmed-id',
1130
+ },
1124
1131
  },
1125
1132
  },
1126
1133
  ]
@@ -462,15 +462,35 @@ describe('Transition Handler (Unit Tests)', () => {
462
462
  })
463
463
 
464
464
  test('should reject transition if pre-conditions fail', async () => {
465
+ // Create a definition with preConditions
466
+ const definitionWithPreConditions = {
467
+ ...mockDefinition,
468
+ definition: {
469
+ ...mockDefinition.definition,
470
+ transitions: [
471
+ { fromStepId: 'start', toStepId: 'step-1' },
472
+ {
473
+ fromStepId: 'step-1',
474
+ toStepId: 'step-2',
475
+ preConditions: [{ ruleId: 'test-guard-rule', required: true }],
476
+ },
477
+ { fromStepId: 'step-2', toStepId: 'end' },
478
+ ],
479
+ },
480
+ }
481
+
465
482
  mockEm.findOne
466
- .mockResolvedValueOnce(mockDefinition) // evaluateTransition
467
- .mockResolvedValueOnce(mockDefinition) // evaluatePreConditions
468
-
469
- ;(ruleEngine.executeRules as jest.Mock).mockResolvedValueOnce({
470
- allowed: false,
471
- executedRules: [],
472
- totalExecutionTime: 10,
473
- errors: ['Pre-condition rule failed'],
483
+ .mockResolvedValueOnce(definitionWithPreConditions) // evaluateTransition
484
+ .mockResolvedValueOnce(definitionWithPreConditions) // evaluatePreConditions
485
+
486
+ ;(ruleEngine.executeRuleByRuleId as jest.Mock).mockResolvedValueOnce({
487
+ success: false,
488
+ ruleId: 'test-guard-rule',
489
+ ruleName: 'Test Guard Rule',
490
+ conditionResult: false,
491
+ actionsExecuted: null,
492
+ executionTime: 10,
493
+ error: undefined,
474
494
  })
475
495
 
476
496
  mockEm.create.mockReturnValue({} as any)
@@ -491,15 +511,35 @@ describe('Transition Handler (Unit Tests)', () => {
491
511
  })
492
512
 
493
513
  test('should log transition rejection event', async () => {
494
- mockEm.findOne
495
- .mockResolvedValueOnce(mockDefinition)
496
- .mockResolvedValueOnce(mockDefinition)
514
+ // Create a definition with preConditions
515
+ const definitionWithPreConditions = {
516
+ ...mockDefinition,
517
+ definition: {
518
+ ...mockDefinition.definition,
519
+ transitions: [
520
+ { fromStepId: 'start', toStepId: 'step-1' },
521
+ {
522
+ fromStepId: 'step-1',
523
+ toStepId: 'step-2',
524
+ preConditions: [{ ruleId: 'test-guard-rule', required: true }],
525
+ },
526
+ { fromStepId: 'step-2', toStepId: 'end' },
527
+ ],
528
+ },
529
+ }
497
530
 
498
- ;(ruleEngine.executeRules as jest.Mock).mockResolvedValueOnce({
499
- allowed: false,
500
- executedRules: [],
501
- totalExecutionTime: 10,
502
- errors: ['Rule XYZ failed'],
531
+ mockEm.findOne
532
+ .mockResolvedValueOnce(definitionWithPreConditions)
533
+ .mockResolvedValueOnce(definitionWithPreConditions)
534
+
535
+ ;(ruleEngine.executeRuleByRuleId as jest.Mock).mockResolvedValueOnce({
536
+ success: false,
537
+ ruleId: 'test-guard-rule',
538
+ ruleName: 'Test Guard Rule',
539
+ conditionResult: false,
540
+ actionsExecuted: null,
541
+ executionTime: 10,
542
+ error: undefined,
503
543
  })
504
544
 
505
545
  mockEm.create.mockReturnValue({} as any)
@@ -527,27 +567,36 @@ describe('Transition Handler (Unit Tests)', () => {
527
567
  })
528
568
 
529
569
  test('should execute transition even if post-conditions fail (warning only)', async () => {
530
- mockEm.findOne.mockReset()
531
- mockEm.findOne
532
- .mockResolvedValueOnce(mockDefinition)
533
- .mockResolvedValueOnce(mockDefinition)
534
- .mockResolvedValueOnce(mockDefinition)
535
- .mockResolvedValueOnce(mockDefinition)
536
- .mockResolvedValueOnce(mockDefinition)
537
- .mockResolvedValue(mockDefinition)
570
+ // Create a definition with postConditions
571
+ const definitionWithPostConditions = {
572
+ ...mockDefinition,
573
+ definition: {
574
+ ...mockDefinition.definition,
575
+ transitions: [
576
+ { fromStepId: 'start', toStepId: 'step-1' },
577
+ {
578
+ fromStepId: 'step-1',
579
+ toStepId: 'step-2',
580
+ postConditions: [{ ruleId: 'test-post-rule', required: true }],
581
+ },
582
+ { fromStepId: 'step-2', toStepId: 'end' },
583
+ ],
584
+ },
585
+ }
538
586
 
539
- ;(ruleEngine.executeRules as jest.Mock)
540
- .mockResolvedValueOnce({
541
- allowed: true,
542
- executedRules: [],
543
- totalExecutionTime: 10,
544
- })
545
- .mockResolvedValueOnce({
546
- allowed: false, // Post-condition failed
547
- executedRules: [],
548
- totalExecutionTime: 5,
549
- errors: ['Post-condition rule failed'],
550
- })
587
+ mockEm.findOne.mockReset()
588
+ mockEm.findOne.mockResolvedValue(definitionWithPostConditions)
589
+
590
+ // Post-condition fails
591
+ ;(ruleEngine.executeRuleByRuleId as jest.Mock).mockResolvedValueOnce({
592
+ success: false,
593
+ ruleId: 'test-post-rule',
594
+ ruleName: 'Test Post Rule',
595
+ conditionResult: false,
596
+ actionsExecuted: null,
597
+ executionTime: 5,
598
+ error: undefined,
599
+ })
551
600
 
552
601
  mockEm.create.mockReturnValue({} as any)
553
602
 
@@ -709,22 +758,33 @@ describe('Transition Handler (Unit Tests)', () => {
709
758
 
710
759
  describe('Business Rules Integration', () => {
711
760
  test('should call rule engine for pre-conditions with correct entityType', async () => {
712
- mockEm.findOne
713
- .mockResolvedValueOnce(mockDefinition)
714
- .mockResolvedValueOnce(mockDefinition)
715
- .mockResolvedValueOnce(mockDefinition)
761
+ // Create a definition with preConditions
762
+ const definitionWithPreConditions = {
763
+ ...mockDefinition,
764
+ definition: {
765
+ ...mockDefinition.definition,
766
+ transitions: [
767
+ { fromStepId: 'start', toStepId: 'step-1' },
768
+ {
769
+ fromStepId: 'step-1',
770
+ toStepId: 'step-2',
771
+ preConditions: [{ ruleId: 'test-guard-rule', required: true }],
772
+ },
773
+ { fromStepId: 'step-2', toStepId: 'end' },
774
+ ],
775
+ },
776
+ }
716
777
 
717
- ;(ruleEngine.executeRules as jest.Mock)
718
- .mockResolvedValueOnce({
719
- allowed: true,
720
- executedRules: [],
721
- totalExecutionTime: 10,
722
- })
723
- .mockResolvedValueOnce({
724
- allowed: true,
725
- executedRules: [],
726
- totalExecutionTime: 5,
727
- })
778
+ mockEm.findOne.mockResolvedValue(definitionWithPreConditions)
779
+
780
+ ;(ruleEngine.executeRuleByRuleId as jest.Mock).mockResolvedValueOnce({
781
+ success: true,
782
+ ruleId: 'test-guard-rule',
783
+ ruleName: 'Test Guard Rule',
784
+ conditionResult: true,
785
+ actionsExecuted: null,
786
+ executionTime: 10,
787
+ })
728
788
 
729
789
  mockEm.create.mockReturnValue({} as any)
730
790
 
@@ -737,9 +797,10 @@ describe('Transition Handler (Unit Tests)', () => {
737
797
  { workflowContext: {} }
738
798
  )
739
799
 
740
- expect(ruleEngine.executeRules).toHaveBeenCalledWith(
800
+ expect(ruleEngine.executeRuleByRuleId).toHaveBeenCalledWith(
741
801
  mockEm,
742
802
  expect.objectContaining({
803
+ ruleId: 'test-guard-rule',
743
804
  entityType: 'workflow:simple-approval:transition',
744
805
  eventType: 'pre_transition',
745
806
  data: expect.objectContaining({
@@ -752,26 +813,34 @@ describe('Transition Handler (Unit Tests)', () => {
752
813
  })
753
814
 
754
815
  test('should call rule engine for post-conditions with correct eventType', async () => {
755
- mockEm.findOne.mockReset()
756
- mockEm.findOne
757
- .mockResolvedValueOnce(mockDefinition)
758
- .mockResolvedValueOnce(mockDefinition)
759
- .mockResolvedValueOnce(mockDefinition)
760
- .mockResolvedValueOnce(mockDefinition)
761
- .mockResolvedValueOnce(mockDefinition)
762
- .mockResolvedValue(mockDefinition)
816
+ // Create a definition with postConditions
817
+ const definitionWithPostConditions = {
818
+ ...mockDefinition,
819
+ definition: {
820
+ ...mockDefinition.definition,
821
+ transitions: [
822
+ { fromStepId: 'start', toStepId: 'step-1' },
823
+ {
824
+ fromStepId: 'step-1',
825
+ toStepId: 'step-2',
826
+ postConditions: [{ ruleId: 'test-post-rule', required: true }],
827
+ },
828
+ { fromStepId: 'step-2', toStepId: 'end' },
829
+ ],
830
+ },
831
+ }
763
832
 
764
- ;(ruleEngine.executeRules as jest.Mock)
765
- .mockResolvedValueOnce({
766
- allowed: true,
767
- executedRules: [],
768
- totalExecutionTime: 10,
769
- })
770
- .mockResolvedValueOnce({
771
- allowed: true,
772
- executedRules: [],
773
- totalExecutionTime: 5,
774
- })
833
+ mockEm.findOne.mockReset()
834
+ mockEm.findOne.mockResolvedValue(definitionWithPostConditions)
835
+
836
+ ;(ruleEngine.executeRuleByRuleId as jest.Mock).mockResolvedValueOnce({
837
+ success: true,
838
+ ruleId: 'test-post-rule',
839
+ ruleName: 'Test Post Rule',
840
+ conditionResult: true,
841
+ actionsExecuted: null,
842
+ executionTime: 5,
843
+ })
775
844
 
776
845
  mockEm.create.mockReturnValue({} as any)
777
846
 
@@ -784,11 +853,11 @@ describe('Transition Handler (Unit Tests)', () => {
784
853
  { workflowContext: {} }
785
854
  )
786
855
 
787
- // Check second call to executeRules (post-conditions)
788
- expect(ruleEngine.executeRules).toHaveBeenNthCalledWith(
789
- 2,
856
+ // Check call to executeRuleByRuleId for post-conditions
857
+ expect(ruleEngine.executeRuleByRuleId).toHaveBeenCalledWith(
790
858
  mockEm,
791
859
  expect.objectContaining({
860
+ ruleId: 'test-post-rule',
792
861
  entityType: 'workflow:simple-approval:transition',
793
862
  eventType: 'post_transition',
794
863
  }),
@@ -797,22 +866,33 @@ describe('Transition Handler (Unit Tests)', () => {
797
866
  })
798
867
 
799
868
  test('should pass workflow context and trigger data to rule engine', async () => {
800
- mockEm.findOne
801
- .mockResolvedValueOnce(mockDefinition)
802
- .mockResolvedValueOnce(mockDefinition)
803
- .mockResolvedValueOnce(mockDefinition)
869
+ // Create a definition with preConditions
870
+ const definitionWithPreConditions = {
871
+ ...mockDefinition,
872
+ definition: {
873
+ ...mockDefinition.definition,
874
+ transitions: [
875
+ { fromStepId: 'start', toStepId: 'step-1' },
876
+ {
877
+ fromStepId: 'step-1',
878
+ toStepId: 'step-2',
879
+ preConditions: [{ ruleId: 'test-guard-rule', required: true }],
880
+ },
881
+ { fromStepId: 'step-2', toStepId: 'end' },
882
+ ],
883
+ },
884
+ }
804
885
 
805
- ;(ruleEngine.executeRules as jest.Mock)
806
- .mockResolvedValueOnce({
807
- allowed: true,
808
- executedRules: [],
809
- totalExecutionTime: 10,
810
- })
811
- .mockResolvedValueOnce({
812
- allowed: true,
813
- executedRules: [],
814
- totalExecutionTime: 5,
815
- })
886
+ mockEm.findOne.mockResolvedValue(definitionWithPreConditions)
887
+
888
+ ;(ruleEngine.executeRuleByRuleId as jest.Mock).mockResolvedValueOnce({
889
+ success: true,
890
+ ruleId: 'test-guard-rule',
891
+ ruleName: 'Test Guard Rule',
892
+ conditionResult: true,
893
+ actionsExecuted: null,
894
+ executionTime: 10,
895
+ })
816
896
 
817
897
  mockEm.create.mockReturnValue({} as any)
818
898
 
@@ -832,7 +912,7 @@ describe('Transition Handler (Unit Tests)', () => {
832
912
  }
833
913
  )
834
914
 
835
- expect(ruleEngine.executeRules).toHaveBeenCalledWith(
915
+ expect(ruleEngine.executeRuleByRuleId).toHaveBeenCalledWith(
836
916
  mockEm,
837
917
  expect.objectContaining({
838
918
  data: expect.objectContaining({