Skip to content

Commit e023524

Browse files
committed
close connection & check file names on down() + tests
1 parent 0770695 commit e023524

10 files changed

Lines changed: 2000 additions & 855 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,4 @@ dist
104104
.tern-port
105105

106106
.vscode/settings.json
107+
.npmrc

package-lock.json

Lines changed: 1879 additions & 828 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ts-mysql-migrate",
3-
"version": "1.0.1",
3+
"version": "1.0.2",
44
"description": "Library for MySQL migrations",
55
"author": "Tine Mlakar",
66
"license": "MIT",
@@ -26,7 +26,7 @@
2626
"main": "dist/index.js",
2727
"scripts": {
2828
"build": "tsc",
29-
"test": "jest --config jestconfig.json --detectOpenHandles",
29+
"test": "jest --config jestconfig.json -i",
3030
"prepare": "npm run build",
3131
"lint": "tslint -p tsconfig.json"
3232
},
@@ -44,4 +44,4 @@
4444
"ts-jest": "^25.1.0",
4545
"typescript": "^3.7.5"
4646
}
47-
}
47+
}

src/migrations.ts

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { QueryFunction } from 'mysql';
88
*/
99
export interface MigrationConnection {
1010
query: QueryFunction;
11+
end(callback?: (err: Error | null) => void): void;
1112
}
1213

