feat(08-01): add isActive filters to CalendarDao, DailyPlanDao, RoomsDao

- CalendarDao: filter all 6 task queries (watchTasksForDate,
  watchTasksForDateInRoom, watchOverdueTasks, watchOverdueTasksInRoom,
  getTaskCount, getTaskCountInRoom) by isActive=true
- DailyPlanDao: filter all 3 queries (watchAllTasksWithRoomName,
  getOverdueAndTodayTaskCount, getOverdueTaskCount) by isActive=true
- RoomsDao: filter watchRoomWithStats task query by isActive=true
- Update migration test: add schema_v3.dart, test v1->v3 and v2->v3 paths
- Update database_test schemaVersion assertion to expect 3
- Fix test helpers in home_screen_test and task_list_screen_test to pass isActive=true
This commit is contained in:
2026-03-18 20:56:34 +01:00
parent 4b51f5fa04
commit b2f14dcd97
10 changed files with 694 additions and 47 deletions

View File

@@ -6,6 +6,7 @@ import 'package:drift/drift.dart';
import 'package:drift/internal/migrations.dart';
import 'schema_v1.dart' as v1;
import 'schema_v2.dart' as v2;
import 'schema_v3.dart' as v3;
class GeneratedHelper implements SchemaInstantiationHelper {
@override
@@ -15,10 +16,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v1.DatabaseAtV1(db);
case 2:
return v2.DatabaseAtV2(db);
case 3:
return v3.DatabaseAtV3(db);
default:
throw MissingSchemaException(version, versions);
}
}
static const versions = const [1, 2];
static const versions = const [1, 2, 3];
}

View File

@@ -0,0 +1,283 @@
// dart format width=80
// GENERATED BY drift_dev, DO NOT MODIFY.
// ignore_for_file: type=lint,unused_import
//
import 'package:drift/drift.dart';
class Rooms extends Table with TableInfo {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
Rooms(this.attachedDatabase, [this._alias]);
late final GeneratedColumn<int> id = GeneratedColumn<int>(
'id',
aliasedName,
false,
hasAutoIncrement: true,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT',
);
late final GeneratedColumn<String> name = GeneratedColumn<String>(
'name',
aliasedName,
false,
type: DriftSqlType.string,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL',
);
late final GeneratedColumn<String> iconName = GeneratedColumn<String>(
'icon_name',
aliasedName,
false,
type: DriftSqlType.string,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL',
);
late final GeneratedColumn<int> sortOrder = GeneratedColumn<int>(
'sort_order',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'NOT NULL DEFAULT 0',
defaultValue: const CustomExpression('0'),
);
late final GeneratedColumn<int> createdAt = GeneratedColumn<int>(
'created_at',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL',
);
@override
List<GeneratedColumn> get $columns => [
id,
name,
iconName,
sortOrder,
createdAt,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'rooms';
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
throw UnsupportedError('TableInfo.map in schema verification code');
}
@override
Rooms createAlias(String alias) {
return Rooms(attachedDatabase, alias);
}
@override
bool get dontWriteConstraints => true;
}
class Tasks extends Table with TableInfo {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
Tasks(this.attachedDatabase, [this._alias]);
late final GeneratedColumn<int> id = GeneratedColumn<int>(
'id',
aliasedName,
false,
hasAutoIncrement: true,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT',
);
late final GeneratedColumn<int> roomId = GeneratedColumn<int>(
'room_id',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL REFERENCES rooms(id)',
);
late final GeneratedColumn<String> name = GeneratedColumn<String>(
'name',
aliasedName,
false,
type: DriftSqlType.string,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL',
);
late final GeneratedColumn<String> description = GeneratedColumn<String>(
'description',
aliasedName,
true,
type: DriftSqlType.string,
requiredDuringInsert: false,
$customConstraints: 'NULL',
);
late final GeneratedColumn<int> intervalType = GeneratedColumn<int>(
'interval_type',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL',
);
late final GeneratedColumn<int> intervalDays = GeneratedColumn<int>(
'interval_days',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'NOT NULL DEFAULT 1',
defaultValue: const CustomExpression('1'),
);
late final GeneratedColumn<int> anchorDay = GeneratedColumn<int>(
'anchor_day',
aliasedName,
true,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'NULL',
);
late final GeneratedColumn<int> effortLevel = GeneratedColumn<int>(
'effort_level',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL',
);
late final GeneratedColumn<int> nextDueDate = GeneratedColumn<int>(
'next_due_date',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL',
);
late final GeneratedColumn<int> createdAt = GeneratedColumn<int>(
'created_at',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL',
);
late final GeneratedColumn<int> isActive = GeneratedColumn<int>(
'is_active',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'NOT NULL DEFAULT 1 CHECK (is_active IN (0, 1))',
defaultValue: const CustomExpression('1'),
);
@override
List<GeneratedColumn> get $columns => [
id,
roomId,
name,
description,
intervalType,
intervalDays,
anchorDay,
effortLevel,
nextDueDate,
createdAt,
isActive,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'tasks';
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
throw UnsupportedError('TableInfo.map in schema verification code');
}
@override
Tasks createAlias(String alias) {
return Tasks(attachedDatabase, alias);
}
@override
bool get dontWriteConstraints => true;
}
class TaskCompletions extends Table with TableInfo {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
TaskCompletions(this.attachedDatabase, [this._alias]);
late final GeneratedColumn<int> id = GeneratedColumn<int>(
'id',
aliasedName,
false,
hasAutoIncrement: true,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT',
);
late final GeneratedColumn<int> taskId = GeneratedColumn<int>(
'task_id',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL REFERENCES tasks(id)',
);
late final GeneratedColumn<int> completedAt = GeneratedColumn<int>(
'completed_at',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL',
);
@override
List<GeneratedColumn> get $columns => [id, taskId, completedAt];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'task_completions';
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
Never map(Map<String, dynamic> data, {String? tablePrefix}) {
throw UnsupportedError('TableInfo.map in schema verification code');
}
@override
TaskCompletions createAlias(String alias) {
return TaskCompletions(attachedDatabase, alias);
}
@override
bool get dontWriteConstraints => true;
}
class DatabaseAtV3 extends GeneratedDatabase {
DatabaseAtV3(QueryExecutor e) : super(e);
late final Rooms rooms = Rooms(this);
late final Tasks tasks = Tasks(this);
late final TaskCompletions taskCompletions = TaskCompletions(this);
@override
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@override
List<DatabaseSchemaEntity> get allSchemaEntities => [
rooms,
tasks,
taskCompletions,
];
@override
int get schemaVersion => 3;
}

