Skip to content

Commit 066fe25

Browse files
committed
db: Add a migration, and a migration test
We didn't really need to do this as a migration, because we already know we need this column and so we could have included it from the start. But this lets us exercise Drift's support for migrations, and for testing migrations.
1 parent ea14c73 commit 066fe25

File tree

6 files changed

+521
-35
lines changed

6 files changed

+521
-35
lines changed

lib/model/database.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class Accounts extends Table {
2020
Column<String> get zulipMergeBase => text().nullable()();
2121
Column<int> get zulipFeatureLevel => integer()();
2222

23+
Column<String> get ackedPushToken => text().nullable()();
24+
2325
@override
2426
List<Set<Column<Object>>> get uniqueKeys => [
2527
{realmUrl, userId},
@@ -58,7 +60,7 @@ class AppDatabase extends _$AppDatabase {
5860
// * Write tests.
5961
// TODO run those `drift_dev schema` commands in CI: https://github.com/zulip/zulip-flutter/issues/60
6062
@override
61-
int get schemaVersion => 1; // See note.
63+
int get schemaVersion => 2; // See note.
6264

6365
@override
6466
MigrationStrategy get migration {
@@ -80,6 +82,11 @@ class AppDatabase extends _$AppDatabase {
8082
return;
8183
}
8284
assert(1 <= from && from <= to && to <= schemaVersion);
85+
86+
if (from < 2 && 2 <= to) {
87+
await m.addColumn(accounts, accounts.ackedPushToken);
88+
}
89+
// New migrations go here.
8390
}
8491
);
8592
}

lib/model/database.g.dart

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

test/model/database_test.dart

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,87 @@
11
import 'package:checks/checks.dart';
22
import 'package:drift/drift.dart';
33
import 'package:drift/native.dart';
4+
import 'package:drift_dev/api/migrations.dart';
45
import 'package:test/scaffolding.dart';
56
import 'package:zulip/model/database.dart';
67

8+
import 'schemas/schema.dart';
9+
import 'schemas/schema_v1.dart' as v1;
10+
import 'schemas/schema_v2.dart' as v2;
11+
712
void main() {
8-
late AppDatabase database;
13+
group('non-migration tests', () {
14+
late AppDatabase database;
915

10-
setUp(() {
11-
database = AppDatabase(NativeDatabase.memory());
12-
});
13-
tearDown(() async {
14-
await database.close();
16+
setUp(() {
17+
database = AppDatabase(NativeDatabase.memory());
18+
});
19+
tearDown(() async {
20+
await database.close();
21+
});
22+
23+
test('create account', () async {
24+
final accountData = AccountsCompanion.insert(
25+
realmUrl: Uri.parse('https://chat.example/'),
26+
userId: 1,
27+
28+
apiKey: '1234',
29+
zulipVersion: '6.0',
30+
zulipMergeBase: const Value('6.0'),
31+
zulipFeatureLevel: 42,
32+
);
33+
final accountId = await database.createAccount(accountData);
34+
final account = await (database.select(database.accounts)
35+
..where((a) => a.id.equals(accountId)))
36+
.watchSingle()
37+
.first;
38+
check(account.toCompanion(false).toJson()).deepEquals({
39+
...accountData.toJson(),
40+
'id': it(),
41+
'acked_push_token': null,
42+
});
43+
});
1544
});
1645

17-
test('create account', () async {
18-
// TODO use example_data
19-
final accountData = AccountsCompanion.insert(
20-
realmUrl: Uri.parse('https://chat.example/'),
21-
userId: 1,
22-
23-
apiKey: '1234',
24-
zulipVersion: '6.0',
25-
zulipMergeBase: const Value('6.0'),
26-
zulipFeatureLevel: 42,
27-
);
28-
final accountId = await database.createAccount(accountData);
29-
final account = await (database.select(database.accounts)
30-
..where((a) => a.id.equals(accountId)))
31-
.watchSingle()
32-
.first;
33-
check(account.toCompanion(false).toJson()).deepEquals({
34-
...accountData.toJson(),
35-
'id': it(),
46+
group('migrations', () {
47+
late SchemaVerifier verifier;
48+
49+
setUpAll(() {
50+
verifier = SchemaVerifier(GeneratedHelper());
51+
});
52+
53+
test('upgrade to v2, empty', () async {
54+
final connection = await verifier.startAt(1);
55+
final db = AppDatabase(connection);
56+
await verifier.migrateAndValidate(db, 2);
57+
await db.close();
58+
});
59+
60+
test('upgrade to v2, with data', () async {
61+
final schema = await verifier.schemaAt(1);
62+
final before = v1.DatabaseAtV1(schema.newConnection());
63+
await before.into(before.accounts).insert(v1.AccountsCompanion.insert(
64+
realmUrl: 'https://chat.example/',
65+
userId: 1,
66+
67+
apiKey: '1234',
68+
zulipVersion: '6.0',
69+
zulipMergeBase: const Value('6.0'),
70+
zulipFeatureLevel: 42,
71+
));
72+
final accountV1 = await before.select(before.accounts).watchSingle().first;
73+
await before.close();
74+
75+
final db = AppDatabase(schema.newConnection());
76+
await verifier.migrateAndValidate(db, 2);
77+
await db.close();
78+
79+
final after = v2.DatabaseAtV2(schema.newConnection());
80+
final account = await after.select(after.accounts).getSingle();
81+
check(account.toJson()).deepEquals({
82+
...accountV1.toJson(),
83+
'ackedPushToken': null,
84+
});
3685
});
3786
});
3887
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.0.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"accounts","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"int","nullable":false,"customConstraints":null,"defaultConstraints":"PRIMARY KEY AUTOINCREMENT","default_dart":null,"default_client_dart":null,"dsl_features":["auto-increment"]},{"name":"realm_url","getter_name":"realmUrl","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"user_id","getter_name":"userId","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"api_key","getter_name":"apiKey","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"zulip_version","getter_name":"zulipVersion","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"zulip_merge_base","getter_name":"zulipMergeBase","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"zulip_feature_level","getter_name":"zulipFeatureLevel","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"acked_push_token","getter_name":"ackedPushToken","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"unique_keys":[["realm_url","user_id"],["realm_url","email"]]}}]}

test/model/schemas/schema.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@
44
import 'package:drift/drift.dart';
55
import 'package:drift/internal/migrations.dart';
66
import 'schema_v1.dart' as v1;
7+
import 'schema_v2.dart' as v2;
78

89
class GeneratedHelper implements SchemaInstantiationHelper {
910
@override
1011
GeneratedDatabase databaseForVersion(QueryExecutor db, int version) {
1112
switch (version) {
1213
case 1:
1314
return v1.DatabaseAtV1(db);
15+
case 2:
16+
return v2.DatabaseAtV2(db);
1417
default:
15-
throw MissingSchemaException(version, const {1});
18+
throw MissingSchemaException(version, const {1, 2});
1619
}
1720
}
1821
}

0 commit comments

Comments
 (0)