@temporalio/testing 1.11.8 → 1.12.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,205 @@
1
+ import 'abort-controller/polyfill';
2
+ import { AsyncCompletionClient, Client, WorkflowClient } from '@temporalio/client';
3
+ import { Duration } from '@temporalio/common';
4
+ import { NativeConnection, Runtime } from '@temporalio/worker';
5
+ import { native } from '@temporalio/core-bridge';
6
+ import { Connection } from './connection';
7
+ import { DevServerConfig, TimeSkippingServerConfig } from './ephemeral-server';
8
+ import { ClientOptionsForTestEnv } from './client';
9
+ /**
10
+ * Options for {@link TestWorkflowEnvironment.createLocal}
11
+ */
12
+ export type LocalTestWorkflowEnvironmentOptions = {
13
+ server?: Omit<DevServerConfig, 'type'>;
14
+ client?: ClientOptionsForTestEnv;
15
+ };
16
+ /**
17
+ * Options for {@link TestWorkflowEnvironment.createTimeSkipping}
18
+ */
19
+ export type TimeSkippingTestWorkflowEnvironmentOptions = {
20
+ server?: Omit<TimeSkippingServerConfig, 'type'>;
21
+ client?: ClientOptionsForTestEnv;
22
+ };
23
+ /**
24
+ * Options for {@link TestWorkflowEnvironment.createExistingServer}
25
+ */
26
+ export type ExistingServerTestWorkflowEnvironmentOptions = {
27
+ /** If not set, defaults to localhost:7233 */
28
+ address?: string;
29
+ /** If not set, defaults to default */
30
+ namespace?: string;
31
+ client?: ClientOptionsForTestEnv;
32
+ };
33
+ /**
34
+ * An execution environment for running Workflow integration tests.
35
+ *
36
+ * Runs an external server.
37
+ * By default, the Java test server is used which supports time skipping.
38
+ */
39
+ export declare class TestWorkflowEnvironment {
40
+ private readonly runtime;
41
+ readonly options: TestWorkflowEnvironmentOptionsWithDefaults;
42
+ readonly supportsTimeSkipping: boolean;
43
+ protected readonly server: native.EphemeralServer | 'existing';
44
+ /**
45
+ * Namespace used in this environment (taken from {@link TestWorkflowEnvironmentOptions})
46
+ */
47
+ readonly namespace?: string;
48
+ /**
49
+ * Get an established {@link Connection} to the ephemeral server
50
+ */
51
+ readonly connection: Connection;
52
+ /**
53
+ * A {@link TestEnvClient} for interacting with the ephemeral server
54
+ */
55
+ readonly client: Client;
56
+ /**
57
+ * An {@link AsyncCompletionClient} for interacting with the test server
58
+ *
59
+ * @deprecated - use `client.activity` instead
60
+ */
61
+ readonly asyncCompletionClient: AsyncCompletionClient;
62
+ /**
63
+ * A {@link TimeSkippingWorkflowClient} for interacting with the test server
64
+ *
65
+ * @deprecated - use `client.workflow` instead
66
+ */
67
+ readonly workflowClient: WorkflowClient;
68
+ /**
69
+ * A {@link NativeConnection} for interacting with the test server.
70
+ *
71
+ * Use this connection when creating Workers for testing.
72
+ */
73
+ readonly nativeConnection: NativeConnection;
74
+ protected constructor(runtime: Runtime, options: TestWorkflowEnvironmentOptionsWithDefaults, supportsTimeSkipping: boolean, server: native.EphemeralServer | 'existing', connection: Connection, nativeConnection: NativeConnection, namespace: string | undefined);
75
+ /**
76
+ * Start a time skipping workflow environment.
77
+ *
78
+ * This environment automatically skips to the next events in time when a workflow handle's `result` is awaited on
79
+ * (which includes {@link WorkflowClient.execute}). Before the result is awaited on, time can be manually skipped
80
+ * forward using {@link sleep}. The currently known time can be obtained via {@link currentTimeMs}.
81
+ *
82
+ * This environment will be powered by the Temporal Time Skipping Test Server (part of the [Java SDK](https://github.com/temporalio/sdk-java)).
83
+ * Note that the Time Skipping Test Server does not support full capabilities of the regular Temporal Server, and may
84
+ * occasionally present different behaviors. For general Workflow testing, it is generally preferable to use {@link createLocal}
85
+ * instead.
86
+ *
87
+ * Users can reuse this environment for testing multiple independent workflows, but not concurrently. Time skipping,
88
+ * which is automatically done when awaiting a workflow result and manually done on sleep, is global to the
89
+ * environment, not to the workflow under test. We highly recommend running tests serially when using a single
90
+ * environment or creating a separate environment per test.
91
+ *
92
+ * By default, the latest release of the Test Server will be downloaded and cached to a temporary directory
93
+ * (e.g. `$TMPDIR/temporal-test-server-sdk-typescript-*` or `%TEMP%/temporal-test-server-sdk-typescript-*.exe`). Note
94
+ * that existing cached binaries will be reused without validation that they are still up-to-date, until the SDK
95
+ * itself is updated. Alternatively, a specific version number of the Test Server may be provided, or the path to an
96
+ * existing Test Server binary may be supplied; see {@link LocalTestWorkflowEnvironmentOptions.server.executable}.
97
+ *
98
+ * Note that the Test Server implementation may be changed to another one in the future. Therefore, there is no
99
+ * guarantee that Test Server options, and particularly those provided through the `extraArgs` array, will continue to
100
+ * be supported in the future.
101
+ *
102
+ * IMPORTANT: At this time, the Time Skipping Test Server is not supported on ARM platforms. Execution on Apple
103
+ * silicon Macs will work if Rosetta 2 is installed.
104
+ */
105
+ static createTimeSkipping(opts?: TimeSkippingTestWorkflowEnvironmentOptions): Promise<TestWorkflowEnvironment>;
106
+ /**
107
+ * Start a full Temporal server locally.
108
+ *
109
+ * This environment is good for testing full server capabilities, but does not support time skipping like
110
+ * {@link createTimeSkipping} does. {@link supportsTimeSkipping} will always return `false` for this environment.
111
+ * {@link sleep} will sleep the actual amount of time and {@link currentTimeMs} will return the current time.
112
+ *
113
+ * This local environment will be powered by [Temporal CLI](https://github.com/temporalio/cli), which is a
114
+ * self-contained executable for Temporal. By default, Temporal's database will not be persisted to disk, and no UI
115
+ * will be launched.
116
+ *
117
+ * By default, the latest release of the CLI will be downloaded and cached to a temporary directory
118
+ * (e.g. `$TMPDIR/temporal-sdk-typescript-*` or `%TEMP%/temporal-sdk-typescript-*.exe`). Note that existing cached
119
+ * binaries will be reused without validation that they are still up-to-date, until the SDK itself is updated.
120
+ * Alternatively, a specific version number of the CLI may be provided, or the path to an existing CLI binary may be
121
+ * supplied; see {@link LocalTestWorkflowEnvironmentOptions.server.executable}.
122
+ *
123
+ * Note that the Dev Server implementation may be changed to another one in the future. Therefore, there is no
124
+ * guarantee that Dev Server options, and particularly those provided through the `extraArgs` array, will continue to
125
+ * be supported in the future.
126
+ */
127
+ static createLocal(opts?: LocalTestWorkflowEnvironmentOptions): Promise<TestWorkflowEnvironment>;
128
+ /**
129
+ * Create a new test environment using an existing server. You must already be running a server, which the test
130
+ * environment will connect to.
131
+ */
132
+ static createFromExistingServer(opts?: ExistingServerTestWorkflowEnvironmentOptions): Promise<TestWorkflowEnvironment>;
133
+ /**
134
+ * Create a new test environment
135
+ */
136
+ private static create;
137
+ /**
138
+ * Kill the test server process and close the connection to it
139
+ */
140
+ teardown(): Promise<void>;
141
+ /**
142
+ * Wait for `durationMs` in "server time".
143
+ *
144
+ * This awaits using regular setTimeout in regular environments or manually skips time in time-skipping environments.
145
+ *
146
+ * Useful for simulating events far into the future like completion of long running activities.
147
+ *
148
+ * **Time skippping**:
149
+ *
150
+ * The time skippping server toggles between skipped time and normal time depending on what it needs to execute.
151
+ *
152
+ * This method is _likely_ to resolve in less than `durationMs` of "real time".
153
+ *
154
+ * @param durationMs number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
155
+ *
156
+ * @example
157
+ *
158
+ * `workflow.ts`
159
+ *
160
+ * ```ts
161
+ * const activities = proxyActivities({ startToCloseTimeout: 2_000_000 });
162
+ *
163
+ * export async function raceActivityAndTimer(): Promise<string> {
164
+ * return await Promise.race([
165
+ * wf.sleep(500_000).then(() => 'timer'),
166
+ * activities.longRunning().then(() => 'activity'),
167
+ * ]);
168
+ * }
169
+ * ```
170
+ *
171
+ * `test.ts`
172
+ *
173
+ * ```ts
174
+ * const worker = await Worker.create({
175
+ * connection: testEnv.nativeConnection,
176
+ * activities: {
177
+ * async longRunning() {
178
+ * await testEnv.sleep(1_000_000); // <-- sleep called here
179
+ * },
180
+ * },
181
+ * // ...
182
+ * });
183
+ * ```
184
+ */
185
+ sleep: (durationMs: Duration) => Promise<void>;
186
+ /**
187
+ * Get the current time known to this environment.
188
+ *
189
+ * For non-time-skipping environments this is simply the system time. For time-skipping environments this is whatever
190
+ * time has been skipped to.
191
+ */
192
+ currentTimeMs(): Promise<number>;
193
+ }
194
+ /**
195
+ * Options for {@link TestWorkflowEnvironment.create}
196
+ */
197
+ type TestWorkflowEnvironmentOptions = {
198
+ server: DevServerConfig | TimeSkippingServerConfig | ExistingServerConfig;
199
+ client?: ClientOptionsForTestEnv;
200
+ };
201
+ type ExistingServerConfig = {
202
+ type: 'existing';
203
+ };
204
+ type TestWorkflowEnvironmentOptionsWithDefaults = Required<TestWorkflowEnvironmentOptions>;
205
+ export {};
@@ -0,0 +1,278 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TestWorkflowEnvironment = void 0;
4
+ require("abort-controller/polyfill"); // eslint-disable-line import/no-unassigned-import
5
+ const common_1 = require("@temporalio/common");
6
+ const time_1 = require("@temporalio/common/lib/time");
7
+ const worker_1 = require("@temporalio/worker");
8
+ const core_bridge_1 = require("@temporalio/core-bridge");
9
+ const internal_workflow_1 = require("@temporalio/common/lib/internal-workflow");
10
+ const connection_1 = require("./connection");
11
+ const ephemeral_server_1 = require("./ephemeral-server");
12
+ const client_1 = require("./client");
13
+ /**
14
+ * An execution environment for running Workflow integration tests.
15
+ *
16
+ * Runs an external server.
17
+ * By default, the Java test server is used which supports time skipping.
18
+ */
19
+ class TestWorkflowEnvironment {
20
+ runtime;
21
+ options;
22
+ supportsTimeSkipping;
23
+ server;
24
+ /**
25
+ * Namespace used in this environment (taken from {@link TestWorkflowEnvironmentOptions})
26
+ */
27
+ namespace;
28
+ /**
29
+ * Get an established {@link Connection} to the ephemeral server
30
+ */
31
+ connection;
32
+ /**
33
+ * A {@link TestEnvClient} for interacting with the ephemeral server
34
+ */
35
+ client;
36
+ /**
37
+ * An {@link AsyncCompletionClient} for interacting with the test server
38
+ *
39
+ * @deprecated - use `client.activity` instead
40
+ */
41
+ asyncCompletionClient;
42
+ /**
43
+ * A {@link TimeSkippingWorkflowClient} for interacting with the test server
44
+ *
45
+ * @deprecated - use `client.workflow` instead
46
+ */
47
+ workflowClient;
48
+ /**
49
+ * A {@link NativeConnection} for interacting with the test server.
50
+ *
51
+ * Use this connection when creating Workers for testing.
52
+ */
53
+ nativeConnection;
54
+ constructor(runtime, options, supportsTimeSkipping, server, connection, nativeConnection, namespace) {
55
+ this.runtime = runtime;
56
+ this.options = options;
57
+ this.supportsTimeSkipping = supportsTimeSkipping;
58
+ this.server = server;
59
+ this.connection = connection;
60
+ this.nativeConnection = nativeConnection;
61
+ this.namespace = namespace;
62
+ this.client = new client_1.TestEnvClient({
63
+ connection,
64
+ namespace: this.namespace,
65
+ enableTimeSkipping: supportsTimeSkipping,
66
+ ...options.client,
67
+ });
68
+ this.asyncCompletionClient = this.client.activity; // eslint-disable-line deprecation/deprecation
69
+ this.workflowClient = this.client.workflow; // eslint-disable-line deprecation/deprecation
70
+ }
71
+ /**
72
+ * Start a time skipping workflow environment.
73
+ *
74
+ * This environment automatically skips to the next events in time when a workflow handle's `result` is awaited on
75
+ * (which includes {@link WorkflowClient.execute}). Before the result is awaited on, time can be manually skipped
76
+ * forward using {@link sleep}. The currently known time can be obtained via {@link currentTimeMs}.
77
+ *
78
+ * This environment will be powered by the Temporal Time Skipping Test Server (part of the [Java SDK](https://github.com/temporalio/sdk-java)).
79
+ * Note that the Time Skipping Test Server does not support full capabilities of the regular Temporal Server, and may
80
+ * occasionally present different behaviors. For general Workflow testing, it is generally preferable to use {@link createLocal}
81
+ * instead.
82
+ *
83
+ * Users can reuse this environment for testing multiple independent workflows, but not concurrently. Time skipping,
84
+ * which is automatically done when awaiting a workflow result and manually done on sleep, is global to the
85
+ * environment, not to the workflow under test. We highly recommend running tests serially when using a single
86
+ * environment or creating a separate environment per test.
87
+ *
88
+ * By default, the latest release of the Test Server will be downloaded and cached to a temporary directory
89
+ * (e.g. `$TMPDIR/temporal-test-server-sdk-typescript-*` or `%TEMP%/temporal-test-server-sdk-typescript-*.exe`). Note
90
+ * that existing cached binaries will be reused without validation that they are still up-to-date, until the SDK
91
+ * itself is updated. Alternatively, a specific version number of the Test Server may be provided, or the path to an
92
+ * existing Test Server binary may be supplied; see {@link LocalTestWorkflowEnvironmentOptions.server.executable}.
93
+ *
94
+ * Note that the Test Server implementation may be changed to another one in the future. Therefore, there is no
95
+ * guarantee that Test Server options, and particularly those provided through the `extraArgs` array, will continue to
96
+ * be supported in the future.
97
+ *
98
+ * IMPORTANT: At this time, the Time Skipping Test Server is not supported on ARM platforms. Execution on Apple
99
+ * silicon Macs will work if Rosetta 2 is installed.
100
+ */
101
+ static async createTimeSkipping(opts) {
102
+ return await this.create({
103
+ server: { type: 'time-skipping', ...opts?.server },
104
+ client: opts?.client,
105
+ supportsTimeSkipping: true,
106
+ });
107
+ }
108
+ /**
109
+ * Start a full Temporal server locally.
110
+ *
111
+ * This environment is good for testing full server capabilities, but does not support time skipping like
112
+ * {@link createTimeSkipping} does. {@link supportsTimeSkipping} will always return `false` for this environment.
113
+ * {@link sleep} will sleep the actual amount of time and {@link currentTimeMs} will return the current time.
114
+ *
115
+ * This local environment will be powered by [Temporal CLI](https://github.com/temporalio/cli), which is a
116
+ * self-contained executable for Temporal. By default, Temporal's database will not be persisted to disk, and no UI
117
+ * will be launched.
118
+ *
119
+ * By default, the latest release of the CLI will be downloaded and cached to a temporary directory
120
+ * (e.g. `$TMPDIR/temporal-sdk-typescript-*` or `%TEMP%/temporal-sdk-typescript-*.exe`). Note that existing cached
121
+ * binaries will be reused without validation that they are still up-to-date, until the SDK itself is updated.
122
+ * Alternatively, a specific version number of the CLI may be provided, or the path to an existing CLI binary may be
123
+ * supplied; see {@link LocalTestWorkflowEnvironmentOptions.server.executable}.
124
+ *
125
+ * Note that the Dev Server implementation may be changed to another one in the future. Therefore, there is no
126
+ * guarantee that Dev Server options, and particularly those provided through the `extraArgs` array, will continue to
127
+ * be supported in the future.
128
+ */
129
+ static async createLocal(opts) {
130
+ return await this.create({
131
+ server: { type: 'dev-server', ...opts?.server },
132
+ client: opts?.client,
133
+ namespace: opts?.server?.namespace,
134
+ supportsTimeSkipping: false,
135
+ });
136
+ }
137
+ /**
138
+ * Create a new test environment using an existing server. You must already be running a server, which the test
139
+ * environment will connect to.
140
+ */
141
+ static async createFromExistingServer(opts) {
142
+ return await this.create({
143
+ server: { type: 'existing' },
144
+ client: opts?.client,
145
+ namespace: opts?.namespace ?? 'default',
146
+ supportsTimeSkipping: false,
147
+ address: opts?.address,
148
+ });
149
+ }
150
+ /**
151
+ * Create a new test environment
152
+ */
153
+ static async create(opts) {
154
+ const { supportsTimeSkipping, namespace, ...rest } = opts;
155
+ const optsWithDefaults = addDefaults((0, internal_workflow_1.filterNullAndUndefined)(rest));
156
+ let address;
157
+ const runtime = worker_1.Runtime.instance();
158
+ let server;
159
+ if (optsWithDefaults.server.type !== 'existing') {
160
+ // Add search attributes to CLI server arguments
161
+ if ('searchAttributes' in optsWithDefaults.server && optsWithDefaults.server.searchAttributes) {
162
+ let newArgs = [];
163
+ for (const { name, type } of optsWithDefaults.server.searchAttributes) {
164
+ newArgs.push('--search-attribute');
165
+ newArgs.push(`${name}=${common_1.TypedSearchAttributes.toMetadataType(type)}`);
166
+ }
167
+ newArgs = newArgs.concat(optsWithDefaults.server.extraArgs ?? []);
168
+ optsWithDefaults.server.extraArgs = newArgs;
169
+ }
170
+ server = await runtime.createEphemeralServer((0, ephemeral_server_1.toNativeEphemeralServerConfig)(optsWithDefaults.server));
171
+ address = core_bridge_1.native.ephemeralServerGetTarget(server);
172
+ }
173
+ else {
174
+ address = opts.address ?? 'localhost:7233';
175
+ server = 'existing';
176
+ }
177
+ const nativeConnection = await worker_1.NativeConnection.connect({ address });
178
+ const connection = await connection_1.Connection.connect({ address });
179
+ return new this(runtime, optsWithDefaults, supportsTimeSkipping, server, connection, nativeConnection, namespace);
180
+ }
181
+ /**
182
+ * Kill the test server process and close the connection to it
183
+ */
184
+ async teardown() {
185
+ await this.connection.close().catch((e) => {
186
+ console.error(e);
187
+ /* ignore */
188
+ });
189
+ await this.nativeConnection.close().catch((e) => {
190
+ console.error(e);
191
+ /* ignore */
192
+ });
193
+ if (this.server !== 'existing') {
194
+ await this.runtime.shutdownEphemeralServer(this.server).catch((e) => {
195
+ console.error(e);
196
+ /* ignore */
197
+ });
198
+ }
199
+ }
200
+ /**
201
+ * Wait for `durationMs` in "server time".
202
+ *
203
+ * This awaits using regular setTimeout in regular environments or manually skips time in time-skipping environments.
204
+ *
205
+ * Useful for simulating events far into the future like completion of long running activities.
206
+ *
207
+ * **Time skippping**:
208
+ *
209
+ * The time skippping server toggles between skipped time and normal time depending on what it needs to execute.
210
+ *
211
+ * This method is _likely_ to resolve in less than `durationMs` of "real time".
212
+ *
213
+ * @param durationMs number of milliseconds or {@link https://www.npmjs.com/package/ms | ms-formatted string}
214
+ *
215
+ * @example
216
+ *
217
+ * `workflow.ts`
218
+ *
219
+ * ```ts
220
+ * const activities = proxyActivities({ startToCloseTimeout: 2_000_000 });
221
+ *
222
+ * export async function raceActivityAndTimer(): Promise<string> {
223
+ * return await Promise.race([
224
+ * wf.sleep(500_000).then(() => 'timer'),
225
+ * activities.longRunning().then(() => 'activity'),
226
+ * ]);
227
+ * }
228
+ * ```
229
+ *
230
+ * `test.ts`
231
+ *
232
+ * ```ts
233
+ * const worker = await Worker.create({
234
+ * connection: testEnv.nativeConnection,
235
+ * activities: {
236
+ * async longRunning() {
237
+ * await testEnv.sleep(1_000_000); // <-- sleep called here
238
+ * },
239
+ * },
240
+ * // ...
241
+ * });
242
+ * ```
243
+ */
244
+ sleep = async (durationMs) => {
245
+ if (this.supportsTimeSkipping) {
246
+ await this.connection.testService.unlockTimeSkippingWithSleep({ duration: (0, time_1.msToTs)(durationMs) });
247
+ }
248
+ else {
249
+ await new Promise((resolve) => setTimeout(resolve, (0, time_1.msToNumber)(durationMs)));
250
+ }
251
+ };
252
+ /**
253
+ * Get the current time known to this environment.
254
+ *
255
+ * For non-time-skipping environments this is simply the system time. For time-skipping environments this is whatever
256
+ * time has been skipped to.
257
+ */
258
+ async currentTimeMs() {
259
+ if (this.supportsTimeSkipping) {
260
+ const { time } = await this.connection.testService.getCurrentTime({});
261
+ return (0, time_1.tsToMs)(time);
262
+ }
263
+ else {
264
+ return Date.now();
265
+ }
266
+ }
267
+ }
268
+ exports.TestWorkflowEnvironment = TestWorkflowEnvironment;
269
+ function addDefaults(opts) {
270
+ return {
271
+ client: {},
272
+ ...opts,
273
+ server: {
274
+ ...opts.server,
275
+ },
276
+ };
277
+ }
278
+ //# sourceMappingURL=testing-workflow-environment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing-workflow-environment.js","sourceRoot":"","sources":["../src/testing-workflow-environment.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@temporalio/testing",
3
- "version": "1.11.8",
3
+ "version": "1.12.0-rc.0",
4
4
  "description": "Temporal.io SDK Testing sub-package",
