Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
fail-fast: false
matrix:
node-version: [20, 22, 24]
test: ["unit", "integration:orkes-v5", "integration:orkes-v4"]
test: ["unit", "integration:v5", "integration:v4"]
name: Node.js v${{ matrix.node-version }} - ${{ matrix.test }} tests
steps:
- name: Checkout
Expand Down
11 changes: 9 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ on:
release:
types: [published]

# Required for npm Trusted Publishing (OIDC). Configure the trusted publisher
# on npm: package → Settings → Trusted publishing → GitHub Actions →
# workflow filename: release.yml, then your repo owner and repo name.
permissions:
id-token: write
contents: read

jobs:
build-package-and-publish-release:
runs-on: ubuntu-latest
Expand All @@ -15,6 +22,8 @@ jobs:
with:
node-version: "22"
registry-url: "https://registry.npmjs.org"
- name: Ensure npm supports trusted publishing
run: npm install -g npm@latest
- name: Bump version to release
run: sed -i "s/v0.0.0/$RELEASE_VERSION/" ./package.json
env:
Expand All @@ -25,5 +34,3 @@ jobs:
run: npm run build
- name: Publish package
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
"test": "cross-env ORKES_BACKEND_VERSION=5 jest --force-exit --detectOpenHandles",
"test:unit": "jest --force-exit --detectOpenHandles --testMatch='**/src/**/__tests__/**/*.test.[jt]s?(x)'",
"test:integration:base": "jest --force-exit --detectOpenHandles --testMatch='**/src/integration-tests/*.test.[jt]s?(x)'",
"test:integration:orkes-v5": "cross-env ORKES_BACKEND_VERSION=5 npm run test:integration:base --",
"test:integration:orkes-v4": "cross-env ORKES_BACKEND_VERSION=4 npm run test:integration:base --",
"test:integration:v5": "cross-env ORKES_BACKEND_VERSION=5 npm run test:integration:base --",
"test:integration:v4": "cross-env ORKES_BACKEND_VERSION=4 npm run test:integration:base --",
"ci": "npm run lint && npm run test",
"build": "tsup index.ts",
"generate-openapi-layer": "openapi-ts",
Expand Down
20 changes: 15 additions & 5 deletions src/integration-tests/EventClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ describe("EventClient", () => {
const retrievedHandler = await eventClient.getEventHandlerByName(
handlerName
);
if (!retrievedHandler) throw new Error("Expected handler to exist");
expect(retrievedHandler.name).toEqual(handlerName);
expect(retrievedHandler.event).toEqual(eventName);
expect(retrievedHandler.active).toEqual(true);
Expand Down Expand Up @@ -154,6 +155,7 @@ describe("EventClient", () => {
const retrievedHandler = await eventClient.getEventHandlerByName(
handlerName
);
if (!retrievedHandler) throw new Error("Expected handler to exist");
expect(retrievedHandler.active).toEqual(false);
expect(retrievedHandler.description).toEqual("Updated description");

Expand All @@ -180,7 +182,7 @@ describe("EventClient", () => {
const retrievedHandler = await eventClient.getEventHandlerByName(
handlerName
);

if (!retrievedHandler) throw new Error("Expected handler to exist");
expect(retrievedHandler.name).toEqual(handlerName);
expect(retrievedHandler.event).toEqual(eventName);

Expand Down Expand Up @@ -227,6 +229,7 @@ describe("EventClient", () => {
const retrievedHandler = await eventClient.getEventHandlerByName(
handlerName
);
if (!retrievedHandler) throw new Error("Expected handler to exist");
expect(retrievedHandler.name).toEqual(handlerName);

// Remove it
Expand Down Expand Up @@ -393,13 +396,18 @@ describe("EventClient", () => {
});

describe("Error Handling", () => {
test("Should throw error when getting non-existent event handler", async () => {
test("Should return null or throw when getting non-existent event handler", async () => {
const eventClient = new EventClient(await orkesConductorClient());
const nonExistentName = createUniqueName("non-existent-handler");

await expect(
eventClient.getEventHandlerByName(nonExistentName)
).rejects.toThrow();
try {
const result = await eventClient.getEventHandlerByName(nonExistentName);
// V5: server may return null or 200 with empty/non-JSON body (e.g. stream)
expect(result == null || typeof (result as EventHandler)?.name !== "string").toBe(true);
} catch {
// V4: server returns 200 with empty body and SDK throws (e.g. "Response is empty")
expect(true).toBe(true);
}
});

test("Should throw error when removing non-existent handler", async () => {
Expand Down Expand Up @@ -506,6 +514,7 @@ describe("EventClient", () => {
const retrievedHandler = await eventClient.getEventHandlerByName(
handlerName
);
if (!retrievedHandler) throw new Error("Expected handler to exist");
expect(retrievedHandler.name).toEqual(handlerName);
expect(retrievedHandler.event).toEqual(eventName);
expect(retrievedHandler.active).toBe(true);
Expand All @@ -528,6 +537,7 @@ describe("EventClient", () => {
const handlerAfterEvent = await eventClient.getEventHandlerByName(
handlerName
);
if (!handlerAfterEvent) throw new Error("Expected handler to exist");
expect(handlerAfterEvent.active).toBe(true);
expect(handlerAfterEvent.event).toEqual(eventName);

Expand Down
21 changes: 20 additions & 1 deletion src/integration-tests/SchedulerClient.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect, describe, test, jest } from "@jest/globals";
import { expect, describe, test, jest, afterAll } from "@jest/globals";
import {
ExtendedWorkflowDef,
SaveScheduleRequest,
Expand All @@ -21,6 +21,25 @@ describe("SchedulerClient", () => {
const workflowName = `jsSdkTestScheduleWf_${now}`;
const workflowVersion = 1;

afterAll(async () => {
const client = await clientPromise;
const schedulerClient = new SchedulerClient(client);
const metadataClient = new MetadataClient(client);
try {
await schedulerClient.deleteSchedule(name);
} catch (e) {
console.debug(`Failed to cleanup schedule ${name}:`, e);
}
try {
await metadataClient.unregisterWorkflow(workflowName, workflowVersion);
} catch (e) {
console.debug(
`Failed to cleanup workflow ${workflowName}:`,
e
);
}
});

test("Should be able to register a workflow and retrieve it", async () => {
const client = await clientPromise;
const executor = new SchedulerClient(client);
Expand Down
14 changes: 10 additions & 4 deletions src/integration-tests/ServiceRegistryClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import * as fs from "fs";
import * as path from "path";
import { describeForOrkesV5 } from "./utils/customJestDescribe";

// Conductor must be able to fetch this URL for discovery; use CONDUCTOR_TEST_SERVICE_URI
// when testing against a remote cluster (e.g. a public Swagger URL it can reach).
const TEST_SERVICE_URI =
process.env.CONDUCTOR_TEST_SERVICE_URI ??
"http://httpbin-server:8081/api-docs";

describeForOrkesV5("ServiceRegistryClient", () => {
const clientPromise = orkesConductorClient();
let serviceRegistryClient: ServiceRegistryClient;
Expand Down Expand Up @@ -37,7 +43,7 @@ describeForOrkesV5("ServiceRegistryClient", () => {
const testServiceRegistry = {
name: `jsSdkTest-test_service_registry${Date.now()}`,
type: ServiceType.HTTP,
serviceURI: "http://httpbin:8081/api-docs",
serviceURI: TEST_SERVICE_URI,
config: {
circuitBreakerConfig: {
failureRateThreshold: 50.0,
Expand Down Expand Up @@ -114,7 +120,7 @@ describeForOrkesV5("ServiceRegistryClient", () => {
const testServiceRegistry = {
name: `jsSdkTest-test_service_registry_to_remove-${Date.now()}`,
type: ServiceType.HTTP,
serviceURI: "http://httpbin:8081/api-docs",
serviceURI: TEST_SERVICE_URI,
};

// Register the service registry
Expand Down Expand Up @@ -143,7 +149,7 @@ describeForOrkesV5("ServiceRegistryClient", () => {
const testServiceRegistry = {
name: `jsSdkTest-test_service_registry_with_method-${Date.now()}`,
type: ServiceType.HTTP,
serviceURI: "http://httpbin:8081/api-docs",
serviceURI: TEST_SERVICE_URI,
};

// Add service to cleanup list
Expand Down Expand Up @@ -202,7 +208,7 @@ describeForOrkesV5("ServiceRegistryClient", () => {
const testServiceRegistry = {
name: `jsSdkTest-test_service_registry_discovery-${Date.now()}`,
type: ServiceType.HTTP,
serviceURI: "http://httpbin:8081/api-docs",
serviceURI: TEST_SERVICE_URI,
};

// Add service to cleanup list
Expand Down
26 changes: 25 additions & 1 deletion src/integration-tests/TaskManager.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect, describe, test, jest } from "@jest/globals";
import { expect, describe, test, jest, afterEach } from "@jest/globals";
import {
MetadataClient,
simpleTask,
Expand All @@ -14,9 +14,26 @@ import { waitForWorkflowCompletion } from "./utils/waitForWorkflowCompletion";
const BASE_TIME = 1000;
describe("TaskManager", () => {
const clientPromise = orkesConductorClient();
const workflowsToCleanup: { name: string; version: number }[] = [];
const tasksToCleanup: string[] = [];

jest.setTimeout(30000);

afterEach(async () => {
const client = await clientPromise;
const metadataClient = new MetadataClient(client);
await Promise.allSettled(
workflowsToCleanup.map((w) =>
metadataClient.unregisterWorkflow(w.name, w.version)
)
);
await Promise.allSettled(
tasksToCleanup.map((t) => metadataClient.unregisterTask(t))
);
workflowsToCleanup.length = 0;
tasksToCleanup.length = 0;
});

test("Should run workflow with worker", async () => {
const client = await clientPromise;
const executor = new WorkflowExecutor(client);
Expand Down Expand Up @@ -49,6 +66,7 @@ describe("TaskManager", () => {
outputParameters: {},
timeoutSeconds: 0,
});
workflowsToCleanup.push({ name: workflowName, version: 1 });

const executionId = await executor.startWorkflow({
name: workflowName,
Expand Down Expand Up @@ -94,6 +112,7 @@ describe("TaskManager", () => {
retryCount: 0,
})
);
tasksToCleanup.push(taskName);

const manager = new TaskManager(client, [worker], {
options: { pollInterval: BASE_TIME },
Expand All @@ -111,6 +130,7 @@ describe("TaskManager", () => {
outputParameters: {},
timeoutSeconds: 0,
});
workflowsToCleanup.push({ name: workflowName, version: 1 });

const status = await executor.startWorkflow({
name: workflowName,
Expand Down Expand Up @@ -155,6 +175,7 @@ describe("TaskManager", () => {
retryCount: 0,
})
);
tasksToCleanup.push(taskName);

const manager = new TaskManager(client, [worker], {
options: { pollInterval: BASE_TIME },
Expand All @@ -171,6 +192,7 @@ describe("TaskManager", () => {
outputParameters: {},
timeoutSeconds: 0,
});
workflowsToCleanup.push({ name: workflowName, version: 1 });

const executionId = await executor.startWorkflow({
name: workflowName,
Expand Down Expand Up @@ -239,6 +261,7 @@ describe("TaskManager", () => {
outputParameters: {},
timeoutSeconds: 0,
});
workflowsToCleanup.push({ name: workflowName, version: 1 });

//Start workflow
const executionId = await executor.startWorkflow({
Expand Down Expand Up @@ -373,6 +396,7 @@ describe("TaskManager", () => {
outputParameters: {},
timeoutSeconds: 0,
});
workflowsToCleanup.push({ name: workflowName, version: 1 });

//Start workflow
const executionId = await executor.startWorkflow({
Expand Down
16 changes: 15 additions & 1 deletion src/integration-tests/TaskRunner.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
import { expect, describe, test, jest } from "@jest/globals";
import { expect, describe, test, jest, afterAll } from "@jest/globals";
import {
TaskRunner,
WorkflowExecutor,
simpleTask,
orkesConductorClient,
MetadataClient,
} from "../sdk";
import { waitForWorkflowStatus } from "./utils/waitForWorkflowStatus";

describe("TaskRunner", () => {
const clientPromise = orkesConductorClient();
const workflowsToCleanup: { name: string; version: number }[] = [];

jest.setTimeout(30000);

afterAll(async () => {
const client = await clientPromise;
const metadataClient = new MetadataClient(client);
await Promise.allSettled(
workflowsToCleanup.map((w) =>
metadataClient.unregisterWorkflow(w.name, w.version)
)
);
});

test("worker example ", async () => {
const client = await clientPromise;
const executor = new WorkflowExecutor(client);
Expand Down Expand Up @@ -50,6 +63,7 @@ describe("TaskRunner", () => {
outputParameters: {},
timeoutSeconds: 0,
});
workflowsToCleanup.push({ name: workflowName, version: 1 });

const { workflowId: executionId } = await executor.executeWorkflow(
{
Expand Down
Loading