@devrev/ts-adaas 1.19.4 → 1.19.5
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/attachments-streaming/attachments-streaming-pool.test.js +3 -6
- package/dist/common/event-type-translation.test.d.ts +2 -0
- package/dist/common/event-type-translation.test.d.ts.map +1 -0
- package/dist/common/event-type-translation.test.js +175 -0
- package/dist/common/time-value-resolver.test.js +0 -1
- package/dist/multithreading/create-worker.test.js +34 -16
- package/dist/multithreading/process-task.test.d.ts +2 -0
- package/dist/multithreading/process-task.test.d.ts.map +1 -0
- package/dist/multithreading/process-task.test.js +166 -0
- package/dist/multithreading/spawn/spawn.test.d.ts +2 -0
- package/dist/multithreading/spawn/spawn.test.d.ts.map +1 -0
- package/dist/multithreading/spawn/spawn.test.js +223 -0
- package/dist/multithreading/worker-adapter/worker-adapter.emit.test.d.ts +2 -0
- package/dist/multithreading/worker-adapter/worker-adapter.emit.test.d.ts.map +1 -0
- package/dist/multithreading/worker-adapter/worker-adapter.emit.test.js +415 -0
- package/dist/multithreading/worker-adapter/worker-adapter.extraction.test.d.ts +2 -0
- package/dist/multithreading/worker-adapter/worker-adapter.extraction.test.d.ts.map +1 -0
- package/dist/multithreading/worker-adapter/worker-adapter.extraction.test.js +801 -0
- package/dist/multithreading/worker-adapter/worker-adapter.loading.test.d.ts +2 -0
- package/dist/multithreading/worker-adapter/worker-adapter.loading.test.d.ts.map +1 -0
- package/dist/multithreading/worker-adapter/worker-adapter.loading.test.js +598 -0
- package/dist/multithreading/worker-adapter/worker-adapter.serialization.test.d.ts +2 -0
- package/dist/multithreading/worker-adapter/worker-adapter.serialization.test.d.ts.map +1 -0
- package/dist/multithreading/worker-adapter/worker-adapter.serialization.test.js +71 -0
- package/dist/repo/repo.test.js +41 -0
- package/dist/state/state.extract-window.test.d.ts +2 -0
- package/dist/state/state.extract-window.test.d.ts.map +1 -0
- package/dist/state/state.extract-window.test.js +163 -0
- package/dist/state/state.pending-boundaries.test.d.ts +2 -0
- package/dist/state/state.pending-boundaries.test.d.ts.map +1 -0
- package/dist/state/state.pending-boundaries.test.js +189 -0
- package/dist/state/state.post-state.test.d.ts +2 -0
- package/dist/state/state.post-state.test.d.ts.map +1 -0
- package/dist/state/state.post-state.test.js +77 -0
- package/dist/state/state.test.js +23 -506
- package/dist/state/state.time-value-resolution.test.d.ts +2 -0
- package/dist/state/state.time-value-resolution.test.d.ts.map +1 -0
- package/dist/state/state.time-value-resolution.test.js +175 -0
- package/dist/types/extraction.test.js +57 -21
- package/dist/uploader/uploader.helpers.test.js +0 -11
- package/dist/uploader/uploader.test.js +0 -9
- package/package.json +7 -6
- package/dist/multithreading/worker-adapter/worker-adapter.test.d.ts +0 -2
- package/dist/multithreading/worker-adapter/worker-adapter.test.d.ts.map +0 -1
- package/dist/multithreading/worker-adapter/worker-adapter.test.js +0 -1243
|
@@ -44,10 +44,6 @@ describe(attachments_streaming_pool_1.AttachmentsStreamingPool.name, () => {
|
|
|
44
44
|
parent_id: 'parent-3',
|
|
45
45
|
},
|
|
46
46
|
];
|
|
47
|
-
// Mock console methods
|
|
48
|
-
jest.spyOn(console, 'log').mockImplementation();
|
|
49
|
-
jest.spyOn(console, 'error').mockImplementation();
|
|
50
|
-
jest.spyOn(console, 'warn').mockImplementation();
|
|
51
47
|
});
|
|
52
48
|
afterEach(() => {
|
|
53
49
|
jest.clearAllMocks();
|
|
@@ -60,7 +56,6 @@ describe(attachments_streaming_pool_1.AttachmentsStreamingPool.name, () => {
|
|
|
60
56
|
attachments: mockAttachments,
|
|
61
57
|
stream: mockStream,
|
|
62
58
|
});
|
|
63
|
-
expect(pool).toBeDefined();
|
|
64
59
|
expect(pool['adapter']).toBe(mockAdapter);
|
|
65
60
|
expect(pool['attachments']).toEqual(mockAttachments);
|
|
66
61
|
expect(pool['batchSize']).toBe(10);
|
|
@@ -227,6 +222,7 @@ describe(attachments_streaming_pool_1.AttachmentsStreamingPool.name, () => {
|
|
|
227
222
|
]);
|
|
228
223
|
});
|
|
229
224
|
it('should handle processing errors gracefully', async () => {
|
|
225
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
230
226
|
const error = new Error('Processing failed');
|
|
231
227
|
mockAdapter.processAttachment
|
|
232
228
|
.mockResolvedValueOnce({}) // First attachment succeeds
|
|
@@ -238,7 +234,7 @@ describe(attachments_streaming_pool_1.AttachmentsStreamingPool.name, () => {
|
|
|
238
234
|
stream: mockStream,
|
|
239
235
|
});
|
|
240
236
|
await pool.streamAll();
|
|
241
|
-
expect(
|
|
237
|
+
expect(warnSpy).toHaveBeenCalledWith('Skipping attachment with ID attachment-2 with extension jpg due to error in processAttachment function', error);
|
|
242
238
|
expect(mockAdapter.state.toDevRev.attachmentsMetadata
|
|
243
239
|
.lastProcessedAttachmentsIdsList).toEqual([
|
|
244
240
|
{
|
|
@@ -490,6 +486,7 @@ describe(attachments_streaming_pool_1.AttachmentsStreamingPool.name, () => {
|
|
|
490
486
|
const mockStreamFn = jest
|
|
491
487
|
.fn()
|
|
492
488
|
.mockImplementation(async () => {
|
|
489
|
+
await Promise.resolve();
|
|
493
490
|
userCallbackExecuted = true;
|
|
494
491
|
// Record that the callback executed
|
|
495
492
|
return Promise.resolve({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-type-translation.test.d.ts","sourceRoot":"","sources":["../../src/common/event-type-translation.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const extraction_1 = require("../types/extraction");
|
|
4
|
+
const loading_1 = require("../types/loading");
|
|
5
|
+
const event_type_translation_1 = require("./event-type-translation");
|
|
6
|
+
describe(event_type_translation_1.translateIncomingEventType.name, () => {
|
|
7
|
+
it.each([
|
|
8
|
+
[
|
|
9
|
+
extraction_1.EventType.ExtractionExternalSyncUnitsStart,
|
|
10
|
+
extraction_1.EventType.StartExtractingExternalSyncUnits,
|
|
11
|
+
],
|
|
12
|
+
[extraction_1.EventType.ExtractionMetadataStart, extraction_1.EventType.StartExtractingMetadata],
|
|
13
|
+
[extraction_1.EventType.ExtractionDataStart, extraction_1.EventType.StartExtractingData],
|
|
14
|
+
[extraction_1.EventType.ExtractionDataContinue, extraction_1.EventType.ContinueExtractingData],
|
|
15
|
+
[extraction_1.EventType.ExtractionDataDelete, extraction_1.EventType.StartDeletingExtractorState],
|
|
16
|
+
[
|
|
17
|
+
extraction_1.EventType.ExtractionAttachmentsStart,
|
|
18
|
+
extraction_1.EventType.StartExtractingAttachments,
|
|
19
|
+
],
|
|
20
|
+
[
|
|
21
|
+
extraction_1.EventType.ExtractionAttachmentsContinue,
|
|
22
|
+
extraction_1.EventType.ContinueExtractingAttachments,
|
|
23
|
+
],
|
|
24
|
+
[
|
|
25
|
+
extraction_1.EventType.ExtractionAttachmentsDelete,
|
|
26
|
+
extraction_1.EventType.StartDeletingExtractorAttachmentsState,
|
|
27
|
+
],
|
|
28
|
+
])('maps legacy extraction event %s to %s', (legacy, modern) => {
|
|
29
|
+
expect((0, event_type_translation_1.translateIncomingEventType)(legacy)).toBe(modern);
|
|
30
|
+
});
|
|
31
|
+
it.each([
|
|
32
|
+
[extraction_1.EventType.StartExtractingExternalSyncUnits],
|
|
33
|
+
[extraction_1.EventType.StartExtractingMetadata],
|
|
34
|
+
[extraction_1.EventType.StartExtractingData],
|
|
35
|
+
[extraction_1.EventType.ContinueExtractingData],
|
|
36
|
+
[extraction_1.EventType.StartDeletingExtractorState],
|
|
37
|
+
[extraction_1.EventType.StartExtractingAttachments],
|
|
38
|
+
[extraction_1.EventType.ContinueExtractingAttachments],
|
|
39
|
+
[extraction_1.EventType.StartDeletingExtractorAttachmentsState],
|
|
40
|
+
[extraction_1.EventType.StartLoadingData],
|
|
41
|
+
[extraction_1.EventType.ContinueLoadingData],
|
|
42
|
+
[extraction_1.EventType.StartLoadingAttachments],
|
|
43
|
+
[extraction_1.EventType.ContinueLoadingAttachments],
|
|
44
|
+
[extraction_1.EventType.StartDeletingLoaderState],
|
|
45
|
+
[extraction_1.EventType.StartDeletingLoaderAttachmentState],
|
|
46
|
+
[extraction_1.EventType.UnknownEventType],
|
|
47
|
+
])('is a no-op for already-modern event type %s', (eventType) => {
|
|
48
|
+
expect((0, event_type_translation_1.translateIncomingEventType)(eventType)).toBe(eventType);
|
|
49
|
+
});
|
|
50
|
+
it('returns the input verbatim for an unrecognised event type', () => {
|
|
51
|
+
const result = (0, event_type_translation_1.translateIncomingEventType)('NONSENSE_EVENT');
|
|
52
|
+
expect(result).toBe('NONSENSE_EVENT');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe(event_type_translation_1.translateExtractorEventType.name, () => {
|
|
56
|
+
it.each([
|
|
57
|
+
[
|
|
58
|
+
extraction_1.ExtractorEventType.ExtractionExternalSyncUnitsDone,
|
|
59
|
+
extraction_1.ExtractorEventType.ExternalSyncUnitExtractionDone,
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
extraction_1.ExtractorEventType.ExtractionExternalSyncUnitsError,
|
|
63
|
+
extraction_1.ExtractorEventType.ExternalSyncUnitExtractionError,
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
extraction_1.ExtractorEventType.ExtractionMetadataDone,
|
|
67
|
+
extraction_1.ExtractorEventType.MetadataExtractionDone,
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
extraction_1.ExtractorEventType.ExtractionMetadataError,
|
|
71
|
+
extraction_1.ExtractorEventType.MetadataExtractionError,
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
extraction_1.ExtractorEventType.ExtractionDataProgress,
|
|
75
|
+
extraction_1.ExtractorEventType.DataExtractionProgress,
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
extraction_1.ExtractorEventType.ExtractionDataDelay,
|
|
79
|
+
extraction_1.ExtractorEventType.DataExtractionDelayed,
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
extraction_1.ExtractorEventType.ExtractionDataDone,
|
|
83
|
+
extraction_1.ExtractorEventType.DataExtractionDone,
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
extraction_1.ExtractorEventType.ExtractionDataError,
|
|
87
|
+
extraction_1.ExtractorEventType.DataExtractionError,
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
extraction_1.ExtractorEventType.ExtractionDataDeleteDone,
|
|
91
|
+
extraction_1.ExtractorEventType.ExtractorStateDeletionDone,
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
extraction_1.ExtractorEventType.ExtractionDataDeleteError,
|
|
95
|
+
extraction_1.ExtractorEventType.ExtractorStateDeletionError,
|
|
96
|
+
],
|
|
97
|
+
[
|
|
98
|
+
extraction_1.ExtractorEventType.ExtractionAttachmentsProgress,
|
|
99
|
+
extraction_1.ExtractorEventType.AttachmentExtractionProgress,
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
extraction_1.ExtractorEventType.ExtractionAttachmentsDelay,
|
|
103
|
+
extraction_1.ExtractorEventType.AttachmentExtractionDelayed,
|
|
104
|
+
],
|
|
105
|
+
[
|
|
106
|
+
extraction_1.ExtractorEventType.ExtractionAttachmentsDone,
|
|
107
|
+
extraction_1.ExtractorEventType.AttachmentExtractionDone,
|
|
108
|
+
],
|
|
109
|
+
[
|
|
110
|
+
extraction_1.ExtractorEventType.ExtractionAttachmentsError,
|
|
111
|
+
extraction_1.ExtractorEventType.AttachmentExtractionError,
|
|
112
|
+
],
|
|
113
|
+
[
|
|
114
|
+
extraction_1.ExtractorEventType.ExtractionAttachmentsDeleteDone,
|
|
115
|
+
extraction_1.ExtractorEventType.ExtractorAttachmentsStateDeletionDone,
|
|
116
|
+
],
|
|
117
|
+
[
|
|
118
|
+
extraction_1.ExtractorEventType.ExtractionAttachmentsDeleteError,
|
|
119
|
+
extraction_1.ExtractorEventType.ExtractorAttachmentsStateDeletionError,
|
|
120
|
+
],
|
|
121
|
+
])('maps legacy extractor event %s to %s', (legacy, modern) => {
|
|
122
|
+
expect((0, event_type_translation_1.translateExtractorEventType)(legacy)).toBe(modern);
|
|
123
|
+
});
|
|
124
|
+
it.each([
|
|
125
|
+
[extraction_1.ExtractorEventType.DataExtractionDone],
|
|
126
|
+
[extraction_1.ExtractorEventType.DataExtractionProgress],
|
|
127
|
+
[extraction_1.ExtractorEventType.AttachmentExtractionDone],
|
|
128
|
+
[extraction_1.ExtractorEventType.MetadataExtractionDone],
|
|
129
|
+
[extraction_1.ExtractorEventType.UnknownEventType],
|
|
130
|
+
])('is a no-op for already-modern extractor event %s', (eventType) => {
|
|
131
|
+
expect((0, event_type_translation_1.translateExtractorEventType)(eventType)).toBe(eventType);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
describe(event_type_translation_1.translateLoaderEventType.name, () => {
|
|
135
|
+
it.each([
|
|
136
|
+
[loading_1.LoaderEventType.DataLoadingDelay, loading_1.LoaderEventType.DataLoadingDelayed],
|
|
137
|
+
[
|
|
138
|
+
loading_1.LoaderEventType.AttachmentsLoadingProgress,
|
|
139
|
+
loading_1.LoaderEventType.AttachmentLoadingProgress,
|
|
140
|
+
],
|
|
141
|
+
[
|
|
142
|
+
loading_1.LoaderEventType.AttachmentsLoadingDelayed,
|
|
143
|
+
loading_1.LoaderEventType.AttachmentLoadingDelayed,
|
|
144
|
+
],
|
|
145
|
+
[
|
|
146
|
+
loading_1.LoaderEventType.AttachmentsLoadingDone,
|
|
147
|
+
loading_1.LoaderEventType.AttachmentLoadingDone,
|
|
148
|
+
],
|
|
149
|
+
[
|
|
150
|
+
loading_1.LoaderEventType.AttachmentsLoadingError,
|
|
151
|
+
loading_1.LoaderEventType.AttachmentLoadingError,
|
|
152
|
+
],
|
|
153
|
+
])('maps legacy loader event %s to %s', (legacy, modern) => {
|
|
154
|
+
expect((0, event_type_translation_1.translateLoaderEventType)(legacy)).toBe(modern);
|
|
155
|
+
});
|
|
156
|
+
it.each([
|
|
157
|
+
[loading_1.LoaderEventType.DataLoadingDone],
|
|
158
|
+
[loading_1.LoaderEventType.DataLoadingProgress],
|
|
159
|
+
[loading_1.LoaderEventType.AttachmentLoadingDone],
|
|
160
|
+
])('is a no-op for already-modern loader event %s', (eventType) => {
|
|
161
|
+
expect((0, event_type_translation_1.translateLoaderEventType)(eventType)).toBe(eventType);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe(event_type_translation_1.translateOutgoingEventType.name, () => {
|
|
165
|
+
it('routes extractor events through translateExtractorEventType', () => {
|
|
166
|
+
expect((0, event_type_translation_1.translateOutgoingEventType)(extraction_1.ExtractorEventType.ExtractionDataDone)).toBe(extraction_1.ExtractorEventType.DataExtractionDone);
|
|
167
|
+
});
|
|
168
|
+
it('routes loader events through translateLoaderEventType', () => {
|
|
169
|
+
expect((0, event_type_translation_1.translateOutgoingEventType)(loading_1.LoaderEventType.AttachmentsLoadingDone)).toBe(loading_1.LoaderEventType.AttachmentLoadingDone);
|
|
170
|
+
});
|
|
171
|
+
it('passes through unknown event types unchanged', () => {
|
|
172
|
+
const unknown = 'SOME_UNKNOWN_EVENT';
|
|
173
|
+
expect((0, event_type_translation_1.translateOutgoingEventType)(unknown)).toBe(unknown);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -135,7 +135,6 @@ describe('time-value-resolver', () => {
|
|
|
135
135
|
const before = new Date().toISOString();
|
|
136
136
|
const result = (0, time_value_resolver_1.resolveTimeValue)({ type: extraction_1.TimeValueType.CURRENT_TIME }, baseState);
|
|
137
137
|
const after = new Date().toISOString();
|
|
138
|
-
expect(result).toBeDefined();
|
|
139
138
|
expect(result >= before).toBe(true);
|
|
140
139
|
expect(result <= after).toBe(true);
|
|
141
140
|
});
|
|
@@ -7,16 +7,20 @@ const extraction_1 = require("../types/extraction");
|
|
|
7
7
|
const create_worker_1 = require("./create-worker");
|
|
8
8
|
describe(create_worker_1.createWorker.name, () => {
|
|
9
9
|
it('should create a Worker instance when valid parameters are provided', async () => {
|
|
10
|
+
// Arrange
|
|
10
11
|
const workerPath = __dirname + '../tests/dummy-worker.ts';
|
|
12
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
13
|
+
payload: { event_type: extraction_1.EventType.ExtractionExternalSyncUnitsStart },
|
|
14
|
+
});
|
|
15
|
+
// Act
|
|
11
16
|
const worker = worker_threads_1.isMainThread
|
|
12
17
|
? await (0, create_worker_1.createWorker)({
|
|
13
|
-
event
|
|
14
|
-
payload: { event_type: extraction_1.EventType.ExtractionExternalSyncUnitsStart },
|
|
15
|
-
}),
|
|
18
|
+
event,
|
|
16
19
|
initialState: {},
|
|
17
20
|
workerPath,
|
|
18
21
|
})
|
|
19
22
|
: null;
|
|
23
|
+
// Assert
|
|
20
24
|
expect(worker).not.toBeNull();
|
|
21
25
|
expect(worker).toBeInstanceOf(worker_threads_1.Worker);
|
|
22
26
|
if (worker) {
|
|
@@ -24,36 +28,43 @@ describe(create_worker_1.createWorker.name, () => {
|
|
|
24
28
|
}
|
|
25
29
|
});
|
|
26
30
|
it('should throw error when not in main thread', async () => {
|
|
31
|
+
// Arrange
|
|
27
32
|
const originalIsMainThread = worker_threads_1.isMainThread;
|
|
28
33
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
34
|
worker_threads_1.isMainThread = false;
|
|
30
35
|
const workerPath = __dirname + '../tests/dummy-worker.ts';
|
|
36
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
37
|
+
payload: { event_type: extraction_1.EventType.ExtractionExternalSyncUnitsStart },
|
|
38
|
+
});
|
|
39
|
+
// Act & Assert
|
|
31
40
|
await expect((0, create_worker_1.createWorker)({
|
|
32
|
-
event
|
|
33
|
-
payload: { event_type: extraction_1.EventType.ExtractionExternalSyncUnitsStart },
|
|
34
|
-
}),
|
|
41
|
+
event,
|
|
35
42
|
initialState: {},
|
|
36
43
|
workerPath,
|
|
37
44
|
})).rejects.toThrow('Worker threads can not start more worker threads.');
|
|
38
|
-
// Restore original value
|
|
39
45
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
46
|
worker_threads_1.isMainThread = originalIsMainThread;
|
|
41
47
|
});
|
|
42
48
|
it('[edge] should handle worker creation with minimal valid data', async () => {
|
|
49
|
+
// Arrange
|
|
43
50
|
const workerPath = __dirname + '../tests/dummy-worker.ts';
|
|
51
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
52
|
+
payload: { event_type: extraction_1.EventType.ExtractionExternalSyncUnitsStart },
|
|
53
|
+
});
|
|
44
54
|
if (worker_threads_1.isMainThread) {
|
|
55
|
+
// Act
|
|
45
56
|
const worker = await (0, create_worker_1.createWorker)({
|
|
46
|
-
event
|
|
47
|
-
payload: { event_type: extraction_1.EventType.ExtractionExternalSyncUnitsStart },
|
|
48
|
-
}),
|
|
57
|
+
event,
|
|
49
58
|
initialState: {},
|
|
50
59
|
workerPath,
|
|
51
60
|
});
|
|
61
|
+
// Assert
|
|
52
62
|
expect(worker).toBeInstanceOf(worker_threads_1.Worker);
|
|
53
63
|
await worker.terminate();
|
|
54
64
|
}
|
|
55
65
|
});
|
|
56
66
|
it('[edge] should handle worker creation with complex initial state', async () => {
|
|
67
|
+
// Arrange
|
|
57
68
|
const workerPath = __dirname + '../tests/dummy-worker.ts';
|
|
58
69
|
const complexState = {
|
|
59
70
|
nested: {
|
|
@@ -61,28 +72,35 @@ describe(create_worker_1.createWorker.name, () => {
|
|
|
61
72
|
config: { enabled: true },
|
|
62
73
|
},
|
|
63
74
|
};
|
|
75
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
76
|
+
payload: { event_type: extraction_1.EventType.ExtractionDataStart },
|
|
77
|
+
});
|
|
64
78
|
if (worker_threads_1.isMainThread) {
|
|
79
|
+
// Act
|
|
65
80
|
const worker = await (0, create_worker_1.createWorker)({
|
|
66
|
-
event
|
|
67
|
-
payload: { event_type: extraction_1.EventType.ExtractionDataStart },
|
|
68
|
-
}),
|
|
81
|
+
event,
|
|
69
82
|
initialState: complexState,
|
|
70
83
|
workerPath,
|
|
71
84
|
});
|
|
85
|
+
// Assert
|
|
72
86
|
expect(worker).toBeInstanceOf(worker_threads_1.Worker);
|
|
73
87
|
await worker.terminate();
|
|
74
88
|
}
|
|
75
89
|
});
|
|
76
90
|
it('[edge] should handle different event types', async () => {
|
|
91
|
+
// Arrange
|
|
77
92
|
const workerPath = __dirname + '../tests/dummy-worker.ts';
|
|
93
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
94
|
+
payload: { event_type: extraction_1.EventType.ExtractionMetadataStart },
|
|
95
|
+
});
|
|
78
96
|
if (worker_threads_1.isMainThread) {
|
|
97
|
+
// Act
|
|
79
98
|
const worker = await (0, create_worker_1.createWorker)({
|
|
80
|
-
event
|
|
81
|
-
payload: { event_type: extraction_1.EventType.ExtractionMetadataStart },
|
|
82
|
-
}),
|
|
99
|
+
event,
|
|
83
100
|
initialState: {},
|
|
84
101
|
workerPath,
|
|
85
102
|
});
|
|
103
|
+
// Assert
|
|
86
104
|
expect(worker).toBeInstanceOf(worker_threads_1.Worker);
|
|
87
105
|
await worker.terminate();
|
|
88
106
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-task.test.d.ts","sourceRoot":"","sources":["../../src/multithreading/process-task.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const extraction_1 = require("../types/extraction");
|
|
4
|
+
const workers_1 = require("../types/workers");
|
|
5
|
+
// These tests cover logic that is NOT exercised by the end-to-end integration
|
|
6
|
+
// tests under src/tests/timeout-handling/:
|
|
7
|
+
// - translation of legacy wire event types into the new enum (mutates event in place)
|
|
8
|
+
// - the hasWorkerEmitted guard that prevents onTimeout from firing after a
|
|
9
|
+
// successful emit (integration tests only exercise the positive case)
|
|
10
|
+
// - the error branch that posts WorkerMessageFailed and exits(1)
|
|
11
|
+
// - the WorkerMessage handler's guard that only flips isTimeout on
|
|
12
|
+
// WorkerMessageExit (integration tests can't cleanly target non-Exit messages)
|
|
13
|
+
//
|
|
14
|
+
// Tests for the happy path, timeout-signal-reaches-worker behavior, and main-thread
|
|
15
|
+
// early return were removed — they either duplicate what the integration suite
|
|
16
|
+
// already exercises or assert mocked behavior with little signal.
|
|
17
|
+
const mockParentPortPostMessage = jest.fn();
|
|
18
|
+
const mockParentPortOn = jest.fn();
|
|
19
|
+
let mockIsMainThread = false;
|
|
20
|
+
jest.mock('node:worker_threads', () => ({
|
|
21
|
+
get isMainThread() {
|
|
22
|
+
return mockIsMainThread;
|
|
23
|
+
},
|
|
24
|
+
get parentPort() {
|
|
25
|
+
return {
|
|
26
|
+
postMessage: mockParentPortPostMessage,
|
|
27
|
+
on: mockParentPortOn,
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
get workerData() {
|
|
31
|
+
var _a;
|
|
32
|
+
return (_a = global.__workerData__) !== null && _a !== void 0 ? _a : {};
|
|
33
|
+
},
|
|
34
|
+
}));
|
|
35
|
+
jest.mock('../common/event-type-translation', () => ({
|
|
36
|
+
translateIncomingEventType: jest.fn((t) => t),
|
|
37
|
+
}));
|
|
38
|
+
jest.mock('../logger/logger', () => ({
|
|
39
|
+
Logger: jest.fn().mockImplementation(() => ({
|
|
40
|
+
log: jest.fn(),
|
|
41
|
+
warn: jest.fn(),
|
|
42
|
+
error: jest.fn(),
|
|
43
|
+
info: jest.fn(),
|
|
44
|
+
logFn: jest.fn(),
|
|
45
|
+
})),
|
|
46
|
+
serializeError: jest.fn((e) => String(e)),
|
|
47
|
+
}));
|
|
48
|
+
jest.mock('../logger/logger.context', () => ({
|
|
49
|
+
runWithSdkLogContext: jest.fn((fn) => fn()),
|
|
50
|
+
runWithUserLogContext: jest.fn((fn) => fn()),
|
|
51
|
+
}));
|
|
52
|
+
jest.mock('../state/state', () => ({
|
|
53
|
+
createAdapterState: jest.fn(),
|
|
54
|
+
}));
|
|
55
|
+
jest.mock('./worker-adapter/worker-adapter', () => ({
|
|
56
|
+
WorkerAdapter: jest.fn().mockImplementation(() => ({
|
|
57
|
+
isTimeout: false,
|
|
58
|
+
hasWorkerEmitted: false,
|
|
59
|
+
})),
|
|
60
|
+
}));
|
|
61
|
+
const process_task_1 = require("./process-task");
|
|
62
|
+
const event_type_translation_1 = require("../common/event-type-translation");
|
|
63
|
+
const state_1 = require("../state/state");
|
|
64
|
+
const worker_adapter_1 = require("./worker-adapter/worker-adapter");
|
|
65
|
+
const test_utils_1 = require("../common/test-utils");
|
|
66
|
+
function setWorkerData(data) {
|
|
67
|
+
global.__workerData__ = data;
|
|
68
|
+
}
|
|
69
|
+
function makeEvent(eventType = extraction_1.EventType.StartExtractingData) {
|
|
70
|
+
return (0, test_utils_1.createMockEvent)('http://localhost:0', {
|
|
71
|
+
payload: { event_type: eventType },
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
// Flush the microtask queue enough to let the async IIFE inside processTask run.
|
|
75
|
+
const flush = async () => new Promise((r) => setTimeout(r, 0));
|
|
76
|
+
describe(process_task_1.processTask.name, () => {
|
|
77
|
+
let processExitSpy;
|
|
78
|
+
beforeEach(() => {
|
|
79
|
+
jest.clearAllMocks();
|
|
80
|
+
mockIsMainThread = false;
|
|
81
|
+
processExitSpy = jest
|
|
82
|
+
.spyOn(process, 'exit')
|
|
83
|
+
.mockImplementation((() => { }));
|
|
84
|
+
state_1.createAdapterState.mockResolvedValue({});
|
|
85
|
+
});
|
|
86
|
+
afterEach(() => {
|
|
87
|
+
processExitSpy.mockRestore();
|
|
88
|
+
});
|
|
89
|
+
it('should translate incoming event type before passing to task', async () => {
|
|
90
|
+
// Arrange
|
|
91
|
+
const event = makeEvent(extraction_1.EventType.StartExtractingData);
|
|
92
|
+
setWorkerData({ event, initialState: {}, options: {} });
|
|
93
|
+
event_type_translation_1.translateIncomingEventType.mockReturnValue(extraction_1.EventType.StartExtractingMetadata);
|
|
94
|
+
const task = jest.fn().mockResolvedValue(undefined);
|
|
95
|
+
const onTimeout = jest.fn().mockResolvedValue(undefined);
|
|
96
|
+
// Act
|
|
97
|
+
(0, process_task_1.processTask)({ task, onTimeout });
|
|
98
|
+
await flush();
|
|
99
|
+
// Assert
|
|
100
|
+
expect(event_type_translation_1.translateIncomingEventType).toHaveBeenCalledWith(extraction_1.EventType.StartExtractingData);
|
|
101
|
+
// The event is mutated in place — downstream code (including task) sees the
|
|
102
|
+
// translated type, not the original wire type.
|
|
103
|
+
expect(event.payload.event_type).toBe(extraction_1.EventType.StartExtractingMetadata);
|
|
104
|
+
});
|
|
105
|
+
it('should NOT call onTimeout when the worker already emitted before timeout check', async () => {
|
|
106
|
+
// Arrange
|
|
107
|
+
const event = makeEvent();
|
|
108
|
+
setWorkerData({ event, initialState: {}, options: {} });
|
|
109
|
+
// Both flags true: a timeout arrived but the worker had already emitted —
|
|
110
|
+
// onTimeout must be skipped. This is the guard the integration suite cannot
|
|
111
|
+
// target cleanly because it requires a precise race between emit and timeout.
|
|
112
|
+
const mockAdapter = { isTimeout: true, hasWorkerEmitted: true };
|
|
113
|
+
worker_adapter_1.WorkerAdapter.mockImplementation(() => mockAdapter);
|
|
114
|
+
const task = jest.fn().mockResolvedValue(undefined);
|
|
115
|
+
const onTimeout = jest.fn().mockResolvedValue(undefined);
|
|
116
|
+
// Act
|
|
117
|
+
(0, process_task_1.processTask)({ task, onTimeout });
|
|
118
|
+
await flush();
|
|
119
|
+
// Assert
|
|
120
|
+
expect(onTimeout).not.toHaveBeenCalled();
|
|
121
|
+
expect(processExitSpy).toHaveBeenCalledWith(0);
|
|
122
|
+
});
|
|
123
|
+
it('should NOT flip adapter.isTimeout when a non-Exit WorkerMessage arrives', async () => {
|
|
124
|
+
// Arrange
|
|
125
|
+
const event = makeEvent();
|
|
126
|
+
setWorkerData({ event, initialState: {}, options: {} });
|
|
127
|
+
const mockAdapter = { isTimeout: false, hasWorkerEmitted: false };
|
|
128
|
+
worker_adapter_1.WorkerAdapter.mockImplementation(() => mockAdapter);
|
|
129
|
+
const task = jest.fn().mockResolvedValue(undefined);
|
|
130
|
+
const onTimeout = jest.fn().mockResolvedValue(undefined);
|
|
131
|
+
// Act
|
|
132
|
+
(0, process_task_1.processTask)({ task, onTimeout });
|
|
133
|
+
await flush();
|
|
134
|
+
// Grab the handler registered for WorkerMessage events and invoke it with
|
|
135
|
+
// subjects that must NOT flip isTimeout (log messages, unknown subjects).
|
|
136
|
+
const messageHandlerCall = mockParentPortOn.mock.calls.find(([eventName]) => eventName === workers_1.WorkerEvent.WorkerMessage);
|
|
137
|
+
expect(messageHandlerCall).toBeDefined();
|
|
138
|
+
const handler = messageHandlerCall[1];
|
|
139
|
+
handler({ subject: workers_1.WorkerMessageSubject.WorkerMessageLog });
|
|
140
|
+
handler({ subject: 'NONSENSE_SUBJECT' });
|
|
141
|
+
// Assert
|
|
142
|
+
expect(mockAdapter.isTimeout).toBe(false);
|
|
143
|
+
});
|
|
144
|
+
it('should post WorkerMessageFailed with the error message and exit(1) when task throws', async () => {
|
|
145
|
+
// Arrange
|
|
146
|
+
const event = makeEvent();
|
|
147
|
+
setWorkerData({ event, initialState: {}, options: {} });
|
|
148
|
+
const mockAdapter = { isTimeout: false, hasWorkerEmitted: false };
|
|
149
|
+
worker_adapter_1.WorkerAdapter.mockImplementation(() => mockAdapter);
|
|
150
|
+
const taskError = new Error('task boom');
|
|
151
|
+
const task = jest.fn().mockRejectedValue(taskError);
|
|
152
|
+
const onTimeout = jest.fn().mockResolvedValue(undefined);
|
|
153
|
+
// Act
|
|
154
|
+
(0, process_task_1.processTask)({ task, onTimeout });
|
|
155
|
+
await flush();
|
|
156
|
+
// Assert
|
|
157
|
+
expect(mockParentPortPostMessage).toHaveBeenCalledWith(expect.objectContaining({
|
|
158
|
+
subject: workers_1.WorkerMessageSubject.WorkerMessageFailed,
|
|
159
|
+
payload: expect.objectContaining({
|
|
160
|
+
message: expect.stringContaining('task boom'),
|
|
161
|
+
}),
|
|
162
|
+
}));
|
|
163
|
+
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
164
|
+
expect(onTimeout).not.toHaveBeenCalled();
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.test.d.ts","sourceRoot":"","sources":["../../../src/multithreading/spawn/spawn.test.ts"],"names":[],"mappings":""}
|