1314
/**
@@ -49,7 +50,7 @@ export class Migration {
4950
private maxVersion = 0;
5051
private isInit = false;
5152

52-
public constructor (config: MigrationConfig) {
53+
public constructor(config: MigrationConfig) {
5354
this.config = config;
5455
this.isInit = false;
5556
}
@@ -81,8 +82,8 @@ export class Migration {
8182
while (infinite || countStep < steps) {
8283
const script = await this.getNextUpgradeScript();
8384
if (!script) {
84-
const ver = await this.getLastVersion();
85-
if (ver != this.maxVersion) {
85+
const { version } = await this.getLastVersion();
86+
if (version != this.maxVersion) {
8687
throw new Error('Next upgrade script not found!');
8788
} else {
8889
this.writeLog('Upgrade complete!');
@@ -120,8 +121,8 @@ export class Migration {
120121
while (infinite || countStep < steps) {
121122
const script = await this.getNextDowngradeScript();
122123
if (!script) {
123-
const ver = await this.getLastVersion();
124-
if (ver) {
124+
const { version } = await this.getLastVersion();
125+
if (version) {
125126
throw new Error('Next downgrade script not found!');
126127
} else {
127128
this.writeLog('Downgrade complete!');
@@ -151,14 +152,28 @@ export class Migration {
151152
/**
152153
* Returns last version of database
153154
*/
154-
public async getLastVersion(): Promise<number> {
155+
public async getLastVersion(): Promise<{ version: number, fileName: string }> {
155156
return await this.query(
156-
`SELECT version
157+
`SELECT version, fileName
157158
FROM ${this.config.tableName}
158159
ORDER BY version DESC
159-
LIMIT 1`, null, true).then((val: any) => !!val && val.length ? val[0].version : 0);
160+
LIMIT 1`, null, true
161+
).then(
162+
(val: any) =>
163+
!!val && val.length ?
164+
{ version: val[0].version, fileName: val[0].fileName } :
165+
{ version: 0, fileName: null }
166+
);
160167
}
161168

169+
public async destroy() {
170+
await new Promise<void>((resolve, reject) => {
171+
this.config.conn.end((e) => {
172+
this.writeLog(`Migrations: DB connection terminated: ${e?.message || 'CLOSED'}`);
173+
resolve();
174+
});
175+
});
176+
}
162177
/**
163178
* Loads scripts from file system
164179
*/
@@ -195,9 +210,9 @@ export class Migration {
195210
* Returns next upgrade script object
196211
*/
197212
private async getNextUpgradeScript(): Promise<MigrationScript> {
198-
const ver = await this.getLastVersion();
213+
const { version } = await this.getLastVersion();
199214
for (const script of this.scripts) {
200-
if (script.version === (ver + 1)) {
215+
if (script.version === (version + 1)) {
201216
return script;
202217
}
203218
}
@@ -208,9 +223,12 @@ export class Migration {
208223
* Returns next downgrade script object
209224
*/
210225
private async getNextDowngradeScript(): Promise<MigrationScript> {
211-
const ver = await this.getLastVersion();
226+
const { version, fileName } = await this.getLastVersion();
212227
for (const script of this.scripts) {
213-
if (script.version === (ver)) {
228+
if (script.version === version) {
229+
if (script.fileName !== fileName) {
230+
throw new Error(`Script version and filename mismatch! version: ${version} -> file system: ${script.fileName} | database: ${fileName}`);
231+
}
214232
return script;
215233
}
216234
}
@@ -269,8 +287,8 @@ export class Migration {
269287
* Sort migration files by prefix numbers.
270288
*/
271289
private sortFiles(a: string, b: string): number {
272-
const verA = a.match( /^(\d*)-/ )[1];
273-
const verB = b.match( /^(\d*)-/ )[1];
290+
const verA = a.match(/^(\d*)-/)[1];
291+
const verB = b.match(/^(\d*)-/)[1];
274292

275293
if (!parseInt(verA) || !parseInt(verB)) {
276294
return 0;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
2+
await queryFn(`
3+
CREATE TABLE TEST (ID INTEGER NULL, NAME VARCHAR(20) NULL);
4+
`);
5+
}
6+
7+
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
8+
await queryFn(`
9+
DROP TABLE TEST;
10+
`);
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
2+
await queryFn(`
3+
INSERT INTO TEST VALUES (?, ?);
4+
`, [10, 'Test-10']);
5+
}
6+
7+
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
8+
await queryFn(`
9+
DELETE FROM TEST WHERE ID = ?;
10+
`, [10]);
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
2+
await queryFn(`
3+
INSERT INTO TEST VALUES (1, 'Test');
4+
`);
5+
}
6+
7+
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
8+
await queryFn(`
9+
DELETE FROM TEST WHERE ID = 1;
10+
`);
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
2+
await queryFn(`
3+
INSERT INTO TEST VALUES (11, 'Test');
4+
`);
5+
}
6+
7+
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
8+
await queryFn(`
9+
DELETE FROM TEST WHERE ID = 11;
10+
`);
11+
}

test/migrations-mysql2.test.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as mysql from 'mysql';
44
require('dotenv').config();
55

66
let migration: Migration;
7+
let invalidMigration: Migration;
78

89
beforeAll(async () => {
910

@@ -19,6 +20,7 @@ beforeAll(async () => {
1920
};
2021

2122
const pool = createPool(poolConfig);
23+
const pool2 = createPool(poolConfig);
2224

2325
migration = new Migration({
2426
conn: pool as unknown as mysql.Connection,
@@ -27,44 +29,58 @@ beforeAll(async () => {
2729
silent: true
2830
});
2931

32+
invalidMigration = new Migration({
33+
conn: pool2 as unknown as mysql.Connection,
34+
tableName: 'migrations',
35+
dir: `./test/invalid-migration-scripts/`,
36+
silent: true
37+
});
38+
3039
await migration.initialize();
40+
await invalidMigration.initialize();
3141

3242
});
3343

3444
afterAll(async () => {
3545

3646
// downgrade all
3747
await migration.down(-1);
48+
await migration.destroy();
49+
await invalidMigration.destroy();
3850

3951
});
4052

4153
test('Test upgrade migrations', async () => {
4254
await migration.up();
43-
const version = await migration.getLastVersion();
55+
const { version } = await migration.getLastVersion();
4456
expect(version).toBe(4);
4557
});
4658

4759
test('Test downgrade migrations', async () => {
4860
await migration.down();
49-
const version = await migration.getLastVersion();
61+
const { version } = await migration.getLastVersion();
5062
expect(version).toBe(3);
5163
});
5264

5365
test('Test downgrade two migration', async () => {
5466
await migration.up();
5567
await migration.down(2);
56-
const version = await migration.getLastVersion();
68+
const { version } = await migration.getLastVersion();
5769
expect(version).toBe(2);
5870
});
5971

6072
test('Test upgrade one migration', async () => {
6173
await migration.up(1);
62-
const version = await migration.getLastVersion();
74+
const { version } = await migration.getLastVersion();
6375
expect(version).toBe(3);
6476
});
6577

6678
test('Test reset', async () => {
6779
await migration.reset();
68-
const version = await migration.getLastVersion();
80+
const { version } = await migration.getLastVersion();
6981
expect(version).toBe(4);
7082
});
83+
84+
test('Test fail invalid migrations', async () => {
85+
await expect(invalidMigration.down()).rejects.toThrow(/filename mismatch/);
86+
});

test/migrations.test.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createPool, PoolConfig } from 'mysql';
33
require('dotenv').config();
44

55
let migration: Migration;
6+
let invalidMigration: Migration;
67

78
beforeAll(async () => {
89

@@ -26,44 +27,58 @@ beforeAll(async () => {
2627
silent: true
2728
});
2829

30+
invalidMigration = new Migration({
31+
conn: pool,
32+
tableName: 'migrations',
33+
dir: `./test/invalid-migration-scripts/`,
34+
silent: true
35+
});
36+
2937
await migration.initialize();
38+
await invalidMigration.initialize();
3039

3140
});
3241

3342
afterAll(async () => {
3443

3544
// downgrade all
3645
await migration.down(-1);
46+
await migration.destroy();
47+
await invalidMigration.destroy();
3748

3849
});
3950

4051
test('Test upgrade migrations', async () => {
4152
await migration.up();
42-
const version = await migration.getLastVersion();
53+
const { version } = await migration.getLastVersion();
4354
expect(version).toBe(4);
4455
});
4556

4657
test('Test downgrade migrations', async () => {
4758
await migration.down();
48-
const version = await migration.getLastVersion();
59+
const { version } = await migration.getLastVersion();
4960
expect(version).toBe(3);
5061
});
5162

5263
test('Test downgrade two migration', async () => {
5364
await migration.up();
5465
await migration.down(2);
55-
const version = await migration.getLastVersion();
66+
const { version } = await migration.getLastVersion();
5667
expect(version).toBe(2);
5768
});
5869

5970
test('Test upgrade one migration', async () => {
6071
await migration.up(1);
61-
const version = await migration.getLastVersion();
72+
const { version } = await migration.getLastVersion();
6273
expect(version).toBe(3);
6374
});
6475

6576
test('Test reset', async () => {
6677
await migration.reset();
67-
const version = await migration.getLastVersion();
78+
const { version } = await migration.getLastVersion();
6879
expect(version).toBe(4);
6980
});
81+
82+
test('Test fail invalid migrations', async () => {
83+
await expect(invalidMigration.down()).rejects.toThrow(/filename mismatch/);
84+
});

0 commit comments

Comments
 (0)