@emartech/program-executor 3.11.0 → 3.12.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 (30) hide show
  1. package/.env.example +9 -1
  2. package/.github/CODEOWNERS +1 -1
  3. package/.github/dependabot.yml +0 -7
  4. package/.github/workflows/main.yaml +17 -5
  5. package/docker-compose.yml +11 -0
  6. package/eslint.config.js +1 -5
  7. package/jest.config.js +13 -0
  8. package/package.json +13 -16
  9. package/src/create-test-databases.js +84 -0
  10. package/src/execution-time-exceeded-error/{index.spec.js → index.test.js} +3 -3
  11. package/src/graphql/{schema.spec.js → schema.test.js} +69 -24
  12. package/src/ignorable-error/{index.spec.js → index.test.js} +5 -5
  13. package/src/index-pubsub-e2e.test.js +94 -0
  14. package/src/index-pubsub.js +78 -0
  15. package/src/index-pubsub.test.js +154 -0
  16. package/src/{index.spec.js → index.test.js} +42 -38
  17. package/src/job-data-handler/{index.spec.js → index.test.js} +15 -21
  18. package/src/program-executor-processor/{index.spec.js → index.test.js} +62 -49
  19. package/src/program-handler/index.js +7 -1
  20. package/src/program-handler/{index.spec.js → index.test.js} +58 -34
  21. package/src/queue-manager/{index.spec.js → index.test.js} +16 -19
  22. package/src/queue-manager-pubsub/index.js +33 -0
  23. package/src/queue-manager-pubsub/index.test.js +44 -0
  24. package/src/repositories/{programs.spec.js → programs.test.js} +54 -37
  25. package/src/retryable-error/{index.spec.js → index.test.js} +5 -5
  26. package/src/runid-generator/{index.spec.js → index.test.js} +2 -2
  27. package/src/test-helper/get-test-db-config.js +29 -0
  28. package/src/testSetup.js +23 -0
  29. package/src/testTeardown.js +13 -0
  30. package/src/setup.spec.js +0 -50
@@ -1,10 +1,13 @@
1
1
  'use strict';
2
2
 
3
- const ProgramExecutorProcessor = require('./');
3
+ const ProgramExecutorProcessor = require('.');
4
4
 
5
5
  const ProgramHandler = require('../program-handler');
6
6
  const JobDataHandler = require('../job-data-handler');
7
7
  const QueueManager = require('../queue-manager');
8
+ const JobHandler = require('../job-data-handler');
9
+
10
+ jest.mock('../job-data-handler');
8
11
 
