@vida-global/core 2.0.6 → 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.
- package/lib/activeRecord/baseRecord.js +17 -2
- package/lib/activeRecord/db/connection.js +42 -17
- package/lib/activeRecord/db/schema.js +47 -16
- package/lib/jobQueue/abstractJob.js +19 -4
- package/lib/jobQueue/queue.js +8 -4
- package/lib/jobQueue/worker.js +35 -20
- package/lib/logger/index.js +10 -6
- package/lib/logger/serverMiddleware.js +41 -26
- package/lib/server/server.js +25 -10
- package/package.json +1 -1
- package/test/activeRecord/baseRecord.test.js +52 -10
- package/test/activeRecord/db/connection.test.js +16 -19
- package/test/activeRecord/db/queryInterface.test.js +1 -9
- package/test/activeRecord/db/schema.test.js +76 -58
- package/test/activeRecord/helpers/fixtures.js +10 -1
- package/test/activeRecord/helpers/queryInterface.js +14 -3
- package/test/jobQueue/abstractJob.test.js +15 -43
- package/test/jobQueue/helpers/abstractJob.js +14 -3
- package/test/jobQueue/helpers/queue.js +26 -0
- package/test/jobQueue/helpers/worker.js +13 -0
- package/test/jobQueue/queue.test.js +7 -26
- package/test/jobQueue/worker.test.js +28 -48
- package/test/logger/index.test.js +26 -0
- package/test/logger/serverMiddleware.test.js +6 -15
- package/test/server/helpers/server.js +40 -6
- package/test/server/server.test.js +6 -35
|
@@ -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 {
|
|
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()
|
|
374
|
+
static get isCacheable() { return true; }
|
|
375
|
+
static get redisClientFactory() { return mockRedisClientFactory; }
|
|
335
376
|
}
|
|
336
377
|
let redisClient
|
|
337
|
-
beforeEach(
|
|
338
|
-
redisClient =
|
|
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()
|
|
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
|
|
32
|
-
const conn2 = new
|
|
33
|
-
const conn3 = new
|
|
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
|
|
43
|
-
['secondary', () => new
|
|
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
|
|
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
|
|
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
|
|
74
|
-
const conn2 = new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
142
|
-
const secondaryConnection = new
|
|
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
|
|
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
|
|
24
|
-
const importSchema
|
|
25
|
-
const {
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const {
|
|
29
|
-
|
|
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('
|
|
160
|
+
describe('SchemaFileGenerator', () => {
|
|
161
161
|
let writeFileSpy;
|
|
162
|
+
let databaseIdsSpy;
|
|
162
163
|
|
|
163
164
|
beforeEach(() => {
|
|
164
|
-
writeFileSpy
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
186
|
+
await new TestSchemaFileGenerator().run();
|
|
187
|
+
expect(clearConnectionsCache).toHaveBeenCalledTimes(2);
|
|
188
|
+
expect(writeFileSpy).toHaveBeenCalledTimes(2);
|
|
188
189
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
214
|
-
|
|
230
|
+
|
|
231
|
+
expect(runSpy).toHaveBeenCalledTimes(1);
|
|
232
|
+
runSpy.mockRestore();
|
|
215
233
|
});
|
|
216
234
|
});
|
|
217
235
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
const
|
|
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
|
-
|
|
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
|
|
2
|
-
const { defineJobSubclass }
|
|
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
|
-
|
|
46
|
+
Subclass.queueResolver.mockReturnValue(stubQueue);
|
|
75
47
|
|
|
76
48
|
const result = Subclass.queue('arg1', 'arg2');
|
|
77
49
|
|
|
78
|
-
expect(
|
|
79
|
-
expect(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
271
|
-
expect(
|
|
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(
|
|
287
|
-
expect(
|
|
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 =
|
|
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
|
|
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
|
|
9
|
-
const nameFunction
|
|
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
|
+
}
|