View File

@@ -8,6 +8,7 @@ import 'generated/schema.dart';
import 'generated/schema_v1.dart' as v1;
import 'generated/schema_v2.dart' as v2;
import 'generated/schema_v3.dart' as v3;
void main() {
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
@@ -21,39 +22,28 @@ void main() {
// 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.
const 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 = AppDatabase(schema.newConnection());
await verifier.migrateAndValidate(db, toVersion);
await db.close();
});
}
//
// Note: since AppDatabase.schemaVersion == 3, all migration paths
// end at v3. We only test migrations to the current schema version.
final fromVersions = [1, 2];
for (final fromVersion in fromVersions) {
test('from $fromVersion to 3', () async {
final schema = await verifier.schemaAt(fromVersion);
final db = AppDatabase(schema.newConnection());
await verifier.migrateAndValidate(db, 3);
await db.close();
});
}
});
// The following template shows how to write tests ensuring your migrations
// preserve existing data.
// Testing this can be useful for migrations that change existing columns
// (e.g. by alterating their type or constraints). Migrations that only add
// tables or columns typically don't need these advanced tests. For more
// information, see https://drift.simonbinder.eu/migrations/tests/#verifying-data-integrity
// TODO: This generated template shows how these tests could be written. Adopt
// it to your own needs when testing migrations with data integrity.
test('migration from v1 to v2 does not corrupt data', () async {
// Add data to insert into the old database, and the expected rows after the
// migration.
// TODO: Fill these lists
test('migration from v2 to v3 does not corrupt data', () async {
// The v2 -> v3 migration adds the isActive column (default true).
// Existing tasks should remain and be accessible with isActive = true.
await verifier.testWithDataIntegrity(
oldVersion: 1,
newVersion: 2,
createOld: v1.DatabaseAtV1.new,
createNew: v2.DatabaseAtV2.new,
oldVersion: 2,
newVersion: 3,
createOld: v2.DatabaseAtV2.new,
createNew: v3.DatabaseAtV3.new,
openTestedDatabase: AppDatabase.new,
createItems: (batch, oldDb) {},
validateItems: (newDb) async {},