import 'package:drift/native.dart'; import 'package:migrations_example/database.dart'; import 'package:drift/drift.dart'; import 'package:test/test.dart'; import 'package:drift_dev/api/migrations.dart'; // Import the generated schema helper to instantiate databases at old versions. import 'generated/schema.dart'; import 'generated/schema_v1.dart' as v1; import 'generated/schema_v2.dart' as v2; import 'generated/schema_v4.dart' as v4; import 'generated/schema_v5.dart' as v5; void main() { driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; late SchemaVerifier verifier; setUpAll(() { verifier = SchemaVerifier(GeneratedHelper()); }); // Test all possible schema migrations with a simple test that just ensures // the schema is correct after the migration. // More complex tests ensuring data integrity are written below. group('general migration', () { const currentSchema = Database.latestSchemaVersion; for (var oldVersion = 1; oldVersion < currentSchema; oldVersion++) { group('from v$oldVersion', () { for (var targetVersion = oldVersion + 1; targetVersion <= currentSchema; targetVersion++) { test('to v$targetVersion', () async { final connection = await verifier.startAt(oldVersion); final db = Database(connection); addTearDown(db.close); await verifier.migrateAndValidate(db, targetVersion); }); } }); } }); test('preserves existing data in migration from v1 to v2', () async { final schema = await verifier.schemaAt(1); // Add some data to the users table, which only has an id column at v1 final oldDb = v1.DatabaseAtV1(schema.newConnection()); await oldDb.into(oldDb.users).insert(const v1.UsersCompanion(id: Value(1))); await oldDb.close(); // Run the migration and verify that it adds the name column. final db = Database(schema.newConnection()); await verifier.migrateAndValidate(db, 2); await db.close(); // Make sure the user is still here final migratedDb = v2.DatabaseAtV2(schema.newConnection()); final user = await migratedDb.select(migratedDb.users).getSingle(); expect(user.id, 1); expect(user.name, 'no name'); // default from the migration await migratedDb.close(); }); test('foreign key constraints work after upgrade from v4 to v5', () async { final schema = await verifier.schemaAt(4); final db = Database(schema.newConnection()); await verifier.migrateAndValidate(db, 5); await db.close(); // Test that the foreign key reference introduced in v5 works as expected. final migratedDb = v5.DatabaseAtV5(schema.newConnection()); // The `foreign_keys` pragma is a per-connection option and the generated // versioned classes don't enable it by default. So, enable it manually. await migratedDb.customStatement('pragma foreign_keys = on;'); await migratedDb.into(migratedDb.users).insert(v5.UsersCompanion.insert()); await migratedDb .into(migratedDb.users) .insert(v5.UsersCompanion.insert(nextUser: Value(1))); // Deleting the first user should now fail due to the constraint await expectLater(migratedDb.users.deleteWhere((tbl) => tbl.id.equals(1)), throwsA(isA())); }); test('view works after upgrade from v4 to v5', () async { final schema = await verifier.schemaAt(4); final oldDb = v4.DatabaseAtV4(schema.newConnection()); await oldDb.batch((batch) { batch ..insert(oldDb.users, v4.UsersCompanion.insert(id: Value(1))) ..insert(oldDb.users, v4.UsersCompanion.insert(id: Value(2))) ..insert( oldDb.groups, v4.GroupsCompanion.insert(title: 'Test', owner: 1)); }); await oldDb.close(); // Run the migration and verify that it adds the view. final db = Database(schema.newConnection()); await verifier.migrateAndValidate(db, 5); await db.close(); // Make sure the view works! final migratedDb = v5.DatabaseAtV5(schema.newConnection()); final viewCount = await migratedDb.select(migratedDb.groupCount).get(); expect( viewCount, contains(isA() .having((e) => e.id, 'id', 1) .having((e) => e.groupCount, 'groupCount', 1))); expect( viewCount, contains(isA() .having((e) => e.id, 'id', 2) .having((e) => e.groupCount, 'groupCount', 0))); await migratedDb.close(); }); }