@defra-fish/recurring-payments-job 1.63.0-rc.10 → 1.63.0-rc.12
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/recurring-payments-job",
|
|
3
|
-
"version": "1.63.0-rc.
|
|
3
|
+
"version": "1.63.0-rc.12",
|
|
4
4
|
"description": "Rod Licensing Recurring Payments Job",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@defra-fish/business-rules-lib": "1.63.0-rc.
|
|
40
|
-
"@defra-fish/connectors-lib": "1.63.0-rc.
|
|
39
|
+
"@defra-fish/business-rules-lib": "1.63.0-rc.12",
|
|
40
|
+
"@defra-fish/connectors-lib": "1.63.0-rc.12",
|
|
41
41
|
"commander": "^7.2.0",
|
|
42
42
|
"debug": "^4.3.3",
|
|
43
43
|
"moment-timezone": "^0.5.34"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "077b17c4a39822d8db2419d99e0a8dce2bb4a8ff"
|
|
46
46
|
}
|
|
@@ -1,34 +1,107 @@
|
|
|
1
1
|
import commander from 'commander'
|
|
2
|
+
import { airbrake } from '@defra-fish/connectors-lib'
|
|
2
3
|
import { processRecurringPayments } from '../recurring-payments-processor.js'
|
|
3
4
|
import fs from 'fs'
|
|
4
5
|
|
|
5
6
|
jest.useFakeTimers()
|
|
6
7
|
|
|
7
|
-
jest.mock('../recurring-payments-processor.js'
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
jest.mock('../recurring-payments-processor.js')
|
|
9
|
+
jest.mock('@defra-fish/connectors-lib', () => ({
|
|
10
|
+
airbrake: {
|
|
11
|
+
initialise: jest.fn(),
|
|
12
|
+
flush: jest.fn()
|
|
10
13
|
}
|
|
11
|
-
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
jest.mock('commander', () => {
|
|
15
|
-
if (!global.commander) {
|
|
16
|
-
global.commander = jest.requireActual('commander')
|
|
17
|
-
}
|
|
18
|
-
return global.commander
|
|
19
|
-
})
|
|
14
|
+
}))
|
|
20
15
|
|
|
21
16
|
jest.mock('fs')
|
|
22
|
-
|
|
23
17
|
describe('recurring-payments-job', () => {
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
fs.readFileSync.mockReturnValue(JSON.stringify({ name: 'recurring-payments-test', version: '1.0.0' }))
|
|
20
|
+
})
|
|
21
|
+
|
|
24
22
|
beforeEach(() => {
|
|
25
23
|
jest.clearAllMocks()
|
|
26
24
|
commander.args = ['test']
|
|
27
25
|
})
|
|
28
26
|
|
|
27
|
+
it('initialises airbrake', () => {
|
|
28
|
+
jest.isolateModules(() => {
|
|
29
|
+
require('../recurring-payments-job.js')
|
|
30
|
+
expect(airbrake.initialise).toHaveBeenCalled()
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('flushes airbrake before script ends', () => {
|
|
35
|
+
jest.isolateModules(() => {
|
|
36
|
+
require('../recurring-payments-job.js')
|
|
37
|
+
expect(airbrake.flush).toHaveBeenCalled()
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it("doesn't flush airbrake before processRecurringPayments has been called", () => {
|
|
42
|
+
jest.isolateModules(() => {
|
|
43
|
+
processRecurringPayments.mockImplementationOnce(() => {
|
|
44
|
+
expect(airbrake.flush).not.toHaveBeenCalled()
|
|
45
|
+
})
|
|
46
|
+
require('../recurring-payments-job.js')
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it.each([
|
|
51
|
+
['SIGINT', 130],
|
|
52
|
+
['SIGTERM', 137]
|
|
53
|
+
])('flushes airbrake on %s signal', (signal, code) => {
|
|
54
|
+
jest.isolateModules(() => {
|
|
55
|
+
// setup a delay so script doesn't call processRecurringPayments and exit naturally
|
|
56
|
+
process.env.RECURRING_PAYMENTS_LOCAL_DELAY = '1'
|
|
57
|
+
const signalCallbacks = {}
|
|
58
|
+
jest.spyOn(process, 'on')
|
|
59
|
+
jest.spyOn(process, 'exit')
|
|
60
|
+
process.on.mockImplementation((signalToken, callback) => {
|
|
61
|
+
signalCallbacks[signalToken] = callback
|
|
62
|
+
})
|
|
63
|
+
process.exit.mockImplementation(() => {
|
|
64
|
+
// so we don't crash out of the tests!
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
require('../recurring-payments-job.js')
|
|
68
|
+
signalCallbacks[signal]()
|
|
69
|
+
|
|
70
|
+
expect(airbrake.flush).toHaveBeenCalled()
|
|
71
|
+
process.on.mockRestore()
|
|
72
|
+
process.exit.mockRestore()
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it.each([
|
|
77
|
+
['SIGINT', 130],
|
|
78
|
+
['SIGTERM', 137]
|
|
79
|
+
])('calls process.exit on %s signal with %i code', (signal, code) => {
|
|
80
|
+
jest.isolateModules(() => {
|
|
81
|
+
// setup a delay so script doesn't call processRecurringPayments and exit naturally
|
|
82
|
+
process.env.RECURRING_PAYMENTS_LOCAL_DELAY = '1'
|
|
83
|
+
const signalCallbacks = {}
|
|
84
|
+
jest.spyOn(process, 'on')
|
|
85
|
+
jest.spyOn(process, 'exit')
|
|
86
|
+
process.on.mockImplementation((signalToken, callback) => {
|
|
87
|
+
signalCallbacks[signalToken] = callback
|
|
88
|
+
})
|
|
89
|
+
process.exit.mockImplementation(() => {
|
|
90
|
+
// so we don't crash out of the tests!
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
require('../recurring-payments-job.js')
|
|
94
|
+
signalCallbacks[signal]()
|
|
95
|
+
|
|
96
|
+
expect(process.exit).toHaveBeenCalledWith(code)
|
|
97
|
+
process.on.mockRestore()
|
|
98
|
+
process.exit.mockRestore()
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
29
102
|
it('logs startup details including name and version', () => {
|
|
30
103
|
const mockPkg = { name: 'recurring-payments-test', version: '1.2.3' }
|
|
31
|
-
fs.readFileSync.
|
|
104
|
+
fs.readFileSync.mockReturnValueOnce(JSON.stringify(mockPkg))
|
|
32
105
|
|
|
33
106
|
jest.isolateModules(() => {
|
|
34
107
|
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {})
|
|
@@ -39,11 +112,13 @@ describe('recurring-payments-job', () => {
|
|
|
39
112
|
mockPkg.name,
|
|
40
113
|
mockPkg.version
|
|
41
114
|
)
|
|
115
|
+
logSpy.mockRestore()
|
|
42
116
|
})
|
|
43
117
|
})
|
|
44
118
|
|
|
45
119
|
it('calls processRecurringPayments when no delay', () => {
|
|
46
120
|
jest.isolateModules(() => {
|
|
121
|
+
process.env.RECURRING_PAYMENTS_LOCAL_DELAY = '0'
|
|
47
122
|
require('../recurring-payments-job.js')
|
|
48
123
|
expect(processRecurringPayments).toHaveBeenCalled()
|
|
49
124
|
})
|
|
@@ -51,6 +126,7 @@ describe('recurring-payments-job', () => {
|
|
|
51
126
|
|
|
52
127
|
it('doesnt call setTimeout when no correct delay', () => {
|
|
53
128
|
jest.isolateModules(() => {
|
|
129
|
+
process.env.RECURRING_PAYMENTS_LOCAL_DELAY = 'invalid-delay'
|
|
54
130
|
const setTimeoutSpy = jest.spyOn(global, 'setTimeout')
|
|
55
131
|
require('../recurring-payments-job.js')
|
|
56
132
|
expect(setTimeoutSpy).not.toHaveBeenCalled()
|
|
@@ -20,6 +20,7 @@ jest.mock('@defra-fish/business-rules-lib', () => ({
|
|
|
20
20
|
}))
|
|
21
21
|
jest.mock('@defra-fish/connectors-lib', () => ({
|
|
22
22
|
salesApi: {
|
|
23
|
+
cancelRecurringPayment: jest.fn(),
|
|
23
24
|
createPaymentJournal: jest.fn(),
|
|
24
25
|
createTransaction: jest.fn(() => ({
|
|
25
26
|
id: 'test-transaction-id',
|
|
@@ -636,6 +637,30 @@ describe('recurring-payments-processor', () => {
|
|
|
636
637
|
}
|
|
637
638
|
)
|
|
638
639
|
|
|
640
|
+
it.each([
|
|
641
|
+
['a failure', 'agreement-id', getPaymentStatusFailure()],
|
|
642
|
+
['a failure', 'test-agreement-id', getPaymentStatusFailure()],
|
|
643
|
+
['a failure', 'another-agreement-id', getPaymentStatusFailure()],
|
|
644
|
+
['an error', 'agreement-id', getPaymentStatusError()],
|
|
645
|
+
['an error', 'test-agreement-id', getPaymentStatusError()],
|
|
646
|
+
['an error', 'another-agreement-id', getPaymentStatusError()]
|
|
647
|
+
])('cancelRecurringPayment is called when payment is %s', async (_status, agreementId, mockStatus) => {
|
|
648
|
+
salesApi.getDueRecurringPayments.mockReturnValueOnce([getMockDueRecurringPayment({ agreementId })])
|
|
649
|
+
const id = Symbol('recurring-payment-id')
|
|
650
|
+
salesApi.createTransaction.mockResolvedValueOnce({
|
|
651
|
+
recurringPayment: {
|
|
652
|
+
id
|
|
653
|
+
}
|
|
654
|
+
})
|
|
655
|
+
const mockPaymentResponse = { payment_id: 'test-payment-id', created_date: '2025-01-01T00:00:00.000Z' }
|
|
656
|
+
sendPayment.mockResolvedValueOnce(mockPaymentResponse)
|
|
657
|
+
getPaymentStatus.mockResolvedValueOnce(mockStatus)
|
|
658
|
+
|
|
659
|
+
await processRecurringPayments()
|
|
660
|
+
|
|
661
|
+
expect(salesApi.cancelRecurringPayment).toHaveBeenCalledWith(id)
|
|
662
|
+
})
|
|
663
|
+
|
|
639
664
|
it('updatePaymentJournal is called with transaction id and failed status code payment is not succesful and payment journal exists', async () => {
|
|
640
665
|
salesApi.getDueRecurringPayments.mockReturnValueOnce([getMockDueRecurringPayment()])
|
|
641
666
|
const transactionId = 'test-transaction-id'
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
import recurringPaymentsJob from 'commander'
|
|
3
3
|
import { processRecurringPayments } from './recurring-payments-processor.js'
|
|
4
|
+
import { airbrake } from '@defra-fish/connectors-lib'
|
|
4
5
|
import path from 'path'
|
|
5
6
|
import fs from 'fs'
|
|
6
7
|
|
|
8
|
+
const SIGINT_CODE = 130
|
|
9
|
+
const SIGTERM_CODE = 137
|
|
7
10
|
const pkgPath = path.join(process.cwd(), 'package.json')
|
|
8
11
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
|
|
9
12
|
|
|
10
13
|
console.log('Recurring payments job starting at %s. name: %s. version: %s', new Date().toISOString(), pkg.name, pkg.version)
|
|
14
|
+
airbrake.initialise()
|
|
11
15
|
|
|
12
16
|
const delay = parseInt(process.env.RECURRING_PAYMENTS_LOCAL_DELAY || '0', 10)
|
|
13
17
|
if (delay > 0) {
|
|
@@ -20,6 +24,15 @@ if (delay > 0) {
|
|
|
20
24
|
|
|
21
25
|
function executeRecurringPaymentsJob () {
|
|
22
26
|
recurringPaymentsJob.action(processRecurringPayments())
|
|
27
|
+
airbrake.flush()
|
|
23
28
|
}
|
|
24
29
|
|
|
30
|
+
const shutdown = code => {
|
|
31
|
+
airbrake.flush()
|
|
32
|
+
process.exit(code)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
process.on('SIGINT', () => shutdown(SIGINT_CODE))
|
|
36
|
+
process.on('SIGTERM', () => shutdown(SIGTERM_CODE))
|
|
37
|
+
|
|
25
38
|
export default recurringPaymentsJob
|
|
@@ -154,6 +154,7 @@ const processRecurringPaymentStatus = async payment => {
|
|
|
154
154
|
paymentStatus: PAYMENT_JOURNAL_STATUS_CODES.Failed
|
|
155
155
|
})
|
|
156
156
|
}
|
|
157
|
+
await salesApi.cancelRecurringPayment(payment.transaction.recurringPayment.id)
|
|
157
158
|
}
|
|
158
159
|
} catch (error) {
|
|
159
160
|
const status = error.response?.status
|
|
@@ -165,5 +166,6 @@ const processRecurringPaymentStatus = async payment => {
|
|
|
165
166
|
} else {
|
|
166
167
|
debug(`Unexpected error fetching payment status for ${payment.paymentId}.`)
|
|
167
168
|
}
|
|
169
|
+
await salesApi.cancelRecurringPayment(payment.transaction.recurringPayment.id)
|
|
168
170
|
}
|
|
169
171
|
}
|