From 98aa74d38a3595fd78fb93890aed139b559b62e0 Mon Sep 17 00:00:00 2001 From: Rostislav Lan Date: Wed, 5 Aug 2026 11:39:45 +0200 Subject: [PATCH 1/3] test(orchestrator): close L1 gaps for cloud-event execute RHIDP-16048 Cover compiled start.stateName happy path, CE call wiring, and non-object workflowdata unwrap. Co-authored-by: Cursor --- .../src/service/SonataFlowService.test.ts | 43 ++++++++++ .../src/service/api/test-utils.ts | 14 ++++ .../src/service/api/v2.test.ts | 80 +++++++++++++++++++ 3 files changed, 137 insertions(+) diff --git a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/SonataFlowService.test.ts b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/SonataFlowService.test.ts index 564ba64cebb..08f100716f3 100644 --- a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/SonataFlowService.test.ts +++ b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/SonataFlowService.test.ts @@ -296,6 +296,49 @@ describe('SonataFlowService', () => { expect(result?.id).toBe('12345'); }); + it('should use empty CE data when workflowdata is missing or non-object', async () => { + const kafkaServiceOptionsMock: OrchestratorKafkaServiceOptions = { + clientId: 'kafkaClientId', + brokers: ['localhost:9091'], + }; + const sonataFlowServiceWithKafka = new SonataFlowService( + dataIndexServiceMock, + loggerMock, + kafkaServiceOptionsMock, + ); + + const sendMock = jest.fn(); + + jest + .spyOn( + sonataFlowServiceWithKafka.getOrchestratorKafkaImpl() as any, + 'producer', + ) + .mockImplementation(() => ({ + connect: jest.fn(), + send: sendMock, + disconnect: jest.fn(), + })); + + await sonataFlowServiceWithKafka.executeWorkflowAsCloudEvent({ + definitionId, + workflowSource: 'workflowSource', + workflowEventType: 'workflowEventType', + contextAttribute: 'lockid', + inputData: { + workflowdata: 'not-an-object' as any, + }, + }); + + const { messages } = sendMock.mock.calls[0][0]; + const parsed = JSON.parse(messages[0].value); + + expect(parsed.data).toEqual({ + lockid: '12345', + }); + expect(parsed.data.workflowdata).toBeUndefined(); + }); + it('should error on a bad connection', async () => { const kafkaServiceOptionsMock: OrchestratorKafkaServiceOptions = { clientId: 'kafkaClientId', diff --git a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/test-utils.ts b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/test-utils.ts index 9cfdb6e832b..711da9861b3 100644 --- a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/test-utils.ts +++ b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/test-utils.ts @@ -350,6 +350,20 @@ states: }; } +/** Compiled workflows use start.stateName instead of a string start. */ +export function generateTestWorkflowInfoForEventypeWithStartStateName( + id: string = 'test_workflowId', +): WorkflowInfo { + const base = generateTestWorkflowInfoForEventype(id); + return { + ...base, + source: (base.source as string).replace( + 'start: listenToLock', + 'start:\n stateName: listenToLock', + ), + }; +} + export function generateTestExecuteWorkflowResponse( id: string = 'test_execId', ): WorkflowExecutionResponse { diff --git a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/v2.test.ts b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/v2.test.ts index 05dadb518ef..13371e173d4 100644 --- a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/v2.test.ts +++ b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/v2.test.ts @@ -49,6 +49,7 @@ import { generateTestWorkflowInfoForEventypeNoStartStateNameStates, generateTestWorkflowInfoForEventypeNoStartStates, generateTestWorkflowInfoForEventypeWithNoCorrelationContextAttribute, + generateTestWorkflowInfoForEventypeWithStartStateName, generateTestWorkflowOverview, generateTestWorkflowOverviewList, generateWorkflowDefinition, @@ -452,6 +453,85 @@ describe('executeWorkflow as event type', () => { // Assert expect(actualResultV2).toBeDefined(); expect(actualResultV2.id).toBeDefined(); + expect( + mockOrchestratorService.executeWorkflowAsCloudEvent, + ).toHaveBeenCalledWith( + expect.objectContaining({ + definitionId: workflowInfo.id, + workflowEventType: 'lock-event', + workflowSource: 'local', + contextAttribute: 'lockid', + inputData: expect.objectContaining({ + workflowdata: workflowData, + initiatorEntity: 'someUserEntity', + targetEntity: 'someEntity', + }), + backstageToken: 'someToken', + }), + ); + }); + + it('executes a given workflow: event type with compiled start.stateName', async () => { + // Arrange + const correlationContextAttributeId = '12345'; + const workflowInfo = + generateTestWorkflowInfoForEventypeWithStartStateName(); + const execResponse = generateTestExecuteWorkflowResponse( + correlationContextAttributeId, + ); + (mockOrchestratorService.fetchWorkflowInfo as jest.Mock).mockResolvedValue( + workflowInfo, + ); + ( + mockOrchestratorService.pingWorkflowService as jest.Mock + ).mockResolvedValue(workflowInfo); + + const processInstance = generateProcessInstanceForEventType( + 1, + correlationContextAttributeId, + ); + + (mockOrchestratorService.fetchInstances as jest.Mock).mockResolvedValue([ + processInstance, + ]); + + ( + mockOrchestratorService.executeWorkflowAsCloudEvent as jest.Mock + ).mockResolvedValue(execResponse); + const workflowData = { + customAttrib: 'My customAttrib', + isEvent: true, + }; + // Act + const actualResultV2: ExecuteWorkflowResponseDTO = await v2.executeWorkflow( + { + inputData: workflowData, + targetEntity: 'someEntity', + }, + workflowInfo.id, + 'someUserEntity', + 'someToken', + ); + + // Assert + expect(actualResultV2).toBeDefined(); + expect(actualResultV2.id).toBeDefined(); + expect( + mockOrchestratorService.executeWorkflowAsCloudEvent, + ).toHaveBeenCalledWith( + expect.objectContaining({ + definitionId: workflowInfo.id, + workflowEventType: 'lock-event', + workflowSource: 'local', + contextAttribute: 'lockid', + inputData: expect.objectContaining({ + workflowdata: workflowData, + initiatorEntity: 'someUserEntity', + targetEntity: 'someEntity', + }), + backstageToken: 'someToken', + }), + ); }); it('executes a given workflow: event type, no instance, not an error', async () => { From 6282e2953e8e703e420c1a697018a85f337bb25e Mon Sep 17 00:00:00 2001 From: Rostislav Lan Date: Wed, 5 Aug 2026 11:51:03 +0200 Subject: [PATCH 2/3] test(orchestrator): harden CE unit tests per review feedback Assert Kafka send was called before inspecting mock args, and fail if the compiled start.stateName fixture rewrite misses. Co-authored-by: Cursor --- .../src/service/SonataFlowService.test.ts | 2 ++ .../src/service/api/test-utils.ts | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/SonataFlowService.test.ts b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/SonataFlowService.test.ts index 08f100716f3..458df1e94cc 100644 --- a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/SonataFlowService.test.ts +++ b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/SonataFlowService.test.ts @@ -330,6 +330,8 @@ describe('SonataFlowService', () => { }, }); + expect(sendMock).toHaveBeenCalledTimes(1); + const { messages } = sendMock.mock.calls[0][0]; const parsed = JSON.parse(messages[0].value); diff --git a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/test-utils.ts b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/test-utils.ts index 711da9861b3..13fcf0c5477 100644 --- a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/test-utils.ts +++ b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/test-utils.ts @@ -355,12 +355,17 @@ export function generateTestWorkflowInfoForEventypeWithStartStateName( id: string = 'test_workflowId', ): WorkflowInfo { const base = generateTestWorkflowInfoForEventype(id); + const startMarker = 'start: listenToLock'; + const compiledStart = 'start:\n stateName: listenToLock'; + const source = base.source as string; + if (!source.includes(startMarker)) { + throw new Error( + 'Failed to rewrite start to start.stateName: base fixture missing "start: listenToLock"', + ); + } return { ...base, - source: (base.source as string).replace( - 'start: listenToLock', - 'start:\n stateName: listenToLock', - ), + source: source.replace(startMarker, compiledStart), }; } From b421989fbc6a9f9c65d612627eabb530e270edd6 Mon Sep 17 00:00:00 2001 From: Rostislav Lan Date: Wed, 5 Aug 2026 13:06:33 +0200 Subject: [PATCH 3/3] test(orchestrator): cover start.stateName fixture rewrite failure path Co-authored-by: Cursor --- .../orchestrator-backend/src/service/api/test-utils.ts | 2 +- .../orchestrator-backend/src/service/api/v2.test.ts | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/test-utils.ts b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/test-utils.ts index 13fcf0c5477..628419237ec 100644 --- a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/test-utils.ts +++ b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/test-utils.ts @@ -353,8 +353,8 @@ states: /** Compiled workflows use start.stateName instead of a string start. */ export function generateTestWorkflowInfoForEventypeWithStartStateName( id: string = 'test_workflowId', + base: WorkflowInfo = generateTestWorkflowInfoForEventype(id), ): WorkflowInfo { - const base = generateTestWorkflowInfoForEventype(id); const startMarker = 'start: listenToLock'; const compiledStart = 'start:\n stateName: listenToLock'; const source = base.source as string; diff --git a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/v2.test.ts b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/v2.test.ts index 13371e173d4..3c6d3b61df7 100644 --- a/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/v2.test.ts +++ b/workspaces/orchestrator/plugins/orchestrator-backend/src/service/api/v2.test.ts @@ -471,6 +471,15 @@ describe('executeWorkflow as event type', () => { ); }); + it('throws when compiled start.stateName fixture cannot rewrite start', () => { + expect(() => + generateTestWorkflowInfoForEventypeWithStartStateName('test_workflowId', { + id: 'test_workflowId', + source: 'id: lock-flow\nstart: somethingElse\n', + }), + ).toThrow(/Failed to rewrite start to start.stateName/); + }); + it('executes a given workflow: event type with compiled start.stateName', async () => { // Arrange const correlationContextAttributeId = '12345';