@outputai/core 0.14.0 → 0.14.1-next.51e8690.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.
- package/dist/credentials/credentials.d.ts +6 -0
- package/dist/credentials/credentials.js +87 -0
- package/dist/credentials/encrypted_yaml_provider.d.ts +2 -0
- package/dist/credentials/encrypted_yaml_provider.js +77 -0
- package/dist/credentials/encryption.d.ts +3 -0
- package/dist/credentials/encryption.js +6 -0
- package/dist/credentials/errors.d.ts +13 -0
- package/dist/credentials/errors.js +39 -0
- package/dist/credentials/index.d.ts +6 -0
- package/dist/credentials/index.js +5 -0
- package/dist/credentials/init.d.ts +1 -0
- package/dist/credentials/init.js +7 -0
- package/dist/credentials/paths.d.ts +7 -0
- package/dist/credentials/paths.js +14 -0
- package/dist/credentials/provider_registry.d.ts +3 -0
- package/dist/credentials/provider_registry.js +6 -0
- package/dist/credentials/types.d.ts +12 -0
- package/dist/credentials/types.js +1 -0
- package/package.json +14 -3
- package/src/async_storage.d.ts +4 -0
- package/src/consts.js +0 -2
- package/src/helpers/object.d.ts +2 -0
- package/src/hooks/index.d.ts +0 -8
- package/src/hooks/index.js +0 -3
- package/src/logger/index.d.ts +4 -0
- package/src/worker/index.js +3 -1
- package/bin/healthcheck.spec.js +0 -90
- package/src/bus.spec.js +0 -196
- package/src/errors.spec.js +0 -19
- package/src/helpers/activity_options.spec.js +0 -55
- package/src/helpers/component.spec.js +0 -54
- package/src/helpers/error_serializer.spec.js +0 -479
- package/src/helpers/errors.spec.js +0 -189
- package/src/helpers/fetch.spec.js +0 -292
- package/src/helpers/function.spec.js +0 -24
- package/src/helpers/object.spec.js +0 -425
- package/src/helpers/promise.spec.js +0 -62
- package/src/helpers/redact.spec.js +0 -66
- package/src/helpers/string.spec.js +0 -99
- package/src/helpers/temporal_context.spec.ts +0 -83
- package/src/helpers/trace_info.spec.js +0 -45
- package/src/helpers/workflow_context.spec.js +0 -46
- package/src/hooks/index.spec.js +0 -265
- package/src/hooks/pending_hooks.spec.js +0 -62
- package/src/interface/evaluator.spec.js +0 -685
- package/src/interface/logger.spec.js +0 -172
- package/src/interface/step.spec.js +0 -128
- package/src/interface/validations/index.spec.js +0 -197
- package/src/interface/validations/schemas.spec.js +0 -229
- package/src/interface/webhook.spec.js +0 -170
- package/src/interface/workflow.spec.js +0 -710
- package/src/interface/workflow_utils.spec.js +0 -271
- package/src/interface/zod_integration.spec.js +0 -646
- package/src/internal_activities/index.spec.js +0 -186
- package/src/logger/development.spec.js +0 -87
- package/src/logger/index.spec.js +0 -27
- package/src/logger/production.spec.js +0 -71
- package/src/sdk/helpers/component_metadata.spec.js +0 -30
- package/src/sdk/helpers/objects.spec.js +0 -16
- package/src/sdk/helpers/path.spec.js +0 -102
- package/src/sdk/runtime/context.spec.js +0 -37
- package/src/sdk/runtime/events.spec.js +0 -40
- package/src/temporal/client.spec.js +0 -124
- package/src/tracing/processors/local/index.spec.js +0 -204
- package/src/tracing/processors/s3/configs.spec.js +0 -64
- package/src/tracing/processors/s3/index.spec.js +0 -185
- package/src/tracing/processors/s3/redis_client.spec.js +0 -179
- package/src/tracing/processors/s3/s3_client.spec.js +0 -68
- package/src/tracing/tools/build_trace_tree.spec.js +0 -216
- package/src/tracing/trace_engine.spec.js +0 -299
- package/src/worker/bundle.spec.js +0 -53
- package/src/worker/bundler_options.spec.js +0 -62
- package/src/worker/catalog_workflow/catalog_publisher.spec.js +0 -317
- package/src/worker/catalog_workflow/index.spec.js +0 -222
- package/src/worker/configs.spec.js +0 -213
- package/src/worker/configs_tuner_schema.spec.js +0 -127
- package/src/worker/connection_monitor.spec.js +0 -328
- package/src/worker/global_functions.spec.js +0 -55
- package/src/worker/index.spec.js +0 -526
- package/src/worker/interceptors/activity.spec.js +0 -369
- package/src/worker/interceptors/headers.spec.js +0 -73
- package/src/worker/interceptors/workflow.spec.js +0 -357
- package/src/worker/interruption.spec.js +0 -177
- package/src/worker/loader/activities.spec.js +0 -212
- package/src/worker/loader/hooks.spec.js +0 -64
- package/src/worker/loader/matchers.spec.js +0 -140
- package/src/worker/loader/tools.spec.js +0 -595
- package/src/worker/loader/workflows.spec.js +0 -287
- package/src/worker/log_hooks.spec.js +0 -303
- package/src/worker/shutdown.spec.js +0 -197
- package/src/worker/sinks.spec.js +0 -220
- package/src/worker/telemetry.spec.js +0 -80
- package/src/worker/temporal_logger.spec.js +0 -90
- package/src/worker/webpack_loaders/tools.spec.js +0 -314
- package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +0 -134
- package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +0 -154
- package/src/worker/webpack_loaders/workflow_rewriter/rewrite_activity_calls.spec.js +0 -165
- package/src/worker/webpack_loaders/workflow_validator/index.spec.js +0 -202
- package/src/worker/webpack_loaders/workflow_validator/npm_workflow_export_resolve.spec.js +0 -374
- package/src/worker/worker_runner.spec.js +0 -220
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
ACTIVITY_LOGGER_SYMBOL,
|
|
5
|
-
BusEventType
|
|
6
|
-
} = vi.hoisted( () => ( {
|
|
7
|
-
ACTIVITY_LOGGER_SYMBOL: Symbol( 'activity_logger' ),
|
|
8
|
-
BusEventType: {
|
|
9
|
-
ACTIVITY_LOG: 'activity:log'
|
|
10
|
-
}
|
|
11
|
-
} ) );
|
|
12
|
-
|
|
13
|
-
const mainEventBusMock = vi.hoisted( () => ( {
|
|
14
|
-
emit: vi.fn()
|
|
15
|
-
} ) );
|
|
16
|
-
const activityInfoMock = vi.hoisted( () => vi.fn( () => ( {
|
|
17
|
-
activityId: 'act-1',
|
|
18
|
-
activityType: 'myStep'
|
|
19
|
-
} ) ) );
|
|
20
|
-
|
|
21
|
-
vi.mock( '#consts', () => ( { ACTIVITY_LOGGER_SYMBOL, BusEventType } ) );
|
|
22
|
-
vi.mock( '#bus', () => ( { mainEventBus: mainEventBusMock } ) );
|
|
23
|
-
vi.mock( '@temporalio/activity', () => ( {
|
|
24
|
-
activityInfo: activityInfoMock
|
|
25
|
-
} ) );
|
|
26
|
-
|
|
27
|
-
describe( 'worker/global_functions', () => {
|
|
28
|
-
beforeEach( () => {
|
|
29
|
-
vi.clearAllMocks();
|
|
30
|
-
delete globalThis[ACTIVITY_LOGGER_SYMBOL];
|
|
31
|
-
} );
|
|
32
|
-
|
|
33
|
-
it( 'binds activity logger global function that emits activity log events with metadata', async () => {
|
|
34
|
-
const { bindGlobalFunctions } = await import( './global_functions.js' );
|
|
35
|
-
const metadata = { requestId: 'req-1' };
|
|
36
|
-
|
|
37
|
-
bindGlobalFunctions();
|
|
38
|
-
globalThis[ACTIVITY_LOGGER_SYMBOL]( {
|
|
39
|
-
level: 'debug',
|
|
40
|
-
message: 'activity detail',
|
|
41
|
-
metadata
|
|
42
|
-
} );
|
|
43
|
-
|
|
44
|
-
expect( activityInfoMock ).toHaveBeenCalledTimes( 1 );
|
|
45
|
-
expect( mainEventBusMock.emit ).toHaveBeenCalledWith( BusEventType.ACTIVITY_LOG, {
|
|
46
|
-
level: 'debug',
|
|
47
|
-
message: 'activity detail',
|
|
48
|
-
metadata,
|
|
49
|
-
activityInfo: {
|
|
50
|
-
activityId: 'act-1',
|
|
51
|
-
activityType: 'myStep'
|
|
52
|
-
}
|
|
53
|
-
} );
|
|
54
|
-
} );
|
|
55
|
-
} );
|
package/src/worker/index.spec.js
DELETED
|
@@ -1,526 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { BusEventType } from '#consts';
|
|
3
|
-
|
|
4
|
-
const {
|
|
5
|
-
bindGlobalFunctionsMock,
|
|
6
|
-
catalogPublisherInstance,
|
|
7
|
-
configValues,
|
|
8
|
-
connectionMonitorInstance,
|
|
9
|
-
createCatalogMock,
|
|
10
|
-
flushPendingHooksMock,
|
|
11
|
-
hashSourceCodeMock,
|
|
12
|
-
initInterceptorsMock,
|
|
13
|
-
interruption,
|
|
14
|
-
loadActivitiesMock,
|
|
15
|
-
loadHooksMock,
|
|
16
|
-
loadWorkflowsMock,
|
|
17
|
-
mainEventBusMock,
|
|
18
|
-
mockConnection,
|
|
19
|
-
mockLog,
|
|
20
|
-
mockWorker,
|
|
21
|
-
promises,
|
|
22
|
-
resetPromises,
|
|
23
|
-
serializeErrorMock,
|
|
24
|
-
setupClientConfigMock,
|
|
25
|
-
setupInterruptionHandlerMock,
|
|
26
|
-
setupTelemetryMock,
|
|
27
|
-
setupTemporalLoggerMock,
|
|
28
|
-
shutdownServicesMock,
|
|
29
|
-
workerRunnerInstance
|
|
30
|
-
} = vi.hoisted( () => {
|
|
31
|
-
const createDeferred = () => {
|
|
32
|
-
const state = {};
|
|
33
|
-
state.promise = new Promise( ( resolve, reject ) => {
|
|
34
|
-
state.resolve = resolve;
|
|
35
|
-
state.reject = reject;
|
|
36
|
-
} );
|
|
37
|
-
return state;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const promises = {};
|
|
41
|
-
const resetPromises = () => {
|
|
42
|
-
promises.workerRunner = createDeferred();
|
|
43
|
-
promises.connectionMonitor = createDeferred();
|
|
44
|
-
};
|
|
45
|
-
resetPromises();
|
|
46
|
-
|
|
47
|
-
const configValues = {
|
|
48
|
-
address: 'localhost:7233',
|
|
49
|
-
apiKey: undefined,
|
|
50
|
-
namespace: 'default',
|
|
51
|
-
taskQueue: 'test-queue',
|
|
52
|
-
catalogId: 'test-catalog',
|
|
53
|
-
grpcProxy: undefined,
|
|
54
|
-
maxConcurrentWorkflowTaskExecutions: 200,
|
|
55
|
-
maxConcurrentActivityTaskExecutions: 40,
|
|
56
|
-
maxCachedWorkflows: 1000,
|
|
57
|
-
maxConcurrentActivityTaskPolls: 5,
|
|
58
|
-
maxConcurrentWorkflowTaskPolls: 5,
|
|
59
|
-
workerTuner: undefined,
|
|
60
|
-
shutdownForceTime: undefined,
|
|
61
|
-
shutdownGraceTime: undefined,
|
|
62
|
-
hookFlushTimeoutMs: 5000
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* The real runner only settles once the worker finished draining, which is triggered by stop().
|
|
67
|
-
* Aborting alone leaves it running, so the shutdown sequence is the one awaiting the drain.
|
|
68
|
-
*/
|
|
69
|
-
const workerRunnerInstance = {
|
|
70
|
-
running: false,
|
|
71
|
-
options: null,
|
|
72
|
-
start: vi.fn( () => {
|
|
73
|
-
workerRunnerInstance.running = true;
|
|
74
|
-
return promises.workerRunner.promise.finally( () => {
|
|
75
|
-
workerRunnerInstance.running = false;
|
|
76
|
-
} );
|
|
77
|
-
} ),
|
|
78
|
-
stop: vi.fn( () => {
|
|
79
|
-
promises.workerRunner.resolve();
|
|
80
|
-
return promises.workerRunner.promise.catch( () => {} );
|
|
81
|
-
} )
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
/** The real monitor exits its loop as soon as the signal aborts, so it settles on its own. */
|
|
85
|
-
const connectionMonitorInstance = {
|
|
86
|
-
running: false,
|
|
87
|
-
options: null,
|
|
88
|
-
start: vi.fn( () => {
|
|
89
|
-
connectionMonitorInstance.running = true;
|
|
90
|
-
connectionMonitorInstance.options?.signal.addEventListener(
|
|
91
|
-
'abort',
|
|
92
|
-
() => promises.connectionMonitor.resolve(),
|
|
93
|
-
{ once: true }
|
|
94
|
-
);
|
|
95
|
-
return promises.connectionMonitor.promise.finally( () => {
|
|
96
|
-
connectionMonitorInstance.running = false;
|
|
97
|
-
} );
|
|
98
|
-
} ),
|
|
99
|
-
stop: vi.fn( () => {
|
|
100
|
-
promises.connectionMonitor.resolve();
|
|
101
|
-
return promises.connectionMonitor.promise.catch( () => {} );
|
|
102
|
-
} )
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const catalogPublisherInstance = {
|
|
106
|
-
running: false,
|
|
107
|
-
options: null,
|
|
108
|
-
run: vi.fn().mockResolvedValue( undefined ),
|
|
109
|
-
interrupt: vi.fn().mockResolvedValue( undefined )
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
class KillSignError extends Error {
|
|
113
|
-
name = 'KillSignError';
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
class UncaughtError extends Error {
|
|
117
|
-
name = 'UncaughtError';
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const interruption = { controller: null, KillSignError, UncaughtError };
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
bindGlobalFunctionsMock: vi.fn(),
|
|
124
|
-
catalogPublisherInstance,
|
|
125
|
-
configValues,
|
|
126
|
-
connectionMonitorInstance,
|
|
127
|
-
createCatalogMock: vi.fn().mockReturnValue( { workflowNames: [ 'demo' ] } ),
|
|
128
|
-
flushPendingHooksMock: vi.fn().mockResolvedValue( undefined ),
|
|
129
|
-
hashSourceCodeMock: vi.fn().mockResolvedValue( 'catalog-hash' ),
|
|
130
|
-
initInterceptorsMock: vi.fn().mockReturnValue( [] ),
|
|
131
|
-
interruption,
|
|
132
|
-
loadActivitiesMock: vi.fn().mockResolvedValue( { activities: {} } ),
|
|
133
|
-
loadHooksMock: vi.fn().mockResolvedValue( undefined ),
|
|
134
|
-
loadWorkflowsMock: vi.fn().mockResolvedValue( { workflows: [], entrypoint: '/fake/workflows/path.js' } ),
|
|
135
|
-
mainEventBusMock: { emit: vi.fn(), on: vi.fn() },
|
|
136
|
-
mockConnection: { close: vi.fn().mockResolvedValue( undefined ) },
|
|
137
|
-
mockLog: { error: vi.fn(), info: vi.fn(), warn: vi.fn() },
|
|
138
|
-
mockWorker: { name: 'temporal-worker' },
|
|
139
|
-
promises,
|
|
140
|
-
resetPromises,
|
|
141
|
-
serializeErrorMock: vi.fn( error => ( { name: error.name, message: error.message } ) ),
|
|
142
|
-
setupClientConfigMock: vi.fn(),
|
|
143
|
-
setupInterruptionHandlerMock: vi.fn( controller => {
|
|
144
|
-
interruption.controller = controller;
|
|
145
|
-
} ),
|
|
146
|
-
setupTelemetryMock: vi.fn(),
|
|
147
|
-
setupTemporalLoggerMock: vi.fn(),
|
|
148
|
-
shutdownServicesMock: vi.fn().mockResolvedValue( [] ),
|
|
149
|
-
workerRunnerInstance
|
|
150
|
-
};
|
|
151
|
-
} );
|
|
152
|
-
|
|
153
|
-
const initTracing = vi.fn().mockResolvedValue( undefined );
|
|
154
|
-
|
|
155
|
-
vi.mock( '#logger', () => ( { createChildLogger: () => mockLog } ) );
|
|
156
|
-
vi.mock( '#helpers/error_serializer', () => ( { serializeError: serializeErrorMock } ) );
|
|
157
|
-
vi.mock( '#tracing', () => ( { init: initTracing } ) );
|
|
158
|
-
vi.mock( '#bus', () => ( { mainEventBus: mainEventBusMock } ) );
|
|
159
|
-
vi.mock( '#hooks/pending_hooks', () => ( { flushPendingHooks: flushPendingHooksMock } ) );
|
|
160
|
-
vi.mock( '#temporal/client', () => ( { setupClientConfig: setupClientConfigMock } ) );
|
|
161
|
-
vi.mock( './configs.js', () => configValues );
|
|
162
|
-
vi.mock( './loader/workflows.js', () => ( { loadWorkflows: loadWorkflowsMock } ) );
|
|
163
|
-
vi.mock( './loader/activities.js', () => ( { loadActivities: loadActivitiesMock } ) );
|
|
164
|
-
vi.mock( './loader/hooks.js', () => ( { loadHooks: loadHooksMock } ) );
|
|
165
|
-
vi.mock( './loader/tools.js', () => ( { hashSourceCode: hashSourceCodeMock } ) );
|
|
166
|
-
vi.mock( './sinks.js', () => ( { sinks: {} } ) );
|
|
167
|
-
vi.mock( './catalog_workflow/index.js', () => ( { createCatalog: createCatalogMock } ) );
|
|
168
|
-
vi.mock( './bundler_options.js', () => ( { webpackConfigHook: vi.fn() } ) );
|
|
169
|
-
vi.mock( './interceptors/index.js', () => ( { initInterceptors: initInterceptorsMock } ) );
|
|
170
|
-
vi.mock( './telemetry.js', () => ( { setupTelemetry: setupTelemetryMock } ) );
|
|
171
|
-
vi.mock( './global_functions.js', () => ( { bindGlobalFunctions: bindGlobalFunctionsMock } ) );
|
|
172
|
-
vi.mock( './temporal_logger.js', () => ( { setupTemporalLogger: setupTemporalLoggerMock } ) );
|
|
173
|
-
vi.mock( './log_hooks.js', () => ( {} ) );
|
|
174
|
-
vi.mock( './interruption.js', () => ( {
|
|
175
|
-
setupInterruptionHandler: setupInterruptionHandlerMock,
|
|
176
|
-
KillSignError: interruption.KillSignError,
|
|
177
|
-
UncaughtError: interruption.UncaughtError
|
|
178
|
-
} ) );
|
|
179
|
-
vi.mock( './worker_runner.js', () => ( {
|
|
180
|
-
WorkerRunner: vi.fn( function ( options ) {
|
|
181
|
-
workerRunnerInstance.options = options;
|
|
182
|
-
return workerRunnerInstance;
|
|
183
|
-
} )
|
|
184
|
-
} ) );
|
|
185
|
-
vi.mock( './connection_monitor.js', () => ( {
|
|
186
|
-
TemporalConnectionMonitor: vi.fn( function ( options ) {
|
|
187
|
-
connectionMonitorInstance.options = options;
|
|
188
|
-
return connectionMonitorInstance;
|
|
189
|
-
} )
|
|
190
|
-
} ) );
|
|
191
|
-
vi.mock( './catalog_workflow/catalog_publisher.js', () => ( {
|
|
192
|
-
CatalogPublisher: vi.fn( function ( options ) {
|
|
193
|
-
catalogPublisherInstance.options = options;
|
|
194
|
-
return catalogPublisherInstance;
|
|
195
|
-
} )
|
|
196
|
-
} ) );
|
|
197
|
-
vi.mock( './shutdown.js', () => ( { shutdownServices: shutdownServicesMock } ) );
|
|
198
|
-
vi.mock( '@temporalio/worker', () => ( {
|
|
199
|
-
NativeConnection: { connect: vi.fn().mockResolvedValue( mockConnection ) },
|
|
200
|
-
Worker: { create: vi.fn().mockResolvedValue( mockWorker ) }
|
|
201
|
-
} ) );
|
|
202
|
-
|
|
203
|
-
const importWorker = async () => {
|
|
204
|
-
vi.resetModules();
|
|
205
|
-
await import( './index.js' );
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
/** Imports the worker and waits until it is polling, which is where a healthy boot settles. */
|
|
209
|
-
const bootWorker = async () => {
|
|
210
|
-
await importWorker();
|
|
211
|
-
await vi.waitFor( () => {
|
|
212
|
-
expect( workerRunnerInstance.start ).toHaveBeenCalled();
|
|
213
|
-
expect( connectionMonitorInstance.start ).toHaveBeenCalled();
|
|
214
|
-
} );
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
const waitForExit = () => vi.waitFor( () => expect( process.exit ).toHaveBeenCalled() );
|
|
218
|
-
|
|
219
|
-
describe( 'worker/index', () => {
|
|
220
|
-
const originalArgv = process.argv;
|
|
221
|
-
|
|
222
|
-
beforeEach( () => {
|
|
223
|
-
vi.clearAllMocks();
|
|
224
|
-
resetPromises();
|
|
225
|
-
configValues.apiKey = undefined;
|
|
226
|
-
configValues.grpcProxy = undefined;
|
|
227
|
-
configValues.workerTuner = undefined;
|
|
228
|
-
configValues.shutdownForceTime = undefined;
|
|
229
|
-
configValues.shutdownGraceTime = undefined;
|
|
230
|
-
workerRunnerInstance.running = false;
|
|
231
|
-
workerRunnerInstance.options = null;
|
|
232
|
-
connectionMonitorInstance.running = false;
|
|
233
|
-
connectionMonitorInstance.options = null;
|
|
234
|
-
catalogPublisherInstance.running = false;
|
|
235
|
-
catalogPublisherInstance.options = null;
|
|
236
|
-
interruption.controller = null;
|
|
237
|
-
mockConnection.close.mockResolvedValue( undefined );
|
|
238
|
-
process.argv = [ ...originalArgv.slice( 0, 2 ), '/test/caller/dir' ];
|
|
239
|
-
vi.spyOn( process, 'exit' ).mockImplementation( () => undefined );
|
|
240
|
-
} );
|
|
241
|
-
|
|
242
|
-
afterEach( () => {
|
|
243
|
-
process.argv = originalArgv;
|
|
244
|
-
vi.restoreAllMocks();
|
|
245
|
-
} );
|
|
246
|
-
|
|
247
|
-
describe( 'startup', () => {
|
|
248
|
-
it( 'wires every lifecycle component with the shared abort signal', async () => {
|
|
249
|
-
const { NativeConnection, Worker } = await import( '@temporalio/worker' );
|
|
250
|
-
const { TemporalConnectionMonitor } = await import( './connection_monitor.js' );
|
|
251
|
-
const { CatalogPublisher } = await import( './catalog_workflow/catalog_publisher.js' );
|
|
252
|
-
const { WorkerRunner } = await import( './worker_runner.js' );
|
|
253
|
-
|
|
254
|
-
await bootWorker();
|
|
255
|
-
|
|
256
|
-
const { signal } = interruption.controller;
|
|
257
|
-
|
|
258
|
-
expect( setupInterruptionHandlerMock ).toHaveBeenCalledWith( expect.any( AbortController ) );
|
|
259
|
-
expect( setupTemporalLoggerMock ).toHaveBeenCalledOnce();
|
|
260
|
-
expect( loadHooksMock ).toHaveBeenCalledWith( '/test/caller/dir' );
|
|
261
|
-
expect( loadWorkflowsMock ).toHaveBeenCalledWith( '/test/caller/dir' );
|
|
262
|
-
expect( loadActivitiesMock ).toHaveBeenCalledWith( '/test/caller/dir', [] );
|
|
263
|
-
expect( mainEventBusMock.emit ).toHaveBeenCalledWith( BusEventType.WORKER_BEFORE_START );
|
|
264
|
-
expect( initTracing ).toHaveBeenCalledOnce();
|
|
265
|
-
expect( createCatalogMock ).toHaveBeenCalledWith( { workflows: [], activities: {} } );
|
|
266
|
-
expect( hashSourceCodeMock ).toHaveBeenCalledWith( '/test/caller/dir' );
|
|
267
|
-
expect( NativeConnection.connect ).toHaveBeenCalledWith( {
|
|
268
|
-
address: configValues.address,
|
|
269
|
-
tls: false,
|
|
270
|
-
apiKey: undefined,
|
|
271
|
-
proxy: undefined
|
|
272
|
-
} );
|
|
273
|
-
expect( setupClientConfigMock ).toHaveBeenCalledWith( {
|
|
274
|
-
connection: mockConnection,
|
|
275
|
-
namespace: configValues.namespace
|
|
276
|
-
} );
|
|
277
|
-
expect( CatalogPublisher ).toHaveBeenCalledWith( {
|
|
278
|
-
connection: mockConnection,
|
|
279
|
-
namespace: configValues.namespace,
|
|
280
|
-
catalog: { workflowNames: [ 'demo' ] },
|
|
281
|
-
catalogHash: 'catalog-hash',
|
|
282
|
-
signal
|
|
283
|
-
} );
|
|
284
|
-
expect( catalogPublisherInstance.run ).toHaveBeenCalledOnce();
|
|
285
|
-
expect( TemporalConnectionMonitor ).toHaveBeenCalledWith( { connection: mockConnection, signal } );
|
|
286
|
-
expect( Worker.create ).toHaveBeenCalledWith( expect.objectContaining( {
|
|
287
|
-
connection: mockConnection,
|
|
288
|
-
namespace: configValues.namespace,
|
|
289
|
-
taskQueue: configValues.taskQueue,
|
|
290
|
-
workflowsPath: '/fake/workflows/path.js',
|
|
291
|
-
activities: {},
|
|
292
|
-
maxConcurrentWorkflowTaskExecutions: configValues.maxConcurrentWorkflowTaskExecutions,
|
|
293
|
-
maxConcurrentActivityTaskExecutions: configValues.maxConcurrentActivityTaskExecutions,
|
|
294
|
-
maxCachedWorkflows: configValues.maxCachedWorkflows,
|
|
295
|
-
maxConcurrentActivityTaskPolls: configValues.maxConcurrentActivityTaskPolls,
|
|
296
|
-
maxConcurrentWorkflowTaskPolls: configValues.maxConcurrentWorkflowTaskPolls
|
|
297
|
-
} ) );
|
|
298
|
-
expect( Worker.create.mock.calls[0][0] ).not.toHaveProperty( 'shutdownForceTime' );
|
|
299
|
-
expect( Worker.create.mock.calls[0][0] ).not.toHaveProperty( 'shutdownGraceTime' );
|
|
300
|
-
expect( initInterceptorsMock ).toHaveBeenCalledWith( { activities: {}, workflows: [] } );
|
|
301
|
-
expect( setupTelemetryMock ).toHaveBeenCalledWith( { worker: mockWorker } );
|
|
302
|
-
expect( WorkerRunner ).toHaveBeenCalledWith( { worker: mockWorker, signal } );
|
|
303
|
-
} );
|
|
304
|
-
|
|
305
|
-
it( 'publishes the catalog before creating the worker', async () => {
|
|
306
|
-
const { Worker } = await import( '@temporalio/worker' );
|
|
307
|
-
|
|
308
|
-
await bootWorker();
|
|
309
|
-
|
|
310
|
-
// The worker never exists unless its catalog matches its own source code.
|
|
311
|
-
expect( catalogPublisherInstance.run.mock.invocationCallOrder[0] )
|
|
312
|
-
.toBeLessThan( Worker.create.mock.invocationCallOrder[0] );
|
|
313
|
-
} );
|
|
314
|
-
|
|
315
|
-
it( 'passes worker tuner instead of incompatible execution concurrency options', async () => {
|
|
316
|
-
configValues.workerTuner = { tunerOptions: { targetMemoryUsage: 0.8, targetCpuUsage: 0.9 } };
|
|
317
|
-
const { Worker } = await import( '@temporalio/worker' );
|
|
318
|
-
|
|
319
|
-
await bootWorker();
|
|
320
|
-
|
|
321
|
-
const workerOptions = Worker.create.mock.calls[0][0];
|
|
322
|
-
expect( workerOptions ).toEqual( expect.objectContaining( { tuner: configValues.workerTuner } ) );
|
|
323
|
-
expect( workerOptions ).not.toHaveProperty( 'maxConcurrentWorkflowTaskExecutions' );
|
|
324
|
-
expect( workerOptions ).not.toHaveProperty( 'maxConcurrentActivityTaskExecutions' );
|
|
325
|
-
} );
|
|
326
|
-
|
|
327
|
-
it( 'enables TLS when an apiKey is set', async () => {
|
|
328
|
-
configValues.apiKey = 'secret';
|
|
329
|
-
const { NativeConnection } = await import( '@temporalio/worker' );
|
|
330
|
-
|
|
331
|
-
await bootWorker();
|
|
332
|
-
|
|
333
|
-
expect( NativeConnection.connect ).toHaveBeenCalledWith( expect.objectContaining( { apiKey: 'secret', tls: true } ) );
|
|
334
|
-
} );
|
|
335
|
-
|
|
336
|
-
it( 'routes the connection through the configured gRPC proxy', async () => {
|
|
337
|
-
configValues.grpcProxy = 'proxy:1234';
|
|
338
|
-
const { NativeConnection } = await import( '@temporalio/worker' );
|
|
339
|
-
|
|
340
|
-
await bootWorker();
|
|
341
|
-
|
|
342
|
-
expect( NativeConnection.connect ).toHaveBeenCalledWith( expect.objectContaining( {
|
|
343
|
-
proxy: { type: 'http-connect', targetHost: 'proxy:1234' }
|
|
344
|
-
} ) );
|
|
345
|
-
} );
|
|
346
|
-
|
|
347
|
-
it( 'passes configured shutdown durations to the worker', async () => {
|
|
348
|
-
configValues.shutdownForceTime = '30s';
|
|
349
|
-
configValues.shutdownGraceTime = '10s';
|
|
350
|
-
const { Worker } = await import( '@temporalio/worker' );
|
|
351
|
-
|
|
352
|
-
await bootWorker();
|
|
353
|
-
|
|
354
|
-
expect( Worker.create ).toHaveBeenCalledWith( expect.objectContaining( {
|
|
355
|
-
shutdownForceTime: '30s',
|
|
356
|
-
shutdownGraceTime: '10s'
|
|
357
|
-
} ) );
|
|
358
|
-
} );
|
|
359
|
-
|
|
360
|
-
it( 'stops advancing through startup once the signal aborts', async () => {
|
|
361
|
-
const { NativeConnection } = await import( '@temporalio/worker' );
|
|
362
|
-
loadHooksMock.mockImplementationOnce( async () => {
|
|
363
|
-
interruption.controller.abort( new interruption.KillSignError( 'SIGTERM' ) );
|
|
364
|
-
} );
|
|
365
|
-
|
|
366
|
-
await importWorker();
|
|
367
|
-
await waitForExit();
|
|
368
|
-
|
|
369
|
-
expect( loadWorkflowsMock ).not.toHaveBeenCalled();
|
|
370
|
-
expect( NativeConnection.connect ).not.toHaveBeenCalled();
|
|
371
|
-
expect( mockLog.error ).not.toHaveBeenCalled();
|
|
372
|
-
expect( process.exit ).toHaveBeenCalledWith( 0 );
|
|
373
|
-
} );
|
|
374
|
-
} );
|
|
375
|
-
|
|
376
|
-
describe( 'clean termination', () => {
|
|
377
|
-
it( 'shuts every service down and exits successfully on a kill signal', async () => {
|
|
378
|
-
await bootWorker();
|
|
379
|
-
|
|
380
|
-
interruption.controller.abort( new interruption.KillSignError( 'SIGTERM' ) );
|
|
381
|
-
await waitForExit();
|
|
382
|
-
|
|
383
|
-
expect( shutdownServicesMock ).toHaveBeenCalledOnce();
|
|
384
|
-
expect( shutdownServicesMock ).toHaveBeenCalledWith( {
|
|
385
|
-
workerRunner: workerRunnerInstance,
|
|
386
|
-
connectionMonitor: connectionMonitorInstance,
|
|
387
|
-
catalogPublisher: catalogPublisherInstance,
|
|
388
|
-
connection: mockConnection
|
|
389
|
-
} );
|
|
390
|
-
expect( mockLog.error ).not.toHaveBeenCalled();
|
|
391
|
-
expect( mainEventBusMock.emit ).not.toHaveBeenCalledWith( BusEventType.RUNTIME_ERROR, expect.anything() );
|
|
392
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Bye', { exitCode: 0 } );
|
|
393
|
-
expect( process.exit ).toHaveBeenCalledWith( 0 );
|
|
394
|
-
} );
|
|
395
|
-
|
|
396
|
-
it( 'still exits cleanly when a service other than the worker failed to stop', async () => {
|
|
397
|
-
await bootWorker();
|
|
398
|
-
shutdownServicesMock.mockResolvedValueOnce( [ { service: 'connection', error: new Error( 'close failed' ) } ] );
|
|
399
|
-
|
|
400
|
-
interruption.controller.abort( new interruption.KillSignError( 'SIGTERM' ) );
|
|
401
|
-
await waitForExit();
|
|
402
|
-
|
|
403
|
-
expect( mockLog.error ).not.toHaveBeenCalled();
|
|
404
|
-
expect( process.exit ).toHaveBeenCalledWith( 0 );
|
|
405
|
-
} );
|
|
406
|
-
|
|
407
|
-
it( 'does not report the worker as terminated when a signal ended the run', async () => {
|
|
408
|
-
await bootWorker();
|
|
409
|
-
|
|
410
|
-
interruption.controller.abort( new interruption.KillSignError( 'SIGTERM' ) );
|
|
411
|
-
await waitForExit();
|
|
412
|
-
|
|
413
|
-
expect( mockLog.info ).not.toHaveBeenCalledWith( 'Worker terminated' );
|
|
414
|
-
} );
|
|
415
|
-
|
|
416
|
-
it( 'reports a terminated worker when the run ends without an abort', async () => {
|
|
417
|
-
await bootWorker();
|
|
418
|
-
|
|
419
|
-
promises.connectionMonitor.resolve();
|
|
420
|
-
await waitForExit();
|
|
421
|
-
|
|
422
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Worker terminated' );
|
|
423
|
-
expect( process.exit ).toHaveBeenCalledWith( 0 );
|
|
424
|
-
} );
|
|
425
|
-
|
|
426
|
-
it( 'flushes pending hooks before exiting', async () => {
|
|
427
|
-
await bootWorker();
|
|
428
|
-
|
|
429
|
-
interruption.controller.abort( new interruption.KillSignError( 'SIGTERM' ) );
|
|
430
|
-
await waitForExit();
|
|
431
|
-
|
|
432
|
-
expect( flushPendingHooksMock ).toHaveBeenCalledWith( 5000 );
|
|
433
|
-
expect( shutdownServicesMock.mock.invocationCallOrder[0] )
|
|
434
|
-
.toBeLessThan( flushPendingHooksMock.mock.invocationCallOrder[0] );
|
|
435
|
-
expect( flushPendingHooksMock.mock.invocationCallOrder[0] )
|
|
436
|
-
.toBeLessThan( process.exit.mock.invocationCallOrder[0] );
|
|
437
|
-
} );
|
|
438
|
-
} );
|
|
439
|
-
|
|
440
|
-
describe( 'failures', () => {
|
|
441
|
-
it( 'reports an uncaught error captured by the interruption handler', async () => {
|
|
442
|
-
const cause = new Error( 'boom' );
|
|
443
|
-
await bootWorker();
|
|
444
|
-
|
|
445
|
-
const fault = new interruption.UncaughtError( 'uncaughtException', { cause } );
|
|
446
|
-
interruption.controller.abort( fault );
|
|
447
|
-
await waitForExit();
|
|
448
|
-
|
|
449
|
-
expect( mockLog.error ).toHaveBeenCalledWith( 'Worker error', {
|
|
450
|
-
error: expect.objectContaining( { name: 'UncaughtError', message: 'uncaughtException' } )
|
|
451
|
-
} );
|
|
452
|
-
expect( mainEventBusMock.emit ).toHaveBeenCalledWith( BusEventType.RUNTIME_ERROR, { error: fault } );
|
|
453
|
-
expect( process.exit ).toHaveBeenCalledWith( 1 );
|
|
454
|
-
} );
|
|
455
|
-
|
|
456
|
-
it( 'exits with a failure when the worker never finished draining', async () => {
|
|
457
|
-
await bootWorker();
|
|
458
|
-
shutdownServicesMock.mockResolvedValueOnce( [ { service: 'worker', error: new Error( 'drain failed' ) } ] );
|
|
459
|
-
|
|
460
|
-
interruption.controller.abort( new interruption.KillSignError( 'SIGTERM' ) );
|
|
461
|
-
await waitForExit();
|
|
462
|
-
|
|
463
|
-
expect( process.exit ).toHaveBeenCalledWith( 1 );
|
|
464
|
-
} );
|
|
465
|
-
it( 'reports a lost connection and shuts down', async () => {
|
|
466
|
-
const error = new Error( 'connection lost' );
|
|
467
|
-
await bootWorker();
|
|
468
|
-
|
|
469
|
-
promises.connectionMonitor.reject( error );
|
|
470
|
-
await waitForExit();
|
|
471
|
-
|
|
472
|
-
expect( shutdownServicesMock ).toHaveBeenCalledOnce();
|
|
473
|
-
expect( serializeErrorMock ).toHaveBeenCalledWith( error );
|
|
474
|
-
expect( mockLog.error ).toHaveBeenCalledWith( 'Worker error', {
|
|
475
|
-
error: expect.objectContaining( { message: 'connection lost' } )
|
|
476
|
-
} );
|
|
477
|
-
expect( mainEventBusMock.emit ).toHaveBeenCalledWith( BusEventType.RUNTIME_ERROR, { error } );
|
|
478
|
-
expect( process.exit ).toHaveBeenCalledWith( 1 );
|
|
479
|
-
} );
|
|
480
|
-
|
|
481
|
-
it( 'reports a failed worker run', async () => {
|
|
482
|
-
const error = new Error( 'worker crashed' );
|
|
483
|
-
await bootWorker();
|
|
484
|
-
|
|
485
|
-
promises.workerRunner.reject( error );
|
|
486
|
-
await waitForExit();
|
|
487
|
-
|
|
488
|
-
expect( shutdownServicesMock ).toHaveBeenCalledOnce();
|
|
489
|
-
expect( mockLog.error ).toHaveBeenCalledWith( 'Worker error', {
|
|
490
|
-
error: expect.objectContaining( { message: 'worker crashed' } )
|
|
491
|
-
} );
|
|
492
|
-
expect( process.exit ).toHaveBeenCalledWith( 1 );
|
|
493
|
-
} );
|
|
494
|
-
|
|
495
|
-
it( 'never creates the worker when publishing the catalog fails', async () => {
|
|
496
|
-
const { Worker } = await import( '@temporalio/worker' );
|
|
497
|
-
catalogPublisherInstance.run.mockRejectedValueOnce( new Error( 'catalog failed' ) );
|
|
498
|
-
|
|
499
|
-
await importWorker();
|
|
500
|
-
await waitForExit();
|
|
501
|
-
|
|
502
|
-
expect( Worker.create ).not.toHaveBeenCalled();
|
|
503
|
-
expect( workerRunnerInstance.start ).not.toHaveBeenCalled();
|
|
504
|
-
expect( shutdownServicesMock ).toHaveBeenCalledOnce();
|
|
505
|
-
expect( mockLog.error ).toHaveBeenCalledWith( 'Worker error', {
|
|
506
|
-
error: expect.objectContaining( { message: 'catalog failed' } )
|
|
507
|
-
} );
|
|
508
|
-
expect( process.exit ).toHaveBeenCalledWith( 1 );
|
|
509
|
-
} );
|
|
510
|
-
|
|
511
|
-
it( 'hands the partially built services to shutdown when startup fails', async () => {
|
|
512
|
-
const { Worker } = await import( '@temporalio/worker' );
|
|
513
|
-
Worker.create.mockRejectedValueOnce( new Error( 'worker create failed' ) );
|
|
514
|
-
|
|
515
|
-
await importWorker();
|
|
516
|
-
await waitForExit();
|
|
517
|
-
|
|
518
|
-
expect( workerRunnerInstance.start ).not.toHaveBeenCalled();
|
|
519
|
-
expect( shutdownServicesMock ).toHaveBeenCalledWith( expect.objectContaining( {
|
|
520
|
-
workerRunner: null,
|
|
521
|
-
connection: mockConnection
|
|
522
|
-
} ) );
|
|
523
|
-
expect( process.exit ).toHaveBeenCalledWith( 1 );
|
|
524
|
-
} );
|
|
525
|
-
} );
|
|
526
|
-
} );
|