@defra-fish/pocl-job 1.61.0-rc.11 → 1.61.0-rc.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra-fish/pocl-job",
3
- "version": "1.61.0-rc.11",
3
+ "version": "1.61.0-rc.13",
4
4
  "description": "Post Office Counter Licence sales processor",
5
5
  "type": "module",
6
6
  "engines": {
@@ -35,8 +35,8 @@
35
35
  "test": "echo \"Error: run tests from root\" && exit 1"
36
36
  },
37
37
  "dependencies": {
38
- "@defra-fish/business-rules-lib": "1.61.0-rc.11",
39
- "@defra-fish/connectors-lib": "1.61.0-rc.11",
38
+ "@defra-fish/business-rules-lib": "1.61.0-rc.13",
39
+ "@defra-fish/connectors-lib": "1.61.0-rc.13",
40
40
  "commander": "^7.2.0",
41
41
  "debug": "^4.3.3",
42
42
  "filesize": "^6.4.0",
@@ -45,5 +45,5 @@
45
45
  "moment-timezone": "^0.5.34",
46
46
  "sax-stream": "^1.3.0"
47
47
  },
48
- "gitHead": "ca0af7012d18aa9414afce422db4af34ed59dacd"
48
+ "gitHead": "fdd397ab2b9e6fa2346d765f4ada01ce5475dd10"
49
49
  }
@@ -1,12 +1,7 @@
1
- import AwsMock from 'aws-sdk'
2
1
  import config from '../config.js'
3
2
 
