Skip to content

Commit

Permalink
Update migration tests template
Browse files Browse the repository at this point in the history
  • Loading branch information
simolus3 committed Oct 10, 2024
1 parent 2cd6f69 commit b027e36
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 113 deletions.
18 changes: 9 additions & 9 deletions drift_dev/lib/api/migrations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,15 @@ abstract class SchemaVerifier {
///
/// Foreign key constraints are disabled for this operation.
Future<void> testWithDataIntegrity<OldDatabase extends GeneratedDatabase,
NewDatabase extends GeneratedDatabase>(
{required SchemaVerifier verifier,
required OldDatabase Function(QueryExecutor) createOld,
required NewDatabase Function(QueryExecutor) createNew,
required GeneratedDatabase Function(QueryExecutor) openTestedDatabase,
required void Function(Batch, OldDatabase) createItems,
required Future Function(NewDatabase) validateItems,
required int oldVersion,
required int newVersion});
NewDatabase extends GeneratedDatabase>({
required OldDatabase Function(QueryExecutor) createOld,
required NewDatabase Function(QueryExecutor) createNew,
required GeneratedDatabase Function(QueryExecutor) openTestedDatabase,
required void Function(Batch, OldDatabase) createItems,
required Future Function(NewDatabase) validateItems,
required int oldVersion,
required int newVersion,
});
}

