If else node tests (#16916)

This commit is contained in:
Abdul Rahman
2026-01-07 16:07:37 +05:30
committed by GitHub
parent e6b5ae825c
commit 10de7acef3
3 changed files with 788 additions and 169 deletions
@@ -0,0 +1,632 @@
import request from 'supertest';
import {
destroyWorkflowRun,
runWorkflowVersion,
waitForWorkflowCompletion,
} from 'test/integration/graphql/suites/workflow/utils/workflow-run-test.util';
import { StepLogicalOperator, ViewFilterOperand } from 'twenty-shared/types';
import { type StepIfElseBranch } from 'twenty-shared/workflow';
import { v4 } from 'uuid';
import { type WorkflowIfElseAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
const client = request(`http://localhost:${APP_PORT}`);
describe('If/Else Workflow (e2e)', () => {
let createdWorkflowId: string | null = null;
let createdWorkflowVersionId: string | null = null;
let ifElseStepId: string | null = null;
let ifBranchEmptyNodeId: string | null = null;
let elseBranchEmptyNodeId: string | null = null;
let elseIfBranchEmptyNodeId: string | null = null;
let elseIfBranchId: string | null = null;
beforeAll(async () => {
const createWorkflowResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation CreateWorkflow {
createWorkflow(data: {
name: "If/Else Test Workflow"
}) {
id
}
}
`,
});
expect(createWorkflowResponse.body.errors).toBeUndefined();
createdWorkflowId = createWorkflowResponse.body.data.createWorkflow.id;
const getWorkflowResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
query GetWorkflow($id: UUID!) {
workflow(filter: { id: { eq: $id } }) {
id
versions {
edges {
node {
id
status
}
}
}
}
}
`,
variables: { id: createdWorkflowId },
});
expect(getWorkflowResponse.body.errors).toBeUndefined();
expect(
getWorkflowResponse.body.data.workflow.versions.edges.length,
).toBeGreaterThan(0);
createdWorkflowVersionId =
getWorkflowResponse.body.data.workflow.versions.edges[0].node.id;
const manualTrigger = {
name: 'Manual Trigger',
type: 'MANUAL',
settings: {
outputSchema: {
number: {
isLeaf: true,
type: 'number',
value: undefined,
},
},
},
nextStepIds: [],
position: { x: 0, y: 0 },
};
const updateWorkflowVersionResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation UpdateWorkflowVersion($id: UUID!, $data: WorkflowVersionUpdateInput!) {
updateWorkflowVersion(id: $id, data: $data) {
id
trigger
}
}
`,
variables: {
id: createdWorkflowVersionId,
data: {
trigger: manualTrigger,
},
},
});
expect(updateWorkflowVersionResponse.body.errors).toBeUndefined();
const createIfElseStepResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation CreateWorkflowVersionStep($input: CreateWorkflowVersionStepInput!) {
createWorkflowVersionStep(input: $input) {
stepsDiff
}
}
`,
variables: {
input: {
workflowVersionId: createdWorkflowVersionId,
stepType: 'IF_ELSE',
parentStepId: 'trigger',
position: { x: 200, y: 0 },
},
},
});
expect(createIfElseStepResponse.body.errors).toBeUndefined();
const getWorkflowVersionResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
query GetWorkflowVersion($id: UUID!) {
workflowVersion(filter: { id: { eq: $id } }) {
id
steps
}
}
`,
variables: { id: createdWorkflowVersionId },
});
expect(getWorkflowVersionResponse.body.errors).toBeUndefined();
const steps = getWorkflowVersionResponse.body.data.workflowVersion.steps;
const ifElseStep = steps.find(
(step: { type: string }) => step.type === 'IF_ELSE',
);
expect(ifElseStep).toBeDefined();
ifElseStepId = ifElseStep.id;
const branches = ifElseStep.settings.input.branches;
const ifBranch = branches.find(
(branch: StepIfElseBranch) => branch.filterGroupId,
);
const elseBranch = branches.find(
(branch: StepIfElseBranch) => !branch.filterGroupId,
);
expect(ifBranch).toBeDefined();
expect(elseBranch).toBeDefined();
expect(ifBranch.nextStepIds.length).toBeGreaterThan(0);
expect(elseBranch.nextStepIds.length).toBeGreaterThan(0);
ifBranchEmptyNodeId = ifBranch.nextStepIds[0];
elseBranchEmptyNodeId = elseBranch.nextStepIds[0];
expect(ifElseStep.settings.input.stepFilterGroups.length).toBeGreaterThan(
0,
);
expect(ifElseStep.settings.input.stepFilters.length).toBeGreaterThan(0);
expect(ifElseStep.settings.input.branches.length).toBe(2);
const ifFilterGroupId = ifBranch.filterGroupId;
expect(ifFilterGroupId).toBeDefined();
const filterGroup = ifElseStep.settings.input.stepFilterGroups.find(
(g: { id: string }) => g.id === ifFilterGroupId,
);
expect(filterGroup).toBeDefined();
const updateIfElseStepResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation UpdateWorkflowVersionStep($input: UpdateWorkflowVersionStepInput!) {
updateWorkflowVersionStep(input: $input) {
id
type
name
}
}
`,
variables: {
input: {
workflowVersionId: createdWorkflowVersionId,
step: {
...ifElseStep,
settings: {
...ifElseStep.settings,
input: {
...ifElseStep.settings.input,
stepFilters: [
{
id: ifElseStep.settings.input.stepFilters[0].id,
type: 'NUMBER',
stepOutputKey: '{{trigger.number}}',
operand: ViewFilterOperand.IS,
value: '10',
stepFilterGroupId: ifFilterGroupId,
positionInStepFilterGroup: 0,
},
],
},
},
},
},
},
});
expect(updateIfElseStepResponse.body.errors).toBeUndefined();
const createEmptyNodeResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation CreateWorkflowVersionStep($input: CreateWorkflowVersionStepInput!) {
createWorkflowVersionStep(input: $input) {
stepsDiff
}
}
`,
variables: {
input: {
workflowVersionId: createdWorkflowVersionId,
stepType: 'EMPTY',
parentStepId: undefined,
position: { x: 300, y: 100 },
},
},
});
expect(createEmptyNodeResponse.body.errors).toBeUndefined();
const getUpdatedWorkflowVersionResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
query GetWorkflowVersion($id: UUID!) {
workflowVersion(filter: { id: { eq: $id } }) {
id
steps
}
}
`,
variables: { id: createdWorkflowVersionId },
});
const updatedSteps =
getUpdatedWorkflowVersionResponse.body.data.workflowVersion.steps;
const newEmptyNodes = updatedSteps.filter(
(step: { type: string }) => step.type === 'EMPTY',
);
const newElseIfEmptyNode = newEmptyNodes.find(
(node: { id: string }) =>
node.id !== ifBranchEmptyNodeId && node.id !== elseBranchEmptyNodeId,
);
expect(newElseIfEmptyNode).toBeDefined();
elseIfBranchEmptyNodeId = newElseIfEmptyNode.id;
const elseIfFilterGroupId = v4();
const elseIfFilterId = v4();
elseIfBranchId = v4();
const getCurrentIfElseStepResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
query GetWorkflowVersion($id: UUID!) {
workflowVersion(filter: { id: { eq: $id } }) {
id
steps
}
}
`,
variables: { id: createdWorkflowVersionId },
});
const currentSteps =
getCurrentIfElseStepResponse.body.data.workflowVersion.steps;
const currentIfElseStep = currentSteps.find(
(step: { id: string }) => step.id === ifElseStepId,
);
const updatedBranches = [...currentIfElseStep.settings.input.branches];
updatedBranches.splice(
currentIfElseStep.settings.input.branches.length - 1,
0,
{
id: elseIfBranchId,
filterGroupId: elseIfFilterGroupId,
nextStepIds: [elseIfBranchEmptyNodeId],
},
);
const updatedStepFilterGroups = [
...currentIfElseStep.settings.input.stepFilterGroups,
{
id: elseIfFilterGroupId,
logicalOperator: StepLogicalOperator.AND,
positionInStepFilterGroup: 0,
},
];
const updatedStepFilters = [
...currentIfElseStep.settings.input.stepFilters,
{
id: elseIfFilterId,
type: 'NUMBER',
stepOutputKey: '{{trigger.number}}',
operand: ViewFilterOperand.IS,
value: '20',
stepFilterGroupId: elseIfFilterGroupId,
positionInStepFilterGroup: 0,
},
];
const updateIfElseStepWithElseIfResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation UpdateWorkflowVersionStep($input: UpdateWorkflowVersionStepInput!) {
updateWorkflowVersionStep(input: $input) {
id
type
name
}
}
`,
variables: {
input: {
workflowVersionId: createdWorkflowVersionId,
step: {
...currentIfElseStep,
settings: {
...currentIfElseStep.settings,
input: {
...currentIfElseStep.settings.input,
branches: updatedBranches,
stepFilterGroups: updatedStepFilterGroups,
stepFilters: updatedStepFilters,
},
},
},
},
},
});
expect(updateIfElseStepWithElseIfResponse.body.errors).toBeUndefined();
const activateResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation ActivateWorkflowVersion($workflowVersionId: UUID!) {
activateWorkflowVersion(workflowVersionId: $workflowVersionId)
}
`,
variables: { workflowVersionId: createdWorkflowVersionId },
});
expect(activateResponse.body.errors).toBeUndefined();
expect(activateResponse.body.data.activateWorkflowVersion).toBe(true);
});
afterAll(async () => {
if (createdWorkflowId) {
await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation DestroyWorkflow($id: ID!) {
destroyWorkflow(id: $id) {
id
}
}
`,
variables: { id: createdWorkflowId },
});
}
});
const identifyBranches = (branches: StepIfElseBranch[]) => {
const ifBranch = branches[0];
const elseBranch = branches.find((branch) => !branch.filterGroupId);
const elseIfBranches = branches.filter(
(branch, index) =>
index > 0 &&
index < branches.length - 1 &&
branch.filterGroupId !== undefined,
);
return { ifBranch, elseBranch, elseIfBranches };
};
describe('Workflow structure', () => {
it('should verify If/Else workflow exists and is active', async () => {
const response = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
query FindWorkflow($id: UUID!) {
workflow(filter: { id: { eq: $id } }) {
id
name
lastPublishedVersionId
statuses
}
}
`,
variables: { id: createdWorkflowId },
});
expect(response.body.errors).toBeUndefined();
expect(response.body.data.workflow.id).toBe(createdWorkflowId);
expect(response.body.data.workflow.name).toBe('If/Else Test Workflow');
expect(response.body.data.workflow.lastPublishedVersionId).toBe(
createdWorkflowVersionId,
);
expect(response.body.data.workflow.statuses).toContain('ACTIVE');
});
it('should verify If/Else workflow version has correct structure', async () => {
const response = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
query FindWorkflowVersion($id: UUID!) {
workflowVersion(filter: { id: { eq: $id } }) {
id
name
status
trigger
steps
}
}
`,
variables: { id: createdWorkflowVersionId },
});
expect(response.body.errors).toBeUndefined();
const workflowVersion = response.body.data.workflowVersion;
expect(workflowVersion.status).toBe('ACTIVE');
const trigger = workflowVersion.trigger;
expect(trigger.type).toBe('MANUAL');
expect(trigger.nextStepIds).toContain(ifElseStepId);
const steps = workflowVersion.steps;
const ifElseStep = steps.find(
(step: { id: string }) => step.id === ifElseStepId,
);
expect(ifElseStep).toBeDefined();
expect(ifElseStep.type).toBe('IF_ELSE');
expect(ifElseStep.name).toBe('If/Else');
expect(ifElseStep.settings.input.branches.length).toBe(3);
const { ifBranch, elseBranch, elseIfBranches } = identifyBranches(
ifElseStep.settings.input.branches,
);
expect(ifBranch?.filterGroupId).toBeDefined();
expect(elseBranch?.filterGroupId).toBeUndefined();
expect(ifBranch?.nextStepIds).toContain(ifBranchEmptyNodeId);
expect(elseBranch?.nextStepIds).toContain(elseBranchEmptyNodeId);
expect(elseIfBranches.length).toBe(1);
expect(elseIfBranches[0].filterGroupId).toBeDefined();
expect(elseIfBranches[0].nextStepIds).toContain(elseIfBranchEmptyNodeId);
});
});
describe('If/Else branching execution', () => {
const getIfElseStepWithBranches = async (): Promise<
Pick<WorkflowIfElseAction, 'id' | 'settings'>
> => {
const getWorkflowVersionResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
query GetWorkflowVersion($id: UUID!) {
workflowVersion(filter: { id: { eq: $id } }) {
id
steps
}
}
`,
variables: { id: createdWorkflowVersionId },
});
expect(getWorkflowVersionResponse.body.errors).toBeUndefined();
const steps = getWorkflowVersionResponse.body.data.workflowVersion.steps;
const ifElseStep = steps.find(
(step: { id: string }) => step.id === ifElseStepId,
) as WorkflowIfElseAction | undefined;
expect(ifElseStep).toBeDefined();
return ifElseStep!;
};
const verifyBranchExecution = async ({
payload,
expectedBranchType,
}: {
payload: { number: number };
expectedBranchType: 'if' | 'else' | 'else-if';
}) => {
const workflowRunId = await runWorkflowVersion({
workflowVersionId: createdWorkflowVersionId!,
payload,
});
const workflowRun = await waitForWorkflowCompletion(workflowRunId);
expect(workflowRun?.status).toBe('COMPLETED');
expect(workflowRun?.state?.stepInfos?.trigger?.status).toBe('SUCCESS');
expect(workflowRun?.state?.stepInfos?.[ifElseStepId!]?.status).toBe(
'SUCCESS',
);
const ifElseStepResult = workflowRun?.state?.stepInfos?.[ifElseStepId!]
?.result as { matchingBranchId?: string } | undefined;
expect(ifElseStepResult?.matchingBranchId).toBeDefined();
const ifElseStep = await getIfElseStepWithBranches();
const matchedBranch = ifElseStep.settings.input.branches.find(
(branch) => branch.id === ifElseStepResult?.matchingBranchId,
);
if (!matchedBranch) {
const branchIds = ifElseStep.settings.input.branches.map((b) => b.id);
throw new Error(
`Branch with ID ${ifElseStepResult?.matchingBranchId} not found. Available branch IDs: ${branchIds.join(', ')}`,
);
}
const { ifBranch, elseBranch, elseIfBranches } = identifyBranches(
ifElseStep.settings.input.branches,
);
let expectedBranch: StepIfElseBranch | undefined;
let expectedEmptyNodeId: string | null = null;
let otherEmptyNodeIds: (string | null)[] = [];
if (expectedBranchType === 'if') {
expectedBranch = ifBranch;
expectedEmptyNodeId = ifBranchEmptyNodeId;
otherEmptyNodeIds = [elseBranchEmptyNodeId, elseIfBranchEmptyNodeId];
} else if (expectedBranchType === 'else') {
expectedBranch = elseBranch;
expectedEmptyNodeId = elseBranchEmptyNodeId;
otherEmptyNodeIds = [ifBranchEmptyNodeId, elseIfBranchEmptyNodeId];
} else if (expectedBranchType === 'else-if') {
expectedBranch = elseIfBranches.find(
(branch) => branch.id === elseIfBranchId,
);
expectedEmptyNodeId = elseIfBranchEmptyNodeId;
otherEmptyNodeIds = [ifBranchEmptyNodeId, elseBranchEmptyNodeId];
}
expect(matchedBranch.id).toBe(expectedBranch?.id);
expect(matchedBranch.nextStepIds).toContain(expectedEmptyNodeId);
otherEmptyNodeIds.forEach((id) => {
if (id) {
expect(matchedBranch.nextStepIds).not.toContain(id);
}
});
await destroyWorkflowRun(workflowRunId);
};
it('should execute IF branch when condition is true', async () => {
await verifyBranchExecution({
payload: { number: 10 },
expectedBranchType: 'if',
});
});
it('should execute ELSE branch when condition is false', async () => {
await verifyBranchExecution({
payload: { number: 5 },
expectedBranchType: 'else',
});
});
it('should execute ELSE-IF branch when condition is true', async () => {
await verifyBranchExecution({
payload: { number: 20 },
expectedBranchType: 'else-if',
});
});
});
});
@@ -1,5 +1,9 @@
import request from 'supertest';
import { WORKFLOW_RUN_GQL_FIELDS } from 'test/integration/constants/workflow-gql-fields.constants';
import {
destroyWorkflowRun,
getWorkflowRun,
runWorkflowVersion,
} from 'test/integration/graphql/suites/workflow/utils/workflow-run-test.util';
import { v4 as uuidv4 } from 'uuid';
const client = request(`http://localhost:${APP_PORT}`);
@@ -9,86 +13,12 @@ const QUICK_LEAD_WORKFLOW_ID = '8b213cac-a68b-4ffe-817a-3ec994e9932d';
const QUICK_LEAD_WORKFLOW_VERSION_ID = 'ac67974f-c524-4288-9d88-af8515400b68';
const FORM_STEP_ID = '6e089bc9-aabd-435f-865f-f31c01c8f4a7';
type WorkflowRunStatusType =
| 'NOT_STARTED'
| 'RUNNING'
| 'COMPLETED'
| 'FAILED'
| 'ENQUEUED'
| 'STOPPING'
| 'STOPPED';
type WorkflowRunState = {
stepInfos?: Record<
string,
{
status: string;
result?: Record<string, unknown>;
}
>;
flow?: {
trigger?: {
type: string;
nextStepIds: string[];
};
steps?: Array<{
id: string;
type: string;
name: string;
}>;
};
};
type WorkflowRunResponse = {
id: string;
status: WorkflowRunStatusType;
state: WorkflowRunState;
workflowVersionId: string;
};
const getWorkflowRun = async (
workflowRunId: string,
): Promise<WorkflowRunResponse | null> => {
const response = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
query FindWorkflowRun($id: UUID!) {
workflowRun(filter: { id: { eq: $id } }) {
${WORKFLOW_RUN_GQL_FIELDS}
}
}
`,
variables: { id: workflowRunId },
});
if (response.body.errors || !response.body.data?.workflowRun) {
return null;
}
return response.body.data.workflowRun;
};
describe('Quick Lead Workflow (e2e)', () => {
let createdWorkflowRunId: string | null = null;
afterAll(async () => {
// Clean up workflow run
if (createdWorkflowRunId) {
await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation DestroyWorkflowRun($id: ID!) {
destroyWorkflowRun(id: $id) {
id
}
}
`,
variables: { id: createdWorkflowRunId },
});
await destroyWorkflowRun(createdWorkflowRunId);
}
});
@@ -189,32 +119,9 @@ describe('Quick Lead Workflow (e2e)', () => {
});
it('should trigger Quick Lead workflow and create workflow run', async () => {
const runWorkflowResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation RunWorkflowVersion($input: RunWorkflowVersionInput!) {
runWorkflowVersion(input: $input) {
workflowRunId
}
}
`,
variables: {
input: {
workflowVersionId: QUICK_LEAD_WORKFLOW_VERSION_ID,
},
},
});
expect(runWorkflowResponse.status).toBe(200);
expect(runWorkflowResponse.body.errors).toBeUndefined();
expect(
runWorkflowResponse.body.data.runWorkflowVersion.workflowRunId,
).toBeDefined();
const workflowRunId =
runWorkflowResponse.body.data.runWorkflowVersion.workflowRunId;
const workflowRunId = await runWorkflowVersion({
workflowVersionId: QUICK_LEAD_WORKFLOW_VERSION_ID,
});
createdWorkflowRunId = workflowRunId;
@@ -251,26 +158,9 @@ describe('Quick Lead Workflow (e2e)', () => {
});
it('should be able to stop a running workflow run', async () => {
const runWorkflowResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation RunWorkflowVersion($input: RunWorkflowVersionInput!) {
runWorkflowVersion(input: $input) {
workflowRunId
}
}
`,
variables: {
input: {
workflowVersionId: QUICK_LEAD_WORKFLOW_VERSION_ID,
},
},
});
const workflowRunId =
runWorkflowResponse.body.data.runWorkflowVersion.workflowRunId;
const workflowRunId = await runWorkflowVersion({
workflowVersionId: QUICK_LEAD_WORKFLOW_VERSION_ID,
});
const stopResponse = await client
.post('/graphql')
@@ -293,19 +183,7 @@ describe('Quick Lead Workflow (e2e)', () => {
const workflowRun = await getWorkflowRun(workflowRunId);
expect(workflowRun?.status).toBe('STOPPED');
await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation DestroyWorkflowRun($id: ID!) {
destroyWorkflowRun(id: $id) {
id
}
}
`,
variables: { id: workflowRunId },
});
await destroyWorkflowRun(workflowRunId);
});
});
@@ -349,44 +227,14 @@ describe('Quick Lead Workflow (e2e)', () => {
}
if (testWorkflowRunId) {
await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation DestroyWorkflowRun($id: ID!) {
destroyWorkflowRun(id: $id) {
id
}
}
`,
variables: { id: testWorkflowRunId },
});
await destroyWorkflowRun(testWorkflowRunId);
}
});
it('should complete full workflow: trigger → submit form → create Company and Person', async () => {
const runWorkflowResponse = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation RunWorkflowVersion($input: RunWorkflowVersionInput!) {
runWorkflowVersion(input: $input) {
workflowRunId
}
}
`,
variables: {
input: {
workflowVersionId: QUICK_LEAD_WORKFLOW_VERSION_ID,
},
},
});
expect(runWorkflowResponse.body.errors).toBeUndefined();
testWorkflowRunId =
runWorkflowResponse.body.data.runWorkflowVersion.workflowRunId;
testWorkflowRunId = await runWorkflowVersion({
workflowVersionId: QUICK_LEAD_WORKFLOW_VERSION_ID,
});
expect(testWorkflowRunId).toBeDefined();
@@ -0,0 +1,139 @@
import request from 'supertest';
import { WORKFLOW_RUN_GQL_FIELDS } from 'test/integration/constants/workflow-gql-fields.constants';
const client = request(`http://localhost:${APP_PORT}`);
export type WorkflowRunStatusType =
| 'NOT_STARTED'
| 'RUNNING'
| 'COMPLETED'
| 'FAILED'
| 'ENQUEUED'
| 'STOPPING'
| 'STOPPED';
export type WorkflowRunState = {
stepInfos?: Record<
string,
{
status: string;
result?: Record<string, unknown>;
}
>;
flow?: {
trigger?: {
type: string;
nextStepIds: string[];
};
steps?: Array<{
id: string;
type: string;
name: string;
}>;
};
};
export type WorkflowRunResponse = {
id: string;
status: WorkflowRunStatusType;
state: WorkflowRunState;
workflowVersionId: string;
};
export const getWorkflowRun = async (
workflowRunId: string,
): Promise<WorkflowRunResponse | null> => {
const response = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
query FindWorkflowRun($id: UUID!) {
workflowRun(filter: { id: { eq: $id } }) {
${WORKFLOW_RUN_GQL_FIELDS}
}
}
`,
variables: { id: workflowRunId },
});
if (response.body.errors || !response.body.data?.workflowRun) {
return null;
}
return response.body.data.workflowRun;
};
export const runWorkflowVersion = async ({
workflowVersionId,
payload,
}: {
workflowVersionId: string;
payload?: object;
}): Promise<string> => {
const response = await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation RunWorkflowVersion($input: RunWorkflowVersionInput!) {
runWorkflowVersion(input: $input) {
workflowRunId
}
}
`,
variables: {
input: {
workflowVersionId,
payload,
},
},
});
if (response.body.errors || !response.body.data?.runWorkflowVersion) {
throw new Error(
`Failed to run workflow version: ${JSON.stringify(response.body.errors)}`,
);
}
return response.body.data.runWorkflowVersion.workflowRunId;
};
export const destroyWorkflowRun = async (
workflowRunId: string,
): Promise<void> => {
await client
.post('/graphql')
.set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`)
.send({
query: `
mutation DestroyWorkflowRun($id: ID!) {
destroyWorkflowRun(id: $id) {
id
}
}
`,
variables: { id: workflowRunId },
});
};
export const waitForWorkflowCompletion = async (
workflowRunId: string,
maxAttempts = 30,
intervalMs = 500,
): Promise<WorkflowRunResponse | null> => {
let workflowRun = await getWorkflowRun(workflowRunId);
let attempts = 0;
while (
workflowRun?.status === 'RUNNING' &&
attempts < maxAttempts &&
workflowRun !== null
) {
await new Promise((resolve) => setTimeout(resolve, intervalMs));
workflowRun = await getWorkflowRun(workflowRunId);
attempts++;
}
return workflowRun;
};