@devrev/ts-adaas 1.19.4 → 1.19.6-beta.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 (50) hide show
  1. package/dist/attachments-streaming/attachments-streaming-pool.test.js +3 -6
  2. package/dist/common/event-type-translation.test.d.ts +2 -0
  3. package/dist/common/event-type-translation.test.d.ts.map +1 -0
  4. package/dist/common/event-type-translation.test.js +175 -0
  5. package/dist/common/time-value-resolver.test.js +0 -1
  6. package/dist/deprecated/uploader/index.d.ts +3 -1
  7. package/dist/deprecated/uploader/index.d.ts.map +1 -1
  8. package/dist/deprecated/uploader/index.js +29 -22
  9. package/dist/multithreading/create-worker.test.js +34 -16
  10. package/dist/multithreading/process-task.test.d.ts +2 -0
  11. package/dist/multithreading/process-task.test.d.ts.map +1 -0
  12. package/dist/multithreading/process-task.test.js +166 -0
  13. package/dist/multithreading/spawn/spawn.test.d.ts +2 -0
  14. package/dist/multithreading/spawn/spawn.test.d.ts.map +1 -0
  15. package/dist/multithreading/spawn/spawn.test.js +223 -0
  16. package/dist/multithreading/worker-adapter/worker-adapter.emit.test.d.ts +2 -0
  17. package/dist/multithreading/worker-adapter/worker-adapter.emit.test.d.ts.map +1 -0
  18. package/dist/multithreading/worker-adapter/worker-adapter.emit.test.js +415 -0
  19. package/dist/multithreading/worker-adapter/worker-adapter.extraction.test.d.ts +2 -0
  20. package/dist/multithreading/worker-adapter/worker-adapter.extraction.test.d.ts.map +1 -0
  21. package/dist/multithreading/worker-adapter/worker-adapter.extraction.test.js +801 -0
  22. package/dist/multithreading/worker-adapter/worker-adapter.loading.test.d.ts +2 -0
  23. package/dist/multithreading/worker-adapter/worker-adapter.loading.test.d.ts.map +1 -0
  24. package/dist/multithreading/worker-adapter/worker-adapter.loading.test.js +598 -0
  25. package/dist/multithreading/worker-adapter/worker-adapter.serialization.test.d.ts +2 -0
  26. package/dist/multithreading/worker-adapter/worker-adapter.serialization.test.d.ts.map +1 -0
  27. package/dist/multithreading/worker-adapter/worker-adapter.serialization.test.js +71 -0
  28. package/dist/repo/repo.test.js +41 -0
  29. package/dist/state/state.extract-window.test.d.ts +2 -0
  30. package/dist/state/state.extract-window.test.d.ts.map +1 -0
  31. package/dist/state/state.extract-window.test.js +163 -0
  32. package/dist/state/state.pending-boundaries.test.d.ts +2 -0
  33. package/dist/state/state.pending-boundaries.test.d.ts.map +1 -0
  34. package/dist/state/state.pending-boundaries.test.js +189 -0
  35. package/dist/state/state.post-state.test.d.ts +2 -0
  36. package/dist/state/state.post-state.test.d.ts.map +1 -0
  37. package/dist/state/state.post-state.test.js +77 -0
  38. package/dist/state/state.test.js +23 -506
  39. package/dist/state/state.time-value-resolution.test.d.ts +2 -0
  40. package/dist/state/state.time-value-resolution.test.d.ts.map +1 -0
  41. package/dist/state/state.time-value-resolution.test.js +175 -0
  42. package/dist/types/extraction.d.ts +20 -1
  43. package/dist/types/extraction.d.ts.map +1 -1
  44. package/dist/types/extraction.test.js +57 -21
  45. package/dist/uploader/uploader.helpers.test.js +0 -11
  46. package/dist/uploader/uploader.test.js +0 -9
  47. package/package.json +7 -7
  48. package/dist/multithreading/worker-adapter/worker-adapter.test.d.ts +0 -2
  49. package/dist/multithreading/worker-adapter/worker-adapter.test.d.ts.map +0 -1
  50. package/dist/multithreading/worker-adapter/worker-adapter.test.js +0 -1243
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=state.extract-window.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=state.pending-boundaries.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=state.post-state.test.d.ts.map
@@ -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
+ });