/// Utilities verifying that the current schema of the database matches what
Expand Down
67 changes: 32 additions & 35 deletions drift_dev/lib/src/cli/commands/make_migrations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ This will generate the following:
${yellow.wrap("}")}
2. A test file containing:
a) Automated tests to validate the correctness of migrations.
a) Automated tests to validate the correctness of migrations.
b) A sample data integrity test for the first migration. This test ensures that the initial schema is created correctly and that basic data operations work as expected.
This sample test should be adapted for subsequent migrations, especially those involving complex modifications to existing tables.
Expand Down Expand Up @@ -367,29 +367,33 @@ void main() {
verifier = SchemaVerifier(GeneratedHelper());
});
group('$dbName database', () {
//////////////////////////////////////////////////////////////////////////////
////////////////////// GENERATED TESTS - DO NOT MODIFY ///////////////////////
//////////////////////////////////////////////////////////////////////////////
if (GeneratedHelper.versions.length < 2) return;
for (var i
in List.generate(GeneratedHelper.versions.length - 1, (i) => i)) {
final oldVersion = GeneratedHelper.versions.elementAt(i);
final newVersion = GeneratedHelper.versions.elementAt(i + 1);
test("migrate from v\$oldVersion to v\$newVersion", () async {
final schema = await verifier.schemaAt(oldVersion);
final db = $dbClassName(schema.newConnection());
await verifier.migrateAndValidate(db, newVersion);
await db.close();
group('simple database migrations', () {
// These simple tests verify all possible schema updates with a simple (no
// data) migration. This is a quick way to ensure that written database
// migrations properly alter the schema.
final versions = GeneratedHelper.versions;
for (final (i, fromVersion) in versions.indexed) {
group('from \$fromVersion', () {
for (final toVersion in versions.skip(i + 1)) {
test('to \$toVersion', () async {
final schema = await verifier.schemaAt(fromVersion);
final db = Database(schema.newConnection());
await verifier.migrateAndValidate(db, toVersion);
await db.close();
});
}
});
}
//////////////////////////////////////////////////////////////////////////////
/////////////////////// END OF GENERATED TESTS ///////////////////////////////
//////////////////////////////////////////////////////////////////////////////
}
});
// Simple tests ensure the schema is transformed correctly, but some
// migrations benefit from a test verifying that data is transformed correctly
// too. This is particularly true for migrations that change existing columns
// (e.g. altering their type or constraints). Migrations that only add tables
// or columns typically don't need these advanced tests.
// TODO: Check whether you have migrations that could benefit from these tests
// and adapt this example to your database if necessary:
${firstMigration.testStepByStepMigrationCode(dbName, dbClassName)}
});
}
""";

Expand Down Expand Up @@ -456,27 +460,23 @@ class _MigrationWriter {
/// It will also import the validation models to test data integrity
String testStepByStepMigrationCode(String dbName, String dbClassName) {
return """
/// Write data integrity tests for migrations that modify existing tables.
/// These tests are important because the auto-generated tests only check empty schemas.
/// Testing with actual data helps ensure migrations don't corrupt existing information.
///
/// The following is an example of how to write such a test:
test("migration from v$from to v$to does not corrupt data",
() async {
// Add data to insert into the old database, and the expected rows after the
// migration.
${tables.map((table) {
return """
final old${table.dbGetterName.pascalCase}Data = <v$from.${table.nameOfRowClass}>[]; // TODO: Add expected data at version $from using v$from.${table.nameOfRowClass}
final expectedNew${table.dbGetterName.pascalCase}Data = <v$to.${table.nameOfRowClass}>[]; // TODO: Add expected data at version $to using v$to.${table.nameOfRowClass}
final old${table.dbGetterName.pascalCase}Data = <v$from.${table.nameOfRowClass}>[];
final expectedNew${table.dbGetterName.pascalCase}Data = <v$to.${table.nameOfRowClass}>[];
""";
}).join('\n')}
await verifier.testWithDataIntegrity(
oldVersion: $from,
newVersion: $to,
verifier: verifier,
createOld: (e) => v1.DatabaseAtV$from(e),
createNew: (e) => v2.DatabaseAtV$to(e),
openTestedDatabase: (e) => $dbClassName(e),
createOld: v1.DatabaseAtV$from.new,
createNew: v2.DatabaseAtV$to.new,
openTestedDatabase: $dbClassName.new,
createItems: (batch, oldDb) {
${tables.map(
(table) {
Expand All @@ -493,9 +493,6 @@ final expectedNew${table.dbGetterName.pascalCase}Data = <v$to.${table.nameOfRowC
},
);
});
/// Add additional data integrity tests here
""";
}
}
2 changes: 1 addition & 1 deletion drift_dev/lib/src/cli/commands/schema/generate_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class GenerateUtils {
}

final versionsSet =
'{${versions.sorted((a, b) => a.compareTo(b)).join(', ')}}';
'[${versions.sorted((a, b) => a.compareTo(b)).join(', ')}]';
buffer
..writeln('default:')
..writeln('throw MissingSchemaException(version, versions);')
Expand Down
7 changes: 3 additions & 4 deletions drift_dev/lib/src/services/schema/verifier_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,22 +104,21 @@ class VerifierImplementation implements SchemaVerifier {
@override
Future<void> testWithDataIntegrity<OldDatabase extends GeneratedDatabase,
NewDatabase extends GeneratedDatabase>(
{required SchemaVerifier verifier,
required OldDatabase Function(QueryExecutor p1) createOld,
{required OldDatabase Function(QueryExecutor p1) createOld,
required NewDatabase Function(QueryExecutor p1) createNew,
required GeneratedDatabase Function(QueryExecutor p1) openTestedDatabase,
required void Function(Batch p1, OldDatabase p2) createItems,
required Future Function(NewDatabase p1) validateItems,
required int oldVersion,
required int newVersion}) async {
final schema = await verifier.schemaAt(oldVersion);
final schema = await schemaAt(oldVersion);

final oldDb = createOld(schema.newConnection());
await oldDb.batch((batch) => createItems(batch, oldDb));
await oldDb.close();

final db = openTestedDatabase(schema.newConnection());
await verifier.migrateAndValidate(db, newVersion);
await migrateAndValidate(db, newVersion);
await db.close();

final newDb = createNew(schema.newConnection());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,48 @@
//@dart=2.12
import 'package:drift/drift.dart';
import 'package:drift/internal/migrations.dart';
import 'schema_v7.dart' as v7;
import 'schema_v5.dart' as v5;
import 'schema_v9.dart' as v9;
import 'schema_v8.dart' as v8;
import 'schema_v3.dart' as v3;
import 'schema_v6.dart' as v6;
import 'schema_v2.dart' as v2;
import 'schema_v1.dart' as v1;
import 'schema_v2.dart' as v2;
import 'schema_v6.dart' as v6;
import 'schema_v7.dart' as v7;
import 'schema_v11.dart' as v11;
import 'schema_v9.dart' as v9;
import 'schema_v10.dart' as v10;
import 'schema_v4.dart' as v4;
import 'schema_v5.dart' as v5;
import 'schema_v3.dart' as v3;
import 'schema_v10.dart' as v10;

class GeneratedHelper implements SchemaInstantiationHelper {
@override
GeneratedDatabase databaseForVersion(QueryExecutor db, int version) {
switch (version) {
case 7:
return v7.DatabaseAtV7(db);
case 5:
return v5.DatabaseAtV5(db);
case 9:
return v9.DatabaseAtV9(db);
case 8:
return v8.DatabaseAtV8(db);
case 3:
return v3.DatabaseAtV3(db);
case 6:
return v6.DatabaseAtV6(db);
case 2:
return v2.DatabaseAtV2(db);
case 1:
return v1.DatabaseAtV1(db);
case 2:
return v2.DatabaseAtV2(db);
case 6:
return v6.DatabaseAtV6(db);
case 7:
return v7.DatabaseAtV7(db);
case 11:
return v11.DatabaseAtV11(db);
case 9:
return v9.DatabaseAtV9(db);
case 10:
return v10.DatabaseAtV10(db);
case 4:
return v4.DatabaseAtV4(db);
case 5:
return v5.DatabaseAtV5(db);
case 3:
return v3.DatabaseAtV3(db);
case 10:
return v10.DatabaseAtV10(db);
default:
throw MissingSchemaException(version, versions);
}
}

static const versions = const {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
}
Loading

0 comments on commit b027e36

Please sign in to comment.