@outputai/core 0.14.0 → 0.14.1-next.2947341.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 +25 -14
- 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,197 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { shutdownServices } from './shutdown.js';
|
|
3
|
-
|
|
4
|
-
const { mockLog, serializeErrorMock } = vi.hoisted( () => ( {
|
|
5
|
-
mockLog: { info: vi.fn(), warn: vi.fn() },
|
|
6
|
-
serializeErrorMock: vi.fn( error => ( { name: error.name, message: error.message } ) )
|
|
7
|
-
} ) );
|
|
8
|
-
|
|
9
|
-
vi.mock( '#logger', () => ( { createChildLogger: () => mockLog } ) );
|
|
10
|
-
vi.mock( '#helpers/error_serializer', () => ( { serializeError: serializeErrorMock } ) );
|
|
11
|
-
|
|
12
|
-
/** Stands in for the services registry built during startup, with every service active. */
|
|
13
|
-
const createServices = () => ( {
|
|
14
|
-
workerRunner: { running: true, stop: vi.fn().mockResolvedValue( undefined ) },
|
|
15
|
-
connectionMonitor: { running: true, stop: vi.fn().mockResolvedValue( undefined ) },
|
|
16
|
-
catalogPublisher: { running: true, interrupt: vi.fn().mockResolvedValue( undefined ) },
|
|
17
|
-
connection: { close: vi.fn().mockResolvedValue( undefined ) }
|
|
18
|
-
} );
|
|
19
|
-
|
|
20
|
-
/** The registry as it looks before startup assigns anything. */
|
|
21
|
-
const createEmptyServices = () => ( {
|
|
22
|
-
workerRunner: null,
|
|
23
|
-
connectionMonitor: null,
|
|
24
|
-
catalogPublisher: null,
|
|
25
|
-
connection: null
|
|
26
|
-
} );
|
|
27
|
-
|
|
28
|
-
describe( 'shutdownServices', () => {
|
|
29
|
-
beforeEach( () => {
|
|
30
|
-
vi.clearAllMocks();
|
|
31
|
-
} );
|
|
32
|
-
|
|
33
|
-
it( 'stops every active service, worker first and connection last', async () => {
|
|
34
|
-
const services = createServices();
|
|
35
|
-
|
|
36
|
-
await shutdownServices( services );
|
|
37
|
-
|
|
38
|
-
expect( services.workerRunner.stop ).toHaveBeenCalledOnce();
|
|
39
|
-
expect( services.connectionMonitor.stop ).toHaveBeenCalledOnce();
|
|
40
|
-
expect( services.catalogPublisher.interrupt ).toHaveBeenCalledOnce();
|
|
41
|
-
expect( services.connection.close ).toHaveBeenCalledOnce();
|
|
42
|
-
|
|
43
|
-
expect( services.workerRunner.stop.mock.invocationCallOrder[0] )
|
|
44
|
-
.toBeLessThan( services.connectionMonitor.stop.mock.invocationCallOrder[0] );
|
|
45
|
-
expect( services.connectionMonitor.stop.mock.invocationCallOrder[0] )
|
|
46
|
-
.toBeLessThan( services.catalogPublisher.interrupt.mock.invocationCallOrder[0] );
|
|
47
|
-
expect( services.catalogPublisher.interrupt.mock.invocationCallOrder[0] )
|
|
48
|
-
.toBeLessThan( services.connection.close.mock.invocationCallOrder[0] );
|
|
49
|
-
} );
|
|
50
|
-
|
|
51
|
-
it( 'announces each step it takes', async () => {
|
|
52
|
-
await shutdownServices( createServices() );
|
|
53
|
-
|
|
54
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Stopping Worker...' );
|
|
55
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Stopping Connection Monitor...' );
|
|
56
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Interrupting Catalog Publisher...' );
|
|
57
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Closing Connection...' );
|
|
58
|
-
expect( mockLog.warn ).not.toHaveBeenCalled();
|
|
59
|
-
} );
|
|
60
|
-
|
|
61
|
-
it( 'waits for a service to settle before stopping the next one', async () => {
|
|
62
|
-
const deferred = {};
|
|
63
|
-
const services = createServices();
|
|
64
|
-
services.workerRunner.stop.mockReturnValue( new Promise( resolve => {
|
|
65
|
-
deferred.resolve = resolve;
|
|
66
|
-
} ) );
|
|
67
|
-
|
|
68
|
-
const stopped = shutdownServices( services );
|
|
69
|
-
await Promise.resolve();
|
|
70
|
-
|
|
71
|
-
expect( services.connectionMonitor.stop ).not.toHaveBeenCalled();
|
|
72
|
-
|
|
73
|
-
deferred.resolve();
|
|
74
|
-
await stopped;
|
|
75
|
-
|
|
76
|
-
expect( services.connectionMonitor.stop ).toHaveBeenCalledOnce();
|
|
77
|
-
} );
|
|
78
|
-
|
|
79
|
-
it( 'skips a worker runner that is no longer running', async () => {
|
|
80
|
-
const services = createServices();
|
|
81
|
-
services.workerRunner.running = false;
|
|
82
|
-
|
|
83
|
-
await shutdownServices( services );
|
|
84
|
-
|
|
85
|
-
expect( services.workerRunner.stop ).not.toHaveBeenCalled();
|
|
86
|
-
expect( mockLog.info ).not.toHaveBeenCalledWith( 'Stopping Worker...' );
|
|
87
|
-
expect( services.connection.close ).toHaveBeenCalledOnce();
|
|
88
|
-
} );
|
|
89
|
-
|
|
90
|
-
it( 'skips a connection monitor that already exited', async () => {
|
|
91
|
-
const services = createServices();
|
|
92
|
-
services.connectionMonitor.running = false;
|
|
93
|
-
|
|
94
|
-
await shutdownServices( services );
|
|
95
|
-
|
|
96
|
-
expect( services.connectionMonitor.stop ).not.toHaveBeenCalled();
|
|
97
|
-
expect( mockLog.info ).not.toHaveBeenCalledWith( 'Stopping Connection Monitor...' );
|
|
98
|
-
} );
|
|
99
|
-
|
|
100
|
-
it( 'skips a catalog publisher that already finished', async () => {
|
|
101
|
-
const services = createServices();
|
|
102
|
-
services.catalogPublisher.running = false;
|
|
103
|
-
|
|
104
|
-
await shutdownServices( services );
|
|
105
|
-
|
|
106
|
-
expect( services.catalogPublisher.interrupt ).not.toHaveBeenCalled();
|
|
107
|
-
expect( mockLog.info ).not.toHaveBeenCalledWith( 'Interrupting Catalog Publisher...' );
|
|
108
|
-
} );
|
|
109
|
-
|
|
110
|
-
it( 'reads the state at each turn, so a service stopped by an earlier step is skipped', async () => {
|
|
111
|
-
const services = createServices();
|
|
112
|
-
services.workerRunner.stop.mockImplementation( async () => {
|
|
113
|
-
services.connectionMonitor.running = false;
|
|
114
|
-
} );
|
|
115
|
-
|
|
116
|
-
await shutdownServices( services );
|
|
117
|
-
|
|
118
|
-
expect( services.connectionMonitor.stop ).not.toHaveBeenCalled();
|
|
119
|
-
expect( services.connection.close ).toHaveBeenCalledOnce();
|
|
120
|
-
} );
|
|
121
|
-
|
|
122
|
-
it( 'does nothing when startup never built a service', async () => {
|
|
123
|
-
await expect( shutdownServices( createEmptyServices() ) ).resolves.toEqual( [] );
|
|
124
|
-
|
|
125
|
-
expect( mockLog.info ).not.toHaveBeenCalled();
|
|
126
|
-
expect( mockLog.warn ).not.toHaveBeenCalled();
|
|
127
|
-
} );
|
|
128
|
-
|
|
129
|
-
it( 'closes the connection when startup failed before anything else existed', async () => {
|
|
130
|
-
const services = { ...createEmptyServices(), connection: { close: vi.fn().mockResolvedValue( undefined ) } };
|
|
131
|
-
|
|
132
|
-
await shutdownServices( services );
|
|
133
|
-
|
|
134
|
-
expect( services.connection.close ).toHaveBeenCalledOnce();
|
|
135
|
-
expect( mockLog.info ).toHaveBeenCalledWith( 'Closing Connection...' );
|
|
136
|
-
} );
|
|
137
|
-
|
|
138
|
-
it( 'reports nothing when every service stops cleanly', async () => {
|
|
139
|
-
await expect( shutdownServices( createServices() ) ).resolves.toEqual( [] );
|
|
140
|
-
} );
|
|
141
|
-
|
|
142
|
-
it( 'warns, reports the failure and keeps going when a service fails to stop', async () => {
|
|
143
|
-
const error = new Error( 'drain failed' );
|
|
144
|
-
const services = createServices();
|
|
145
|
-
services.workerRunner.stop.mockRejectedValue( error );
|
|
146
|
-
|
|
147
|
-
await expect( shutdownServices( services ) ).resolves.toEqual( [ { service: 'worker', error } ] );
|
|
148
|
-
|
|
149
|
-
expect( serializeErrorMock ).toHaveBeenCalledWith( error );
|
|
150
|
-
expect( mockLog.warn ).toHaveBeenCalledWith( 'Stopping Worker error', {
|
|
151
|
-
error: { name: 'Error', message: 'drain failed' }
|
|
152
|
-
} );
|
|
153
|
-
expect( services.connectionMonitor.stop ).toHaveBeenCalledOnce();
|
|
154
|
-
expect( services.catalogPublisher.interrupt ).toHaveBeenCalledOnce();
|
|
155
|
-
expect( services.connection.close ).toHaveBeenCalledOnce();
|
|
156
|
-
} );
|
|
157
|
-
|
|
158
|
-
it( 'warns and reports when closing the connection fails', async () => {
|
|
159
|
-
const error = new Error( 'close failed' );
|
|
160
|
-
const services = createServices();
|
|
161
|
-
services.connection.close.mockRejectedValue( error );
|
|
162
|
-
|
|
163
|
-
await expect( shutdownServices( services ) ).resolves.toEqual( [ { service: 'connection', error } ] );
|
|
164
|
-
|
|
165
|
-
expect( mockLog.warn ).toHaveBeenCalledWith( 'Closing Connection error', {
|
|
166
|
-
error: { name: 'Error', message: 'close failed' }
|
|
167
|
-
} );
|
|
168
|
-
} );
|
|
169
|
-
|
|
170
|
-
it( 'warns and reports a service that throws instead of rejecting', async () => {
|
|
171
|
-
const error = new Error( 'shutdown blew up' );
|
|
172
|
-
const services = createServices();
|
|
173
|
-
services.workerRunner.stop.mockImplementation( () => {
|
|
174
|
-
throw error;
|
|
175
|
-
} );
|
|
176
|
-
|
|
177
|
-
await expect( shutdownServices( services ) ).resolves.toEqual( [ { service: 'worker', error } ] );
|
|
178
|
-
|
|
179
|
-
expect( mockLog.warn ).toHaveBeenCalledWith( 'Stopping Worker error', {
|
|
180
|
-
error: { name: 'Error', message: 'shutdown blew up' }
|
|
181
|
-
} );
|
|
182
|
-
expect( services.connection.close ).toHaveBeenCalledOnce();
|
|
183
|
-
} );
|
|
184
|
-
|
|
185
|
-
it( 'reports every service that failed, in the order they were stopped', async () => {
|
|
186
|
-
const drainError = new Error( 'drain failed' );
|
|
187
|
-
const closeError = new Error( 'close failed' );
|
|
188
|
-
const services = createServices();
|
|
189
|
-
services.workerRunner.stop.mockRejectedValue( drainError );
|
|
190
|
-
services.connection.close.mockRejectedValue( closeError );
|
|
191
|
-
|
|
192
|
-
await expect( shutdownServices( services ) ).resolves.toEqual( [
|
|
193
|
-
{ service: 'worker', error: drainError },
|
|
194
|
-
{ service: 'connection', error: closeError }
|
|
195
|
-
] );
|
|
196
|
-
} );
|
|
197
|
-
} );
|
package/src/worker/sinks.spec.js
DELETED
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { BusEventType, ComponentType } from '#consts';
|
|
3
|
-
|
|
4
|
-
const mainEventBusMock = vi.hoisted( () => ( {
|
|
5
|
-
emit: vi.fn()
|
|
6
|
-
} ) );
|
|
7
|
-
|
|
8
|
-
const addEventStartMock = vi.hoisted( () => vi.fn() );
|
|
9
|
-
const addEventEndMock = vi.hoisted( () => vi.fn() );
|
|
10
|
-
const addEventErrorMock = vi.hoisted( () => vi.fn() );
|
|
11
|
-
|
|
12
|
-
const createWorkflowDetailsMock = vi.hoisted( () => vi.fn( workflowInfo => ( {
|
|
13
|
-
workflowId: workflowInfo.workflowId,
|
|
14
|
-
workflowType: workflowInfo.workflowType,
|
|
15
|
-
runId: workflowInfo.runId
|
|
16
|
-
} ) ) );
|
|
17
|
-
|
|
18
|
-
const rehydrateErrorMock = vi.hoisted( () => vi.fn( error => error ) );
|
|
19
|
-
|
|
20
|
-
vi.mock( '#bus', () => ( { mainEventBus: mainEventBusMock } ) );
|
|
21
|
-
vi.mock( '#tracing', () => ( {
|
|
22
|
-
addEventStart: addEventStartMock,
|
|
23
|
-
addEventEnd: addEventEndMock,
|
|
24
|
-
addEventError: addEventErrorMock
|
|
25
|
-
} ) );
|
|
26
|
-
vi.mock( '#helpers/temporal_context', () => ( {
|
|
27
|
-
createWorkflowDetails: createWorkflowDetailsMock
|
|
28
|
-
} ) );
|
|
29
|
-
vi.mock( '#helpers/errors', () => ( {
|
|
30
|
-
rehydrateError: rehydrateErrorMock
|
|
31
|
-
} ) );
|
|
32
|
-
|
|
33
|
-
describe( 'worker/sinks', () => {
|
|
34
|
-
const workflowInfo = {
|
|
35
|
-
workflowId: 'wf-1',
|
|
36
|
-
workflowType: 'myWorkflow',
|
|
37
|
-
runId: 'run-1',
|
|
38
|
-
memo: {
|
|
39
|
-
traceInfo: { traceId: 'trace-1' }
|
|
40
|
-
},
|
|
41
|
-
parent: {
|
|
42
|
-
runId: 'parent-run-1'
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
const workflowDetails = {
|
|
46
|
-
workflowId: 'wf-1',
|
|
47
|
-
workflowType: 'myWorkflow',
|
|
48
|
-
runId: 'run-1'
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
beforeEach( () => {
|
|
52
|
-
vi.clearAllMocks();
|
|
53
|
-
} );
|
|
54
|
-
|
|
55
|
-
it( 'disables replay calls for side-effecting sinks', async () => {
|
|
56
|
-
const { sinks } = await import( './sinks.js' );
|
|
57
|
-
|
|
58
|
-
expect( sinks.workflow.start.callDuringReplay ).toBe( false );
|
|
59
|
-
expect( sinks.workflow.end.callDuringReplay ).toBe( false );
|
|
60
|
-
expect( sinks.workflow.error.callDuringReplay ).toBe( false );
|
|
61
|
-
expect( sinks.trace.start.callDuringReplay ).toBe( false );
|
|
62
|
-
expect( sinks.trace.end.callDuringReplay ).toBe( false );
|
|
63
|
-
expect( sinks.trace.error.callDuringReplay ).toBe( false );
|
|
64
|
-
} );
|
|
65
|
-
|
|
66
|
-
it( 'workflow.log emits workflow log events with metadata and workflow details', async () => {
|
|
67
|
-
const { sinks } = await import( './sinks.js' );
|
|
68
|
-
const metadata = { requestId: 'req-1' };
|
|
69
|
-
|
|
70
|
-
sinks.workflow.log.fn( workflowInfo, {
|
|
71
|
-
level: 'info',
|
|
72
|
-
message: 'workflow detail',
|
|
73
|
-
metadata
|
|
74
|
-
} );
|
|
75
|
-
|
|
76
|
-
expect( createWorkflowDetailsMock ).toHaveBeenCalledWith( workflowInfo );
|
|
77
|
-
expect( mainEventBusMock.emit ).toHaveBeenCalledWith( BusEventType.WORKFLOW_LOG, {
|
|
78
|
-
level: 'info',
|
|
79
|
-
message: 'workflow detail',
|
|
80
|
-
metadata,
|
|
81
|
-
workflowDetails
|
|
82
|
-
} );
|
|
83
|
-
} );
|
|
84
|
-
|
|
85
|
-
it( 'workflow.start emits workflow start events and trace start events', async () => {
|
|
86
|
-
const { sinks } = await import( './sinks.js' );
|
|
87
|
-
const input = { value: 'input' };
|
|
88
|
-
|
|
89
|
-
sinks.workflow.start.fn( workflowInfo, input );
|
|
90
|
-
|
|
91
|
-
expect( mainEventBusMock.emit ).toHaveBeenCalledWith( BusEventType.WORKFLOW_START, { workflowDetails } );
|
|
92
|
-
expect( addEventStartMock ).toHaveBeenCalledWith( {
|
|
93
|
-
id: 'run-1',
|
|
94
|
-
kind: ComponentType.WORKFLOW,
|
|
95
|
-
name: 'myWorkflow',
|
|
96
|
-
details: input,
|
|
97
|
-
parentId: 'parent-run-1',
|
|
98
|
-
traceInfo: { traceId: 'trace-1' }
|
|
99
|
-
} );
|
|
100
|
-
} );
|
|
101
|
-
|
|
102
|
-
it( 'workflow.start skips tracing when trace info is absent', async () => {
|
|
103
|
-
const { sinks } = await import( './sinks.js' );
|
|
104
|
-
|
|
105
|
-
sinks.workflow.start.fn( { ...workflowInfo, memo: {} }, { value: 'input' } );
|
|
106
|
-
|
|
107
|
-
expect( mainEventBusMock.emit ).toHaveBeenCalledWith( BusEventType.WORKFLOW_START, { workflowDetails } );
|
|
108
|
-
expect( addEventStartMock ).not.toHaveBeenCalled();
|
|
109
|
-
} );
|
|
110
|
-
|
|
111
|
-
it( 'workflow.end emits workflow end events and trace end events', async () => {
|
|
112
|
-
const { sinks } = await import( './sinks.js' );
|
|
113
|
-
const output = { value: 'output' };
|
|
114
|
-
|
|
115
|
-
sinks.workflow.end.fn( workflowInfo, output );
|
|
116
|
-
|
|
117
|
-
expect( mainEventBusMock.emit ).toHaveBeenCalledWith( BusEventType.WORKFLOW_END, { workflowDetails } );
|
|
118
|
-
expect( addEventEndMock ).toHaveBeenCalledWith( {
|
|
119
|
-
id: 'run-1',
|
|
120
|
-
details: output,
|
|
121
|
-
traceInfo: { traceId: 'trace-1' }
|
|
122
|
-
} );
|
|
123
|
-
} );
|
|
124
|
-
|
|
125
|
-
it( 'workflow.end skips tracing when trace info is absent', async () => {
|
|
126
|
-
const { sinks } = await import( './sinks.js' );
|
|
127
|
-
|
|
128
|
-
sinks.workflow.end.fn( { ...workflowInfo, memo: {} }, { value: 'output' } );
|
|
129
|
-
|
|
130
|
-
expect( mainEventBusMock.emit ).toHaveBeenCalledWith( BusEventType.WORKFLOW_END, { workflowDetails } );
|
|
131
|
-
expect( addEventEndMock ).not.toHaveBeenCalled();
|
|
132
|
-
} );
|
|
133
|
-
|
|
134
|
-
it( 'workflow.error emits workflow error events and trace error events', async () => {
|
|
135
|
-
const { sinks } = await import( './sinks.js' );
|
|
136
|
-
const error = { name: 'Error', message: 'workflow failed' };
|
|
137
|
-
const rehydrated = new Error( 'workflow failed' );
|
|
138
|
-
rehydrateErrorMock.mockReturnValueOnce( rehydrated );
|
|
139
|
-
|
|
140
|
-
sinks.workflow.error.fn( workflowInfo, error );
|
|
141
|
-
|
|
142
|
-
expect( rehydrateErrorMock ).toHaveBeenCalledWith( error );
|
|
143
|
-
expect( mainEventBusMock.emit ).toHaveBeenCalledWith(
|
|
144
|
-
BusEventType.WORKFLOW_ERROR,
|
|
145
|
-
{ workflowDetails, error: rehydrated }
|
|
146
|
-
);
|
|
147
|
-
expect( addEventErrorMock ).toHaveBeenCalledWith( {
|
|
148
|
-
id: 'run-1',
|
|
149
|
-
details: error,
|
|
150
|
-
traceInfo: { traceId: 'trace-1' }
|
|
151
|
-
} );
|
|
152
|
-
} );
|
|
153
|
-
|
|
154
|
-
it( 'workflow.error skips tracing when trace info is absent', async () => {
|
|
155
|
-
const { sinks } = await import( './sinks.js' );
|
|
156
|
-
const error = { name: 'Error', message: 'workflow failed' };
|
|
157
|
-
const rehydrated = new Error( 'workflow failed' );
|
|
158
|
-
rehydrateErrorMock.mockReturnValueOnce( rehydrated );
|
|
159
|
-
|
|
160
|
-
sinks.workflow.error.fn( { ...workflowInfo, memo: {} }, error );
|
|
161
|
-
|
|
162
|
-
expect( rehydrateErrorMock ).toHaveBeenCalledWith( error );
|
|
163
|
-
expect( mainEventBusMock.emit ).toHaveBeenCalledWith(
|
|
164
|
-
BusEventType.WORKFLOW_ERROR,
|
|
165
|
-
{ workflowDetails, error: rehydrated }
|
|
166
|
-
);
|
|
167
|
-
expect( addEventErrorMock ).not.toHaveBeenCalled();
|
|
168
|
-
} );
|
|
169
|
-
|
|
170
|
-
it( 'trace.start parents trace start events on the current workflow, not its parent workflow', async () => {
|
|
171
|
-
const { sinks } = await import( './sinks.js' );
|
|
172
|
-
|
|
173
|
-
sinks.trace.start.fn( workflowInfo, {
|
|
174
|
-
id: 'step-1',
|
|
175
|
-
kind: ComponentType.STEP,
|
|
176
|
-
name: 'myStep',
|
|
177
|
-
details: { input: true }
|
|
178
|
-
} );
|
|
179
|
-
|
|
180
|
-
expect( addEventStartMock ).toHaveBeenCalledWith( {
|
|
181
|
-
id: 'step-1',
|
|
182
|
-
kind: ComponentType.STEP,
|
|
183
|
-
name: 'myStep',
|
|
184
|
-
details: { input: true },
|
|
185
|
-
parentId: 'run-1',
|
|
186
|
-
traceInfo: { traceId: 'trace-1' }
|
|
187
|
-
} );
|
|
188
|
-
} );
|
|
189
|
-
|
|
190
|
-
it( 'trace.end records trace end events', async () => {
|
|
191
|
-
const { sinks } = await import( './sinks.js' );
|
|
192
|
-
|
|
193
|
-
sinks.trace.end.fn( workflowInfo, {
|
|
194
|
-
id: 'step-1',
|
|
195
|
-
details: { output: true }
|
|
196
|
-
} );
|
|
197
|
-
|
|
198
|
-
expect( addEventEndMock ).toHaveBeenCalledWith( {
|
|
199
|
-
id: 'step-1',
|
|
200
|
-
details: { output: true },
|
|
201
|
-
traceInfo: { traceId: 'trace-1' }
|
|
202
|
-
} );
|
|
203
|
-
} );
|
|
204
|
-
|
|
205
|
-
it( 'trace.error records trace error events', async () => {
|
|
206
|
-
const { sinks } = await import( './sinks.js' );
|
|
207
|
-
const error = new Error( 'step failed' );
|
|
208
|
-
|
|
209
|
-
sinks.trace.error.fn( workflowInfo, {
|
|
210
|
-
id: 'step-1',
|
|
211
|
-
details: error
|
|
212
|
-
} );
|
|
213
|
-
|
|
214
|
-
expect( addEventErrorMock ).toHaveBeenCalledWith( {
|
|
215
|
-
id: 'step-1',
|
|
216
|
-
details: error,
|
|
217
|
-
traceInfo: { traceId: 'trace-1' }
|
|
218
|
-
} );
|
|
219
|
-
} );
|
|
220
|
-
} );
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
|
|
3
|
-
const configMock = vi.hoisted( () => ( { workerTelemetryIntervalMs: 0 } ) );
|
|
4
|
-
const logMock = vi.hoisted( () => ( { info: vi.fn(), warn: vi.fn() } ) );
|
|
5
|
-
const createChildLoggerMock = vi.hoisted( () => vi.fn( () => logMock ) );
|
|
6
|
-
|
|
7
|
-
vi.mock( './configs.js', () => ( {
|
|
8
|
-
get workerTelemetryIntervalMs() {
|
|
9
|
-
return configMock.workerTelemetryIntervalMs;
|
|
10
|
-
}
|
|
11
|
-
} ) );
|
|
12
|
-
|
|
13
|
-
vi.mock( '#logger', () => ( { createChildLogger: createChildLoggerMock } ) );
|
|
14
|
-
|
|
15
|
-
const loadSetupTelemetry = async () => {
|
|
16
|
-
vi.resetModules();
|
|
17
|
-
return import( './telemetry.js' );
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const mockSetInterval = unrefMock =>
|
|
21
|
-
vi.spyOn( globalThis, 'setInterval' ).mockReturnValue( { unref: unrefMock } );
|
|
22
|
-
|
|
23
|
-
describe( 'worker/telemetry', () => {
|
|
24
|
-
const availableMemoryMock = vi.fn();
|
|
25
|
-
const constrainedMemoryMock = vi.fn();
|
|
26
|
-
const memoryUsageMock = vi.fn();
|
|
27
|
-
const unrefMock = vi.fn();
|
|
28
|
-
|
|
29
|
-
beforeEach( () => {
|
|
30
|
-
vi.clearAllMocks();
|
|
31
|
-
configMock.workerTelemetryIntervalMs = 0;
|
|
32
|
-
|
|
33
|
-
availableMemoryMock.mockReturnValue( 1_000 );
|
|
34
|
-
constrainedMemoryMock.mockReturnValue( 2_000 );
|
|
35
|
-
memoryUsageMock.mockReturnValue( { heapUsed: 300 } );
|
|
36
|
-
|
|
37
|
-
vi.spyOn( process, 'availableMemory' ).mockImplementation( availableMemoryMock );
|
|
38
|
-
vi.spyOn( process, 'constrainedMemory' ).mockImplementation( constrainedMemoryMock );
|
|
39
|
-
vi.spyOn( process, 'memoryUsage' ).mockImplementation( memoryUsageMock );
|
|
40
|
-
} );
|
|
41
|
-
|
|
42
|
-
afterEach( () => {
|
|
43
|
-
vi.restoreAllMocks();
|
|
44
|
-
} );
|
|
45
|
-
|
|
46
|
-
it( 'does not create an interval when telemetry interval is disabled', async () => {
|
|
47
|
-
const setIntervalMock = mockSetInterval( unrefMock );
|
|
48
|
-
const { setupTelemetry } = await loadSetupTelemetry();
|
|
49
|
-
|
|
50
|
-
setupTelemetry( { worker: { getStatus: vi.fn() } } );
|
|
51
|
-
|
|
52
|
-
expect( setIntervalMock ).not.toHaveBeenCalled();
|
|
53
|
-
expect( logMock.info ).not.toHaveBeenCalled();
|
|
54
|
-
} );
|
|
55
|
-
|
|
56
|
-
it( 'logs worker status and memory on the configured interval', async () => {
|
|
57
|
-
const setIntervalMock = mockSetInterval( unrefMock );
|
|
58
|
-
configMock.workerTelemetryIntervalMs = 5_000;
|
|
59
|
-
const worker = { getStatus: vi.fn().mockReturnValue( { runState: 'RUNNING' } ) };
|
|
60
|
-
const { setupTelemetry } = await loadSetupTelemetry();
|
|
61
|
-
|
|
62
|
-
setupTelemetry( { worker } );
|
|
63
|
-
|
|
64
|
-
expect( createChildLoggerMock ).toHaveBeenCalledWith( 'Telemetry' );
|
|
65
|
-
expect( setIntervalMock ).toHaveBeenCalledWith( expect.any( Function ), 5_000 );
|
|
66
|
-
expect( unrefMock ).toHaveBeenCalled();
|
|
67
|
-
|
|
68
|
-
const [ callback ] = setIntervalMock.mock.calls[0];
|
|
69
|
-
callback();
|
|
70
|
-
|
|
71
|
-
expect( logMock.info ).toHaveBeenCalledWith( 'Worker', {
|
|
72
|
-
status: { runState: 'RUNNING' },
|
|
73
|
-
memory: {
|
|
74
|
-
availableMemory: 1_000,
|
|
75
|
-
constrainedMemory: 2_000,
|
|
76
|
-
memoryUsage: { heapUsed: 300 }
|
|
77
|
-
}
|
|
78
|
-
} );
|
|
79
|
-
} );
|
|
80
|
-
} );
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
defaultLoggerMock,
|
|
5
|
-
logMock,
|
|
6
|
-
makeTelemetryFilterStringMock,
|
|
7
|
-
runtimeInstallMock
|
|
8
|
-
} = vi.hoisted( () => ( {
|
|
9
|
-
defaultLoggerMock: vi.fn( function ( level, callback ) {
|
|
10
|
-
this.level = level;
|
|
11
|
-
this.callback = callback;
|
|
12
|
-
} ),
|
|
13
|
-
logMock: { log: vi.fn() },
|
|
14
|
-
makeTelemetryFilterStringMock: vi.fn().mockReturnValue( 'temporal-filter' ),
|
|
15
|
-
runtimeInstallMock: vi.fn()
|
|
16
|
-
} ) );
|
|
17
|
-
|
|
18
|
-
vi.mock( '@temporalio/worker', () => ( {
|
|
19
|
-
DefaultLogger: defaultLoggerMock,
|
|
20
|
-
makeTelemetryFilterString: makeTelemetryFilterStringMock,
|
|
21
|
-
Runtime: { install: runtimeInstallMock }
|
|
22
|
-
} ) );
|
|
23
|
-
vi.mock( '#logger', () => ( { createChildLogger: () => logMock } ) );
|
|
24
|
-
|
|
25
|
-
import { setupTemporalLogger } from './temporal_logger.js';
|
|
26
|
-
|
|
27
|
-
describe( 'setupTemporalLogger', () => {
|
|
28
|
-
beforeEach( () => {
|
|
29
|
-
vi.clearAllMocks();
|
|
30
|
-
vi.stubEnv( 'OUTPUT_TEMPORAL_LOG_LEVEL', undefined );
|
|
31
|
-
} );
|
|
32
|
-
|
|
33
|
-
afterEach( () => {
|
|
34
|
-
vi.unstubAllEnvs();
|
|
35
|
-
} );
|
|
36
|
-
|
|
37
|
-
it( 'forwards Temporal SDK and Core logs through the application logger', () => {
|
|
38
|
-
setupTemporalLogger();
|
|
39
|
-
|
|
40
|
-
expect( makeTelemetryFilterStringMock ).toHaveBeenCalledWith( { core: 'INFO', other: 'INFO' } );
|
|
41
|
-
expect( defaultLoggerMock ).toHaveBeenCalledWith( 'INFO', expect.any( Function ) );
|
|
42
|
-
expect( runtimeInstallMock ).toHaveBeenCalledWith( {
|
|
43
|
-
logger: expect.any( defaultLoggerMock ),
|
|
44
|
-
telemetryOptions: {
|
|
45
|
-
logging: {
|
|
46
|
-
forward: {},
|
|
47
|
-
filter: 'temporal-filter'
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
} );
|
|
51
|
-
|
|
52
|
-
const [ entryCallback ] = defaultLoggerMock.mock.calls[0].slice( 1 );
|
|
53
|
-
entryCallback( { level: 'WARN', message: 'Worker warning', meta: { taskQueue: 'default' } } );
|
|
54
|
-
|
|
55
|
-
expect( logMock.log ).toHaveBeenCalledWith( 'warn', 'Worker warning', { taskQueue: 'default' } );
|
|
56
|
-
} );
|
|
57
|
-
|
|
58
|
-
it.each( [
|
|
59
|
-
[ 'TRACE', 'debug' ],
|
|
60
|
-
[ 'DEBUG', 'debug' ],
|
|
61
|
-
[ 'INFO', 'info' ],
|
|
62
|
-
[ 'WARN', 'warn' ],
|
|
63
|
-
[ 'ERROR', 'error' ]
|
|
64
|
-
] )( 'maps Temporal %s logs to the %s application level', ( temporalLevel, internalLevel ) => {
|
|
65
|
-
setupTemporalLogger();
|
|
66
|
-
const [ entryCallback ] = defaultLoggerMock.mock.calls[0].slice( 1 );
|
|
67
|
-
|
|
68
|
-
entryCallback( { level: temporalLevel, message: `${temporalLevel} event`, meta: { source: 'test' } } );
|
|
69
|
-
|
|
70
|
-
expect( logMock.log ).toHaveBeenCalledWith( internalLevel, `${temporalLevel} event`, { source: 'test' } );
|
|
71
|
-
} );
|
|
72
|
-
|
|
73
|
-
it( 'applies a case-insensitive configured log level to SDK and Core logs', () => {
|
|
74
|
-
vi.stubEnv( 'OUTPUT_TEMPORAL_LOG_LEVEL', 'debug' );
|
|
75
|
-
|
|
76
|
-
setupTemporalLogger();
|
|
77
|
-
|
|
78
|
-
expect( defaultLoggerMock ).toHaveBeenCalledWith( 'DEBUG', expect.any( Function ) );
|
|
79
|
-
expect( makeTelemetryFilterStringMock ).toHaveBeenCalledWith( { core: 'DEBUG', other: 'DEBUG' } );
|
|
80
|
-
} );
|
|
81
|
-
|
|
82
|
-
it.each( [ 'Activity failed', 'Workflow failed' ] )( 'omits redundant "%s" events', message => {
|
|
83
|
-
setupTemporalLogger();
|
|
84
|
-
const [ entryCallback ] = defaultLoggerMock.mock.calls[0].slice( 1 );
|
|
85
|
-
|
|
86
|
-
entryCallback( { level: 'WARN', message, meta: { error: new Error( 'failed' ) } } );
|
|
87
|
-
|
|
88
|
-
expect( logMock.log ).not.toHaveBeenCalled();
|
|
89
|
-
} );
|
|
90
|
-
} );
|