From d324604b2c37bf048735273e6d5d1077024f9092 Mon Sep 17 00:00:00 2001 From: Miguel Prieto Date: Wed, 13 Nov 2024 19:20:11 -0300 Subject: [PATCH] Add `testWorkflow` to `WorkflowResourceService` (#70) * Added test workflow feature to JS SDK - https://orkes.io/content/developer-guides/unit-and-regression-tests * Added registerWorkflowDef to MetadataClient * CI: Fix tests + Publish Test reports --- .github/workflows/pull_request.yml | 12 +++++- package.json | 11 ++++- .../__test__/WorkflowResourceService.test.ts | 40 +++++++++++++++++++ src/common/open-api/models/TaskMock.ts | 13 ++++++ .../open-api/models/WorkflowTestRequest.ts | 20 ++++++++++ .../services/WorkflowResourceService.ts | 18 +++++++++ src/core/metadataClient.ts | 22 ++++++++-- src/task/__tests__/TaskManager.test.ts | 4 +- yarn.lock | 25 ++++++++++++ 9 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 src/common/open-api/__test__/WorkflowResourceService.test.ts create mode 100644 src/common/open-api/models/TaskMock.ts create mode 100644 src/common/open-api/models/WorkflowTestRequest.ts diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 12dd614..378c8e5 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -16,6 +16,7 @@ jobs: run: yarn install - name: Run the linter run: yarn lint + unit-tests: runs-on: ubuntu-latest steps: @@ -27,9 +28,16 @@ jobs: node-version: "18" - name: Install package run: yarn install - - name: Run Unit test - run: yarn test + - name: Run Tests + run: yarn test --ci --reporters=default --reporters=jest-junit env: KEY_ID: ${{ secrets.KEY_ID }} KEY_SECRET: ${{ secrets.KEY_SECRET }} SERVER_URL: ${{ secrets.SERVER_URL }} + - name: Publish Test Results + if: always() + uses: dorny/test-reporter@v1 + with: + name: Test report + path: reports/jest-*.xml + reporter: jest-junit diff --git a/package.json b/package.json index 1a64e4a..da764b1 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "dotenv": "^16.0.1", "eslint": "^6.1.0", "jest": "^29.4.3", + "jest-junit": "^16.0.0", "ts-jest": "^29.0.5", "ts-node": "^10.7.0", "tsup": "^7.1.0", @@ -83,6 +84,14 @@ "engines": { "node": ">=18" }, - "dependencies": { + "dependencies": {}, + "jest-junit": { + "outputDirectory": "reports", + "outputName": "jest-junit.xml", + "ancestorSeparator": " › ", + "uniqueOutputName": "false", + "suiteNameTemplate": "{filepath}", + "classNameTemplate": "{classname}", + "titleTemplate": "{title}" } } diff --git a/src/common/open-api/__test__/WorkflowResourceService.test.ts b/src/common/open-api/__test__/WorkflowResourceService.test.ts new file mode 100644 index 0000000..f99ffaa --- /dev/null +++ b/src/common/open-api/__test__/WorkflowResourceService.test.ts @@ -0,0 +1,40 @@ +import { expect, describe, test, jest } from "@jest/globals"; +import { MetadataClient } from "../../../core"; +import { simpleTask, workflow } from "../../../core/sdk"; +import { orkesConductorClient } from "../../../orkes"; +import { TaskDefTypes } from "../../types"; + +const config = { + keyId: `${process.env.KEY_ID}`, + keySecret: `${process.env.KEY_SECRET}`, + serverUrl: `${process.env.SERVER_URL}`, +}; + +describe("WorkflowResourceService", () => { + jest.setTimeout(120000); + + test("Should test a workflow", async () => { + const client = await orkesConductorClient(config); + const metadataClient = new MetadataClient(client); + const tasks: TaskDefTypes[] = [ + simpleTask("simple_ref", "le_simple_task", {}), + ]; + + const wfDef = workflow("unit_test_wf", tasks); + wfDef.outputParameters = { message: "${simple_ref.output.message}" }; + metadataClient.registerWorkflowDef(wfDef, true); + + const status = "COMPLETED"; + const output = { message: "Mocked message" }; + + const wf = await client.workflowResource.testWorkflow({ + name: wfDef.name, + taskRefToMockOutput: { + "simple_ref": [{ status, output }], + }, + }); + + expect(wf.status).toEqual(status); + expect(wf.output).toEqual(output); + }); +}); diff --git a/src/common/open-api/models/TaskMock.ts b/src/common/open-api/models/TaskMock.ts new file mode 100644 index 0000000..4e4c1fe --- /dev/null +++ b/src/common/open-api/models/TaskMock.ts @@ -0,0 +1,13 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type TaskMock = { + executionTime?: number; + output: Record; + queueWaitTime?: number; + status: + | "IN_PROGRESS" + | "FAILED" + | "FAILED_WITH_TERMINAL_ERROR" + | "COMPLETED"; +}; diff --git a/src/common/open-api/models/WorkflowTestRequest.ts b/src/common/open-api/models/WorkflowTestRequest.ts new file mode 100644 index 0000000..e35a828 --- /dev/null +++ b/src/common/open-api/models/WorkflowTestRequest.ts @@ -0,0 +1,20 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { TaskMock } from "./TaskMock"; +import type { WorkflowDef } from "./WorkflowDef"; +export type WorkflowTestRequest = { + correlationId?: string; + createdBy?: string; + externalInputPayloadStoragePath?: string; + idempotencyKey?: string; + idempotencyStrategy?: "FAIL" | "RETURN_EXISTING"; + input?: Record>; + name: string; + priority?: number; + subWorkflowTestRequest?: Record; + taskRefToMockOutput?: Record>; + taskToDomain?: Record; + version?: number; + workflowDef?: WorkflowDef; +}; diff --git a/src/common/open-api/services/WorkflowResourceService.ts b/src/common/open-api/services/WorkflowResourceService.ts index dd36315..f750ad2 100644 --- a/src/common/open-api/services/WorkflowResourceService.ts +++ b/src/common/open-api/services/WorkflowResourceService.ts @@ -11,6 +11,8 @@ import type { StartWorkflowRequest } from '../models/StartWorkflowRequest'; import type { Workflow } from '../models/Workflow'; import type { WorkflowRun } from '../models/WorkflowRun'; import type { WorkflowStatus } from '../models/WorkflowStatus'; +import type { WorkflowTestRequest } from '../models/WorkflowTestRequest'; + import type { CancelablePromise } from '../core/CancelablePromise'; import type { BaseHttpRequest } from '../core/BaseHttpRequest'; @@ -605,4 +607,20 @@ export class WorkflowResourceService { }); } + /** + * Test workflow execution using mock data + * @param requestBody + * @returns Workflow OK + * @throws ApiError + */ + public testWorkflow( + requestBody: WorkflowTestRequest + ): CancelablePromise { + return this.httpRequest.request({ + method: 'POST', + url: '/workflow/test', + body: requestBody, + mediaType: 'application/json', + }); + } } diff --git a/src/core/metadataClient.ts b/src/core/metadataClient.ts index 459081a..c5c8b8e 100644 --- a/src/core/metadataClient.ts +++ b/src/core/metadataClient.ts @@ -1,7 +1,5 @@ -import { - ConductorClient, - TaskDef, -} from "../common"; +import { ConductorClient, TaskDef } from "../common"; +import { WorkflowDef } from "../common/open-api"; import { tryCatchReThrow } from "./helpers"; export class MetadataClient { @@ -46,4 +44,20 @@ export class MetadataClient { this._client.metadataResource.updateTaskDef(taskDef) ); } + + /** + * Creates or updates (overwrite: true) a workflow definition + * + * @param workflowDef + * @param overwrite + * @returns + */ + public registerWorkflowDef( + workflowDef: WorkflowDef, + overwrite: boolean = false + ) { + return tryCatchReThrow(() => + this._client.metadataResource.create(workflowDef, overwrite) + ); + } } diff --git a/src/task/__tests__/TaskManager.test.ts b/src/task/__tests__/TaskManager.test.ts index c4f0a6f..9b35bcb 100644 --- a/src/task/__tests__/TaskManager.test.ts +++ b/src/task/__tests__/TaskManager.test.ts @@ -55,7 +55,7 @@ describe("TaskManager", () => { const executor = new WorkflowExecutor(client); const worker: ConductorWorker = { - taskDefName: "taskmanager-error-test", + taskDefName: "taskmanager-error-test2", execute: async () => { throw Error("This is a forced error"); }, @@ -71,7 +71,7 @@ describe("TaskManager", () => { manager.startPolling(); await executor.startWorkflow({ - name: "TaskManagerTestE", + name: "TaskManagerTestE2", input: {}, version: 1, }); diff --git a/yarn.lock b/yarn.lock index 711bd0a..7960d4d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2413,6 +2413,16 @@ jest-haste-map@^29.5.0: optionalDependencies: fsevents "^2.3.2" +jest-junit@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-16.0.0.tgz#d838e8c561cf9fdd7eb54f63020777eee4136785" + integrity sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ== + dependencies: + mkdirp "^1.0.4" + strip-ansi "^6.0.1" + uuid "^8.3.2" + xml "^1.0.1" + jest-leak-detector@^29.5.0: version "29.5.0" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c" @@ -2823,6 +2833,11 @@ mkdirp@^0.5.1: dependencies: minimist "^1.2.6" +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + ms@2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" @@ -3578,6 +3593,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + uuid@^9.0.0: version "9.0.0" resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz" @@ -3686,6 +3706,11 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" +xml@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" + integrity sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz"