@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.
Files changed (100) hide show
  1. package/dist/credentials/credentials.d.ts +6 -0
  2. package/dist/credentials/credentials.js +87 -0
  3. package/dist/credentials/encrypted_yaml_provider.d.ts +2 -0
  4. package/dist/credentials/encrypted_yaml_provider.js +77 -0
  5. package/dist/credentials/encryption.d.ts +3 -0
  6. package/dist/credentials/encryption.js +6 -0
  7. package/dist/credentials/errors.d.ts +13 -0
  8. package/dist/credentials/errors.js +39 -0
  9. package/dist/credentials/index.d.ts +6 -0
  10. package/dist/credentials/index.js +5 -0
  11. package/dist/credentials/init.d.ts +1 -0
  12. package/dist/credentials/init.js +7 -0
  13. package/dist/credentials/paths.d.ts +7 -0
  14. package/dist/credentials/paths.js +14 -0
  15. package/dist/credentials/provider_registry.d.ts +3 -0
  16. package/dist/credentials/provider_registry.js +6 -0
  17. package/dist/credentials/types.d.ts +12 -0
  18. package/dist/credentials/types.js +1 -0
  19. package/package.json +25 -14
  20. package/src/async_storage.d.ts +4 -0
  21. package/src/consts.js +0 -2
  22. package/src/helpers/object.d.ts +2 -0
  23. package/src/hooks/index.d.ts +0 -8
  24. package/src/hooks/index.js +0 -3
  25. package/src/logger/index.d.ts +4 -0
  26. package/src/worker/index.js +3 -1
  27. package/bin/healthcheck.spec.js +0 -90
  28. package/src/bus.spec.js +0 -196
  29. package/src/errors.spec.js +0 -19
  30. package/src/helpers/activity_options.spec.js +0 -55
  31. package/src/helpers/component.spec.js +0 -54
  32. package/src/helpers/error_serializer.spec.js +0 -479
  33. package/src/helpers/errors.spec.js +0 -189
  34. package/src/helpers/fetch.spec.js +0 -292
  35. package/src/helpers/function.spec.js +0 -24
  36. package/src/helpers/object.spec.js +0 -425
  37. package/src/helpers/promise.spec.js +0 -62
  38. package/src/helpers/redact.spec.js +0 -66
  39. package/src/helpers/string.spec.js +0 -99
  40. package/src/helpers/temporal_context.spec.ts +0 -83
  41. package/src/helpers/trace_info.spec.js +0 -45
  42. package/src/helpers/workflow_context.spec.js +0 -46
  43. package/src/hooks/index.spec.js +0 -265
  44. package/src/hooks/pending_hooks.spec.js +0 -62
  45. package/src/interface/evaluator.spec.js +0 -685
  46. package/src/interface/logger.spec.js +0 -172
  47. package/src/interface/step.spec.js +0 -128
  48. package/src/interface/validations/index.spec.js +0 -197
  49. package/src/interface/validations/schemas.spec.js +0 -229
  50. package/src/interface/webhook.spec.js +0 -170
  51. package/src/interface/workflow.spec.js +0 -710
  52. package/src/interface/workflow_utils.spec.js +0 -271
  53. package/src/interface/zod_integration.spec.js +0 -646
  54. package/src/internal_activities/index.spec.js +0 -186
  55. package/src/logger/development.spec.js +0 -87
  56. package/src/logger/index.spec.js +0 -27
  57. package/src/logger/production.spec.js +0 -71
  58. package/src/sdk/helpers/component_metadata.spec.js +0 -30
  59. package/src/sdk/helpers/objects.spec.js +0 -16
  60. package/src/sdk/helpers/path.spec.js +0 -102
  61. package/src/sdk/runtime/context.spec.js +0 -37
  62. package/src/sdk/runtime/events.spec.js +0 -40
  63. package/src/temporal/client.spec.js +0 -124
  64. package/src/tracing/processors/local/index.spec.js +0 -204
  65. package/src/tracing/processors/s3/configs.spec.js +0 -64
  66. package/src/tracing/processors/s3/index.spec.js +0 -185
  67. package/src/tracing/processors/s3/redis_client.spec.js +0 -179
  68. package/src/tracing/processors/s3/s3_client.spec.js +0 -68
  69. package/src/tracing/tools/build_trace_tree.spec.js +0 -216
  70. package/src/tracing/trace_engine.spec.js +0 -299
  71. package/src/worker/bundle.spec.js +0 -53
  72. package/src/worker/bundler_options.spec.js +0 -62
  73. package/src/worker/catalog_workflow/catalog_publisher.spec.js +0 -317
  74. package/src/worker/catalog_workflow/index.spec.js +0 -222
  75. package/src/worker/configs.spec.js +0 -213
  76. package/src/worker/configs_tuner_schema.spec.js +0 -127
  77. package/src/worker/connection_monitor.spec.js +0 -328
  78. package/src/worker/global_functions.spec.js +0 -55
  79. package/src/worker/index.spec.js +0 -526
  80. package/src/worker/interceptors/activity.spec.js +0 -369
  81. package/src/worker/interceptors/headers.spec.js +0 -73
  82. package/src/worker/interceptors/workflow.spec.js +0 -357
  83. package/src/worker/interruption.spec.js +0 -177
  84. package/src/worker/loader/activities.spec.js +0 -212
  85. package/src/worker/loader/hooks.spec.js +0 -64
  86. package/src/worker/loader/matchers.spec.js +0 -140
  87. package/src/worker/loader/tools.spec.js +0 -595
  88. package/src/worker/loader/workflows.spec.js +0 -287
  89. package/src/worker/log_hooks.spec.js +0 -303
  90. package/src/worker/shutdown.spec.js +0 -197
  91. package/src/worker/sinks.spec.js +0 -220
  92. package/src/worker/telemetry.spec.js +0 -80
  93. package/src/worker/temporal_logger.spec.js +0 -90
  94. package/src/worker/webpack_loaders/tools.spec.js +0 -314
  95. package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +0 -134
  96. package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +0 -154
  97. package/src/worker/webpack_loaders/workflow_rewriter/rewrite_activity_calls.spec.js +0 -165
  98. package/src/worker/webpack_loaders/workflow_validator/index.spec.js +0 -202
  99. package/src/worker/webpack_loaders/workflow_validator/npm_workflow_export_resolve.spec.js +0 -374
  100. 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
- } );
@@ -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
- } );