4
3
  describe('config', () => {
5
4
  beforeAll(async () => {
6
- AwsMock.SecretsManager.__setResponse('getSecretValue', {
7
- SecretString: 'test-ssh-key'
8
- })
9
-
10
5
  process.env.POCL_FILE_STAGING_TABLE = 'test-file-staging-table'
11
6
  process.env.POCL_RECORD_STAGING_TABLE = 'test-record-staging-table'
12
7
  process.env.POCL_STAGING_TTL = 1234
@@ -1,5 +1,6 @@
1
1
  import * as db from '../db.js'
2
- import AwsMock from 'aws-sdk'
2
+ import { AWS } from '@defra-fish/connectors-lib'
3
+ const { docClient } = AWS.mock.results[0].value
3
4
 
4
5
  jest.mock('../../config.js', () => ({
5
6
  db: {
@@ -9,16 +10,28 @@ jest.mock('../../config.js', () => ({
9
10
  }
10
11
  }))
11
12
 
13
+ jest.mock('@defra-fish/connectors-lib', () => ({
14
+ AWS: jest.fn(() => ({
15
+ docClient: {
16
+ batchWriteAllPromise: jest.fn(),
17
+ get: jest.fn(() => ({ Item: undefined })),
18
+ scanAllPromise: jest.fn(),
19
+ update: jest.fn(),
20
+ createUpdateExpression: jest.fn(() => ({})),
21
+ queryAllPromise: jest.fn()
22
+ }
23
+ }))
24
+ }))
25
+
12
26
  describe('database operations', () => {
13
27
  const TEST_FILENAME = 'testfile.xml'
14
28
  beforeEach(() => {
15
29
  jest.clearAllMocks()
16
- AwsMock.__resetAll()
17
30
  })
18
31
  describe('getFileRecord', () => {
19
32
  it('calls a get operation on dynamodb', async () => {
20
33
  await db.getFileRecord(TEST_FILENAME)
21
- expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.get).toHaveBeenCalledWith({
34
+ expect(docClient.get).toHaveBeenCalledWith({
22
35
  TableName: 'TestFileTable',
23
36
  Key: { filename: TEST_FILENAME },
24
37
  ConsistentRead: true
@@ -29,7 +42,7 @@ describe('database operations', () => {
29
42
  describe('getFileRecords', () => {
30
43
  it('retrieves all records for the given file if no stages are provided', async () => {
31
44
  await db.getFileRecords()
32
- expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.scan).toHaveBeenCalledWith(
45
+ expect(docClient.scanAllPromise).toHaveBeenCalledWith(
33
46
  expect.objectContaining({
34
47
  TableName: 'TestFileTable',
35
48
  ConsistentRead: true
@@ -39,7 +52,7 @@ describe('database operations', () => {
39
52
 
40
53
  it('retrieves all records a given set of stages', async () => {
41
54
  await db.getFileRecords('STAGE 1', 'STAGE 2')
42
- expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.scan).toHaveBeenCalledWith(
55
+ expect(docClient.scanAllPromise).toHaveBeenCalledWith(
43
56
  expect.objectContaining({
44
57
  TableName: 'TestFileTable',
45
58
  FilterExpression: 'stage IN (:stage0,:stage1)',
@@ -52,8 +65,21 @@ describe('database operations', () => {
52
65
 
53
66
  describe('updateFileStagingTable', () => {
54
67
  it('calls update on dynamodb including all necessary parameters', async () => {
68
+ docClient.createUpdateExpression.mockReturnValueOnce({
69
+ UpdateExpression: 'SET #expires = :expires,#param1 = :param1,#param2 = :param2',
70
+ ExpressionAttributeNames: {
71
+ '#expires': 'expires',
72
+ '#param1': 'param1',
73
+ '#param2': 'param2'
74
+ },
75
+ ExpressionAttributeValues: {
76
+ ':expires': expect.any(Number),
77
+ ':param1': 'test1',
78
+ ':param2': 'test2'
79
+ }
80
+ })
55
81
  await db.updateFileStagingTable({ filename: TEST_FILENAME, param1: 'test1', param2: 'test2' })
56
- expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.update).toHaveBeenCalledWith(
82
+ expect(docClient.update).toHaveBeenCalledWith(
57
83
  expect.objectContaining({
58
84
  TableName: 'TestFileTable',
59
85
  Key: { filename: TEST_FILENAME },
@@ -77,7 +103,7 @@ describe('database operations', () => {
77
103
  it('calls batchWrite on dynamodb including all necessary parameters', async () => {
78
104
  const records = [{ id: 'test1' }, { id: 'test2' }]
79
105
  await db.updateRecordStagingTable(TEST_FILENAME, records)
80
- expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.batchWrite).toHaveBeenCalledWith(
106
+ expect(docClient.batchWriteAllPromise).toHaveBeenCalledWith(
81
107
  expect.objectContaining({
82
108
  RequestItems: {
83
109
  TestRecordTable: [
@@ -99,14 +125,14 @@ describe('database operations', () => {
99
125
 
100
126
  it('is a no-op if records is empty', async () => {
101
127
  await db.updateRecordStagingTable(TEST_FILENAME, [])
102
- expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.batchWrite).not.toHaveBeenCalled()
128
+ expect(docClient.batchWriteAllPromise).not.toHaveBeenCalled()
103
129
  })
104
130
  })
105
131
 
106
132
  describe('getProcessedRecords', () => {
107
133
  it('retrieves all records for the given file if no stages are provided', async () => {
108
134
  await db.getProcessedRecords(TEST_FILENAME)
109
- expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.query).toHaveBeenCalledWith(
135
+ expect(docClient.queryAllPromise).toHaveBeenCalledWith(
110
136
  expect.objectContaining({
111
137
  TableName: 'TestRecordTable',
112
138
  KeyConditionExpression: 'filename = :filename',
@@ -118,7 +144,7 @@ describe('database operations', () => {
118
144
 
119
145
  it('retrieves all records a given set of stages', async () => {
120
146
  await db.getProcessedRecords(TEST_FILENAME, 'STAGE 1', 'STAGE 2')
121
- expect(AwsMock.DynamoDB.DocumentClient.mockedMethods.query).toHaveBeenCalledWith(
147
+ expect(docClient.queryAllPromise).toHaveBeenCalledWith(
122
148
  expect.objectContaining({
123
149
  TableName: 'TestRecordTable',
124
150
  KeyConditionExpression: 'filename = :filename',
@@ -2,19 +2,32 @@ import { refreshS3Metadata } from '../s3'
2
2
  import moment from 'moment'
3
3
  import { updateFileStagingTable } from '../../io/db.js'
4
4
  import { DYNAMICS_IMPORT_STAGE, FILE_STAGE, POST_OFFICE_DATASOURCE } from '../../staging/constants.js'
5
- import { salesApi } from '@defra-fish/connectors-lib'
5
+ import { salesApi, AWS } from '@defra-fish/connectors-lib'
6
6
  import fs from 'fs'
7
- import AwsMock from 'aws-sdk'
7
+ const { s3 } = AWS.mock.results[0].value
8
8
 
9
- jest.mock('fs')
10
9
  jest.mock('md5-file')
11
10
  jest.mock('../../io/db.js')
12
11
  jest.mock('../../io/file.js')
13
12
 
14
13
  jest.mock('@defra-fish/connectors-lib', () => {
15
14
  const actual = jest.requireActual('@defra-fish/connectors-lib')
15
+ const AWS = jest.fn(() => ({
16
+ docClient: {
17
+ update: jest.fn(),
18
+ createUpdateExpression: jest.fn(() => ({}))
19
+ },
20
+ s3: {
21
+ listObjectsV2: jest.fn(() => ({
22
+ Contents: []
23
+ })),
24
+ getObject: jest.fn(() => ({
25
+ createReadStream: jest.fn()
26
+ }))
27
+ }
28
+ }))
16
29
  return {
17
- AWS: actual.AWS,
30
+ AWS,
18
31
  salesApi: {
19
32
  ...Object.keys(actual.salesApi).reduce((acc, k) => ({ ...acc, [k]: jest.fn(async () => {}) }), {})
20
33
  }
@@ -29,19 +42,19 @@ jest.mock('../../config.js', () => ({
29
42
  bucket: 'testbucket'
30
43
  }
31
44
  }))
45
+
32
46
  describe('s3 operations', () => {
33
47
  beforeEach(() => {
34
48
  jest.clearAllMocks()
35
- AwsMock.__resetAll()
36
49
  })
37
50
 
38
51
  describe('refreshS3Metadata', () => {
39
- it('gets a list of files from S3', async () => {
52
+ describe('gets a list of files from S3', () => {
40
53
  const s3Key1 = `${moment().format('YYYY-MM-DD')}/test1.xml`
41
54
  const s3Key2 = `${moment().format('YYYY-MM-DD')}/test2.xml`
42
55
 
43
- AwsMock.S3.mockedMethods.listObjectsV2.mockReturnValueOnce({
44
- promise: () => ({
56
+ beforeEach(async () => {
57
+ s3.listObjectsV2.mockReturnValueOnce({
45
58
  IsTruncated: false,
46
59
  Contents: [
47
60
  {
@@ -58,52 +71,66 @@ describe('s3 operations', () => {
58
71
  }
59
72
  ]
60
73
  })
61
- })
62
74
 
63
- await refreshS3Metadata()
75
+ await refreshS3Metadata()
76
+ })
64
77
 
65
- expect(AwsMock.S3.mockedMethods.listObjectsV2).toHaveBeenNthCalledWith(1, {
66
- Bucket: 'testbucket',
67
- ContinuationToken: undefined
78
+ it('calls listObjectsV2, with bucket name and no continuation token', () => {
79
+ expect(s3.listObjectsV2).toHaveBeenNthCalledWith(1, {
80
+ Bucket: 'testbucket',
81
+ ContinuationToken: undefined
82
+ })
68
83
  })
69
- expect(updateFileStagingTable).toHaveBeenNthCalledWith(1, {
70
- filename: 'test1.xml',
71
- md5: 'example-md5',
72
- fileSize: '1 KB',
73
- stage: FILE_STAGE.Pending,
74
- s3Key: s3Key1
84
+
85
+ it('calls updateFileStagingTable first with initial test file', () => {
86
+ expect(updateFileStagingTable).toHaveBeenNthCalledWith(1, {
87
+ filename: 'test1.xml',
88
+ md5: 'example-md5',
89
+ fileSize: '1 KB',
90
+ stage: FILE_STAGE.Pending,
91
+ s3Key: s3Key1
92
+ })
75
93
  })
76
- expect(updateFileStagingTable).toHaveBeenNthCalledWith(2, {
77
- filename: 'test2.xml',
78
- md5: 'example-md5',
79
- fileSize: '2 KB',
80
- stage: FILE_STAGE.Pending,
81
- s3Key: s3Key2
94
+
95
+ it('calls updateFileStagingTable a second time with second test file', () => {
96
+ expect(updateFileStagingTable).toHaveBeenNthCalledWith(2, {
97
+ filename: 'test2.xml',
98
+ md5: 'example-md5',
99
+ fileSize: '2 KB',
100
+ stage: FILE_STAGE.Pending,
101
+ s3Key: s3Key2
102
+ })
82
103
  })
83
- expect(salesApi.upsertTransactionFile).toHaveBeenNthCalledWith(1, 'test1.xml', {
84
- status: DYNAMICS_IMPORT_STAGE.Pending,
85
- dataSource: POST_OFFICE_DATASOURCE,
86
- fileSize: '1 KB',
87
- receiptTimestamp: expect.any(String),
88
- salesDate: expect.any(String),
89
- notes: 'Retrieved from the remote server and awaiting processing'
104
+
105
+ it('calls upsertTransactionFile for first test file', () => {
106
+ expect(salesApi.upsertTransactionFile).toHaveBeenNthCalledWith(1, 'test1.xml', {
107
+ status: DYNAMICS_IMPORT_STAGE.Pending,
108
+ dataSource: POST_OFFICE_DATASOURCE,
109
+ fileSize: '1 KB',
110
+ receiptTimestamp: expect.any(String),
111
+ salesDate: expect.any(String),
112
+ notes: 'Retrieved from the remote server and awaiting processing'
113
+ })
90
114
  })
91
- expect(salesApi.upsertTransactionFile).toHaveBeenNthCalledWith(2, 'test2.xml', {
92
- status: DYNAMICS_IMPORT_STAGE.Pending,
93
- dataSource: POST_OFFICE_DATASOURCE,
94
- fileSize: '2 KB',
95
- receiptTimestamp: expect.any(String),
96
- salesDate: expect.any(String),
97
- notes: 'Retrieved from the remote server and awaiting processing'
115
+
116
+ it('calls upsertTransactionFile for second test file', () => {
117
+ expect(salesApi.upsertTransactionFile).toHaveBeenNthCalledWith(2, 'test2.xml', {
118
+ status: DYNAMICS_IMPORT_STAGE.Pending,
119
+ dataSource: POST_OFFICE_DATASOURCE,
120
+ fileSize: '2 KB',
121
+ receiptTimestamp: expect.any(String),
122
+ salesDate: expect.any(String),
123
+ notes: 'Retrieved from the remote server and awaiting processing'
124
+ })
98
125
  })
99
126
  })
100
127
 
101
- it('gets a truncated list of files from S3', async () => {
128
+ describe('gets a truncated list of files from S3', () => {
102
129
  const s3Key1 = `${moment().format('YYYY-MM-DD')}/test1.xml`
103
130
 
104
- AwsMock.S3.mockedMethods.listObjectsV2
105
- .mockReturnValue({
106
- promise: () => ({
131
+ beforeEach(async () => {
132
+ s3.listObjectsV2
133
+ .mockReturnValue({
107
134
  IsTruncated: false,
108
135
  Contents: [
109
136
  {
@@ -114,9 +141,7 @@ describe('s3 operations', () => {
114
141
  }
115
142
  ]
116
143
  })
117
- })
118
- .mockReturnValueOnce({
119
- promise: () => ({
144
+ .mockReturnValueOnce({
120
145
  IsTruncated: true,
121
146
  NextContinuationToken: 'token',
122
147
  Contents: [
@@ -128,53 +153,72 @@ describe('s3 operations', () => {
128
153
  }
129
154
  ]
130
155
  })
131
- })
132
156
 
133
- await refreshS3Metadata()
157
+ await refreshS3Metadata()
158
+ })
134
159
 
135
- expect(AwsMock.S3.mockedMethods.listObjectsV2).toHaveBeenNthCalledWith(1, {
136
- Bucket: 'testbucket',
137
- ContinuationToken: undefined
160
+ it('calls listObjectsV2 a first time with bucket name and no continuation token', () => {
161
+ expect(s3.listObjectsV2).toHaveBeenNthCalledWith(1, {
162
+ Bucket: 'testbucket',
163
+ ContinuationToken: undefined
164
+ })
138
165
  })
139
- expect(AwsMock.S3.mockedMethods.listObjectsV2).toHaveBeenNthCalledWith(2, {
140
- Bucket: 'testbucket',
141
- ContinuationToken: 'token'
166
+
167
+ it('calls listObjectsV2 a second time with bucket name and continuation token', () => {
168
+ expect(s3.listObjectsV2).toHaveBeenNthCalledWith(2, {
169
+ Bucket: 'testbucket',
170
+ ContinuationToken: 'token'
171
+ })
142
172
  })
143
- expect(updateFileStagingTable).toHaveBeenNthCalledWith(1, {
144
- filename: 'test1.xml',
145
- md5: 'example-md5',
146
- fileSize: '1 KB',
147
- stage: FILE_STAGE.Pending,
148
- s3Key: s3Key1
173
+
174
+ it('updates file staging table with first test file', () => {
175
+ expect(updateFileStagingTable).toHaveBeenNthCalledWith(1, {
176
+ filename: 'test1.xml',
177
+ md5: 'example-md5',
178
+ fileSize: '1 KB',
179
+ stage: FILE_STAGE.Pending,
180
+ s3Key: s3Key1
181
+ })
149
182
  })
150
- expect(salesApi.upsertTransactionFile).toHaveBeenNthCalledWith(1, 'test1.xml', {
151
- status: DYNAMICS_IMPORT_STAGE.Pending,
152
- dataSource: POST_OFFICE_DATASOURCE,
153
- fileSize: '1 KB',
154
- receiptTimestamp: expect.any(String),
155
- salesDate: expect.any(String),
156
- notes: 'Retrieved from the remote server and awaiting processing'
183
+
184
+ it('updates file staging table with second test file', () => {
185
+ expect(updateFileStagingTable).toHaveBeenNthCalledWith(2, {
186
+ filename: 'test1.xml',
187
+ md5: 'example-md5',
188
+ fileSize: '1 KB',
189
+ stage: FILE_STAGE.Pending,
190
+ s3Key: s3Key1
191
+ })
192
+ })
193
+
194
+ it('upserts sales api with transaction file details', () => {
195
+ expect(salesApi.upsertTransactionFile).toHaveBeenNthCalledWith(1, 'test1.xml', {
196
+ status: DYNAMICS_IMPORT_STAGE.Pending,
197
+ dataSource: POST_OFFICE_DATASOURCE,
198
+ fileSize: '1 KB',
199
+ receiptTimestamp: expect.any(String),
200
+ salesDate: expect.any(String),
201
+ notes: 'Retrieved from the remote server and awaiting processing'
202
+ })
157
203
  })
158
204
  })
159
205
 
160
206
  it('skips file processing if a file has already been marked as processed in Dynamics', async () => {
161
- fs.createReadStream.mockReturnValueOnce('teststream')
162
- fs.statSync.mockReturnValueOnce({ size: 1024 })
207
+ jest.spyOn(fs, 'createReadStream').mockReturnValueOnce('teststream')
208
+ jest.spyOn(fs, 'statSync').mockReturnValueOnce({ size: 1024 })
163
209
  salesApi.getTransactionFile.mockResolvedValueOnce({ status: { description: 'Processed' } })
164
210
  const s3Key = `${moment().format('YYYY-MM-DD')}/test-already-processed.xml`
165
211
 
166
- AwsMock.S3.mockedMethods.listObjectsV2.mockReturnValueOnce({
167
- promise: () => ({
168
- IsTruncated: false,
169
- Contents: [
170
- {
171
- Key: s3Key,
172
- LastModified: moment().toISOString(),
173
- ETag: 'example-md5',
174
- Size: 1024
175
- }
176
- ]
177
- })
212
+ s3.listObjectsV2.mockReturnValueOnce({
213
+ IsTruncated: false,
214
+ Contents: [
215
+ {
216
+ Key: s3Key,
217
+ LastModified: moment().toISOString(),
218
+ ETag: 'example-md5',
219
+ Size: 1024
220
+ }
221
+ ]
178
222
  })
179
223
 
180
224
  await refreshS3Metadata()
@@ -186,33 +230,29 @@ describe('s3 operations', () => {
186
230
  it('skips file processing if a file is older than one week', async () => {
187
231
  const s3Key1 = `${moment().format('YYYY-MM-DD')}/test1.xml`
188
232
 
189
- AwsMock.S3.mockedMethods.listObjectsV2
233
+ s3.listObjectsV2
190
234
  .mockReturnValue({
191
- promise: () => ({
192
- IsTruncated: false,
193
- Contents: [
194
- {
195
- Key: s3Key1,
196
- LastModified: moment().subtract(1, 'days').toISOString(),
197
- ETag: 'example-md5',
198
- Size: 1024
199
- }
200
- ]
201
- })
235
+ IsTruncated: false,
236
+ Contents: [
237
+ {
238
+ Key: s3Key1,
239
+ LastModified: moment().subtract(1, 'days').toISOString(),
240
+ ETag: 'example-md5',
241
+ Size: 1024
242
+ }
243
+ ]
202
244
  })
203
245
  .mockReturnValueOnce({
204
- promise: () => ({
205
- IsTruncated: true,
206
- NextContinuationToken: 'token',
207
- Contents: [
208
- {
209
- Key: s3Key1,
210
- LastModified: moment().subtract(1, 'days').toISOString(),
211
- ETag: 'example-md5',
212
- Size: 1024
213
- }
214
- ]
215
- })
246
+ IsTruncated: true,
247
+ NextContinuationToken: 'token',
248
+ Contents: [
249
+ {
250
+ Key: s3Key1,
251
+ LastModified: moment().subtract(1, 'days').toISOString(),
252
+ ETag: 'example-md5',
253
+ Size: 1024
254
+ }
255
+ ]
216
256
  })
217
257
 
218
258
  await refreshS3Metadata()
@@ -225,11 +265,7 @@ describe('s3 operations', () => {
225
265
  const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
226
266
 
227
267
  const testError = new Error('Test error')
228
- AwsMock.S3.mockedMethods.listObjectsV2.mockReturnValue({
229
- promise: () => {
230
- throw testError
231
- }
232
- })
268
+ s3.listObjectsV2.mockRejectedValueOnce(testError)
233
269
 
234
270
  await expect(refreshS3Metadata()).rejects.toThrow(testError)
235
271
  expect(consoleErrorSpy).toHaveBeenCalledWith(testError)
@@ -238,10 +274,8 @@ describe('s3 operations', () => {
238
274
  it('raises a warning if the bucket is empty', async () => {
239
275
  const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {})
240
276
 
241
- AwsMock.S3.mockedMethods.listObjectsV2.mockReturnValueOnce({
242
- promise: () => ({
243
- IsTruncated: false
244
- })
277
+ s3.listObjectsV2.mockReturnValueOnce({
278
+ IsTruncated: false
245
279
  })
246
280
 
247
281
  await refreshS3Metadata()
package/src/io/db.js CHANGED
@@ -10,13 +10,11 @@ const { docClient } = AWS()
10
10
  * @returns {Promise<void>}
11
11
  */
12
12
  export const updateFileStagingTable = async ({ filename, ...entries }) => {
13
- await docClient
14
- .update({
15
- TableName: config.db.fileStagingTable,
16
- Key: { filename },
17
- ...docClient.createUpdateExpression({ expires: Math.floor(Date.now() / 1000) + config.db.stagingTtlDelta, ...entries })
18
- })
19
- .promise()
13
+ await docClient.update({
14
+ TableName: config.db.fileStagingTable,
15
+ Key: { filename },
16
+ ...docClient.createUpdateExpression({ expires: Math.floor(Date.now() / 1000) + config.db.stagingTtlDelta, ...entries })
17
+ })
20
18
  }
21
19
 
22
20
  /**
@@ -42,7 +40,7 @@ export const getFileRecords = async (...stages) => {
42
40
  * @returns {DocumentClient.AttributeMap}
43
41
  */
44
42
  export const getFileRecord = async filename => {
45
- const result = await docClient.get({ TableName: config.db.fileStagingTable, Key: { filename }, ConsistentRead: true }).promise()
43
+ const result = await docClient.get({ TableName: config.db.fileStagingTable, Key: { filename }, ConsistentRead: true })
46
44
  return result.Item
47
45
  }
48
46
 
package/src/io/s3.js CHANGED
@@ -8,7 +8,7 @@ const { s3 } = AWS()
8
8
 
9
9
  const listObjectsV2 = async function (params) {
10
10
  try {
11
- return s3.listObjectsV2(params).promise()
11
+ return await s3.listObjectsV2(params)
12
12
  } catch (e) {
13
13
  console.error(e)
14
14
  throw e
@@ -5,12 +5,12 @@ import { stage } from '../pocl-data-staging.js'
5
5
  import { createTransactions } from '../create-transactions.js'
6
6
  import { finaliseTransactions } from '../finalise-transactions.js'
7
7
  import { getFileRecord, updateFileStagingTable } from '../../io/db.js'
8
+
8
9
  import fs from 'fs'
9
10
 
10
11
  jest.mock('../create-transactions.js')
11
12
  jest.mock('../finalise-transactions.js')
12
13
  jest.mock('../../io/db.js')
13
- jest.mock('fs')
14
14
  jest.mock('md5-file', () => () => 'test-md5')
15
15
 
16
16
  jest.mock('@defra-fish/connectors-lib', () => {
@@ -37,6 +37,7 @@ describe('pocl data staging', () => {
37
37
  ['the file has not previously been processed', undefined],
38
38
  ['the file has pending state', { stage: FILE_STAGE.Pending }]
39
39
  ])('%s', async (desc, val) => {
40
+ jest.spyOn(fs, 'statSync')
40
41
  getFileRecord.mockResolvedValueOnce(val)
41
42
  getFileRecord.mockResolvedValueOnce({
42
43
  stagingSucceeded: 5,
@@ -87,6 +88,7 @@ describe('pocl data staging', () => {
87
88
  })
88
89
 
89
90
  it('only runs finalisation if the creation phase has previously been completed', async () => {
91
+ jest.spyOn(fs, 'statSync')
90
92
  getFileRecord.mockResolvedValue({ stage: FILE_STAGE.Finalising })
91
93
  fs.statSync.mockReturnValueOnce({ size: 1024 })
92
94
  salesApi.getTransactionFile.mockResolvedValueOnce({ status: { description: DYNAMICS_IMPORT_STAGE.InProgress } })
@@ -104,6 +106,7 @@ describe('pocl data staging', () => {
104
106
  })
105
107
 
106
108
  it('updates the status in Dynamics if the Dynamics returns InProgress but now marked completed in DynamoDB', async () => {
109
+ jest.spyOn(fs, 'statSync')
107
110
  salesApi.getTransactionFile.mockResolvedValueOnce({ status: { description: DYNAMICS_IMPORT_STAGE.InProgress } })
108
111
  getFileRecord.mockResolvedValue({ stage: FILE_STAGE.Completed })
109
112
  fs.statSync.mockReturnValueOnce({ size: 1024 })
@@ -116,6 +119,7 @@ describe('pocl data staging', () => {
116
119
  })
117
120
 
118
121
  it('is a no-op if the file is marked as processed in Dynamics', async () => {
122
+ jest.spyOn(fs, 'statSync')
119
123
  const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
120
124
  salesApi.getTransactionFile.mockResolvedValueOnce({ status: { description: DYNAMICS_IMPORT_STAGE.ProcessedWithWarnings } })
121
125
  getFileRecord.mockResolvedValue({ stage: FILE_STAGE.Completed })
@@ -1,9 +1,10 @@
1
1
  import { s3ToLocal } from '../s3-to-local.js'
2
2
  import stream from 'stream'
3
- import AwsMock from 'aws-sdk'
3
+ import { AWS } from '@defra-fish/connectors-lib'
4
+ import fs from 'fs'
5
+ const { s3 } = AWS.mock.results[0].value
4
6
 
5
7
  const MOCK_TMP = '/tmp/local/mock'
6
- jest.mock('fs')
7
8
  jest.mock('stream')
8
9
  jest.mock('../../io/file.js', () => ({
9
10
  getTempDir: jest.fn((...subfolders) => `${MOCK_TMP}/${subfolders.join('/')}`)
@@ -15,17 +16,23 @@ jest.mock('../../config.js', () => ({
15
16
  }
16
17
  }))
17
18
 
19
+ jest.mock('@defra-fish/connectors-lib', () => ({
20
+ AWS: jest.fn(() => ({
21
+ s3: {
22
+ getObject: jest.fn(() => ({
23
+ createReadStream: jest.fn(() => ({}))
24
+ }))
25
+ }
26
+ }))
27
+ }))
28
+
18
29
  describe('s3-to-local', () => {
19
30
  beforeEach(() => {
20
31
  jest.clearAllMocks()
21
- AwsMock.__resetAll()
22
32
  })
23
33
 
24
34
  it('retrieves a file from s3 for a given key', async () => {
25
- const mockCreateReadStream = jest.fn()
26
- AwsMock.S3.mockedMethods.getObject.mockImplementationOnce(() => {
27
- return { createReadStream: mockCreateReadStream }
28
- })
35
+ jest.spyOn(fs, 'createWriteStream').mockReturnValueOnce({})
29
36
  stream.pipeline.mockImplementation(
30
37
  jest.fn((streams, callback) => {
31
38
  callback()
@@ -33,10 +40,12 @@ describe('s3-to-local', () => {
33
40
  )
34
41
 
35
42
  const result = await s3ToLocal('/example/testS3Key.xml')
43
+ const { createReadStream } = s3.getObject.mock.results[0].value
44
+
36
45
  expect(result).toBe(`${MOCK_TMP}/example/testS3Key.xml`)
37
- expect(mockCreateReadStream).toHaveBeenCalled()
46
+ expect(createReadStream).toHaveBeenCalled()
38
47
  expect(stream.pipeline).toHaveBeenCalled()
39
- expect(AwsMock.S3.mockedMethods.getObject).toHaveBeenCalledWith({
48
+ expect(s3.getObject).toHaveBeenCalledWith({
40
49
  Bucket: 'testbucket',
41
50
  Key: '/example/testS3Key.xml'
42
51
  })