Skip to content
This repository was archived by the owner on Dec 26, 2022. It is now read-only.

Commit 42e3254

Browse files
committed
Fixed migration, updated tests
1 parent 67d33ea commit 42e3254

File tree

4 files changed

+151
-33
lines changed

4 files changed

+151
-33
lines changed

src/base/MigrationRunner.test.ts

Lines changed: 113 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
import MigrationRunner from './MigrationRunner';
22
import { SchemaMigration } from '../types/SchemaMigration';
33
import { JSONSchemaType } from 'ajv';
4+
import MigrationErrorCodes from '../types/MigrationErrorCodes';
45

56
describe('MigrationRunner tests', () => {
6-
describe('constructor tests', () => {
7-
test('throw error when schemaMigrations is empty', () => {
7+
describe('Constructor tests', () => {
8+
test(`Throw ErrorCode=${MigrationErrorCodes.NoMigrations} NoMigrations`, () => {
89
expect(() => new MigrationRunner([])).toThrow(
9-
'schemaMigrations can`t be empty'
10+
`schemaMigrations can't be empty`
1011
);
1112
});
1213

13-
test('throw error when there is no migration `version=0`', () => {
14+
test(`Throw ErrorCode=${MigrationErrorCodes.NoZeroMigration} NoZeroMigration`, () => {
1415
expect(() => new MigrationRunner([{ version: 1, schema: {} }])).toThrow(
1516
'schemaMigrations should have migration for `version=0`'
1617
);
1718
});
1819

19-
test('throw error when versions doesn`t go one after the other', () => {
20+
test(`Throw ErrorCode=${MigrationErrorCodes.IncorrectMigrationsOrder} IncorrectMigrationsOrder`, () => {
2021
expect(
2122
() =>
2223
new MigrationRunner([
@@ -27,8 +28,8 @@ describe('MigrationRunner tests', () => {
2728
});
2829
});
2930

30-
describe('migration tests', () => {
31-
test('Test migration', () => {
31+
describe('Migration tests', () => {
32+
test('Test migration with 3 iterations', () => {
3233
type TypeV0 = { data: number };
3334
type TypeV1 = { data: number[]; __version: number };
3435
type TypeV2 = {
@@ -67,7 +68,7 @@ describe('MigrationRunner tests', () => {
6768
schema: schemaV1,
6869
migration(item: TypeV0): TypeV1 {
6970
return {
70-
data: [item.data],
71+
data: [item.data] as number[],
7172
__version: 1,
7273
};
7374
},
@@ -97,7 +98,7 @@ describe('MigrationRunner tests', () => {
9798
expect(resultData).toStrictEqual(expectedData);
9899
});
99100

100-
test('Test when there is schema validation error', () => {
101+
test(`Throw ErrorCode=${MigrationErrorCodes.ValidationFailed} ValidationFailed`, () => {
101102
type TypeV0 = { data: number; text: string; prop: { test: 1 } };
102103

103104
const schemaV0: JSONSchemaType<TypeV0> = {
@@ -129,5 +130,108 @@ describe('MigrationRunner tests', () => {
129130
].join('\n')
130131
);
131132
});
133+
134+
test(`Throw ErrorCode=${MigrationErrorCodes.MigrationNotFound} MigrationNotFound`, () => {
135+
type TypeV0 = { data: number };
136+
137+
const schemaV0: JSONSchemaType<TypeV0> = {
138+
type: 'object',
139+
properties: {
140+
data: { type: 'number' },
141+
},
142+
required: ['data'],
143+
};
144+
145+
const migrations: SchemaMigration[] = [
146+
{ version: 0, schema: schemaV0 },
147+
{ version: 1, schema: schemaV0 },
148+
];
149+
150+
const dataV0 = { data: 1 };
151+
152+
const mr = new MigrationRunner(migrations);
153+
154+
expect(() => mr.runMigration(dataV0)).toThrow(
155+
/Migration {undefined->\d} not found/
156+
);
157+
});
158+
159+
test(`Throw ErrorCode=${MigrationErrorCodes.MigrationFailed} MigrationFailed - migration returned undefined`, () => {
160+
type TypeV0 = { data: number };
161+
type TypeV1 = { data: number };
162+
163+
const schemaV0: JSONSchemaType<TypeV0> = {
164+
type: 'object',
165+
properties: {
166+
data: { type: 'number' },
167+
},
168+
required: ['data'],
169+
};
170+
const schemaV1: JSONSchemaType<TypeV1> = {
171+
type: 'object',
172+
properties: {
173+
data: { type: 'number' },
174+
},
175+
required: ['data'],
176+
};
177+
178+
const migrations: SchemaMigration[] = [
179+
{ version: 0, schema: schemaV0 },
180+
{
181+
version: 1,
182+
schema: schemaV1,
183+
migration() {
184+
return undefined;
185+
},
186+
},
187+
];
188+
189+
const dataV0 = { data: 1 };
190+
191+
const mr = new MigrationRunner(migrations);
192+
193+
expect(() => mr.runMigration(dataV0)).toThrow(
194+
`migration returned 'undefined'`
195+
);
196+
});
197+
198+
test(`Throw ErrorCode=${MigrationErrorCodes.MigrationFailed} MigrationFailed migration returned 'data' without '__version'`, () => {
199+
type TypeV0 = { data: number };
200+
type TypeV1 = { data: number };
201+
202+
const schemaV0: JSONSchemaType<TypeV0> = {
203+
type: 'object',
204+
properties: {
205+
data: { type: 'number' },
206+
},
207+
required: ['data'],
208+
};
209+
const schemaV1: JSONSchemaType<TypeV1> = {
210+
type: 'object',
211+
properties: {
212+
data: { type: 'number' },
213+
},
214+
required: ['data'],
215+
};
216+
217+
const migrations: SchemaMigration[] = [
218+
{ version: 0, schema: schemaV0 },
219+
{
220+
version: 1,
221+
schema: schemaV1,
222+
migration(item) {
223+
return item;
224+
},
225+
},
226+
];
227+
228+
const dataV0 = { data: 1 };
229+
230+
const mr = new MigrationRunner(migrations);
231+
232+
expect(() => mr.runMigration(dataV0)).toThrow(
233+
`migration returned 'data' without '__version'`
234+
);
235+
});
132236
});
133237
});

src/base/MigrationRunner.ts

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ export default class MigrationRunner {
2525
);
2626

2727
constructor(schemaMigrations: SchemaMigration[]) {
28-
const schemaMigrationsSorted = schemaMigrations.slice();
29-
schemaMigrationsSorted.sort((a, b) => a.version - b.version);
30-
31-
if (!schemaMigrationsSorted.length) {
28+
if (!schemaMigrations.length) {
3229
throw new Error(
3330
this.genErrorMsg(
3431
MigrationErrorCodes.NoMigrations,
@@ -37,6 +34,9 @@ export default class MigrationRunner {
3734
);
3835
}
3936

37+
const schemaMigrationsSorted = schemaMigrations.slice();
38+
schemaMigrationsSorted.sort((a, b) => a.version - b.version);
39+
4040
if (!schemaMigrationsSorted.find((i) => i.version === 0)) {
4141
throw new Error(
4242
this.genErrorMsg(
@@ -80,7 +80,12 @@ export default class MigrationRunner {
8080
(i) => i.version === zeroVersion
8181
);
8282
if (!firstMigration) {
83-
throw new Error();
83+
throw new Error(
84+
this.genErrorMsg(
85+
MigrationErrorCodes.NoMigrations,
86+
`There are no migrations 'version=${zeroVersion}'`
87+
)
88+
);
8489
}
8590

8691
const validate = this.ajv.compile(firstMigration.schema);
@@ -101,34 +106,42 @@ export default class MigrationRunner {
101106
(m) => m.version === toVersion
102107
);
103108

104-
if (!migration) {
109+
if (!migration?.migration) {
105110
throw new Error(
106111
this.genErrorMsg(
107112
MigrationErrorCodes.MigrationNotFound,
108-
`Migration from ${fromVersion} to ${toVersion} not found`
113+
`Migration {${fromVersion}->${toVersion}} not found`
109114
)
110115
);
111116
}
112117

113-
if (migration?.migration) {
114-
const nextData: T = migration.migration(newData);
115-
116-
if (nextData === undefined) {
117-
throw new Error();
118-
}
119-
if (nextData.__version === undefined) {
120-
throw new Error();
121-
}
118+
const nextData: T = migration.migration(newData);
122119

123-
const validate = this.ajv.compile(migration.schema);
124-
const validateResult = validate(nextData);
125-
if (!validateResult) {
126-
throw new Error(this.genValidationErrors(validate, toVersion));
127-
}
120+
if (nextData === undefined) {
121+
throw new Error(
122+
this.genErrorMsg(
123+
MigrationErrorCodes.MigrationFailed,
124+
`After run migration {${fromVersion}->${toVersion}}, migration returned 'undefined'`
125+
)
126+
);
127+
}
128+
if (nextData.__version === undefined) {
129+
throw new Error(
130+
this.genErrorMsg(
131+
MigrationErrorCodes.MigrationFailed,
132+
`After run migration {${fromVersion}->${toVersion}}, migration returned 'data' without '__version'`
133+
)
134+
);
135+
}
128136

129-
newData = nextData;
130-
toVersion = nextData.__version + 1;
137+
const validate = this.ajv.compile(migration.schema);
138+
const validateResult = validate(nextData);
139+
if (!validateResult) {
140+
throw new Error(this.genValidationErrors(validate, toVersion));
131141
}
142+
143+
newData = nextData;
144+
toVersion = nextData.__version + 1;
132145
}
133146
}
134147
}

src/types/MigrationErrorCodes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ enum MigrationErrorCodes {
44
IncorrectMigrationsOrder = 2,
55
ValidationFailed = 3,
66
MigrationNotFound = 4,
7+
MigrationFailed = 5,
78
}
89

910
export default MigrationErrorCodes;

src/types/SchemaMigration.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export type SchemaMigration = {
22
version: number;
33
schema: any;
4-
migration?: <TIn = unknown, TOut = unknown>(data: TIn) => TOut;
4+
migration?: (data: any) => any;
55
};

0 commit comments

Comments
 (0)