5
5
  "main": "lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -12,13 +12,13 @@
12
12
  "author": "Temporal Technologies Inc. <sdk@temporal.io>",
13
13
  "license": "MIT",
14
14
  "dependencies": {
15
- "@temporalio/activity": "1.11.8",
16
- "@temporalio/client": "1.11.8",
17
- "@temporalio/common": "1.11.8",
18
- "@temporalio/core-bridge": "1.11.8",
19
- "@temporalio/proto": "1.11.8",
20
- "@temporalio/worker": "1.11.8",
21
- "@temporalio/workflow": "1.11.8",
15
+ "@temporalio/activity": "1.12.0-rc.0",
16
+ "@temporalio/client": "1.12.0-rc.0",
17
+ "@temporalio/common": "1.12.0-rc.0",
18
+ "@temporalio/core-bridge": "1.12.0-rc.0",
19
+ "@temporalio/proto": "1.12.0-rc.0",
20
+ "@temporalio/worker": "1.12.0-rc.0",
21
+ "@temporalio/workflow": "1.12.0-rc.0",
22
22
  "abort-controller": "^3.0.0"
23
23
  },
24
24
  "bugs": {
@@ -30,6 +30,9 @@
30
30
  "directory": "packages/testing"
31
31
  },
32
32
  "homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/testing",
