@vida-global/core 2.0.5 → 2.0.7

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.
@@ -1,8 +1,9 @@
1
1
  const { BaseRecord } = require('../../lib/activeRecord/baseRecord');
2
+ const { Connection } = require('../../lib/activeRecord/db/connection');
2
3
  const { getActiveRecordSchema } = require('../../lib/activeRecord/db/schema');
3
4
  const importSchema = require('../../lib/activeRecord/db/importSchema');
4
5
  const { Model, Sequelize } = require('sequelize');
5
- const { redisClientFactory } = require('../../lib/redis');
6
+ const { mockRedisClientFactory } = require('./helpers/baseRecordMocks');
6
7
  const TestHelpers = require('@vida-global/test-helpers');
7
8
  const { camelize } = require('inflection');
8
9
  const Helpers = require('./helpers/baseRecord');
@@ -13,11 +14,6 @@ jest.mock('sequelize', () => {
13
14
  return { Model: MockModel, Sequelize: MockSequelize } ;
14
15
  });
15
16
 
16
- jest.mock('../../lib/redis', () => {
17
- const { mockRedisClientFactory } = require('./helpers/baseRecordMocks');
18
- return { redisClientFactory: mockRedisClientFactory };
19
- });
20
-
21
17
  jest.mock('../../lib/activeRecord/db/importSchema', () => jest.fn());
22
18
  importSchema.mockImplementation(() => structuredClone(Helpers.schema));
23
19
 
@@ -40,6 +36,50 @@ describe('BaseRecord', () => {
40
36
  });
41
37
 
42
38
 
