focus-flow/packages/scheduler_persistence_sqlite/test/scheduler_db_test.dart

238 lines
6.6 KiB
Dart

// SPDX-FileCopyrightText: 2026 FocusFlow contributors
// SPDX-License-Identifier: AGPL-3.0-only
/// Tests Scheduler Db behavior for the Scheduler Persistence Sqlite package.
library;
import 'dart:io';
import 'package:drift/native.dart';
import 'package:scheduler_persistence_sqlite/sqlite.dart';
import 'package:sqlite3/sqlite3.dart' as sqlite;
import 'package:test/test.dart';
/// Entry point for this executable Dart file.
/// It wires the local command, test, or application behavior needed by this repository without exposing additional public API.
void main() {
test('opens schema version 2 with expected tables', () async {
final db = SchedulerDb(NativeDatabase.memory());
addTearDown(db.close);
expect(db.schemaVersion, 2);
final rows = await db
.customSelect(
"SELECT name FROM sqlite_master "
"WHERE type = 'table' AND name NOT LIKE 'sqlite_%' "
'ORDER BY name',
)
.get();
final tableNames = rows.map((row) => row.data['name']).toList();
expect(
tableNames,
containsAll(<String>[
'application_operations',
'locked_blocks',
'locked_overrides',
'notice_acknowledgements',
'projects',
'project_statistics',
'settings',
'snapshots',
'task_activities',
'tasks',
]),
);
});
test('migrates schema version 1 file and preserves existing rows', () async {
final directory = await Directory.systemTemp.createTemp(
'focus-flow-sqlite-migration-',
);
addTearDown(() => directory.delete(recursive: true));
final file = File('${directory.path}/scheduler.sqlite');
_createVersionOneDatabase(file);
final db = SchedulerDb(NativeDatabase(file));
addTearDown(db.close);
final rows = await db
.customSelect(
"SELECT name FROM sqlite_master "
"WHERE type = 'table' AND name NOT LIKE 'sqlite_%' "
'ORDER BY name',
)
.get();
final tableNames = rows.map((row) => row.data['name']).toList();
expect(tableNames, contains('application_operations'));
expect(tableNames, contains('task_activities'));
expect(tableNames, contains('project_statistics'));
expect(tableNames, contains('notice_acknowledgements'));
final taskRows = await db
.customSelect("SELECT title FROM tasks WHERE id = 'v1-task'")
.get();
final projectRows = await db
.customSelect("SELECT name FROM projects WHERE id = 'home'")
.get();
final settingsRows = await db
.customSelect(
"SELECT timezone_id FROM settings WHERE owner_id = 'owner-1'")
.get();
expect(taskRows.single.data['title'], 'Existing V1 task');
expect(projectRows.single.data['name'], 'Home');
expect(settingsRows.single.data['timezone_id'], 'UTC');
});
}
/// Creates a minimal version-1 SQLite database file.
void _createVersionOneDatabase(File file) {
final database = sqlite.sqlite3.open(file.path);
try {
database
..execute('''
CREATE TABLE tasks (
id TEXT PRIMARY KEY NOT NULL,
owner_id TEXT NOT NULL,
title TEXT NOT NULL,
project_id TEXT NOT NULL,
parent_id TEXT NULL,
type TEXT NOT NULL,
status TEXT NOT NULL,
priority TEXT NULL,
reward TEXT NOT NULL,
difficulty TEXT NOT NULL,
duration_minutes INTEGER NULL,
scheduled_start_utc INTEGER NULL,
scheduled_end_utc INTEGER NULL,
actual_start_utc INTEGER NULL,
actual_end_utc INTEGER NULL,
completed_at_utc INTEGER NULL,
backlog_tags_json TEXT NOT NULL,
reminder_override TEXT NULL,
stats_json TEXT NOT NULL,
backlog_entered_at_utc INTEGER NULL,
backlog_entered_provenance TEXT NULL,
revision INTEGER NOT NULL,
created_at_utc INTEGER NOT NULL,
updated_at_utc INTEGER NOT NULL
)
''')
..execute('''
CREATE TABLE projects (
id TEXT PRIMARY KEY NOT NULL,
owner_id TEXT NOT NULL,
name TEXT NOT NULL,
color_key TEXT NOT NULL,
default_priority TEXT NOT NULL,
default_reward TEXT NOT NULL,
default_difficulty TEXT NOT NULL,
default_reminder_profile TEXT NOT NULL,
default_duration_minutes INTEGER NULL,
archived_at_utc INTEGER NULL,
revision INTEGER NOT NULL,
created_at_utc INTEGER NOT NULL,
updated_at_utc INTEGER NOT NULL
)
''')
..execute('''
CREATE TABLE settings (
owner_id TEXT PRIMARY KEY NOT NULL,
timezone_id TEXT NOT NULL,
day_start_minutes INTEGER NOT NULL,
day_end_minutes INTEGER NOT NULL,
compact_mode INTEGER NOT NULL,
backlog_staleness_json TEXT NOT NULL,
revision INTEGER NOT NULL,
created_at_utc INTEGER NOT NULL,
updated_at_utc INTEGER NOT NULL
)
''')
..execute('''
INSERT INTO tasks (
id,
owner_id,
title,
project_id,
type,
status,
reward,
difficulty,
backlog_tags_json,
stats_json,
revision,
created_at_utc,
updated_at_utc
) VALUES (
'v1-task',
'owner-1',
'Existing V1 task',
'home',
'flexible',
'backlog',
'notSet',
'notSet',
'[]',
'{}',
1,
0,
0
)
''')
..execute('''
INSERT INTO projects (
id,
owner_id,
name,
color_key,
default_priority,
default_reward,
default_difficulty,
default_reminder_profile,
revision,
created_at_utc,
updated_at_utc
) VALUES (
'home',
'owner-1',
'Home',
'project-home',
'medium',
'notSet',
'notSet',
'none',
1,
0,
0
)
''')
..execute('''
INSERT INTO settings (
owner_id,
timezone_id,
day_start_minutes,
day_end_minutes,
compact_mode,
backlog_staleness_json,
revision,
created_at_utc,
updated_at_utc
) VALUES (
'owner-1',
'UTC',
0,
1440,
1,
'{}',
1,
0,
0
)
''')
..execute('PRAGMA user_version = 1');
} finally {
database.close();
}
}