9
12
  describe('ProgramExecutorProcessor', function () {
10
13
  let programHandler;
@@ -16,6 +19,9 @@ describe('ProgramExecutorProcessor', function () {
16
19
  let failingJobExecuteWithIgnorableErrorStub;
17
20
  let failingJobExecuteWithRetryableErrorStub;
18
21
  let programHandlerIsProgramFinishedWithErrorStub;
22
+ let mockProgramHandler;
23
+ let setProgramToErrorStub;
24
+ let mockQueueProgram;
19
25
 
20
26
  beforeEach(async function () {
21
27
  const ignorableError = new Error('Something wrong happened, but ignore this!');
@@ -25,56 +31,63 @@ describe('ProgramExecutorProcessor', function () {
25
31
  const executionTimeExceededError = new Error('Timeout exceeded, but please retry!');
26
32
  executionTimeExceededError.executionTimeExceeded = true;
27
33
 
28
- testJobExecuteStub = this.sandbox.stub();
29
- failingJobExecuteStub = this.sandbox.stub().rejects(new Error('Something wrong happened!'));
30
- failingJobExecuteWithIgnorableErrorStub = this.sandbox.stub().rejects(ignorableError);
31
- failingJobExecuteWithRetryableErrorStub = this.sandbox.stub().rejects(retryableError);
34
+ testJobExecuteStub = jest.fn();
35
+ failingJobExecuteStub = jest.fn().mockRejectedValue(new Error('Something wrong happened!'));
36
+ failingJobExecuteWithIgnorableErrorStub = jest.fn().mockRejectedValue(ignorableError);
37
+ failingJobExecuteWithRetryableErrorStub = jest.fn().mockRejectedValue(retryableError);
32
38
  jobLibrary = {
33
39
  testJob: {
34
- create: this.sandbox.stub().returns({
40
+ create: jest.fn().mockReturnValue({
35
41
  execute: testJobExecuteStub
36
42
  })
37
43
  },
38
44
  currentJob: {
39
- create: this.sandbox.stub().returns({ execute() {} })
45
+ create: jest.fn().mockReturnValue({ execute() {} })
40
46
  },
41
47
  nextJob: {
42
- create: this.sandbox.stub().returns({ execute() {} })
48
+ create: jest.fn().mockReturnValue({ execute() {} })
43
49
  },
44
50
  failingJob: {
45
- create: this.sandbox.stub().returns({
51
+ create: jest.fn().mockReturnValue({
46
52
  execute: failingJobExecuteStub
47
53
  })
48
54
  },
49
55
  failingJobWithIgnorableError: {
50
- create: this.sandbox.stub().returns({
56
+ create: jest.fn().mockReturnValue({
51
57
  execute: failingJobExecuteWithIgnorableErrorStub
52
58
  })
53
59
  },
54
60
  failingJobWithRetryableError: {
55
- create: this.sandbox.stub().returns({
61
+ create: jest.fn().mockReturnValue({
56
62
  execute: failingJobExecuteWithRetryableErrorStub
57
63
  })
58
64
  },
59
65
  failingJobWithExecutionTimeExceededError: {
60
- create: this.sandbox.stub().returns({
61
- execute: this.sandbox.stub().rejects(executionTimeExceededError)
66
+ create: jest.fn().mockReturnValue({
67
+ execute: jest.fn().mockRejectedValue(executionTimeExceededError)
62
68
  })
63
69
  }
64
70
  };
71
+ mockQueueProgram = jest.fn().mockResolvedValue(true);
72
+ programHandlerIsProgramFinishedWithErrorStub = jest.fn().mockResolvedValue(false);
73
+ setProgramToErrorStub = jest.fn().mockResolvedValue(true);
74
+
75
+ mockProgramHandler = {
76
+ finishProgram: jest.fn().mockResolvedValue(true),
77
+ setProgramToError: jest.fn().mockResolvedValue(true),
78
+ setJobRetriableErrorMessage: jest.fn().mockResolvedValue(true),
79
+ incrementStep: jest.fn().mockResolvedValue(true),
80
+ incrementStepRetryCount: jest.fn().mockResolvedValue(true),
81
+ isProgramFinishedWithError: programHandlerIsProgramFinishedWithErrorStub,
82
+ setProgramToError: setProgramToErrorStub
83
+ };
84
+ ProgramHandler.create = jest.fn().mockReturnValue(mockProgramHandler);
65
85
 
66
- this.sandbox.spy(JobDataHandler, 'create');
67
- this.sandbox.stub(ProgramHandler.prototype, 'finishProgram').resolves(true);
68
- this.sandbox.stub(ProgramHandler.prototype, 'setProgramToError').resolves(true);
69
- this.sandbox.stub(ProgramHandler.prototype, 'setJobRetriableErrorMessage').resolves(true);
70
- this.sandbox.stub(ProgramHandler.prototype, 'incrementStep').resolves(true);
71
- this.sandbox.stub(ProgramHandler.prototype, 'incrementStepRetryCount').resolves(true);
72
- programHandlerIsProgramFinishedWithErrorStub = this.sandbox
73
- .stub(ProgramHandler.prototype, 'isProgramFinishedWithError')
74
- .resolves(false);
75
-
76
- this.sandbox.stub(QueueManager.prototype, 'queueProgram').resolves(true);
86
+ QueueManager.create = jest.fn().mockReturnValue({
87
+ queueProgram: mockQueueProgram
88
+ });
77
89
 
90
+ JobHandler.create = jest.fn().mockReturnValue(new JobHandler());
78
91
  programHandler = ProgramHandler.create();
79
92
  queueManager = QueueManager.create();
80
93
  });
@@ -92,12 +105,12 @@ describe('ProgramExecutorProcessor', function () {
92
105
  runId: '1'
93
106
  });
94
107
 
95
- expect(jobLibrary.testJob.create).to.be.calledWithExactly(programData);
96
- expect(testJobExecuteStub).to.be.calledWith({
108
+ expect(jobLibrary.testJob.create).toHaveBeenCalledWith(programData);
109
+ expect(testJobExecuteStub).toHaveBeenCalledWith(({
97
110
  programData,
98
111
  jobs: ['testJob'],
99
112
  runId: '1'
100
- });
113
+ }), expect.any(JobDataHandler));
101
114
  });
102
115
 
103
116
  it('should pass job data handler for the given job and runId pair', async function () {
@@ -107,22 +120,20 @@ describe('ProgramExecutorProcessor', function () {
107
120
  runId: '1'
108
121
  });
109
122
 
110
- const jobDataHandler = testJobExecuteStub.getCall(0).args[1];
111
-
112
- expect(JobDataHandler.create).to.be.calledWith(this.sinon.match.instanceOf(ProgramHandler), '1', 'testJob');
113
- expect(jobDataHandler).to.be.an.instanceOf(JobDataHandler);
123
+ const jobDataHandler = testJobExecuteStub.mock.calls[0][1];
124
+ expect(jobDataHandler).toBeInstanceOf(JobDataHandler);
114
125
  });
115
126
 
116
127
  it('should cancel execution if program already encountered an error', async function () {
117
- programHandlerIsProgramFinishedWithErrorStub.resolves(true);
128
+ programHandlerIsProgramFinishedWithErrorStub.mockResolvedValue(true)(true);
118
129
  await ProgramExecutorProcessor.create(programHandler, queueManager, jobLibrary).process({
119
130
  jobs: ['testJob'],
120
131
  programData: {},
121
132
  runId: '1'
122
133
  });
123
134
 
124
- expect(testJobExecuteStub).not.to.have.been.called;
125
- expect(QueueManager.prototype.queueProgram).not.to.have.been.called;
135
+ expect(testJobExecuteStub).not.toHaveBeenCalled();
136
+ expect(mockQueueProgram).not.toHaveBeenCalled();
126
137
  });
127
138
 
128
139
  it('should throw an error if a job fails with non-ignorable error', async function () {
@@ -137,8 +148,8 @@ describe('ProgramExecutorProcessor', function () {
137
148
  caughtError = error;
138
149
  }
139
150
 
140
- expect(caughtError).not.to.be.undefined;
141
- expect(failingJobExecuteStub).to.be.called;
151
+ expect(caughtError).not.toBe(undefined);
152
+ expect(failingJobExecuteStub).toHaveBeenCalled();
142
153
  });
143
154
 
144
155
  it('should throw a retryable error if a job fails with ExecutionTimeExceededError', async function () {
@@ -153,7 +164,7 @@ describe('ProgramExecutorProcessor', function () {
153
164
  caughtError = error;
154
165
  }
155
166
 
156
- expect(caughtError.retryable).to.be.true;
167
+ expect(caughtError.retryable).toBe(true);
157
168
  });
158
169
 
159
170
  it('should not throw an error if a job fails with an ignorable error', async function () {
@@ -162,7 +173,7 @@ describe('ProgramExecutorProcessor', function () {
162
173
  programData: {},
163
174
  runId: '1'
164
175
  });
165
- expect(ProgramHandler.prototype.setProgramToError).to.be.calledWith(
176
+ expect(mockProgramHandler.setProgramToError).toHaveBeenCalledWith(
166
177
  '1',
167
178
  'Something wrong happened, but ignore this!'
168
179
  );
@@ -175,9 +186,10 @@ describe('ProgramExecutorProcessor', function () {
175
186
  programData: {},
176
187
  runId: '1'
177
188
  });
189
+ // eslint-disable-next-line no-unused-vars
178
190
  } catch (error) {
179
- expect(ProgramHandler.prototype.setProgramToError).to.be.calledWith('1', 'Something wrong happened!');
180
- expect(ProgramHandler.prototype.setJobRetriableErrorMessage).not.have.been.called;
191
+ expect(mockProgramHandler.setProgramToError).toHaveBeenCalledWith('1', 'Something wrong happened!');
192
+ expect(mockProgramHandler.setJobRetriableErrorMessage).not.toHaveBeenCalled();
181
193
  }
182
194
  });
183
195
 
@@ -188,10 +200,10 @@ describe('ProgramExecutorProcessor', function () {
188
200
  programData: {},
189
201
  runId: '1'
190
202
  });
191
- // eslint-disable-next-line no-empty
203
+ // eslint-disable-next-line no-empty, no-unused-vars
192
204
  } catch (_error) {}
193
205
 
194
- expect(ProgramHandler.prototype.incrementStepRetryCount).to.have.been.calledWith('1');
206
+ expect(mockProgramHandler.incrementStepRetryCount).toHaveBeenCalledWith('1');
195
207
  });
196
208
 
197
209
  it('should update error message only if job fails with retriable error', async function () {
@@ -201,9 +213,10 @@ describe('ProgramExecutorProcessor', function () {
201
213
  programData: {},
202
214
  runId: '1'
203
215
  });
216
+ // eslint-disable-next-line no-unused-vars
204
217
  } catch (error) {
205
- expect(ProgramHandler.prototype.setProgramToError).not.have.been.called;
206
- expect(ProgramHandler.prototype.setJobRetriableErrorMessage).to.be.calledWith(
218
+ expect(mockProgramHandler.setProgramToError).not.toHaveBeenCalled();
219
+ expect(mockProgramHandler.setJobRetriableErrorMessage).toHaveBeenCalledWith(
207
220
  '1',
208
221
  'Something wrong happened, but please retry!'
209
222
  );
@@ -219,7 +232,7 @@ describe('ProgramExecutorProcessor', function () {
219
232
  }
220
233
  });
221
234
 
222
- expect(QueueManager.prototype.queueProgram).to.have.been.calledWith({
235
+ expect(mockQueueProgram).toHaveBeenCalledWith({
223
236
  jobs: ['nextJob'],
224
237
  programData: {
225
238
  customerId: 123,
@@ -235,7 +248,7 @@ describe('ProgramExecutorProcessor', function () {
235
248
  runId: '1'
236
249
  });
237
250
 
238
- expect(ProgramHandler.prototype.incrementStep).to.have.been.calledWith('1');
251
+ expect(mockProgramHandler.incrementStep).toHaveBeenCalledWith('1');
239
252
  });
240
253
 
241
254
  it('should not requeue when it was the last job', async function () {
@@ -245,7 +258,7 @@ describe('ProgramExecutorProcessor', function () {
245
258
  runId: '1'
246
259
  });
247
260
 
248
- expect(QueueManager.prototype.queueProgram).not.to.have.been.called;
261
+ expect(mockQueueProgram).not.toHaveBeenCalled();
249
262
  });
250
263
 
251
264
  it('should set program to finished when it was the last job', async function () {
@@ -255,7 +268,7 @@ describe('ProgramExecutorProcessor', function () {
255
268
  runId: '1'
256
269
  });
257
270
 
258
- expect(ProgramHandler.prototype.finishProgram).to.have.been.calledWith('1');
271
+ expect(mockProgramHandler.finishProgram).toHaveBeenCalledWith('1');
259
272
  });
260
273
 
261
274
  it('should not set job to finished when it was not the last job', async function () {
@@ -265,7 +278,7 @@ describe('ProgramExecutorProcessor', function () {
265
278
  runId: '1'
266
279
  });
267
280
 
268
- expect(ProgramHandler.prototype.finishProgram).not.to.have.been.called;
281
+ expect(mockProgramHandler.finishProgram).not.toHaveBeenCalled();
269
282
  });
270
283
  });
271
284
  });
@@ -52,7 +52,13 @@ class ProgramHandler {
52
52
 
53
53
  jobData[jobName] = merge ? { ...jobData[jobName], ...payload } : payload;
54
54
 
55
- devLogger.info('db update job data', { run_id: runId, jobName, payload: JSON.stringify(payload), merge, OSPID: process.pid });
55
+ devLogger.info('db update job data', {
56
+ run_id: runId,
57
+ jobName,
58
+ payload: JSON.stringify(payload),
59
+ merge,
60
+ OSPID: process.pid
61
+ });
56
62
  return this._programsRepository.setJobDataByRunId(runId, jobData);
57
63
  }
58
64
 
@@ -1,8 +1,11 @@
1
1
  'use strict';
2
2
 
3
- const ProgramHandler = require('./');
3
+ const ProgramHandler = require('.');
4
4
  const QueueManager = require('../queue-manager');
5
5
  const ProgramsRepository = require('../repositories/programs');
6
+ const knex = require('knex');
7
+ const DbCleaner = require('../test-helper/db-cleaner');
8
+ const { db } = require('../test-helper/get-test-db-config');
6
9
 
7
10
  const hostname = 'yolo.myshopify.com';
8
11
  const customerId = 123;
@@ -10,15 +13,36 @@ const customerId = 123;
10
13
  describe('ProgramHandler', function () {
11
14
  let programsRepository;
12
15
  let queueManager;
13
- let queueProgramStub;
16
+ let mockQueueProgram;
17
+ let dbConnection;
18
+
19
+ beforeAll(function () {
20
+
21
+ dbConnection = knex({
22
+ client: 'pg',
23
+ connection: db.connection.connString
24
+ });
25
+ });
26
+
27
+ afterAll(async function () {
28
+ await DbCleaner.create(dbConnection).tearDown();
29
+ dbConnection.destroy();
30
+ });
31
+
14
32
 
15
33
  beforeEach(async function () {
16
- programsRepository = ProgramsRepository.create(this.db, 'programs');
34
+ await DbCleaner.create(dbConnection).tearDown();
35
+ mockQueueProgram = jest.fn().mockResolvedValue(true);
36
+ QueueManager.create = jest.fn().mockReturnValue({
37
+ queueProgram: mockQueueProgram
38
+ });
39
+
40
+
41
+ programsRepository = ProgramsRepository.create(dbConnection, 'programs');
17
42
  queueManager = QueueManager.create('amqp://guest:guest@localhost:9999', 'program-executor');
18
- queueProgramStub = this.sandbox.stub(QueueManager.prototype, 'queueProgram').resolves(true);
19
43
  });
20
44
 
21
- describe('#createProgram', async function () {
45
+ describe('#createProgram', function () {
22
46
  it('should create a program with the given jobs and program data', async function () {
23
47
  const programData = { test: 'data' };
24
48
 
@@ -27,10 +51,10 @@ describe('ProgramHandler', function () {
27
51
  jobs: ['current_program', 'next_program']
28
52
  });
29
53
 
30
- expect(QueueManager.prototype.queueProgram).to.have.been.calledWithMatch({
54
+ expect(mockQueueProgram).toHaveBeenCalledWith(expect.objectContaining({
31
55
  jobs: ['current_program', 'next_program'],
32
56
  programData
33
- });
57
+ }));
34
58
  });
35
59
 
36
60
  it('should save program in db', async function () {
@@ -39,7 +63,7 @@ describe('ProgramHandler', function () {
39
63
  });
40
64
  const program = await programsRepository.getProgramByRunId(runId);
41
65
 
42
- expect(program.jobs).to.have.members(['current_program', 'next_program']);
66
+ expect(program.jobs).toEqual(['current_program', 'next_program']);
43
67
  });
44
68
 
45
69
  it('should save program data in db', async function () {
@@ -53,7 +77,7 @@ describe('ProgramHandler', function () {
53
77
  });
54
78
  const program = await programsRepository.getProgramByRunId(runId);
55
79
 
56
- expect(program.programData).to.eql({ hostname, customerId });
80
+ expect(program.programData).toEqual({ hostname, customerId });
57
81
  });
58
82
 
59
83
  it('should save job data in db', async function () {
@@ -69,7 +93,7 @@ describe('ProgramHandler', function () {
69
93
  });
70
94
  const program = await programsRepository.getProgramByRunId(runId);
71
95
 
72
- expect(program.jobData).to.eql(jobData);
96
+ expect(program.jobData).toEqual(jobData);
73
97
  });
74
98
 
75
99
  it('should generate a runid for the program', async function () {
@@ -77,7 +101,7 @@ describe('ProgramHandler', function () {
77
101
  jobs: ['current_program', 'next_program']
78
102
  });
79
103
 
80
- expect(queueProgramStub.lastCall.lastArg.runId).not.to.be.undefined;
104
+ expect(mockQueueProgram.mock.calls[0][0].runId).not.toBe(undefined);
81
105
  });
82
106
 
83
107
  it('should return the generated runid', async function () {
@@ -85,12 +109,12 @@ describe('ProgramHandler', function () {
85
109
  jobs: ['current_program', 'next_program']
86
110
  });
87
111
 
88
- const generatedRunId = queueProgramStub.lastCall.lastArg.runId;
89
- expect(generatedRunId).to.eql(result);
112
+ const generatedRunId = mockQueueProgram.mock.calls[0][0].runId;
113
+ expect(generatedRunId).toEqual(result);
90
114
  });
91
115
  });
92
116
 
93
- describe('#finishProgram', async function () {
117
+ describe('#finishProgram', function () {
94
118
  it('should set job to finished in db', async function () {
95
119
  const programHandler = ProgramHandler.create(programsRepository, queueManager);
96
120
 
@@ -101,11 +125,11 @@ describe('ProgramHandler', function () {
101
125
  await programHandler.finishProgram(runId);
102
126
 
103
127
  const program = await programsRepository.getProgramByRunId(runId);
104
- expect(program.finishedAt).not.to.be.undefined;
128
+ expect(program.finishedAt).not.toBe(undefined);
105
129
  });
106
130
  });
107
131
 
108
- describe('#getJobData', async function () {
132
+ describe('#getJobData', function () {
109
133
  it('should get program data for runid and program name', async function () {
110
134
  const programHandler = ProgramHandler.create(programsRepository, queueManager);
111
135
  const runId = await programHandler.createProgram({
@@ -114,11 +138,11 @@ describe('ProgramHandler', function () {
114
138
  await programHandler.updateJobData(runId, 'product_sync', { page: 123 });
115
139
 
116
140
  const jobData = await programHandler.getJobData(runId, 'product_sync');
117
- expect(jobData).to.eql({ page: 123 });
141
+ expect(jobData).toEqual({ page: 123 });
118
142
  });
119
143
  });
120
144
 
121
- describe('#setProgramToError', async function () {
145
+ describe('#setProgramToError', function () {
122
146
  it('should set job to errored in db', async function () {
123
147
  const programHandler = ProgramHandler.create(programsRepository, queueManager);
124
148
 
@@ -128,12 +152,12 @@ describe('ProgramHandler', function () {
128
152
  await programHandler.setProgramToError(runId, 'Something wrong happened!');
129
153
 
130
154
  const program = await programsRepository.getProgramByRunId(runId);
131
- expect(program.erroredAt).not.to.be.undefined;
132
- expect(program.errorMessage).to.equal('Something wrong happened!');
155
+ expect(program.erroredAt).not.toBe(undefined);
156
+ expect(program.errorMessage).toEqual('Something wrong happened!');
133
157
  });
134
158
  });
135
159
 
136
- describe('#isProgramFinishedWithError', async function () {
160
+ describe('#isProgramFinishedWithError', function () {
137
161
  it('should return true if erroredAt is not null', async function () {
138
162
  const programHandler = ProgramHandler.create(programsRepository, queueManager);
139
163
 
@@ -143,7 +167,7 @@ describe('ProgramHandler', function () {
143
167
  await programHandler.setProgramToError(runId, 'Something wrong happened!');
144
168
 
145
169
  const result = await programHandler.isProgramFinishedWithError(runId);
146
- expect(result).to.be.true;
170
+ expect(result).toBe(true);
147
171
  });
148
172
 
149
173
  it('should return false if erroredAt is null', async function () {
@@ -154,11 +178,11 @@ describe('ProgramHandler', function () {
154
178
  });
155
179
 
156
180
  const result = await programHandler.isProgramFinishedWithError(runId);
157
- expect(result).to.be.false;
181
+ expect(result).toBe(false);
158
182
  });
159
183
  });
160
184
 
161
- describe('#setJobRetriableErrorMessage', async function () {
185
+ describe('#setJobRetriableErrorMessage', function () {
162
186
  it('should set program to errored in db', async function () {
163
187
  const programHandler = ProgramHandler.create(programsRepository, queueManager);
164
188
 
@@ -168,13 +192,13 @@ describe('ProgramHandler', function () {
168
192
  await programHandler.setJobRetriableErrorMessage(runId, 'Something wrong happened!');
169
193
 
170
194
  const program = await programsRepository.getProgramByRunId(runId);
171
- expect(program.finishedAt).to.eql(null);
172
- expect(program.erroredAt).to.eql(null);
173
- expect(program.errorMessage).to.eql('Something wrong happened!');
195
+ expect(program.finishedAt).toEqual(null);
196
+ expect(program.erroredAt).toEqual(null);
197
+ expect(program.errorMessage).toEqual('Something wrong happened!');
174
198
  });
175
199
  });
176
200
 
177
- describe('#incrementStep', async function () {
201
+ describe('#incrementStep', function () {
178
202
  it('should increment step', async function () {
179
203
  const programHandler = ProgramHandler.create(programsRepository, queueManager);
180
204
 
@@ -184,11 +208,11 @@ describe('ProgramHandler', function () {
184
208
  await programHandler.incrementStep(runId);
185
209
 
186
210
  const program = await programsRepository.getProgramByRunId(runId);
187
- expect(program.step).to.eql(1);
211
+ expect(program.step).toEqual(1);
188
212
  });
189
213
  });
190
214
 
191
- describe('#incrementStepRetryCount', async function () {
215
+ describe('#incrementStepRetryCount', function () {
192
216
  it('should increment StepRetryCount', async function () {
193
217
  const programHandler = ProgramHandler.create(programsRepository, queueManager);
194
218
 
@@ -198,11 +222,11 @@ describe('ProgramHandler', function () {
198
222
  await programHandler.incrementStepRetryCount(runId);
199
223
 
200
224
  const program = await programsRepository.getProgramByRunId(runId);
201
- expect(program.stepRetryCount).to.eql(1);
225
+ expect(program.stepRetryCount).toEqual(1);
202
226
  });
203
227
  });
204
228
 
205
- describe('#updateJobData', async function () {
229
+ describe('#updateJobData', function () {
206
230
  it('should update job data related to jobName overwriting the full payload', async function () {
207
231
  const programHandler = ProgramHandler.create(programsRepository, queueManager);
208
232
  const runId = await programHandler.createProgram({
@@ -213,7 +237,7 @@ describe('ProgramHandler', function () {
213
237
  await programHandler.updateJobData(runId, 'contact-sync', { page: 10 });
214
238
 
215
239
  const program = await programsRepository.getProgramByRunId(runId);
216
- expect(program.jobData).to.eql({
240
+ expect(program.jobData).toEqual({
217
241
  'contact-sync': { page: 10 }
218
242
  });
219
243
  });
@@ -228,7 +252,7 @@ describe('ProgramHandler', function () {
228
252
  await programHandler.updateJobData(runId, 'contact-sync', { page: 10 }, true);
229
253
 
230
254
  const program = await programsRepository.getProgramByRunId(runId);
231
- expect(program.jobData).to.eql({
255
+ expect(program.jobData).toEqual({
232
256
  'contact-sync': { this_wont_be_gone: ':)', page: 10 }
233
257
  });
234
258
  });
@@ -1,27 +1,26 @@
1
1
  'use strict';
2
2
 
3
- const QueueManager = require('./');
3
+ const QueueManager = require('.');
4
4
  const RabbitMq = require('@emartech/rabbitmq-client').RabbitMq;
5
-
5
+ const Logger = require('@emartech/json-logger').Logger;
6
6
  const testAmqpUrl = 'amqp://guest:guest@localhost:5672';
7
7
  const testChannelName = 'program-executor';
8
8
 
9
9
  describe('Queue-Manager', function () {
10
10
  describe('queueProgram', function () {
11
11
  it('should add proper queue item to the given channel', async function () {
12
- const rabbitMock = {
13
- insert: this.sandbox.stub().resolves(true),
14
- waitForConfirms: this.sandbox.stub().resolves(true)
15
- };
16
-
17
- this.sandbox.stub(RabbitMq, 'create').resolves(rabbitMock);
12
+ const insertMock = jest.fn();
13
+ RabbitMq.create = jest.fn().mockResolvedValue({
14
+ insert: insertMock,
15
+ waitForConfirms: jest.fn().mockResolvedValue(true)
16
+ });
18
17
 
19
18
  const queueManager = new QueueManager(testAmqpUrl, testChannelName);
20
19
  const queueData = { test_data: 123 };
21
20
 
22
21
  await queueManager.queueProgram(queueData);
23
22
 
24
- expect(RabbitMq.create).to.have.been.calledWith(
23
+ expect(RabbitMq.create).toHaveBeenCalledWith(
25
24
  { programExecutor: { url: testAmqpUrl, useConfirmChannel: true } },
26
25
  testChannelName,
27
26
  'programExecutor',
@@ -30,24 +29,22 @@ describe('Queue-Manager', function () {
30
29
  deadLetterRoutingKey: `${testChannelName}-retry-1000`
31
30
  }
32
31
  );
33
- expect(rabbitMock.insert).to.have.been.calledWith(queueData);
32
+ expect(insertMock).toHaveBeenCalledWith(queueData, expect.anything());
34
33
  });
35
34
 
36
35
  it('should log if confirmation fails instead of throwing', async function () {
37
36
  const errorToThrow = new Error('Boom!');
38
- const rabbitMock = {
39
- insert: this.sandbox.stub().resolves(true),
40
- waitForConfirms: this.sandbox.stub().rejects(errorToThrow)
41
- };
42
-
43
- this.sandbox.stub(RabbitMq, 'create').resolves(rabbitMock);
37
+ Logger.prototype.fromError = jest.fn();
38
+ const insertMock = jest.fn();
39
+ RabbitMq.create = jest.fn().mockResolvedValue({
40
+ insert: insertMock,
41
+ waitForConfirms: jest.fn(() => { throw errorToThrow; })
42
+ });
44
43
 
45
44
  const queueManager = new QueueManager(testAmqpUrl, testChannelName);
46
45
  const queueData = { test_data: 123 };
47
-
48
46
  await queueManager.queueProgram(queueData);
49
-
50
- expect(this.formErrorStub).to.have.been.calledWith('confirm-error', errorToThrow);
47
+ expect(Logger.prototype.fromError ).toHaveBeenCalledWith('confirm-error', errorToThrow, {queue_data: JSON.stringify(queueData)});
51
48
  });
52
49
  });
53
50
  });
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ const PubSubClient = require('@emartech/pubsub-client-js').PubSubClient;
4
+ const logger = require('@emartech/json-logger')('program-executor-queue-manager-pubsub');
5
+
6
+ class QueueManager {
7
+ constructor(topicName, projectId, gcpKeyFileName) {
8
+ this._gcpKeyFileName = gcpKeyFileName;
9
+ this._projectId = projectId;
10
+ this._topicName = topicName;
11
+ }
12
+
13
+ async queueProgram(queueData) {
14
+ try {
15
+ const pubsub = await PubSubClient.create(
16
+ this._topicName, this._projectId, this._gcpKeyFileName
17
+ );
18
+
19
+ await pubsub.insert(queueData);
20
+
21
+ } catch (error) {
22
+ logger.fromError('queue-error', error, { queue_data: JSON.stringify(queueData) });
23
+ PubSubClient.clearPublisherCache();
24
+ throw error;
25
+ }
26
+ }
27
+
28
+ static create(topicName, projectId, gcpKeyFileName) {
29
+ return new QueueManager(topicName, projectId, gcpKeyFileName);
30
+ }
31
+ }
32
+
33
+ module.exports = QueueManager;
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ const QueueManager = require('.');
4
+ const PubSubClient = require('@emartech/pubsub-client-js').PubSubClient;
5
+ const Logger = require('@emartech/json-logger').Logger;
6
+ const testProjectId = 'test-project';
7
+ const testGcpKeyFileName = { key: 'test-key-file.json' };
8
+ const testTopicName = 'program-executor';
9
+
10
+ describe('Queue-Manager', function () {
11
+ describe('queueProgram', function () {
12
+ it('should add proper queue item to the given topic', async function () {
13
+ const insertMock = jest.fn();
14
+ PubSubClient.create = jest.fn().mockResolvedValue({
15
+ insert: insertMock
16
+ });
17
+
18
+ const queueManager = new QueueManager(testTopicName, testProjectId, testGcpKeyFileName);
19
+ const queueData = { test_data: 123 };
20
+
21
+ await queueManager.queueProgram(queueData);
22
+
23
+ expect(PubSubClient.create).toHaveBeenCalledWith(
24
+ testTopicName, testProjectId, testGcpKeyFileName
25
+ );
26
+ expect(insertMock).toHaveBeenCalledWith(queueData);
27
+ });
28
+
29
+ it('should log if confirmation fails instead of throwing', async function () {
30
+ const errorToThrow = new Error('Boom!');
31
+ Logger.prototype.fromError = jest.fn();
32
+ const insertMock = jest.fn().mockRejectedValue(errorToThrow);
33
+ PubSubClient.create = jest.fn().mockResolvedValue({
34
+ insert: insertMock
35
+ });
36
+
37
+ const queueManager = new QueueManager(testTopicName, testProjectId, testGcpKeyFileName);
38
+ const queueData = { test_data: 123 };
39
+ await expect(queueManager.queueProgram(queueData)).rejects.toThrow(errorToThrow);
40
+ expect(Logger.prototype.fromError)
41
+ .toHaveBeenCalledWith('queue-error', errorToThrow, { queue_data: JSON.stringify(queueData) });
42
+ });
43
+ });
44
+ });