@vida-global/core 1.1.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.
- package/README.md +9 -0
- package/index.js +17 -0
- package/lib/active_record/README.md +205 -0
- package/lib/active_record/baseRecord.js +112 -0
- package/lib/active_record/db/connection.js +128 -0
- package/lib/active_record/db/connectionConfiguration.js +114 -0
- package/lib/active_record/db/importSchema.js +4 -0
- package/lib/active_record/db/migration.js +132 -0
- package/lib/active_record/db/migrationTemplate.js +8 -0
- package/lib/active_record/db/migrationVersion.js +68 -0
- package/lib/active_record/db/migrator.js +169 -0
- package/lib/active_record/db/queryInterface.js +47 -0
- package/lib/active_record/db/schema.js +113 -0
- package/lib/active_record/index.js +6 -0
- package/lib/active_record/utils.js +43 -0
- package/lib/http/README.md +32 -0
- package/lib/http/client.js +129 -0
- package/lib/http/error.js +34 -0
- package/lib/logger/README.md +2 -0
- package/lib/logger/index.js +16 -0
- package/lib/release/develop.js +27 -0
- package/lib/release/git.js +86 -0
- package/lib/release/increment.js +56 -0
- package/lib/release/index.js +10 -0
- package/lib/release/release.js +30 -0
- package/lib/release/utils.js +44 -0
- package/lib/server/README.md +37 -0
- package/lib/server/index.js +9 -0
- package/lib/server/server.js +359 -0
- package/lib/server/serverController.js +344 -0
- package/lib/server/systemController.js +23 -0
- package/package.json +37 -0
- package/scripts/active_record/migrate.js +30 -0
- package/scripts/release.js +62 -0
- package/test/active_record/baseRecord.test.js +179 -0
- package/test/active_record/db/connection.test.js +221 -0
- package/test/active_record/db/connectionConfiguration.test.js +184 -0
- package/test/active_record/db/migrator.test.js +266 -0
- package/test/active_record/db/queryInterface.test.js +66 -0
- package/test/http/client.test.js +271 -0
- package/test/http/error.test.js +71 -0
- package/test/release/develop.test.js +57 -0
- package/test/release/git.test.js +189 -0
- package/test/release/increment.test.js +145 -0
- package/test/release/release.test.js +72 -0
- package/test/release/utils.test.js +148 -0
- package/test/server/helpers/controllers/barController.js +9 -0
- package/test/server/helpers/controllers/fooController.js +48 -0
- package/test/server/helpers/controllers/sub/bazController.js +10 -0
- package/test/server/helpers/server.js +14 -0
- package/test/server/server.test.js +188 -0
- package/test/server/serverController.test.js +251 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
const { ConnectionConfiguration } = require('../../../lib/active_record/db/connectionConfiguration');
|
|
2
|
+
const TestHelpers = require('@vida-global/test-helpers');
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const test = {
|
|
6
|
+
database: TestHelpers.Faker.Text.randomString(),
|
|
7
|
+
host: TestHelpers.Faker.Text.randomString(),
|
|
8
|
+
password: TestHelpers.Faker.Text.randomString(),
|
|
9
|
+
port: Math.random(),
|
|
10
|
+
username: TestHelpers.Faker.Text.randomString()
|
|
11
|
+
}
|
|
12
|
+
const production = {
|
|
13
|
+
database: TestHelpers.Faker.Text.randomString(),
|
|
14
|
+
host: TestHelpers.Faker.Text.randomString(),
|
|
15
|
+
password: TestHelpers.Faker.Text.randomString(),
|
|
16
|
+
port: Math.random(),
|
|
17
|
+
username: TestHelpers.Faker.Text.randomString()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const sqliteConfig = {
|
|
21
|
+
dialect: 'sqlite'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const databaseId1 = TestHelpers.Faker.Text.randomString();
|
|
25
|
+
const databaseId2 = TestHelpers.Faker.Text.randomString();
|
|
26
|
+
|
|
27
|
+
const origEnvDBPoolMin = process.env.DB_POOL_MIN;
|
|
28
|
+
const origEnvDBPoolMax = process.env.DB_POOL_MAX;
|
|
29
|
+
|
|
30
|
+
const spy = jest.spyOn(ConnectionConfiguration, '_fetchAllConfigs');
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
spy.mockReset();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
describe('ConnectionConfiguration', () => {
|
|
37
|
+
it ('throws an error when there is no configuration for the database id', () => {
|
|
38
|
+
spy.mockImplementation(() => ({ test }));
|
|
39
|
+
expect(() => new ConnectionConfiguration(databaseId1)).toThrow();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it ('throws an error when there is no configuration for the environment', () => {
|
|
43
|
+
spy.mockImplementation(() => ({[databaseId1]: { production }}));
|
|
44
|
+
expect(() => new ConnectionConfiguration(databaseId1)).toThrow();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it ('throws an error when there is missing information for postgres', () => {
|
|
48
|
+
spy.mockImplementation(() => ({[databaseId1]: {test: {port: Math.random()}}}));
|
|
49
|
+
expect(() => new ConnectionConfiguration(databaseId1)).toThrow();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it ('does not require database details for sqlite', () => {
|
|
53
|
+
spy.mockImplementation(() => ({[databaseId1]: {test: sqliteConfig}}));
|
|
54
|
+
expect(() => new ConnectionConfiguration(databaseId1)).not.toThrow();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it ('returns the correct configuration values', () => {
|
|
58
|
+
spy.mockImplementation(() => {
|
|
59
|
+
return {
|
|
60
|
+
[databaseId1]: { test, production },
|
|
61
|
+
[databaseId2]: { test: {}, production: {} },
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const config = new ConnectionConfiguration(databaseId1);
|
|
66
|
+
|
|
67
|
+
expect(config.database).toEqual(test.database);
|
|
68
|
+
expect(config.host).toEqual(test.host);
|
|
69
|
+
expect(config.password).toEqual(test.password);
|
|
70
|
+
expect(config.username).toEqual(test.username);
|
|
71
|
+
expect(config.ssl).toBeFalsy();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it ('returns the correct configuration values', () => {
|
|
75
|
+
const rawConfig = {...test, ssl: true};
|
|
76
|
+
spy.mockImplementation(() => ({[databaseId1]: { test: rawConfig }}));
|
|
77
|
+
|
|
78
|
+
const config = new ConnectionConfiguration(databaseId1);
|
|
79
|
+
expect(config.ssl).toBeTruthy();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
describe('ConnectionConfiguration#loggingEnabled', () => {
|
|
84
|
+
it ('enables logging in development', () => {
|
|
85
|
+
const origEnv = process.env.NODE_ENV;
|
|
86
|
+
process.env.NODE_ENV = 'development';
|
|
87
|
+
|
|
88
|
+
spy.mockImplementation(() => ({[databaseId1]: {development: test}}));
|
|
89
|
+
|
|
90
|
+
const config = new ConnectionConfiguration(databaseId1);
|
|
91
|
+
expect(config.loggingEnabled).toBeTruthy();
|
|
92
|
+
|
|
93
|
+
process.env.NODE_ENV = origEnv;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it ('does not enable logging in production', () => {
|
|
97
|
+
const origEnv = process.env.NODE_ENV;
|
|
98
|
+
process.env.NODE_ENV = 'production';
|
|
99
|
+
|
|
100
|
+
spy.mockImplementation(() => ({[databaseId1]: { production }}));
|
|
101
|
+
|
|
102
|
+
const config = new ConnectionConfiguration(databaseId1);
|
|
103
|
+
expect(config.loggingEnabled).toBeFalsy();
|
|
104
|
+
|
|
105
|
+
process.env.NODE_ENV = origEnv;
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
describe('ConnectionConfiguration#pool', () => {
|
|
111
|
+
afterEach(() => {
|
|
112
|
+
process.env.DB_POOL_MIN = origEnvDBPoolMin;
|
|
113
|
+
process.env.DB_POOL_MAX = origEnvDBPoolMax;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it ('uses default pool values', () => {
|
|
117
|
+
spy.mockImplementation(() => ({[databaseId1]: {test}}));
|
|
118
|
+
|
|
119
|
+
const config = new ConnectionConfiguration(databaseId1);
|
|
120
|
+
expect(config.pool).toEqual({min: 0, max: 5});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it ('uses config variables when configured', () => {
|
|
124
|
+
const poolMax = Math.random();
|
|
125
|
+
const poolMin = Math.random();
|
|
126
|
+
process.env.DB_POOL_MIN = Math.random();
|
|
127
|
+
process.env.DB_POOL_MAX = Math.random();
|
|
128
|
+
const pooledTest = {...test, pool: {min: poolMin, max: poolMax}};
|
|
129
|
+
spy.mockImplementation(() => ({[databaseId1]: {test: pooledTest}}));
|
|
130
|
+
|
|
131
|
+
const config = new ConnectionConfiguration(databaseId1);
|
|
132
|
+
expect(config.pool).toEqual({min: poolMin, max: poolMax});
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
describe('ConnectionConfiguration.configuredDatabaseIds', () => {
|
|
138
|
+
it ('returns a list of all configured databaseIds', () => {
|
|
139
|
+
const configs = {
|
|
140
|
+
[databaseId1]: { production, test },
|
|
141
|
+
[databaseId2]: { production, test }
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
spy.mockImplementation(() => configs);
|
|
145
|
+
|
|
146
|
+
expect(ConnectionConfiguration.configuredDatabaseIds()).toEqual([databaseId1, databaseId2]);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it ('returns only databaseIds configured for the current environment', () => {
|
|
150
|
+
const configs = {
|
|
151
|
+
[databaseId1]: { production },
|
|
152
|
+
[databaseId2]: { production, test }
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
spy.mockImplementation(() => configs);
|
|
156
|
+
expect(ConnectionConfiguration.configuredDatabaseIds()).toEqual([databaseId2]);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
describe('ConnectionConfiguration#dialect', () => {
|
|
162
|
+
it ('defaults to postgres', () => {
|
|
163
|
+
spy.mockImplementation(() => ({[databaseId1]: { test }}));
|
|
164
|
+
const config = new ConnectionConfiguration(databaseId1);
|
|
165
|
+
expect(config.dialect).toEqual('postgres');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it ('returns sqlite when configured to do so', () => {
|
|
169
|
+
spy.mockImplementation(() => ({[databaseId1]: {test: sqliteConfig}}));
|
|
170
|
+
const config = new ConnectionConfiguration(databaseId1);
|
|
171
|
+
expect(config.dialect).toEqual('sqlite');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
describe('ConnectionConfiguration#storage', () => {
|
|
177
|
+
it ('returns the correct path for sqlite database file', () => {
|
|
178
|
+
spy.mockImplementation(() => ({[databaseId1]: {test: sqliteConfig}}));
|
|
179
|
+
const config = new ConnectionConfiguration(databaseId1);
|
|
180
|
+
const expected = `${process.cwd()}/config/db/database.test.sqlite`;
|
|
181
|
+
expect(config.storage).toEqual(expected);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
const { ConnectionConfiguration } = require('../../../lib/active_record/db/connectionConfiguration');
|
|
2
|
+
const { Migrator } = require('../../../lib/active_record/db/migrator');
|
|
3
|
+
const { QueryInterface } = require('../../../lib/active_record/db/queryInterface');
|
|
4
|
+
const { Sequelize } = require('sequelize');
|
|
5
|
+
const TestHelpers = require('@vida-global/test-helpers');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
const connectionConfig = {database: ' ', host: ' ', password: ' ', username: ' '};
|
|
9
|
+
const migrationModule = {up: jest.fn(), down: jest.fn()};
|
|
10
|
+
|
|
11
|
+
const configSpy = jest.spyOn(ConnectionConfiguration, '_fetchAllConfigs');
|
|
12
|
+
configSpy.mockImplementation(() => ({default: {test: connectionConfig}}));
|
|
13
|
+
|
|
14
|
+
const transactionSpy = jest.spyOn(QueryInterface.prototype, 'transaction');
|
|
15
|
+
transactionSpy.mockImplementation(callback => callback());
|
|
16
|
+
|
|
17
|
+
const MockQueryInterface = {
|
|
18
|
+
createTable: jest.fn(),
|
|
19
|
+
dropTable: jest.fn(),
|
|
20
|
+
addColumn: jest.fn(),
|
|
21
|
+
removeColumn: jest.fn(),
|
|
22
|
+
addIndex: jest.fn(),
|
|
23
|
+
removeIndex: jest.fn(),
|
|
24
|
+
renameColumn: jest.fn(),
|
|
25
|
+
changeColumn: jest.fn()
|
|
26
|
+
}
|
|
27
|
+
jest.mock('sequelize', () => {
|
|
28
|
+
class MockSequelize {
|
|
29
|
+
static DataTypes = {INTEGER: 'INTEGER', DATE: 'DATE'};
|
|
30
|
+
}
|
|
31
|
+
return {Sequelize: MockSequelize};
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
Sequelize.prototype.getQueryInterface = () => MockQueryInterface;
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
jest.clearAllMocks();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
describe('Migrator', () => {
|
|
43
|
+
const migrator = new Migrator();
|
|
44
|
+
const moduleSpy = jest.spyOn(migrator, 'migrationModule', 'get');
|
|
45
|
+
moduleSpy.mockImplementation(() => migrationModule);
|
|
46
|
+
|
|
47
|
+
describe('Migrator#run', () => {
|
|
48
|
+
it ('calls the migration migrate `up` callback within a transaction', async () => {
|
|
49
|
+
await migrator.run();
|
|
50
|
+
expect(transactionSpy).toHaveBeenCalledTimes(1);
|
|
51
|
+
expect(migrationModule.up).toHaveBeenCalledTimes(1);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it ('throws an error if there is no `up`', async () => {
|
|
55
|
+
const origUp = migrationModule.up;
|
|
56
|
+
delete migrationModule.up
|
|
57
|
+
|
|
58
|
+
expect(async () => {await migrator.run()}).rejects.toThrow();
|
|
59
|
+
|
|
60
|
+
migrationModule.up = origUp;
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
describe('Migrator#down', () => {
|
|
66
|
+
it ('calls the migration migrate `down` callback within a transaction', async () => {
|
|
67
|
+
await migrator.rollback();
|
|
68
|
+
expect(transactionSpy).toHaveBeenCalledTimes(1);
|
|
69
|
+
expect(migrationModule.down).toHaveBeenCalledTimes(1);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it ('does not throw an error if there is no `down`', async () => {
|
|
73
|
+
const origDown = migrationModule.down;
|
|
74
|
+
delete migrationModule.down
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
await migrator.rollback();
|
|
78
|
+
} catch(err) {
|
|
79
|
+
expect(1).toBe(2); // if we get here, an error was thrown
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
migrationModule.down = origDown;
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
describe('Migrator#createTable', () => {
|
|
88
|
+
const idCol = { allowNull: false, autoIncrement: true, primaryKey: true, type: 'INTEGER'};
|
|
89
|
+
const createdCol = {allowNull: false, type: 'DATE'};
|
|
90
|
+
const updatedCol = {allowNull: false, type: 'DATE'};
|
|
91
|
+
|
|
92
|
+
it ('calls `createTable` on the query interface and includes default columns', async () => {
|
|
93
|
+
const tableName = TestHelpers.Faker.Text.randomString();
|
|
94
|
+
const details = {
|
|
95
|
+
id: idCol,
|
|
96
|
+
[TestHelpers.Faker.Text.randomString().toLowerCase()]: Math.random(),
|
|
97
|
+
created_at: createdCol,
|
|
98
|
+
updated_at: updatedCol,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
await migrator.createTable(tableName, details);
|
|
102
|
+
expect(MockQueryInterface.createTable).toHaveBeenCalledTimes(1);
|
|
103
|
+
expect(MockQueryInterface.createTable).toHaveBeenCalledWith(tableName, details);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it ('ensures the columns are snake cased', async () => {
|
|
107
|
+
const tableName = TestHelpers.Faker.Text.randomString();
|
|
108
|
+
const details = {
|
|
109
|
+
id: idCol,
|
|
110
|
+
myColumn: 1,
|
|
111
|
+
created_at: createdCol,
|
|
112
|
+
updated_at: updatedCol,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
await migrator.createTable(tableName, details);
|
|
116
|
+
expect(MockQueryInterface.createTable).toHaveBeenCalledWith(tableName, {
|
|
117
|
+
id: idCol,
|
|
118
|
+
my_column: 1,
|
|
119
|
+
created_at: createdCol,
|
|
120
|
+
updated_at: updatedCol,
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
describe('Migrator#dropTable', () => {
|
|
127
|
+
it ('calls `dropTable` on the query interface', async () => {
|
|
128
|
+
const tableName = TestHelpers.Faker.Text.randomString();
|
|
129
|
+
await migrator.dropTable(tableName);
|
|
130
|
+
expect(MockQueryInterface.dropTable).toHaveBeenCalledTimes(1);
|
|
131
|
+
expect(MockQueryInterface.dropTable).toHaveBeenCalledWith(tableName);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
describe('Migrator#addColumn', () => {
|
|
137
|
+
it ('calls `addColumn` on the query interface', async () => {
|
|
138
|
+
const tableName = TestHelpers.Faker.Text.randomString();
|
|
139
|
+
const columnName = TestHelpers.Faker.Text.randomString();
|
|
140
|
+
const details = {
|
|
141
|
+
[TestHelpers.Faker.Text.randomString()]: Math.random(),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
await migrator.addColumn(tableName, columnName, details);
|
|
145
|
+
expect(MockQueryInterface.addColumn).toHaveBeenCalledTimes(1);
|
|
146
|
+
expect(MockQueryInterface.addColumn).toHaveBeenCalledWith(tableName, columnName, details);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
describe('Migrator#removeColumn', () => {
|
|
152
|
+
it ('calls `removeColumn` on the query interface', async () => {
|
|
153
|
+
const tableName = TestHelpers.Faker.Text.randomString();
|
|
154
|
+
const columnName = TestHelpers.Faker.Text.randomString();
|
|
155
|
+
await migrator.removeColumn(tableName, columnName);
|
|
156
|
+
expect(MockQueryInterface.removeColumn).toHaveBeenCalledTimes(1);
|
|
157
|
+
expect(MockQueryInterface.removeColumn).toHaveBeenCalledWith(tableName, columnName);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
describe('Migrator#addIndex', () => {
|
|
163
|
+
const tableName = TestHelpers.Faker.Text.randomString();
|
|
164
|
+
const fields = [
|
|
165
|
+
TestHelpers.Faker.Text.randomString(),
|
|
166
|
+
TestHelpers.Faker.Text.randomString()
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
it ('calls `addColumn` on the query interface', async () => {
|
|
170
|
+
const options = {concurrently: true, unique: true, where: {}, name: 'foo'};
|
|
171
|
+
const expectedOptions = structuredClone(options);
|
|
172
|
+
expectedOptions.fields = fields;
|
|
173
|
+
|
|
174
|
+
await migrator.addIndex(tableName, fields, options);
|
|
175
|
+
|
|
176
|
+
expect(MockQueryInterface.addIndex).toHaveBeenCalledTimes(1);
|
|
177
|
+
expect(MockQueryInterface.addIndex).toHaveBeenCalledWith(tableName, expectedOptions);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
it ('does not pass unset options', async () => {
|
|
182
|
+
const options = {};
|
|
183
|
+
const expectedOptions = {fields: fields};
|
|
184
|
+
|
|
185
|
+
await migrator.addIndex(tableName, fields, options);
|
|
186
|
+
|
|
187
|
+
expect(MockQueryInterface.addIndex).toHaveBeenCalledTimes(1);
|
|
188
|
+
expect(MockQueryInterface.addIndex).toHaveBeenCalledWith(tableName, expectedOptions);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
it ('does not pass false or null options', async () => {
|
|
193
|
+
const options = {concurrently: false, unique: false, where: null, name: null};
|
|
194
|
+
const expectedOptions = {fields: fields};
|
|
195
|
+
|
|
196
|
+
await migrator.addIndex(tableName, fields, options);
|
|
197
|
+
|
|
198
|
+
expect(MockQueryInterface.addIndex).toHaveBeenCalledTimes(1);
|
|
199
|
+
expect(MockQueryInterface.addIndex).toHaveBeenCalledWith(tableName, expectedOptions);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
describe('Migrator#removeIndex', () => {
|
|
205
|
+
it ('calls `removeIndex` on the query interface', async () => {
|
|
206
|
+
const tableName = TestHelpers.Faker.Text.randomString();
|
|
207
|
+
const fields = [
|
|
208
|
+
TestHelpers.Faker.Text.randomString(),
|
|
209
|
+
TestHelpers.Faker.Text.randomString()
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
await migrator.removeIndex(tableName, fields);
|
|
213
|
+
expect(MockQueryInterface.removeIndex).toHaveBeenCalledTimes(1);
|
|
214
|
+
expect(MockQueryInterface.removeIndex).toHaveBeenCalledWith(tableName, fields, {});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it ('sets `concurrently` when set to true', async () => {
|
|
218
|
+
const tableName = TestHelpers.Faker.Text.randomString();
|
|
219
|
+
const fields = [
|
|
220
|
+
TestHelpers.Faker.Text.randomString(),
|
|
221
|
+
TestHelpers.Faker.Text.randomString()
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
await migrator.removeIndex(tableName, fields, true);
|
|
225
|
+
expect(MockQueryInterface.removeIndex).toHaveBeenCalledTimes(1);
|
|
226
|
+
expect(MockQueryInterface.removeIndex).toHaveBeenCalledWith(tableName, fields, {concurrently: true});
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
describe('Migrator#renameColumn', () => {
|
|
232
|
+
it ('calls `renameColumn` on the query interface', async () => {
|
|
233
|
+
const tableName = TestHelpers.Faker.Text.randomString();
|
|
234
|
+
const oldName = TestHelpers.Faker.Text.randomString();
|
|
235
|
+
const newName = TestHelpers.Faker.Text.randomString().toLowerCase();
|
|
236
|
+
|
|
237
|
+
await migrator.renameColumn(tableName, oldName, newName);
|
|
238
|
+
expect(MockQueryInterface.renameColumn).toHaveBeenCalledTimes(1);
|
|
239
|
+
expect(MockQueryInterface.renameColumn).toHaveBeenCalledWith(tableName, oldName, newName);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it ('ensures the columns are snake cased', async () => {
|
|
243
|
+
const tableName = TestHelpers.Faker.Text.randomString();
|
|
244
|
+
const oldName = TestHelpers.Faker.Text.randomString();
|
|
245
|
+
const newName = 'myColumn';
|
|
246
|
+
|
|
247
|
+
await migrator.renameColumn(tableName, oldName, newName);
|
|
248
|
+
expect(MockQueryInterface.renameColumn).toHaveBeenCalledWith(tableName, oldName, 'my_column');
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
describe('Migrator#changeColumn', () => {
|
|
254
|
+
it ('calls `changeColumn` on the query interface', async () => {
|
|
255
|
+
const tableName = TestHelpers.Faker.Text.randomString();
|
|
256
|
+
const colName = TestHelpers.Faker.Text.randomString();
|
|
257
|
+
const options = {
|
|
258
|
+
[TestHelpers.Faker.Text.randomString()]: Math.random(),
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
await migrator.changeColumn(tableName, colName, options);
|
|
262
|
+
expect(MockQueryInterface.changeColumn).toHaveBeenCalledTimes(1);
|
|
263
|
+
expect(MockQueryInterface.changeColumn).toHaveBeenCalledWith(tableName, colName, options);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const { Connection } = require('../../../lib/active_record/db/connection');
|
|
2
|
+
const { ConnectionConfiguration } = require('../../../lib/active_record/db/connectionConfiguration');
|
|
3
|
+
const { QueryInterface } = require('../../../lib/active_record/db/queryInterface');
|
|
4
|
+
const { Sequelize } = require('sequelize');
|
|
5
|
+
const TestHelpers = require('@vida-global/test-helpers');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
const tableName1 = TestHelpers.Faker.Text.randomString();
|
|
9
|
+
const tableName2 = TestHelpers.Faker.Text.randomString();
|
|
10
|
+
const MockQueryInterface = {
|
|
11
|
+
showAllTables: jest.fn(() => [tableName1, tableName2]),
|
|
12
|
+
describeTable: jest.fn((tableName) => ({name: tableName}))
|
|
13
|
+
}
|
|
14
|
+
jest.mock('sequelize', () => {
|
|
15
|
+
class MockSequelize {
|
|
16
|
+
transaction = jest.fn()
|
|
17
|
+
}
|
|
18
|
+
return {Sequelize: MockSequelize};
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
Sequelize.prototype.getQueryInterface = () => MockQueryInterface;
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
const connectionConfig = {database: ' ', host: ' ', password: ' ', username: ' '};
|
|
25
|
+
const configSpy = jest.spyOn(ConnectionConfiguration, '_fetchAllConfigs');
|
|
26
|
+
configSpy.mockImplementation(() => ({default: {test: connectionConfig}}));
|
|
27
|
+
|
|
28
|
+
const connection = new Connection();
|
|
29
|
+
const queryInterface = new QueryInterface(connection);
|
|
30
|
+
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
jest.clearAllMocks();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
describe('QueryInterface', () => {
|
|
37
|
+
describe('QueryInterface#allTables', () => {
|
|
38
|
+
it ('uses the sequelize queryInterface to fetch the list of tables', async () => {
|
|
39
|
+
await queryInterface.allTables();
|
|
40
|
+
expect(MockQueryInterface.showAllTables).toHaveBeenCalledTimes(1);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it ('uses the sequelize queryInterface to fetch the details of all tables', async () => {
|
|
44
|
+
await queryInterface.allTables();
|
|
45
|
+
expect(MockQueryInterface.describeTable).toHaveBeenCalledTimes(2);
|
|
46
|
+
expect(MockQueryInterface.describeTable).toHaveBeenCalledWith(tableName1);
|
|
47
|
+
expect(MockQueryInterface.describeTable).toHaveBeenCalledWith(tableName2);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it ('returns the details of all tables', async () => {
|
|
51
|
+
const tables = await queryInterface.allTables();
|
|
52
|
+
expect(tables[tableName1]).toEqual({name: tableName1});
|
|
53
|
+
expect(tables[tableName2]).toEqual({name: tableName2});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
describe('QueryInterface#transaction', () => {
|
|
59
|
+
it ('uses the sequelize queryInterface wrap a callback in a transaction', async () => {
|
|
60
|
+
const callback = jest.fn();
|
|
61
|
+
await queryInterface.transaction(callback);
|
|
62
|
+
expect(connection._sequelize.transaction).toHaveBeenCalledTimes(1);
|
|
63
|
+
expect(connection._sequelize.transaction).toHaveBeenCalledWith(callback);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|