@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
package/dist/repo/repo.test.js
CHANGED
|
@@ -25,13 +25,17 @@ describe(repo_1.Repo.name, () => {
|
|
|
25
25
|
jest.clearAllMocks();
|
|
26
26
|
});
|
|
27
27
|
it('should normalize and push items when array contains items', async () => {
|
|
28
|
+
// Arrange
|
|
28
29
|
const items = (0, test_helpers_1.createItems)(10);
|
|
30
|
+
// Act
|
|
29
31
|
await repo.push(items);
|
|
32
|
+
// Assert
|
|
30
33
|
expect(normalize).toHaveBeenCalledTimes(10);
|
|
31
34
|
const normalizedItems = items.map((item) => (0, test_helpers_1.normalizeItem)(item));
|
|
32
35
|
expect(repo.getItems()).toEqual(normalizedItems);
|
|
33
36
|
});
|
|
34
37
|
it('should not normalize items when normalize function is not provided', async () => {
|
|
38
|
+
// Arrange
|
|
35
39
|
repo = new repo_1.Repo({
|
|
36
40
|
event: (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
37
41
|
payload: { event_type: types_1.EventType.ExtractionDataStart },
|
|
@@ -41,14 +45,19 @@ describe(repo_1.Repo.name, () => {
|
|
|
41
45
|
options: {},
|
|
42
46
|
});
|
|
43
47
|
const items = (0, test_helpers_1.createItems)(10);
|
|
48
|
+
// Act
|
|
44
49
|
await repo.push(items);
|
|
50
|
+
// Assert
|
|
45
51
|
expect(normalize).not.toHaveBeenCalled();
|
|
46
52
|
});
|
|
47
53
|
it('[edge] should not push items when items array is empty', async () => {
|
|
54
|
+
// Act
|
|
48
55
|
await repo.push([]);
|
|
56
|
+
// Assert
|
|
49
57
|
expect(repo.getItems()).toEqual([]);
|
|
50
58
|
});
|
|
51
59
|
it('should not normalize items when item type is external_domain_metadata', async () => {
|
|
60
|
+
// Arrange
|
|
52
61
|
repo = new repo_1.Repo({
|
|
53
62
|
event: (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
54
63
|
payload: { event_type: types_1.EventType.ExtractionDataStart },
|
|
@@ -59,10 +68,13 @@ describe(repo_1.Repo.name, () => {
|
|
|
59
68
|
options: {},
|
|
60
69
|
});
|
|
61
70
|
const items = (0, test_helpers_1.createItems)(10);
|
|
71
|
+
// Act
|
|
62
72
|
await repo.push(items);
|
|
73
|
+
// Assert
|
|
63
74
|
expect(normalize).not.toHaveBeenCalled();
|
|
64
75
|
});
|
|
65
76
|
it('should not normalize items when item type is ssor_attachment', async () => {
|
|
77
|
+
// Arrange
|
|
66
78
|
repo = new repo_1.Repo({
|
|
67
79
|
event: (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
68
80
|
payload: { event_type: types_1.EventType.ExtractionDataStart },
|
|
@@ -73,29 +85,43 @@ describe(repo_1.Repo.name, () => {
|
|
|
73
85
|
options: {},
|
|
74
86
|
});
|
|
75
87
|
const items = (0, test_helpers_1.createItems)(10);
|
|
88
|
+
// Act
|
|
76
89
|
await repo.push(items);
|
|
90
|
+
// Assert
|
|
77
91
|
expect(normalize).not.toHaveBeenCalled();
|
|
78
92
|
});
|
|
79
93
|
it('should leave 5 items in the items array after pushing 2005 items with batch size of 2000', async () => {
|
|
94
|
+
// Arrange
|
|
80
95
|
const items = (0, test_helpers_1.createItems)(2005);
|
|
96
|
+
// Act
|
|
81
97
|
await repo.push(items);
|
|
98
|
+
// Assert
|
|
82
99
|
expect(repo.getItems().length).toBe(5);
|
|
83
100
|
});
|
|
84
101
|
it('should normalize all items when pushing 4005 items with batch size of 2000', async () => {
|
|
102
|
+
// Arrange
|
|
85
103
|
const items = (0, test_helpers_1.createItems)(4005);
|
|
104
|
+
// Act
|
|
86
105
|
await repo.push(items);
|
|
106
|
+
// Assert
|
|
87
107
|
expect(normalize).toHaveBeenCalledTimes(4005);
|
|
88
108
|
});
|
|
89
109
|
it('should upload 2 batches when pushing 4005 items with batch size of 2000', async () => {
|
|
110
|
+
// Arrange
|
|
90
111
|
const uploadSpy = jest.spyOn(repo, 'upload');
|
|
91
112
|
const items = (0, test_helpers_1.createItems)(4005);
|
|
113
|
+
// Act
|
|
92
114
|
await repo.push(items);
|
|
115
|
+
// Assert
|
|
93
116
|
expect(uploadSpy).toHaveBeenCalledTimes(2);
|
|
94
117
|
uploadSpy.mockRestore();
|
|
95
118
|
});
|
|
96
119
|
it('should leave 5 items in array after pushing 4005 items with batch size of 2000', async () => {
|
|
120
|
+
// Arrange
|
|
97
121
|
const items = (0, test_helpers_1.createItems)(4005);
|
|
122
|
+
// Act
|
|
98
123
|
await repo.push(items);
|
|
124
|
+
// Assert
|
|
99
125
|
expect(repo.getItems().length).toBe(5);
|
|
100
126
|
});
|
|
101
127
|
describe('should take batch size into account', () => {
|
|
@@ -113,30 +139,45 @@ describe(repo_1.Repo.name, () => {
|
|
|
113
139
|
});
|
|
114
140
|
});
|
|
115
141
|
it('should empty the items array after pushing 50 items with batch size of 50', async () => {
|
|
142
|
+
// Arrange
|
|
116
143
|
const items = (0, test_helpers_1.createItems)(50);
|
|
144
|
+
// Act
|
|
117
145
|
await repo.push(items);
|
|
146
|
+
// Assert
|
|
118
147
|
expect(repo.getItems()).toEqual([]);
|
|
119
148
|
});
|
|
120
149
|
it('should leave 5 items in the items array after pushing 205 items with batch size of 50', async () => {
|
|
150
|
+
// Arrange
|
|
121
151
|
const items = (0, test_helpers_1.createItems)(205);
|
|
152
|
+
// Act
|
|
122
153
|
await repo.push(items);
|
|
154
|
+
// Assert
|
|
123
155
|
expect(repo.getItems().length).toBe(5);
|
|
124
156
|
});
|
|
125
157
|
it('should normalize all items when pushing 205 items with batch size of 50', async () => {
|
|
158
|
+
// Arrange
|
|
126
159
|
const items = (0, test_helpers_1.createItems)(205);
|
|
160
|
+
// Act
|
|
127
161
|
await repo.push(items);
|
|
162
|
+
// Assert
|
|
128
163
|
expect(normalize).toHaveBeenCalledTimes(205);
|
|
129
164
|
});
|
|
130
165
|
it('should upload 4 batches when pushing 205 items with batch size of 50', async () => {
|
|
166
|
+
// Arrange
|
|
131
167
|
const uploadSpy = jest.spyOn(repo, 'upload');
|
|
132
168
|
const items = (0, test_helpers_1.createItems)(205);
|
|
169
|
+
// Act
|
|
133
170
|
await repo.push(items);
|
|
171
|
+
// Assert
|
|
134
172
|
expect(uploadSpy).toHaveBeenCalledTimes(4);
|
|
135
173
|
uploadSpy.mockRestore();
|
|
136
174
|
});
|
|
137
175
|
it('should leave 5 items in array after pushing 205 items with batch size of 50', async () => {
|
|
176
|
+
// Arrange
|
|
138
177
|
const items = (0, test_helpers_1.createItems)(205);
|
|
178
|
+
// Act
|
|
139
179
|
await repo.push(items);
|
|
180
|
+
// Assert
|
|
140
181
|
expect(repo.getItems().length).toBe(5);
|
|
141
182
|
});
|
|
142
183
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.extract-window.test.d.ts","sourceRoot":"","sources":["../../src/state/state.extract-window.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jest_setup_1 = require("../tests/jest.setup");
|
|
4
|
+
const test_utils_1 = require("../common/test-utils");
|
|
5
|
+
const extraction_1 = require("../types/extraction");
|
|
6
|
+
const state_1 = require("./state");
|
|
7
|
+
describe('State — extraction window validation', () => {
|
|
8
|
+
let fetchStateSpy;
|
|
9
|
+
let processExitSpy;
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
jest.clearAllMocks();
|
|
12
|
+
jest.restoreAllMocks();
|
|
13
|
+
fetchStateSpy = jest.spyOn(state_1.State.prototype, 'fetchState');
|
|
14
|
+
processExitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
|
|
15
|
+
throw new Error('process.exit called');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe('Enhanced Control Protocol - extraction window validation', () => {
|
|
19
|
+
it('should exit the process if extract_from >= extract_to', async () => {
|
|
20
|
+
// Arrange: start is after end (inverted window)
|
|
21
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
22
|
+
payload: {
|
|
23
|
+
event_type: extraction_1.EventType.StartExtractingMetadata,
|
|
24
|
+
event_context: {
|
|
25
|
+
extraction_start_time: {
|
|
26
|
+
type: extraction_1.TimeValueType.ABSOLUTE_TIME,
|
|
27
|
+
value: '2025-06-01T00:00:00Z',
|
|
28
|
+
},
|
|
29
|
+
extraction_end_time: {
|
|
30
|
+
type: extraction_1.TimeValueType.ABSOLUTE_TIME,
|
|
31
|
+
value: '2024-01-01T00:00:00Z',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
const stringifiedState = JSON.stringify({
|
|
37
|
+
snapInVersionId: 'test_snap_in_version_id',
|
|
38
|
+
});
|
|
39
|
+
fetchStateSpy.mockResolvedValue({ state: stringifiedState });
|
|
40
|
+
// Act & Assert
|
|
41
|
+
await expect((0, state_1.createAdapterState)({
|
|
42
|
+
event,
|
|
43
|
+
initialState: {},
|
|
44
|
+
initialDomainMapping: {},
|
|
45
|
+
})).rejects.toThrow('process.exit called');
|
|
46
|
+
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
47
|
+
});
|
|
48
|
+
it('should exit the process if extract_from equals extract_to', async () => {
|
|
49
|
+
// Arrange: start equals end (zero-width window)
|
|
50
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
51
|
+
payload: {
|
|
52
|
+
event_type: extraction_1.EventType.StartExtractingMetadata,
|
|
53
|
+
event_context: {
|
|
54
|
+
extraction_start_time: {
|
|
55
|
+
type: extraction_1.TimeValueType.ABSOLUTE_TIME,
|
|
56
|
+
value: '2024-06-01T00:00:00Z',
|
|
57
|
+
},
|
|
58
|
+
extraction_end_time: {
|
|
59
|
+
type: extraction_1.TimeValueType.ABSOLUTE_TIME,
|
|
60
|
+
value: '2024-06-01T00:00:00Z',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
const stringifiedState = JSON.stringify({
|
|
66
|
+
snapInVersionId: 'test_snap_in_version_id',
|
|
67
|
+
});
|
|
68
|
+
fetchStateSpy.mockResolvedValue({ state: stringifiedState });
|
|
69
|
+
// Act & Assert
|
|
70
|
+
await expect((0, state_1.createAdapterState)({
|
|
71
|
+
event,
|
|
72
|
+
initialState: {},
|
|
73
|
+
initialDomainMapping: {},
|
|
74
|
+
})).rejects.toThrow('process.exit called');
|
|
75
|
+
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
76
|
+
});
|
|
77
|
+
it('should not exit when extract_from < extract_to', async () => {
|
|
78
|
+
// Arrange: valid window
|
|
79
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
80
|
+
payload: {
|
|
81
|
+
event_type: extraction_1.EventType.StartExtractingMetadata,
|
|
82
|
+
event_context: {
|
|
83
|
+
extraction_start_time: {
|
|
84
|
+
type: extraction_1.TimeValueType.ABSOLUTE_TIME,
|
|
85
|
+
value: '2024-01-01T00:00:00Z',
|
|
86
|
+
},
|
|
87
|
+
extraction_end_time: {
|
|
88
|
+
type: extraction_1.TimeValueType.ABSOLUTE_TIME,
|
|
89
|
+
value: '2025-06-01T00:00:00Z',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
const stringifiedState = JSON.stringify({
|
|
95
|
+
snapInVersionId: 'test_snap_in_version_id',
|
|
96
|
+
});
|
|
97
|
+
fetchStateSpy.mockResolvedValue({ state: stringifiedState });
|
|
98
|
+
// Act
|
|
99
|
+
await (0, state_1.createAdapterState)({
|
|
100
|
+
event,
|
|
101
|
+
initialState: {},
|
|
102
|
+
initialDomainMapping: {},
|
|
103
|
+
});
|
|
104
|
+
// Assert: process.exit should NOT have been called
|
|
105
|
+
expect(processExitSpy).not.toHaveBeenCalled();
|
|
106
|
+
});
|
|
107
|
+
it('should not validate when only extract_from is set', async () => {
|
|
108
|
+
// Arrange: only start, no end
|
|
109
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
110
|
+
payload: {
|
|
111
|
+
event_type: extraction_1.EventType.StartExtractingMetadata,
|
|
112
|
+
event_context: {
|
|
113
|
+
extraction_start_time: {
|
|
114
|
+
type: extraction_1.TimeValueType.ABSOLUTE_TIME,
|
|
115
|
+
value: '2024-01-01T00:00:00Z',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
const stringifiedState = JSON.stringify({
|
|
121
|
+
snapInVersionId: 'test_snap_in_version_id',
|
|
122
|
+
});
|
|
123
|
+
fetchStateSpy.mockResolvedValue({ state: stringifiedState });
|
|
124
|
+
// Act
|
|
125
|
+
await (0, state_1.createAdapterState)({
|
|
126
|
+
event,
|
|
127
|
+
initialState: {},
|
|
128
|
+
initialDomainMapping: {},
|
|
129
|
+
});
|
|
130
|
+
// Assert: process.exit should NOT have been called
|
|
131
|
+
expect(processExitSpy).not.toHaveBeenCalled();
|
|
132
|
+
});
|
|
133
|
+
it('should not exit when extract_from is UNBOUNDED and extract_to is a real timestamp', async () => {
|
|
134
|
+
// Arrange: UNBOUNDED start (epoch) with a real ABSOLUTE end timestamp
|
|
135
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
136
|
+
payload: {
|
|
137
|
+
event_type: extraction_1.EventType.StartExtractingMetadata,
|
|
138
|
+
event_context: {
|
|
139
|
+
extraction_start_time: {
|
|
140
|
+
type: extraction_1.TimeValueType.UNBOUNDED,
|
|
141
|
+
},
|
|
142
|
+
extraction_end_time: {
|
|
143
|
+
type: extraction_1.TimeValueType.ABSOLUTE_TIME,
|
|
144
|
+
value: '2025-06-01T00:00:00Z',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
const stringifiedState = JSON.stringify({
|
|
150
|
+
snapInVersionId: 'test_snap_in_version_id',
|
|
151
|
+
});
|
|
152
|
+
fetchStateSpy.mockResolvedValue({ state: stringifiedState });
|
|
153
|
+
// Act
|
|
154
|
+
await (0, state_1.createAdapterState)({
|
|
155
|
+
event,
|
|
156
|
+
initialState: {},
|
|
157
|
+
initialDomainMapping: {},
|
|
158
|
+
});
|
|
159
|
+
// Assert: process.exit should NOT have been called
|
|
160
|
+
expect(processExitSpy).not.toHaveBeenCalled();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.pending-boundaries.test.d.ts","sourceRoot":"","sources":["../../src/state/state.pending-boundaries.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jest_setup_1 = require("../tests/jest.setup");
|
|
4
|
+
const test_utils_1 = require("../common/test-utils");
|
|
5
|
+
const extraction_1 = require("../types/extraction");
|
|
6
|
+
const state_1 = require("./state");
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
8
|
+
const FIXED_NOW = '2026-03-26T10:00:00.000Z';
|
|
9
|
+
describe('State — pending extraction boundaries', () => {
|
|
10
|
+
let postStateSpy;
|
|
11
|
+
let fetchStateSpy;
|
|
12
|
+
let installInitialDomainMappingSpy;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
jest.restoreAllMocks();
|
|
16
|
+
postStateSpy = jest.spyOn(state_1.State.prototype, 'postState');
|
|
17
|
+
fetchStateSpy = jest.spyOn(state_1.State.prototype, 'fetchState');
|
|
18
|
+
installInitialDomainMappingSpy = jest.spyOn(require('../common/install-initial-domain-mapping'), 'installInitialDomainMapping');
|
|
19
|
+
jest.spyOn(process, 'exit').mockImplementation(() => {
|
|
20
|
+
throw new Error('process.exit called');
|
|
21
|
+
});
|
|
22
|
+
jest.useFakeTimers();
|
|
23
|
+
jest.setSystemTime(new Date(FIXED_NOW));
|
|
24
|
+
});
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
jest.useRealTimers();
|
|
27
|
+
});
|
|
28
|
+
it('should store resolved values in pendingWorkersOldest/pendingWorkersNewest on StartExtractingMetadata', async () => {
|
|
29
|
+
// Arrange
|
|
30
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
31
|
+
context: {
|
|
32
|
+
snap_in_version_id: '',
|
|
33
|
+
},
|
|
34
|
+
payload: {
|
|
35
|
+
event_type: extraction_1.EventType.StartExtractingMetadata,
|
|
36
|
+
event_context: {
|
|
37
|
+
extraction_start_time: {
|
|
38
|
+
type: extraction_1.TimeValueType.UNBOUNDED,
|
|
39
|
+
},
|
|
40
|
+
extraction_end_time: {
|
|
41
|
+
type: extraction_1.TimeValueType.CURRENT_TIME,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
fetchStateSpy.mockRejectedValue({
|
|
47
|
+
isAxiosError: true,
|
|
48
|
+
response: { status: 404 },
|
|
49
|
+
});
|
|
50
|
+
installInitialDomainMappingSpy.mockResolvedValue({ success: true });
|
|
51
|
+
postStateSpy.mockResolvedValue({ success: true });
|
|
52
|
+
// Act
|
|
53
|
+
const state = await (0, state_1.createAdapterState)({
|
|
54
|
+
event,
|
|
55
|
+
initialState: {},
|
|
56
|
+
initialDomainMapping: {},
|
|
57
|
+
});
|
|
58
|
+
// Assert
|
|
59
|
+
expect(state.state.pendingWorkersOldest).toBe('1970-01-01T00:00:00.000Z');
|
|
60
|
+
expect(state.state.pendingWorkersNewest).toBe(FIXED_NOW);
|
|
61
|
+
expect(event.payload.event_context.extract_from).toBe('1970-01-01T00:00:00.000Z');
|
|
62
|
+
expect(event.payload.event_context.extract_to).toBe(FIXED_NOW);
|
|
63
|
+
});
|
|
64
|
+
it('should overwrite pending values on a retry (new StartExtractingMetadata after failure)', async () => {
|
|
65
|
+
// Arrange: state has stale pending values from a previous failed attempt
|
|
66
|
+
const staleOldest = '2026-03-25T08:00:00.000Z';
|
|
67
|
+
const staleNewest = '2026-03-25T09:00:00.000Z';
|
|
68
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
69
|
+
context: {
|
|
70
|
+
snap_in_version_id: 'test_snap_in_version_id',
|
|
71
|
+
},
|
|
72
|
+
payload: {
|
|
73
|
+
event_type: extraction_1.EventType.StartExtractingMetadata,
|
|
74
|
+
event_context: {
|
|
75
|
+
extraction_start_time: {
|
|
76
|
+
type: extraction_1.TimeValueType.UNBOUNDED,
|
|
77
|
+
},
|
|
78
|
+
extraction_end_time: {
|
|
79
|
+
type: extraction_1.TimeValueType.CURRENT_TIME,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
const stringifiedState = JSON.stringify({
|
|
85
|
+
snapInVersionId: 'test_snap_in_version_id',
|
|
86
|
+
pendingWorkersOldest: staleOldest,
|
|
87
|
+
pendingWorkersNewest: staleNewest,
|
|
88
|
+
});
|
|
89
|
+
fetchStateSpy.mockResolvedValue({ state: stringifiedState });
|
|
90
|
+
// Act
|
|
91
|
+
const state = await (0, state_1.createAdapterState)({
|
|
92
|
+
event,
|
|
93
|
+
initialState: {},
|
|
94
|
+
initialDomainMapping: {},
|
|
95
|
+
});
|
|
96
|
+
// Assert: pending values are overwritten with fresh resolution, not stale values
|
|
97
|
+
expect(state.state.pendingWorkersOldest).toBe('1970-01-01T00:00:00.000Z');
|
|
98
|
+
expect(state.state.pendingWorkersNewest).toBe(FIXED_NOW);
|
|
99
|
+
expect(state.state.pendingWorkersNewest).not.toBe(staleNewest);
|
|
100
|
+
});
|
|
101
|
+
it('should reuse pending values from state on ContinueExtractingData instead of re-resolving', async () => {
|
|
102
|
+
// Arrange: state has pending values from a prior StartExtractingMetadata phase
|
|
103
|
+
const pendingOldest = '1970-01-01T00:00:00.000Z';
|
|
104
|
+
const pendingNewest = '2026-03-26T08:00:00.000Z'; // Earlier than FIXED_NOW
|
|
105
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
106
|
+
context: {
|
|
107
|
+
snap_in_version_id: 'test_snap_in_version_id',
|
|
108
|
+
},
|
|
109
|
+
payload: {
|
|
110
|
+
event_type: extraction_1.EventType.ContinueExtractingData,
|
|
111
|
+
event_context: {
|
|
112
|
+
// Platform still sends TimeValue objects, but they should be ignored
|
|
113
|
+
extraction_start_time: {
|
|
114
|
+
type: extraction_1.TimeValueType.CURRENT_TIME,
|
|
115
|
+
},
|
|
116
|
+
extraction_end_time: {
|
|
117
|
+
type: extraction_1.TimeValueType.CURRENT_TIME,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
const stringifiedState = JSON.stringify({
|
|
123
|
+
snapInVersionId: 'test_snap_in_version_id',
|
|
124
|
+
pendingWorkersOldest: pendingOldest,
|
|
125
|
+
pendingWorkersNewest: pendingNewest,
|
|
126
|
+
});
|
|
127
|
+
fetchStateSpy.mockResolvedValue({ state: stringifiedState });
|
|
128
|
+
// Act
|
|
129
|
+
const state = await (0, state_1.createAdapterState)({
|
|
130
|
+
event,
|
|
131
|
+
initialState: {},
|
|
132
|
+
initialDomainMapping: {},
|
|
133
|
+
});
|
|
134
|
+
// Assert: uses cached pending values, NOT new Date() resolution
|
|
135
|
+
expect(event.payload.event_context.extract_from).toBe(pendingOldest);
|
|
136
|
+
expect(event.payload.event_context.extract_to).toBe(pendingNewest);
|
|
137
|
+
// Pending values in state remain unchanged
|
|
138
|
+
expect(state.state.pendingWorkersOldest).toBe(pendingOldest);
|
|
139
|
+
expect(state.state.pendingWorkersNewest).toBe(pendingNewest);
|
|
140
|
+
});
|
|
141
|
+
it('should not set extract_from/extract_to on ContinueExtractingData if no pending values exist', async () => {
|
|
142
|
+
// Arrange: state has no pending values (e.g. old state from before this feature)
|
|
143
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
144
|
+
context: {
|
|
145
|
+
snap_in_version_id: 'test_snap_in_version_id',
|
|
146
|
+
},
|
|
147
|
+
payload: { event_type: extraction_1.EventType.ContinueExtractingData },
|
|
148
|
+
});
|
|
149
|
+
const stringifiedState = JSON.stringify({
|
|
150
|
+
snapInVersionId: 'test_snap_in_version_id',
|
|
151
|
+
});
|
|
152
|
+
fetchStateSpy.mockResolvedValue({ state: stringifiedState });
|
|
153
|
+
// Act
|
|
154
|
+
await (0, state_1.createAdapterState)({
|
|
155
|
+
event,
|
|
156
|
+
initialState: {},
|
|
157
|
+
initialDomainMapping: {},
|
|
158
|
+
});
|
|
159
|
+
// Assert: no extraction timestamps are set
|
|
160
|
+
expect(event.payload.event_context.extract_from).toBeUndefined();
|
|
161
|
+
expect(event.payload.event_context.extract_to).toBeUndefined();
|
|
162
|
+
});
|
|
163
|
+
it('should reuse pending values on StartExtractingAttachments', async () => {
|
|
164
|
+
// Arrange: state has pending values from the StartExtractingMetadata phase
|
|
165
|
+
const pendingOldest = '1970-01-01T00:00:00.000Z';
|
|
166
|
+
const pendingNewest = '2026-03-26T08:00:00.000Z';
|
|
167
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
168
|
+
context: {
|
|
169
|
+
snap_in_version_id: 'test_snap_in_version_id',
|
|
170
|
+
},
|
|
171
|
+
payload: { event_type: extraction_1.EventType.StartExtractingAttachments },
|
|
172
|
+
});
|
|
173
|
+
const stringifiedState = JSON.stringify({
|
|
174
|
+
snapInVersionId: 'test_snap_in_version_id',
|
|
175
|
+
pendingWorkersOldest: pendingOldest,
|
|
176
|
+
pendingWorkersNewest: pendingNewest,
|
|
177
|
+
});
|
|
178
|
+
fetchStateSpy.mockResolvedValue({ state: stringifiedState });
|
|
179
|
+
// Act
|
|
180
|
+
await (0, state_1.createAdapterState)({
|
|
181
|
+
event,
|
|
182
|
+
initialState: {},
|
|
183
|
+
initialDomainMapping: {},
|
|
184
|
+
});
|
|
185
|
+
// Assert: pending values are reused
|
|
186
|
+
expect(event.payload.event_context.extract_from).toBe(pendingOldest);
|
|
187
|
+
expect(event.payload.event_context.extract_to).toBe(pendingNewest);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.post-state.test.d.ts","sourceRoot":"","sources":["../../src/state/state.post-state.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jest_setup_1 = require("../tests/jest.setup");
|
|
4
|
+
const test_utils_1 = require("../common/test-utils");
|
|
5
|
+
const extraction_1 = require("../types/extraction");
|
|
6
|
+
const state_1 = require("./state");
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
8
|
+
describe('State.postState', () => {
|
|
9
|
+
let postStateSpy;
|
|
10
|
+
let fetchStateSpy;
|
|
11
|
+
let processExitSpy;
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.clearAllMocks();
|
|
14
|
+
jest.restoreAllMocks();
|
|
15
|
+
postStateSpy = jest.spyOn(state_1.State.prototype, 'postState');
|
|
16
|
+
fetchStateSpy = jest.spyOn(state_1.State.prototype, 'fetchState');
|
|
17
|
+
processExitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
|
|
18
|
+
throw new Error('process.exit called');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
it('should POST the stringified state to the update endpoint', async () => {
|
|
22
|
+
// Arrange
|
|
23
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
24
|
+
context: {
|
|
25
|
+
snap_in_version_id: '1.0.0',
|
|
26
|
+
secrets: { service_account_token: 'test_token' },
|
|
27
|
+
},
|
|
28
|
+
payload: { event_type: extraction_1.EventType.StartExtractingData },
|
|
29
|
+
});
|
|
30
|
+
const stateToPost = { snapInVersionId: '1.0.0', foo: 'bar' };
|
|
31
|
+
fetchStateSpy.mockResolvedValue({
|
|
32
|
+
state: JSON.stringify({ snapInVersionId: '1.0.0' }),
|
|
33
|
+
});
|
|
34
|
+
postStateSpy.mockRestore();
|
|
35
|
+
const adapterState = await (0, state_1.createAdapterState)({
|
|
36
|
+
event,
|
|
37
|
+
initialState: {},
|
|
38
|
+
initialDomainMapping: {},
|
|
39
|
+
});
|
|
40
|
+
// Act
|
|
41
|
+
await adapterState.postState(stateToPost);
|
|
42
|
+
// Assert: the mock server records all incoming requests — inspect what was sent
|
|
43
|
+
const requests = jest_setup_1.mockServer.getRequests('POST', '/worker_data_url.update');
|
|
44
|
+
expect(requests).toHaveLength(1);
|
|
45
|
+
const body = requests[0].body;
|
|
46
|
+
// Body must contain the stringified state, preserving the original fields
|
|
47
|
+
expect(typeof body.state).toBe('string');
|
|
48
|
+
const parsed = JSON.parse(body.state);
|
|
49
|
+
expect(parsed.foo).toBe('bar');
|
|
50
|
+
expect(parsed.snapInVersionId).toBe('1.0.0');
|
|
51
|
+
});
|
|
52
|
+
it('should exit(1) when postState HTTP request fails', async () => {
|
|
53
|
+
// Arrange
|
|
54
|
+
const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
|
|
55
|
+
context: { snap_in_version_id: '1.0.0' },
|
|
56
|
+
payload: { event_type: extraction_1.EventType.StartExtractingData },
|
|
57
|
+
});
|
|
58
|
+
fetchStateSpy.mockResolvedValue({
|
|
59
|
+
state: JSON.stringify({ snapInVersionId: '1.0.0' }),
|
|
60
|
+
});
|
|
61
|
+
postStateSpy.mockRestore();
|
|
62
|
+
const adapterState = await (0, state_1.createAdapterState)({
|
|
63
|
+
event,
|
|
64
|
+
initialState: {},
|
|
65
|
+
initialDomainMapping: {},
|
|
66
|
+
});
|
|
67
|
+
// Mock axiosClient.post directly to bypass the retry backoff
|
|
68
|
+
const axiosClientModule = require('../http/axios-client-internal');
|
|
69
|
+
const axiosPostSpy = jest
|
|
70
|
+
.spyOn(axiosClientModule.axiosClient, 'post')
|
|
71
|
+
.mockRejectedValue(new Error('network error'));
|
|
72
|
+
// Act & Assert
|
|
73
|
+
await expect(adapterState.postState()).rejects.toThrow('process.exit called');
|
|
74
|
+
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
75
|
+
axiosPostSpy.mockRestore();
|
|
76
|
+
});
|
|
77
|
+
});
|