// 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([ '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(); } }