39
+ describe('BaseRecord.buildConnection', () => {
40
+ it ('builds an instance of the connectionClass', () => {
41
+ class FakeConnection {}
42
+ class User extends BaseRecord {
43
+ static get connectionClass() { return FakeConnection; }
44
+ }
45
+ expect(User.buildConnection()).toBeInstanceOf(FakeConnection);
46
+ });
47
+
48
+ it ('passes the model databaseId to the connection', () => {
49
+ const databaseId = TestHelpers.Faker.Text.randomString();
50
+ let receivedId;
51
+ class FakeConnection {
52
+ constructor(id) { receivedId = id; }
53
+ }
54
+ class User extends BaseRecord {
55
+ static get connectionClass() { return FakeConnection; }
56
+ static get databaseId() { return databaseId; }
57
+ }
58
+
59
+ User.buildConnection();
60
+
61
+ expect(receivedId).toEqual(databaseId);
62
+ });
63
+ });
64
+
65
+
66
+ describe('BaseRecord.connection', () => {
67
+ it ('memoizes the connection built by buildConnection', () => {
68
+ class FakeConnection {}
69
+ class User extends BaseRecord {
70
+ static get connectionClass() { return FakeConnection; }
71
+ }
72
+ const buildSpy = jest.spyOn(User, 'buildConnection');
73
+
74
+ const first = User.connection;
75
+ const second = User.connection;
76
+
77
+ expect(first).toBe(second);
78
+ expect(buildSpy).toHaveBeenCalledTimes(1);
79
+ });
80
+ });
81
+
82
+
43
83
  describe('BaseRecord.initialize', () => {
44
84
  it ('calls `getBaseRecordSchema` and passes the response to init', () => {
45
85
  class User extends BaseRecord {}
@@ -331,11 +371,12 @@ describe('BaseRecord', () => {
331
371
 
332
372
  describe('Caching', () => {
333
373
  class Person extends BaseRecord {
334
- static get isCacheable() { return true; }
374
+ static get isCacheable() { return true; }
375
+ static get redisClientFactory() { return mockRedisClientFactory; }
335
376
  }
336
377
  let redisClient
337
- beforeEach(async () => {
338
- redisClient = await redisClientFactory();
378
+ beforeEach(() => {
379
+ redisClient = mockRedisClientFactory();
339
380
  redisClient.mGet.mockImplementation(keys => new Array(keys.length));
340
381
  });
341
382
 
@@ -617,7 +658,8 @@ describe('BaseRecord', () => {
617
658
  const hooks = ['Save', 'Destroy'];
618
659
  describe.each(hooks)('BaseRecord._after%sCacheHook', (hook) => {
619
660
  class Cacheable extends BaseRecord {
620
- static get isCacheable() { return true; }
661
+ static get isCacheable() { return true; }
662
+ static get redisClientFactory() { return mockRedisClientFactory; }
621
663
  }
622
664
  const hookMethod = `_after${hook}CacheHook`;
623
665
 
@@ -1,16 +1,13 @@
1
1
  const { Connection } = require('../../../lib/activeRecord/db/connection');
2
2
  const { ConnectionConfiguration } = require('../../../lib/activeRecord/db/connectionConfiguration');
3
3
  const Helpers = require('../helpers/connection');
4
+ const { MockConnection } = require('../helpers/fixtures');
4
5
  const { snapshotEnv } = require('../../helpers/env');
5
6
 
6
7
 
7
8
  let fetchConfigsSpy;
8
9
  let restoreEnv;
9
10
 
10
- jest.mock('sequelize', () => {
11
- const { MockSequelize } = require('../helpers/fixtures');
12
- return { Sequelize: MockSequelize };
13
- });
14
11
 
15
12
  beforeEach(() => {
16
13
  restoreEnv = snapshotEnv(['NODE_ENV']);
@@ -28,9 +25,9 @@ afterEach(() => {
28
25
  describe('Connection', () => {
29
26
  describe('Connection#_sequelize', () => {
30
27
  it ('caches connections per database id', () => {
31
- const conn1 = new Connection();
32
- const conn2 = new Connection();
33
- const conn3 = new Connection(Helpers.databaseId1);
28
+ const conn1 = new MockConnection();
29
+ const conn2 = new MockConnection();
30
+ const conn3 = new MockConnection(Helpers.databaseId1);
34
31
 
35
32
  expect(conn1._sequelize).toBe(conn2._sequelize);
36
33
  expect(conn1._sequelize).not.toBe(conn3._sequelize);
@@ -39,8 +36,8 @@ describe('Connection', () => {
39
36
 
40
37
  describe('settings (postgres)', () => {
41
38
  it.each([
42
- ['default', () => new Connection(), Helpers.defaultConfig],
43
- ['secondary', () => new Connection(Helpers.databaseId1), Helpers.secondaryConfig],
39
+ ['default', () => new MockConnection(), Helpers.defaultConfig],
40
+ ['secondary', () => new MockConnection(Helpers.databaseId1), Helpers.secondaryConfig],
44
41
  ])('maps %s postgres config into sequelize options', (_label, connectionFactory, expectedConfig) => {
45
42
  const connection = connectionFactory();
46
43
  const options = connection._sequelize.options;
@@ -54,7 +51,7 @@ describe('Connection', () => {
54
51
  });
55
52
 
56
53
  it ('requires ssl when configured', () => {
57
- const conn = new Connection(Helpers.databaseId2);
54
+ const conn = new MockConnection(Helpers.databaseId2);
58
55
 
59
56
  expect(conn._sequelize.options.dialectOptions).toEqual({ ssl: { require: true } });
60
57
  });
@@ -64,21 +61,21 @@ describe('Connection', () => {
64
61
  ['production', false],
65
62
  ])('sets logging for %s', (environment, expectedLogging) => {
66
63
  process.env.NODE_ENV = environment;
67
- const conn = new Connection();
64
+ const conn = new MockConnection();
68
65
 
69
66
  expect(conn._sequelize.options.logging).toBe(expectedLogging);
70
67
  });
71
68
 
72
69
  it ('sets the configured pool or defaults', () => {
73
- const conn1 = new Connection();
74
- const conn2 = new Connection(Helpers.databaseId1);
70
+ const conn1 = new MockConnection();
71
+ const conn2 = new MockConnection(Helpers.databaseId1);
75
72
 
76
73
  expect(conn1._sequelize.options.pool).toEqual(Helpers.defaultConfig.pool);
77
74
  expect(conn2._sequelize.options.pool).toEqual({ min: 0, max: 5 });
78
75
  });
79
76
 
80
77
  it ('configures underscored naming', () => {
81
- const conn = new Connection();
78
+ const conn = new MockConnection();
82
79
 
83
80
  expect(conn._sequelize.options.define.underscored).toBeTruthy();
84
81
  });
@@ -89,7 +86,7 @@ describe('Connection', () => {
89
86
  });
90
87
  fetchConfigsSpy.mockImplementation(() => ({ default: { test: replicatedConfig } }));
91
88
 
92
- const conn = new Connection();
89
+ const conn = new MockConnection();
93
90
  const options = conn._sequelize.options;
94
91
 
95
92
  expect(options.database).toBe(undefined);
@@ -113,7 +110,7 @@ describe('Connection', () => {
113
110
 
114
111
  describe('settings (sqlite)', () => {
115
112
  it ('maps sqlite options', () => {
116
- const conn = new Connection('sqlite');
113
+ const conn = new MockConnection('sqlite');
117
114
 
118
115
  expect(conn._sequelize.options.dialect).toEqual('sqlite');
119
116
  expect(conn._sequelize.options.storage).toEqual(
@@ -126,7 +123,7 @@ describe('Connection', () => {
126
123
 
127
124
  describe('Connection#close', () => {
128
125
  it ('closes a single connection', () => {
129
- const conn = new Connection();
126
+ const conn = new MockConnection();
130
127
  conn._sequelize; // reference it to initiate the connection
131
128
 
132
129
  conn.close();
@@ -138,8 +135,8 @@ describe('Connection', () => {
138
135
 
139
136
  describe('Connection.closeAll', () => {
140
137
  it ('closes all cached connections', () => {
141
- const defaultConnection = new Connection();
142
- const secondaryConnection = new Connection(Helpers.databaseId1);
138
+ const defaultConnection = new MockConnection();
139
+ const secondaryConnection = new MockConnection(Helpers.databaseId1);
143
140
  defaultConnection._sequelize; // reference it to initiate the connection
144
141
  secondaryConnection._sequelize; // reference it to initiate the connection
145
142
 
@@ -1,18 +1,10 @@
1
- const { ConnectionConfiguration } = require('../../../lib/activeRecord/db/connectionConfiguration');
2
- const { Connection } = require('../../../lib/activeRecord/db/connection');
3
1
  const { QueryInterface } = require('../../../lib/activeRecord/db/queryInterface');
4
- const { Sequelize } = require('sequelize');
5
2
  const Helpers = require('../helpers/queryInterface');
6
3
 
7
4
 
8
- const connection = new Connection();
5
+ const connection = new Helpers.MockConnection();
9
6
  const queryInterface = new QueryInterface(connection);
10
7
 
11
- jest.mock('sequelize', () => {
12
- const { MockSequelize } = require('../helpers/fixtures');
13
- return { Sequelize: MockSequelize };
14
- });
15
-
16
8
  afterEach(() => {
17
9
  jest.clearAllMocks();
18
10
  });
@@ -1,32 +1,32 @@
1
1
  jest.mock('../../../lib/activeRecord/db/importSchema', () => jest.fn());
2
2
 
3
- jest.mock('../../../lib/activeRecord/db/connection', () => {
4
- const clearConnectionsCache = jest.fn();
5
- const allTables = jest.fn();
6
- function Connection() {
7
- this.queryInterface = { allTables };
8
- }
9
- Connection.clearConnectionsCache = clearConnectionsCache;
10
- Connection.__allTables = allTables;
11
- return { Connection };
12
- });
13
-
14
- jest.mock('../../../lib/activeRecord/db/connectionConfiguration', () => ({
15
- ConnectionConfiguration: { configuredDatabaseIds: jest.fn() }
16
- }));
17
-
18
3
  jest.mock('../../../lib/activeRecord/utils', () => {
19
4
  const actual = jest.requireActual('../../../lib/activeRecord/utils');
20
5
  return { ...actual, ensureSchemasDirectory: jest.fn() };
21
6
  });
22
7
 
23
- const fs = require('node:fs');
24
- const importSchema = require('../../../lib/activeRecord/db/importSchema');
25
- const { Connection } = require('../../../lib/activeRecord/db/connection');
26
- const { ConnectionConfiguration } = require('../../../lib/activeRecord/db/connectionConfiguration');
27
- const utils = require('../../../lib/activeRecord/utils');
28
- const { generateActiveRecordSchemaFiles, getActiveRecordSchema } = require('../../../lib/activeRecord/db/schema');
29
- const { Sequelize } = require('sequelize');
8
+ const fs = require('node:fs');
9
+ const importSchema = require('../../../lib/activeRecord/db/importSchema');
10
+ const { ConnectionConfiguration } = require('../../../lib/activeRecord/db/connectionConfiguration');
11
+ const utils = require('../../../lib/activeRecord/utils');
12
+ const { SchemaFileGenerator, generateActiveRecordSchemaFiles, getActiveRecordSchema } = require('../../../lib/activeRecord/db/schema');
13
+ const { Sequelize } = require('sequelize');
14
+
15
+
16
+ const allTables = jest.fn();
17
+ const clearConnectionsCache = jest.fn();
18
+
19
+
20
+ class FakeConnection {
21
+ static clearConnectionsCache = clearConnectionsCache;
22
+ get queryInterface() { return { allTables }; }
23
+ }
24
+
25
+
26
+ class TestSchemaFileGenerator extends SchemaFileGenerator {
27
+ get connectionClass() { return FakeConnection; }
28
+ }
29
+
30
30
 
31
31
  afterEach(() => {
32
32
  jest.clearAllMocks();
@@ -157,61 +157,79 @@ describe('activeRecord/db/schema', () => {
157
157
  });
158
158
 
159
159
 
160
- describe('.generateActiveRecordSchemaFiles', () => {
160
+ describe('SchemaFileGenerator', () => {
161
161
  let writeFileSpy;
162
+ let databaseIdsSpy;
162
163
 
163
164
  beforeEach(() => {
164
- writeFileSpy = jest.spyOn(fs, 'writeFile').mockImplementation((p, c, cb) => cb && cb(null));
165
+ writeFileSpy = jest.spyOn(fs, 'writeFile').mockImplementation((p, c, cb) => cb && cb(null));
166
+ databaseIdsSpy = jest.spyOn(ConnectionConfiguration, 'configuredDatabaseIds');
165
167
  });
166
168
 
167
169
  afterEach(() => {
168
170
  writeFileSpy.mockRestore();
171
+ databaseIdsSpy.mockRestore();
169
172
  });
170
173
 
171
- it ('ensures the schemas directory exists before writing', async () => {
172
- ConnectionConfiguration.configuredDatabaseIds.mockReturnValue([]);
173
- await generateActiveRecordSchemaFiles();
174
- expect(utils.ensureSchemasDirectory).toHaveBeenCalledTimes(1);
175
- });
176
174
 
177
- it ('writes one schema file per configured database id', async () => {
178
- ConnectionConfiguration.configuredDatabaseIds.mockReturnValue(['main', 'analytics']);
179
- Connection.__allTables.mockResolvedValue({ users: { id: { type: 'INTEGER' } } });
175
+ describe('#run', () => {
176
+ it ('ensures the schemas directory exists before writing', async () => {
177
+ databaseIdsSpy.mockReturnValue([]);
178
+ await new TestSchemaFileGenerator().run();
179
+ expect(utils.ensureSchemasDirectory).toHaveBeenCalledTimes(1);
180
+ });
180
181
 
181
- await generateActiveRecordSchemaFiles();
182
- expect(Connection.clearConnectionsCache).toHaveBeenCalledTimes(2);
183
- expect(writeFileSpy).toHaveBeenCalledTimes(2);
182
+ it ('writes one schema file per configured database id', async () => {
183
+ databaseIdsSpy.mockReturnValue(['main', 'analytics']);
184
+ allTables.mockResolvedValue({ users: { id: { type: 'INTEGER' } } });
184
185
 
185
- const writtenPaths = writeFileSpy.mock.calls.map(c => c[0]);
186
- expect(writtenPaths[0]).toEqual(`${utils.schemasDirectory}/main.js`);
187
- expect(writtenPaths[1]).toEqual(`${utils.schemasDirectory}/analytics.js`);
186
+ await new TestSchemaFileGenerator().run();
187
+ expect(clearConnectionsCache).toHaveBeenCalledTimes(2);
188
+ expect(writeFileSpy).toHaveBeenCalledTimes(2);
188
189
 
189
- const writtenBody = writeFileSpy.mock.calls[0][1];
190
- expect(writtenBody).toContain('module.exports = ');
191
- expect(writtenBody).toContain('"users"');
192
- });
190
+ const writtenPaths = writeFileSpy.mock.calls.map(c => c[0]);
191
+ expect(writtenPaths[0]).toEqual(`${utils.schemasDirectory}/main.js`);
192
+ expect(writtenPaths[1]).toEqual(`${utils.schemasDirectory}/analytics.js`);
193
193
 
194
- it ('logs an error when fs.writeFile fails (does not reject)', async () => {
195
- const { logger } = require('../../../lib/logger');
196
- const errSpy = jest.spyOn(logger, 'error').mockImplementation(() => {});
197
- ConnectionConfiguration.configuredDatabaseIds.mockReturnValue(['main']);
198
- Connection.__allTables.mockResolvedValue({});
199
- writeFileSpy.mockImplementation((p, c, cb) => cb(new Error('disk full')));
194
+ const writtenBody = writeFileSpy.mock.calls[0][1];
195
+ expect(writtenBody).toContain('module.exports = ');
196
+ expect(writtenBody).toContain('"users"');
197
+ });
200
198
 
201
- await generateActiveRecordSchemaFiles();
202
- expect(errSpy).toHaveBeenCalledWith(expect.stringContaining('Unable to write new schema file'));
203
- errSpy.mockRestore();
199
+ it ('logs an error when fs.writeFile fails (does not reject)', async () => {
200
+ const { logger } = require('../../../lib/logger');
201
+ const errSpy = jest.spyOn(logger, 'error').mockImplementation(() => {});
202
+ databaseIdsSpy.mockReturnValue(['main']);
203
+ allTables.mockResolvedValue({});
204
+ writeFileSpy.mockImplementation((p, c, cb) => cb(new Error('disk full')));
205
+
206
+ await new TestSchemaFileGenerator().run();
207
+ expect(errSpy).toHaveBeenCalledWith(expect.stringContaining('Unable to write new schema file'));
208
+ errSpy.mockRestore();
209
+ });
210
+
211
+ it ('logs success when fs.writeFile succeeds', async () => {
212
+ const { logger } = require('../../../lib/logger');
213
+ const infoSpy = jest.spyOn(logger, 'info').mockImplementation(() => {});
214
+ databaseIdsSpy.mockReturnValue(['main']);
215
+ allTables.mockResolvedValue({});
216
+
217
+ await new TestSchemaFileGenerator().run();
218
+ expect(infoSpy).toHaveBeenCalledWith(expect.stringContaining('New schemas file written'));
219
+ infoSpy.mockRestore();
220
+ });
204
221
  });
222
+ });
205
223
 
206
- it ('logs success when fs.writeFile succeeds', async () => {
207
- const { logger } = require('../../../lib/logger');
208
- const infoSpy = jest.spyOn(logger, 'info').mockImplementation(() => {});
209
- ConnectionConfiguration.configuredDatabaseIds.mockReturnValue(['main']);
210
- Connection.__allTables.mockResolvedValue({});
224
+
225
+ describe('.generateActiveRecordSchemaFiles', () => {
226
+ it ('runs a SchemaFileGenerator', async () => {
227
+ const runSpy = jest.spyOn(SchemaFileGenerator.prototype, 'run').mockResolvedValue();
211
228
 
212
229
  await generateActiveRecordSchemaFiles();
213
- expect(infoSpy).toHaveBeenCalledWith(expect.stringContaining('New schemas file written'));
214
- infoSpy.mockRestore();
230
+
231
+ expect(runSpy).toHaveBeenCalledTimes(1);
232
+ runSpy.mockRestore();
215
233
  });
216
234
  });
217
235
  });
@@ -1,4 +1,5 @@
1
- const TestHelpers = require('@vida-global/test-helpers');
1
+ const { Connection } = require('../../../lib/activeRecord/db/connection');
2
+ const TestHelpers = require('@vida-global/test-helpers');
2
3
 
3
4
  /***************************************************************************************************
4
5
  * VARIABLES / VARIABLE GENERATION
@@ -31,9 +32,17 @@ class MockSequelize {
31
32
  }
32
33
 
33
34
 
35
+ class MockConnection extends Connection {
36
+ get ormEngineClass() {
37
+ return MockSequelize;
38
+ }
39
+ }
40
+
41
+
34
42
  module.exports = {
35
43
  buildPostgresConfig,
36
44
  databaseId1,
37
45
  databaseId2,
46
+ MockConnection,
38
47
  MockSequelize,
39
48
  };
@@ -1,8 +1,6 @@
1
1
  const { Connection } = require('../../../lib/activeRecord/db/connection');
2
2
  const { ConnectionConfiguration } = require('../../../lib/activeRecord/db/connectionConfiguration');
3
3
  const Fixtures = require('../helpers/fixtures');
4
- const { QueryInterface } = require('../../../lib/activeRecord/db/queryInterface');
5
- const { Sequelize } = require('sequelize');
6
4
  const TestHelpers = require('@vida-global/test-helpers');
7
5
 
8
6
 
@@ -18,11 +16,24 @@ const connectionConfig = { database: ' ', host: ' ', password: ' ', username: '
18
16
  const configSpy = jest.spyOn(ConnectionConfiguration, '_fetchAllConfigs');
19
17
  configSpy.mockImplementation(() => ({ default: { test: connectionConfig } }));
20
18
 
21
- Sequelize.prototype.getQueryInterface = () => mockQueryInterface;
19
+
20
+ /***************************************************************************************************
21
+ * MOCKS
22
+ ***************************************************************************************************/
23
+ class MockSequelize extends Fixtures.MockSequelize {
24
+ getQueryInterface() { return mockQueryInterface; }
25
+ }
26
+
27
+
28
+ class MockConnection extends Connection {
29
+ get ormEngineClass() { return MockSequelize; }
30
+ }
22
31
 
23
32
 
24
33
  module.exports = {
25
34
  ...Fixtures,
35
+ MockConnection,
36
+ MockSequelize,
26
37
  mockQueryInterface,
27
38
  tableName1,
28
39
  tableName2,
@@ -1,34 +1,6 @@
1
- const TestHelpers = require('@vida-global/test-helpers');
2
- const { defineJobSubclass } = require('./helpers/abstractJob');
3
-
4
-
5
- jest.mock('../../lib/jobQueue/queue', () => ({
6
- getQueue: jest.fn(),
7
- }));
8
-
9
- jest.mock('../../lib/logger', () => {
10
- const { buildLoggerMock } = require('./helpers/loggerMock');
11
- return { logger: buildLoggerMock() };
12
- });
13
-
14
- jest.mock('../../lib/apm', () => {
15
- const { buildApmMock } = require('./helpers/apmMock');
16
- return buildApmMock();
17
- });
18
-
19
-
20
- let AbstractJob;
21
- let getQueue;
22
- let logger;
23
- let APM;
24
-
25
- beforeEach(() => {
26
- jest.resetModules();
27
- ({ AbstractJob } = require('../../lib/jobQueue/abstractJob'));
28
- ({ getQueue } = require('../../lib/jobQueue/queue'));
29
- ({ logger } = require('../../lib/logger'));
30
- APM = require('../../lib/apm');
31
- });
1
+ const TestHelpers = require('@vida-global/test-helpers');
2
+ const { defineJobSubclass } = require('./helpers/abstractJob');
3
+ const { AbstractJob } = require('../../lib/jobQueue/abstractJob');
32
4
 
33
5
 
34
6
  afterEach(() => {
@@ -71,12 +43,12 @@ describe('AbstractJob', () => {
71
43
  const name = TestHelpers.Faker.Text.randomString();
72
44
  const Subclass = defineJobSubclass(AbstractJob, { queueName: name });
73
45
  const stubQueue = { queueJob: jest.fn() };
74
- getQueue.mockReturnValue(stubQueue);
46
+ Subclass.queueResolver.mockReturnValue(stubQueue);
75
47
 
76
48
  const result = Subclass.queue('arg1', 'arg2');
77
49
 
78
- expect(getQueue).toHaveBeenCalledTimes(1);
79
- expect(getQueue).toHaveBeenCalledWith(name);
50
+ expect(Subclass.queueResolver).toHaveBeenCalledTimes(1);
51
+ expect(Subclass.queueResolver).toHaveBeenCalledWith(name);
80
52
  expect(result).toBe(stubQueue);
81
53
  });
82
54
 
@@ -85,12 +57,12 @@ describe('AbstractJob', () => {
85
57
  const arg2 = TestHelpers.Faker.Math.randomNumber();
86
58
  const queueNameSpy = jest.fn(() => 'dynamic-queue');
87
59
  const Subclass = defineJobSubclass(AbstractJob, { queueNameFn: queueNameSpy });
88
- getQueue.mockReturnValue({ queueJob: jest.fn() });
60
+ Subclass.queueResolver.mockReturnValue({ queueJob: jest.fn() });
89
61
 
90
62
  Subclass.queue(arg1, arg2);
91
63
 
92
64
  expect(queueNameSpy).toHaveBeenCalledWith(arg1, arg2);
93
- expect(getQueue).toHaveBeenCalledWith('dynamic-queue');
65
+ expect(Subclass.queueResolver).toHaveBeenCalledWith('dynamic-queue');
94
66
  });
95
67
  });
96
68
 
@@ -100,7 +72,7 @@ describe('AbstractJob', () => {
100
72
  const name = TestHelpers.Faker.Text.randomString();
101
73
  const Subclass = defineJobSubclass(AbstractJob, { queueName: name });
102
74
  const stubQueue = { queueJob: jest.fn().mockResolvedValue(undefined) };
103
- getQueue.mockReturnValue(stubQueue);
75
+ Subclass.queueResolver.mockReturnValue(stubQueue);
104
76
  const arg1 = TestHelpers.Faker.Text.randomString();
105
77
  const arg2 = TestHelpers.Faker.Math.randomNumber();
106
78
 
@@ -118,7 +90,7 @@ describe('AbstractJob', () => {
118
90
  const Subclass = defineJobSubclass(AbstractJob);
119
91
  let resolveQueueJob;
120
92
  const queueJobPromise = new Promise((resolve) => { resolveQueueJob = resolve; });
121
- getQueue.mockReturnValue({ queueJob: jest.fn().mockReturnValue(queueJobPromise) });
93
+ Subclass.queueResolver.mockReturnValue({ queueJob: jest.fn().mockReturnValue(queueJobPromise) });
122
94
 
123
95
  const callPromise = Subclass.queueJob();
124
96
  let resolved = false;
@@ -267,8 +239,8 @@ describe('AbstractJob', () => {
267
239
 
268
240
  job.addApmAttribute(key, value);
269
241
 
270
- expect(APM.addCustomAttribute).toHaveBeenCalledTimes(1);
271
- expect(APM.addCustomAttribute).toHaveBeenCalledWith(key, value);
242
+ expect(job.apm.addCustomAttribute).toHaveBeenCalledTimes(1);
243
+ expect(job.apm.addCustomAttribute).toHaveBeenCalledWith(key, value);
272
244
  });
273
245
  });
274
246
 
@@ -283,8 +255,8 @@ describe('AbstractJob', () => {
283
255
 
284
256
  const result = job.startApmSegment(name, true, handler);
285
257
 
286
- expect(APM.startSegment).toHaveBeenCalledTimes(1);
287
- expect(APM.startSegment).toHaveBeenCalledWith(name, true, handler);
258
+ expect(job.apm.startSegment).toHaveBeenCalledTimes(1);
259
+ expect(job.apm.startSegment).toHaveBeenCalledWith(name, true, handler);
288
260
  expect(result).toEqual(sentinel);
289
261
  });
290
262
  });
@@ -297,7 +269,7 @@ describe('AbstractJob', () => {
297
269
  const id = TestHelpers.Faker.Text.randomString();
298
270
  await job._run(id, [], jest.fn());
299
271
 
300
- const scope = logger[Subclass.name];
272
+ const scope = job.loggerLibrary[Subclass.name];
301
273
  const child = job.logger;
302
274
 
303
275
  expect(scope.child).toHaveBeenCalledWith(id);
@@ -1,14 +1,25 @@
1
- const TestHelpers = require('@vida-global/test-helpers');
1
+ const TestHelpers = require('@vida-global/test-helpers');
2
+ const { buildLoggerMock } = require('./loggerMock');
3
+ const { buildApmMock } = require('./apmMock');
2
4
 
3
5
 
4
6
  /***************************************************************************************************
5
7
  * SUBCLASS FACTORY
8
+ * Each subclass injects the queueResolver/logger/apm seams so tests assert on the injected doubles
9
+ * (Subclass.queueResolver, job.loggerLibrary, job.apm) instead of mocking the modules.
6
10
  ***************************************************************************************************/
7
11
  function defineJobSubclass(AbstractJob, { queueName, queueNameFn, run, ...overrides } = {}) {
8
- const resolvedName = queueName || TestHelpers.Faker.Text.randomString();
9
- const nameFunction = queueNameFn || (() => resolvedName);
12
+ const resolvedName = queueName || TestHelpers.Faker.Text.randomString();
13
+ const nameFunction = queueNameFn || (() => resolvedName);
14
+ const loggerMock = buildLoggerMock();
15
+ const apmMock = buildApmMock();
16
+ const queueResolver = jest.fn();
17
+
10
18
  const Subclass = class extends AbstractJob {
11
19
  static queueName(...jobArgs) { return nameFunction(...jobArgs); }
20
+ static get queueResolver() { return queueResolver; }
21
+ get loggerLibrary() { return loggerMock; }
22
+ get apm() { return apmMock; }
12
23
  async run(...args) {
13
24
  if (run) return await run(...args);
14
25
  }
@@ -0,0 +1,26 @@
1
+ /***************************************************************************************************
2
+ * QUEUE FACTORY
3
+ * bullmqMock/loggerMock are required lazily so the mock instances align with whatever the test
4
+ * re-requires after jest.resetModules(). MockQueue injects the BullMQ, crypto, and logger seams.
5
+ ***************************************************************************************************/
6
+ function buildQueue(queueName) {
7
+ const { Queue } = require('../../../lib/jobQueue/queue');
8
+ const bullmqMock = require('./bullmqMock');
9
+ const { buildLoggerMock } = require('./loggerMock');
10
+
11
+ const loggerMock = buildLoggerMock();
12
+ const idGenerator = jest.fn();
13
+
14
+ class MockQueue extends Queue {
15
+ get queueClass() { return bullmqMock.Queue; }
16
+ get idGenerator() { return idGenerator; }
17
+ get loggerLibrary() { return loggerMock; }
18
+ }
19
+
20
+ return new MockQueue(queueName);
21
+ }
22
+
23
+
24
+ module.exports = {
25
+ buildQueue,
26
+ }