Skip to content

Commit e462213

Browse files
committed
wip db: Add a migration, and a migration test
TODO change that generated_migrations pathname 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 bae7718 commit e462213

10 files changed

+851
-34
lines changed

.gitattributes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
# Dart files generated from the files next to them:
44
*.g.dart -diff
55

6+
# Generated files for testing migrations:
7+
test/model/generated_migrations/*.dart -diff
8+
test/model/schemas/*.json -diff
9+
610
# On the other hand, keep diffs for pubspec.lock. It contains
711
# information independent of any non-generated file in the tree.
812
# And thankfully it's much less verbose than, say, a yarn.lock.

analysis_options.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,10 @@ linter:
2626
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
2727
prefer_relative_imports: true
2828

29+
analyzer:
30+
exclude:
31+
# https://github.com/simolus3/drift/issues/2337
32+
- test/**/schema_v*.dart
33+
2934
# Additional information about this file can be found at
3035
# https://dart.dev/guides/language/analysis-options

lib/model/database.dart

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

22+
Column<String> get ackedPushToken => text().nullable()();
23+
2224
@override
2325
List<Set<Column<Object>>> get uniqueKeys => [
2426
{realmUrl, userId},
@@ -41,8 +43,30 @@ class AppDatabase extends _$AppDatabase {
4143

4244
AppDatabase.live() : this(_openConnection());
4345

46+
// When updating the schema:
47+
// * Make the change in the table classes, and bump schemaVersion.
48+
// * Export the new schema:
49+
// $ dart run drift_dev schema dump lib/model/database.dart test/model/schemas/
50+
// * Generate test migrations from the schemas:
51+
// $ dart run drift_dev schema generate --data-classes --companions test/model/schemas/ test/model/generated_migrations/
52+
// * Write a migration in `onUpgrade` below.
53+
// * Write tests.
54+
@override
55+
int get schemaVersion => 2; // See note.
56+
4457
@override
45-
int get schemaVersion => 1; // TODO migrations
58+
MigrationStrategy get migration {
59+
return MigrationStrategy(
60+
onCreate: (Migrator m) async {
61+
await m.createAll();
62+
},
63+
onUpgrade: (Migrator m, int from, int to) async {
64+
if (from < 2 && 2 <= to) {
65+
await m.addColumn(accounts, accounts.ackedPushToken);
66+
}
67+
}
68+
);
69+
}
4670

4771
Future<int> createAccount(AccountsCompanion values) {
4872
return into(accounts).insert(values);

lib/model/database.g.dart

Lines changed: 58 additions & 10 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: 72 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,85 @@
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 'generated_migrations/schema.dart';
9+
import 'generated_migrations/schema_v1.dart' as v1;
10+
import 'generated_migrations/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: 'https://chat.example/',
26+
userId: 1,
27+
28+
apiKey: '1234',
29+
zulipVersion: '6.0',
30+
zulipFeatureLevel: 42,
31+
);
32+
final accountId = await database.createAccount(accountData);
33+
final account = await (database.select(database.accounts)
34+
..where((a) => a.id.equals(accountId)))
35+
.watchSingle()
36+
.first;
37+
check(account.toCompanion(false).toJson()).deepEquals({
38+
...accountData.toJson(),
39+
'id': it(),
40+
'acked_push_token': null,
41+
});
42+
});
1543
});
1644

17-
test('create account', () async {
18-
final accountData = AccountsCompanion.insert(
19-
realmUrl: 'https://chat.example/',
20-
userId: 1,
21-
22-
apiKey: '1234',
23-
zulipVersion: '6.0',
24-
zulipFeatureLevel: 42,
25-
);
26-
final accountId = await database.createAccount(accountData);
27-
final account = await (database.select(database.accounts)
28-
..where((a) => a.id.equals(accountId)))
29-
.watchSingle()
30-
.first;
31-
check(account.toCompanion(false).toJson()).deepEquals({
32-
...accountData.toJson(),
33-
'id': it(),
45+
group('migrations', () {
46+
late SchemaVerifier verifier;
47+
48+
setUpAll(() {
49+
verifier = SchemaVerifier(GeneratedHelper());
50+
});
51+
52+
test('upgrade to v2, empty', () async {
53+
final connection = await verifier.startAt(1);
54+
final db = AppDatabase(connection);
55+
await verifier.migrateAndValidate(db, 2);
56+
await db.close();
57+
});
58+
59+
test('upgrade to v2, with data', () async {
60+
final schema = await verifier.schemaAt(1);
61+
final before = v1.DatabaseAtV1(schema.newConnection());
62+
await before.into(before.accounts).insert(v1.AccountsCompanion.insert(
63+
realmUrl: 'https://chat.example/',
64+
userId: 1,
65+
66+
apiKey: '1234',
67+
zulipVersion: '6.0',
68+
zulipFeatureLevel: 42,
69+
));
70+
final accountV1 = await before.select(before.accounts).watchSingle().first;
71+
await before.close();
72+
73+
final db = AppDatabase(schema.newConnection());
74+
await verifier.migrateAndValidate(db, 2);
75+
await db.close();
76+
77+
final after = v2.DatabaseAtV2(schema.newConnection());
78+
final account = await after.select(after.accounts).getSingle();
79+
check(account.toJson()).deepEquals({
80+
...accountV1.toJson(),
81+
'ackedPushToken': null,
82+
});
3483
});
3584
});
3685
}

0 commit comments

Comments
 (0)