Skip to content

Commit 90de8ee

Browse files
Separate unit, integration (v4/v5) tests; Run tests on Node 20, 22, 24; Add retry for HTTP 429 error (#82)
- Separated integration tests from unit tests - Separated integration tests that only work with the new backend version (v5) from common tests - Updated GitHub Action to ensure that during the CI/CD process the appropriate tests run against both v4 and v5 backend versions using all LTS Node.js versions (24 will become LTS in a few weeks) - Adjusted the timeout duration for a few integration tests to improve stability - Fixed Jest warnings related to "isolatedModules": true (updated a few type exports) - Fixed TypeScript and Jest errors related to dynamically imported optional undici module on Node.js v18 - Limited max HTTP2 connections to 1, added ability to change maxHttp2Connections via config and env var. - Updated some integration tests logic to allow them running in parallel on the same cluster without breaking each other during CI/CD. - Added a simple retry mechanism for requests that resulted in a 429 HTTP error (in case if we reach load balancer limit).
1 parent ec0091f commit 90de8ee

42 files changed

Lines changed: 1319 additions & 1095 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/pull_request.yml

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,32 @@ jobs:
1919

2020
tests:
2121
runs-on: ubuntu-latest
22+
strategy:
23+
fail-fast: false
24+
matrix:
25+
node-version: [20, 22, 24]
26+
test: ["unit", "integration:v5", "integration:v4"]
27+
name: Node.js v${{ matrix.node-version }} - ${{ matrix.test }} tests
2228
steps:
2329
- name: Checkout
2430
uses: actions/checkout@v4
2531
- name: Set up Node
2632
uses: actions/setup-node@v4
2733
with:
28-
node-version: "22"
34+
node-version: ${{ matrix.node-version }}
2935
- name: Install Dependencies
3036
run: npm ci
31-
- name: Run Tests and Add Annotations
32-
run: npm test -- --ci --reporters=default --reporters=github-actions --reporters=jest-junit
37+
- name: Run ${{ matrix.test }} Tests and Add Annotations
38+
run: npm run test:${{ matrix.test }} -- --ci --reporters=default --reporters=github-actions --reporters=jest-junit
3339
env:
34-
CONDUCTOR_SERVER_URL: ${{ vars.SERVER_URL }}
35-
CONDUCTOR_AUTH_KEY: ${{ secrets.AUTH_KEY }}
36-
CONDUCTOR_AUTH_SECRET: ${{ secrets.AUTH_SECRET }}
37-
- name: Publish Dorny Test Results Summary
40+
CONDUCTOR_SERVER_URL: ${{ matrix.test == 'integration:v4' && vars.SERVER_URL_V4 || vars.SERVER_URL }}
41+
CONDUCTOR_AUTH_KEY: ${{ matrix.test == 'integration:v4' && secrets.AUTH_KEY_V4 || secrets.AUTH_KEY }}
42+
CONDUCTOR_AUTH_SECRET: ${{ matrix.test == 'integration:v4' && secrets.AUTH_SECRET_V4 || secrets.AUTH_SECRET }}
43+
JEST_JUNIT_OUTPUT_NAME: ${{ matrix.test }}-test-results.xml
44+
- name: Publish ${{ matrix.test }} Test Results
3845
uses: dorny/test-reporter@v2
3946
if: ${{ !cancelled() }}
4047
with:
41-
name: Test report
42-
path: reports/jest-junit.xml
48+
name: ${{ matrix.test }} Test Report
49+
path: reports/${{ matrix.test }}-test-results.xml
4350
reporter: jest-junit

.node-version

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/common/open-api/__test__/EventResourceService.test.ts renamed to integration-tests/common/EventResourceService.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { expect, describe, test } from "@jest/globals";
2-
import { orkesConductorClient } from "../../../orkes";
2+
import { orkesConductorClient } from "../../src/orkes";
33

44
describe("EventResourceService", () => {
55
test("Should create an event handler with description and tags and then delete it", async () => {
66
const orkesClient = await orkesConductorClient();
77
const eventApi = orkesClient.eventResource;
88

9+
const now = Date.now();
910
const [eventName, event, eventDescription, eventTagKey, eventTagValue] = [
10-
"jsSdkTestEventName",
11-
"jsSdkTest:eventHandler:1",
11+
`jsSdkTest-EventName-${now}`,
12+
`jsSdkTest:eventHandler:1${now}`,
1213
"jsSdkTestDescription",
1314
"jsSdkTestTagKey",
1415
"jsSdkTestTagValue",

src/core/__test__/MetadataClient.test.ts renamed to integration-tests/common/MetadataClient.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import { expect, describe, test, jest } from "@jest/globals";
2-
import { MetadataClient } from "../metadataClient";
3-
import { taskDefinition } from "../sdk";
4-
import { orkesConductorClient } from "../../orkes";
2+
import { MetadataClient } from "../../src/core/metadataClient";
3+
import { taskDefinition } from "../../src/core/sdk";
4+
import { orkesConductorClient } from "../../src/orkes";
55

66
describe("MetadataClient", () => {
77
const clientPromise = orkesConductorClient();
8+
const taskName = `jsSdkTest-test_task_definition-${Date.now()}`;
89

910
jest.setTimeout(15000);
1011
test("Should register a task definition", async () => {
1112
const client = await clientPromise;
1213
const metadataClient = new MetadataClient(client);
1314

1415
const newTaskDefinition = taskDefinition({
15-
name: "test_task_definition",
16+
name: taskName,
1617
description: "New Task Definition",
1718
retryCount: 4,
1819
timeoutSeconds: 7200,
@@ -62,7 +63,7 @@ describe("MetadataClient", () => {
6263
const metadataClient = new MetadataClient(client);
6364

6465
const newTaskDefinition = taskDefinition({
65-
name: "test_task_definition",
66+
name: taskName,
6667
description: "New Task Definition Update",
6768
retryCount: 5,
6869
timeoutSeconds: 7201,
@@ -110,14 +111,13 @@ describe("MetadataClient", () => {
110111
test("Should unregister a task definition", async () => {
111112
const client = await clientPromise;
112113
const metadataClient = new MetadataClient(client);
113-
const name ="test_task_definition";
114114

115115
await expect(
116-
metadataClient.unregisterTask("test_task_definition")
116+
metadataClient.unregisterTask(taskName)
117117
).resolves.not.toThrow();
118118

119119
await expect(client.metadataResource.getTaskDef(
120-
name
120+
taskName
121121
)).rejects.toThrow();
122122
})
123123
});

src/task/__tests__/TaskManager.test.ts renamed to integration-tests/common/TaskManager.test.ts

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
import { expect, describe, test, jest } from "@jest/globals";
2-
import { simpleTask, taskDefinition, WorkflowExecutor } from "../../core";
3-
import { orkesConductorClient } from "../../orkes";
4-
import { TaskManager, ConductorWorker } from "../index";
5-
import { mockLogger } from "./mockLogger";
6-
import { TestUtil } from "../../core/__test__/utils/test-util";
2+
import { simpleTask, taskDefinition, WorkflowExecutor } from "../../src/core";
3+
import { orkesConductorClient } from "../../src/orkes";
4+
import { TaskManager, ConductorWorker } from "../../src/task/index";
5+
import { mockLogger } from "../utils/mockLogger";
6+
import { waitForWorkflowCompletion } from "../utils/waitForWorkflowCompletion";
77

88

9-
const BASE_TIME = 500;
9+
const BASE_TIME = 1000;
1010
describe("TaskManager", () => {
1111
const clientPromise = orkesConductorClient();
1212

13-
jest.setTimeout(15000);
13+
jest.setTimeout(30000);
1414

1515
test("Should run workflow with worker", async () => {
1616
const client = await clientPromise;
1717
const executor = new WorkflowExecutor(client);
18+
const taskName = `jsSdkTest-taskmanager-test-${Date.now()}`;
19+
const workflowName = `jsSdkTest-taskmanager-test-wf-${Date.now()}`;
1820

1921
const worker: ConductorWorker = {
20-
taskDefName: "taskmanager-test",
22+
taskDefName: taskName,
2123
execute: async () => {
2224
return {
2325
outputData: {
@@ -34,22 +36,22 @@ describe("TaskManager", () => {
3436
manager.startPolling();
3537

3638
await executor.registerWorkflow(true, {
37-
name: "TaskManagerTest",
39+
name: workflowName,
3840
version: 1,
3941
ownerEmail: "developers@orkes.io",
40-
tasks: [simpleTask("taskmanager-test", "taskmanager-test", {})],
42+
tasks: [simpleTask(taskName, taskName, {})],
4143
inputParameters: [],
4244
outputParameters: {},
4345
timeoutSeconds: 0,
4446
});
4547

4648
const executionId = await executor.startWorkflow({
47-
name: "TaskManagerTest",
49+
name: workflowName,
4850
input: {},
4951
version: 1,
5052
});
5153

52-
const workflowStatus = await TestUtil.waitForWorkflowCompletion(executor, executionId, BASE_TIME * 4);
54+
const workflowStatus = await waitForWorkflowCompletion(executor, executionId, BASE_TIME * 30);
5355

5456
expect(workflowStatus.status).toEqual("COMPLETED");
5557

@@ -59,18 +61,20 @@ describe("TaskManager", () => {
5961
test("On error it should call the errorHandler provided", async () => {
6062
const client = await clientPromise;
6163
const executor = new WorkflowExecutor(client);
64+
const taskName = `jsSdkTest-taskmanager-error-handler-test-${Date.now()}`;
65+
const workflowName = `jsSdkTest-taskmanager-error-handler-test-wf-${Date.now()}`;
6266

6367
const mockErrorHandler = jest.fn();
6468

6569
const worker: ConductorWorker = {
66-
taskDefName: "taskmanager-error-handler-test-unique",
70+
taskDefName: taskName,
6771
execute: async () => {
6872
throw new Error("This is a forced error for testing error handler");
6973
},
7074
};
7175

7276
await client.metadataResource.registerTaskDef([taskDefinition({
73-
name: "taskmanager-error-handler-test-unique",
77+
name: taskName,
7478
timeoutSeconds: 0,
7579
retryCount: 0,
7680
})]);
@@ -83,23 +87,23 @@ describe("TaskManager", () => {
8387
manager.startPolling();
8488

8589
await executor.registerWorkflow(true, {
86-
name: "TaskManagerTestErrorHandlerUnique",
90+
name: workflowName,
8791
version: 1,
8892
ownerEmail: "developers@orkes.io",
89-
tasks: [simpleTask("taskmanager-error-handler-test-unique", "taskmanager-error-handler-test-unique", {})],
93+
tasks: [simpleTask(taskName, taskName, {})],
9094
inputParameters: [],
9195
outputParameters: {},
9296
timeoutSeconds: 0,
9397
});
9498

9599
const status = await executor.startWorkflow({
96-
name: "TaskManagerTestErrorHandlerUnique",
100+
name: workflowName,
97101
input: {},
98102
version: 1,
99-
correlationId: "errorHandlerTestIdentifierUnique"
103+
correlationId: `${workflowName}-id`
100104
});
101105

102-
const workflowStatus = await TestUtil.waitForWorkflowCompletion(executor, status, BASE_TIME * 6);
106+
const workflowStatus = await waitForWorkflowCompletion(executor, status, BASE_TIME * 30);
103107

104108
expect(workflowStatus.status).toEqual("FAILED");
105109
expect(mockErrorHandler).toHaveBeenCalledTimes(1);
@@ -109,16 +113,18 @@ describe("TaskManager", () => {
109113
test("If no error handler provided. it should just update the task", async () => {
110114
const client = await clientPromise;
111115
const executor = new WorkflowExecutor(client);
116+
const taskName = `jsSdkTest-taskmanager-error-test-${Date.now()}`;
117+
const workflowName = `jsSdkTest-taskmanager-error-test-wf-${Date.now()}`;
112118

113119
const worker: ConductorWorker = {
114-
taskDefName: "taskmanager-error-test",
120+
taskDefName: taskName,
115121
execute: async () => {
116122
throw new Error("This is a forced error");
117123
},
118124
};
119125

120126
await client.metadataResource.registerTaskDef([taskDefinition({
121-
name: "taskmanager-error-test",
127+
name: taskName,
122128
timeoutSeconds: 0,
123129
retryCount: 0,
124130
})]);
@@ -130,23 +136,23 @@ describe("TaskManager", () => {
130136
manager.startPolling();
131137

132138
await executor.registerWorkflow(true, {
133-
name: "TaskManagerTestE",
139+
name: workflowName,
134140
version: 1,
135141
ownerEmail: "developers@orkes.io",
136-
tasks: [simpleTask("taskmanager-error-test", "taskmanager-error-test", {})],
142+
tasks: [simpleTask(taskName, taskName, {})],
137143
inputParameters: [],
138144
outputParameters: {},
139145
timeoutSeconds: 0,
140146
});
141147

142148
const executionId = await executor.startWorkflow({
143-
name: "TaskManagerTestE",
149+
name: workflowName,
144150
input: {},
145151
version: 1,
146-
correlationId: "noErrorHandlerProvidedIdentifier"
152+
correlationId: `${workflowName}-id`
147153
});
148154

149-
const workflowStatus = await TestUtil.waitForWorkflowCompletion(executor, executionId!, BASE_TIME * 10);
155+
const workflowStatus = await waitForWorkflowCompletion(executor, executionId!, BASE_TIME * 30);
150156
expect(workflowStatus.status).toEqual("FAILED");
151157
await manager.stopPolling();
152158
});
@@ -158,7 +164,7 @@ describe("TaskManager", () => {
158164
// just create a bunch of worker names
159165
const workerNames: string[] = Array.from({ length: 3 })
160166
.fill(0)
161-
.map((_, i: number) => `taskman-multi-${1 + i}`);
167+
.map((_, i: number) => `jsSdkTest-taskman-multi-${1 + i}-${Date.now()}`);
162168

163169
// names to actual workers
164170
const workers: ConductorWorker[] = workerNames.map((name) => ({
@@ -183,7 +189,7 @@ describe("TaskManager", () => {
183189

184190
expect(manager.isPolling).toBeTruthy();
185191

186-
const workflowName = "TaskManagerTestMulti";
192+
const workflowName = `jsSdkTest-taskmanager-multi-test-wf-${Date.now()}`;
187193

188194
// increase polling speed
189195
manager.updatePollingOptions({ concurrency: 4 });
@@ -203,15 +209,15 @@ describe("TaskManager", () => {
203209
const executionId = await executor.startWorkflow({
204210
name: workflowName,
205211
version: 1,
206-
correlationId: "identifierTaskManMulti"
212+
correlationId: `${workflowName}-id`
207213
});
208214

209215
expect(executionId).toBeDefined();
210216

211217
// decrease speed again
212218
manager.updatePollingOptions({ pollInterval: BASE_TIME, concurrency: 1 });
213219

214-
const workflowStatus = await TestUtil.waitForWorkflowCompletion(executor, executionId, BASE_TIME * 4);
220+
const workflowStatus = await waitForWorkflowCompletion(executor, executionId, BASE_TIME * 30);
215221

216222
expect(workflowStatus.status).toEqual("COMPLETED");
217223
await manager.stopPolling();
@@ -233,10 +239,11 @@ describe("TaskManager", () => {
233239

234240
test("Should not be able to startPolling if duplicate workers", async () => {
235241
const client = await clientPromise;
242+
const workerName = `jsSdkTest-worker-name-${Date.now()}`;
236243

237244
const workerNames: string[] = Array.from({ length: 3 })
238245
.fill(0)
239-
.map(() => `worker-name`);
246+
.map(() => workerName);
240247

241248
// names to actual workers
242249
const workers: ConductorWorker[] = workerNames.map((name) => ({
@@ -255,20 +262,21 @@ describe("TaskManager", () => {
255262
options: { pollInterval: BASE_TIME, concurrency: 2 },
256263
});
257264
expect(() => manager.startPolling()).toThrow(
258-
"Duplicate worker taskDefName: worker-name"
265+
`Duplicate worker taskDefName: ${workerName}`
259266
);
260267
});
261268

262269
test("Updates single worker properties", async () => {
263270
const client = await clientPromise;
264271

265272
const executor = new WorkflowExecutor(client);
273+
const workerName = `jsSdkTest-taskman-single-worker-update-${Date.now()}`;
266274
// just create a bunch of worker names
267275
const workerNames: string[] = Array.from({ length: 3 })
268276
.fill(0)
269-
.map((_, i: number) => `taskman-single-worker-update${1 + i}`);
277+
.map((_, i: number) => `${workerName}-${1 + i}`);
270278

271-
const candidateWorkerUpdate = "taskman-single-worker-update1";
279+
const candidateWorkerUpdate = `${workerName}-1`;
272280
const initialCandidateWorkflowOptions = {
273281
concurrency: 1,
274282
pollInterval: BASE_TIME * 3,
@@ -300,7 +308,7 @@ describe("TaskManager", () => {
300308

301309
expect(manager.isPolling).toBeTruthy();
302310

303-
const workflowName = "TaskManagerTestMultiSingleWorkerUpdate";
311+
const workflowName = `jsSdkTest-taskmanager-multi-single-worker-update-wf-${Date.now()}`;
304312

305313
const updatedWorkerOptions = {
306314
concurrency: 3,
@@ -328,14 +336,14 @@ describe("TaskManager", () => {
328336
const executionId = await executor.startWorkflow({
329337
name: workflowName,
330338
version: 1,
331-
correlationId: "identifierTaskManMulti"
339+
correlationId: `${workflowName}-id`
332340
});
333341
expect(executionId).toBeDefined();
334342

335343
// decrease speed again
336344
manager.updatePollingOptions({ pollInterval: BASE_TIME, concurrency: 1 });
337345

338-
const workflowStatus = await TestUtil.waitForWorkflowCompletion(executor, executionId, BASE_TIME * 10);
346+
const workflowStatus = await waitForWorkflowCompletion(executor, executionId, BASE_TIME * 30);
339347

340348
expect(workflowStatus.status).toEqual("COMPLETED");
341349
await manager.stopPolling();

0 commit comments

Comments
 (0)