/// Exercises Scheduler Flow integration behavior for the Scheduler Integration Tests package. library; import 'package:drift/native.dart'; import 'package:scheduler_core/scheduler_core.dart' as core; import 'package:scheduler_notifications/notifications.dart'; import 'package:scheduler_persistence_memory/persistence_memory.dart'; import 'package:scheduler_persistence_sqlite/sqlite.dart'; import 'package:test/test.dart'; void main() { test('scheduler flow schedules, notifies, persists, and reloads', () async { final runtime = Stopwatch()..start(); addTearDown(() { runtime.stop(); expect(runtime.elapsed, lessThan(const Duration(seconds: 2))); }); final ownerId = core.OwnerId('owner-a'); final now = DateTime.utc(2026, 6, 26, 8); final window = core.SchedulingWindow( start: DateTime.utc(2026, 6, 26, 9), end: DateTime.utc(2026, 6, 26, 10, 30), ); final lockedInterval = core.TimeInterval( start: DateTime.utc(2026, 6, 26, 9), end: DateTime.utc(2026, 6, 26, 9, 15), label: 'Protected setup time', ); final backlogTask = core.Task( id: 'backlog-a', title: 'Write planning note', projectId: 'project-a', type: core.TaskType.flexible, status: core.TaskStatus.backlog, durationMinutes: 15, createdAt: now, updatedAt: now, ); final plannedTask = core.Task( id: 'planned-a', title: 'Existing focus block', projectId: 'project-a', type: core.TaskType.flexible, status: core.TaskStatus.planned, durationMinutes: 30, scheduledStart: DateTime.utc(2026, 6, 26, 9, 30), scheduledEnd: DateTime.utc(2026, 6, 26, 10), createdAt: now, updatedAt: now, ); final memoryTasks = InMemoryTaskRepository(); expect( (await memoryTasks.insert(ownerId: ownerId, task: backlogTask)).isRight, isTrue, ); expect( (await memoryTasks.insert(ownerId: ownerId, task: plannedTask)).isRight, isTrue, ); final loaded = await memoryTasks.findByOwner(ownerId: ownerId); expect(loaded.isRight, isTrue); final result = const core.SchedulingEngine().insertBacklogTaskIntoNextAvailableSlot( input: core.SchedulingInput( tasks: loaded.right.items, window: window, lockedIntervals: [lockedInterval], ), taskId: backlogTask.id, updatedAt: now, ); expect(result.outcomeCode, core.SchedulingOutcomeCode.success); final scheduledBacklog = _taskById(result.tasks, backlogTask.id); expect(scheduledBacklog.status, core.TaskStatus.planned); expect(scheduledBacklog.scheduledStart, lockedInterval.end); expect( scheduledBacklog.scheduledEnd, DateTime.utc(2026, 6, 26, 9, 30), ); final notifications = FakeNotificationAdapter(); addTearDown(notifications.close); await notifications.schedule(NotificationRequest( id: 'task-${scheduledBacklog.id}-start', title: scheduledBacklog.title, body: 'Scheduled task is ready.', scheduledDateTimeUtc: scheduledBacklog.scheduledStart!, payloadJson: '{"taskId":"${scheduledBacklog.id}"}', )); expect( notifications.scheduledRequests.keys, contains('task-backlog-a-start'), ); final db = SchedulerDb(NativeDatabase.memory()); addTearDown(db.close); final sqliteTasks = SqliteTaskRepository(db); for (final task in result.tasks) { final inserted = await sqliteTasks.insert(ownerId: ownerId, task: task); expect(inserted.isRight, isTrue); } final reloaded = await sqliteTasks.findById( ownerId: ownerId, taskId: backlogTask.id, ); expect(reloaded.isRight, isTrue); final persistedBacklog = reloaded.right; expect(persistedBacklog, isNotNull); expect(persistedBacklog!.scheduledStart, scheduledBacklog.scheduledStart); expect(persistedBacklog.scheduledEnd, scheduledBacklog.scheduledEnd); expect(persistedBacklog.status, core.TaskStatus.planned); }); } core.Task _taskById(Iterable tasks, String id) { return tasks.singleWhere((task) => task.id == id); }