33
+ "engines": {
34
+ "node": ">= 18.0.0"
35
+ },
33
36
  "files": [
34
37
  "lib",
35
38
  "src"
@@ -37,5 +40,5 @@
37
40
  "publishConfig": {
38
41
  "access": "public"
39
42
  },
40
- "gitHead": "0a757c370012d89babb668fbd799d83b55c83a1b"
43
+ "gitHead": "3e02f59f5c3edbdd3a4d05bc77c3bee9dbdc1226"
41
44
  }
package/src/client.ts ADDED
@@ -0,0 +1,81 @@
1
+ import 'abort-controller/polyfill'; // eslint-disable-line import/no-unassigned-import
2
+ import {
3
+ Client,
4
+ ClientOptions,
5
+ WorkflowClient,
6
+ WorkflowClientOptions,
7
+ WorkflowResultOptions,
8
+ } from '@temporalio/client';
9
+ import { Connection, TestService } from './connection';
10
+
11
+ export interface TimeSkippingWorkflowClientOptions extends WorkflowClientOptions {
12
+ connection: Connection;
13
+ enableTimeSkipping: boolean;
14
+ }
15
+
16
+ export interface TestEnvClientOptions extends ClientOptions {
17
+ connection: Connection;
18
+ enableTimeSkipping: boolean;
19
+ }
20
+
21
+ /**
22
+ * Subset of the "normal" client options that are used to create a client for the test environment.
23
+ */
24
+ export type ClientOptionsForTestEnv = Omit<ClientOptions, 'namespace' | 'connection'>;
25
+
26
+ /**
27
+ * A client with the exact same API as the "normal" client with 1 exception,
28
+ * When this client waits on a Workflow's result, it will enable time skipping
29
+ * in the test server.
30
+ */
31
+ export class TimeSkippingWorkflowClient extends WorkflowClient {
32
+ protected readonly testService: TestService;
33
+ protected readonly enableTimeSkipping: boolean;
34
+
35
+ constructor(options: TimeSkippingWorkflowClientOptions) {
36
+ super(options);
37
+ this.enableTimeSkipping = options.enableTimeSkipping;
38
+ this.testService = options.connection.testService;
39
+ }
40
+
41
+ /**
42
+ * Gets the result of a Workflow execution.
43
+ *
44
+ * @see {@link WorkflowClient.result}
45
+ */
46
+ override async result<T>(
47
+ workflowId: string,
48
+ runId?: string | undefined,
49
+ opts?: WorkflowResultOptions | undefined
50
+ ): Promise<T> {
51
+ if (this.enableTimeSkipping) {
52
+ await this.testService.unlockTimeSkipping({});
53
+ try {
54
+ return await super.result(workflowId, runId, opts);
55
+ } finally {
56
+ await this.testService.lockTimeSkipping({});
57
+ }
58
+ } else {
59
+ return await super.result(workflowId, runId, opts);
60
+ }
61
+ }
62
+ }
63
+
64
+ /**
65
+ * A client with the exact same API as the "normal" client with one exception:
66
+ * when `TestEnvClient.workflow` (an instance of {@link TimeSkippingWorkflowClient}) waits on a Workflow's result, it will enable time skipping
67
+ * in the Test Server.
68
+ */
69
+ export class TestEnvClient extends Client {
70
+ constructor(options: TestEnvClientOptions) {
71
+ super(options);
72
+
73
+ // Recreate the client (this isn't optimal but it's better than adding public methods just for testing).
74
+ // NOTE: we cast to "any" to work around `workflow` being a readonly attribute.
75
+ (this as any).workflow = new TimeSkippingWorkflowClient({
76
+ ...this.workflow.options,
77
+ connection: options.connection,
78
+ enableTimeSkipping: options.enableTimeSkipping,
79
+ });
80
+ }
81
+ }