forked from eva/focus-flow
180 lines
5.1 KiB
Dart
180 lines
5.1 KiB
Dart
import 'package:adhd_scheduler_core/scheduler_core.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
group('Required task action service', () {
|
|
final now = DateTime(2026, 6, 19, 12);
|
|
const service = RequiredTaskActionService();
|
|
|
|
test('done marks inflexible task completed without clearing history', () {
|
|
final task = requiredTask(
|
|
id: 'appointment',
|
|
type: TaskType.inflexible,
|
|
createdAt: now,
|
|
start: DateTime(2026, 6, 19, 9),
|
|
);
|
|
|
|
final result = service.apply(
|
|
task: task,
|
|
action: RequiredTaskAction.done,
|
|
updatedAt: now,
|
|
);
|
|
|
|
expect(result.action, RequiredTaskAction.done);
|
|
expect(result.task.status, TaskStatus.completed);
|
|
expect(result.task.scheduledStart, task.scheduledStart);
|
|
expect(result.task.scheduledEnd, task.scheduledEnd);
|
|
expect(result.removedFromActivePlan, isFalse);
|
|
});
|
|
|
|
test('done marks critical task completed without moving it', () {
|
|
final task = requiredTask(
|
|
id: 'meds',
|
|
type: TaskType.critical,
|
|
createdAt: now,
|
|
start: DateTime(2026, 6, 19, 10),
|
|
);
|
|
|
|
final result = service.apply(
|
|
task: task,
|
|
action: RequiredTaskAction.done,
|
|
updatedAt: now,
|
|
);
|
|
|
|
expect(result.task.status, TaskStatus.completed);
|
|
expect(result.task.scheduledStart, task.scheduledStart);
|
|
expect(result.task.scheduledEnd, task.scheduledEnd);
|
|
});
|
|
|
|
test('missed critical task moves to backlog and clears schedule', () {
|
|
final task = requiredTask(
|
|
id: 'critical',
|
|
type: TaskType.critical,
|
|
createdAt: now,
|
|
start: DateTime(2026, 6, 19, 9),
|
|
);
|
|
|
|
final result = service.apply(
|
|
task: task,
|
|
action: RequiredTaskAction.missed,
|
|
updatedAt: now,
|
|
);
|
|
|
|
expect(result.task.status, TaskStatus.backlog);
|
|
expect(result.task.scheduledStart, isNull);
|
|
expect(result.task.scheduledEnd, isNull);
|
|
expect(result.task.stats.missedCount, 1);
|
|
expect(result.task.stats.movedToBacklogCount, 1);
|
|
expect(result.removedFromActivePlan, isTrue);
|
|
});
|
|
|
|
test('missed inflexible task remains scheduled as history', () {
|
|
final task = requiredTask(
|
|
id: 'appointment',
|
|
type: TaskType.inflexible,
|
|
createdAt: now,
|
|
start: DateTime(2026, 6, 19, 9),
|
|
);
|
|
|
|
final result = service.apply(
|
|
task: task,
|
|
action: RequiredTaskAction.missed,
|
|
updatedAt: now,
|
|
);
|
|
|
|
expect(result.task.status, TaskStatus.missed);
|
|
expect(result.task.scheduledStart, task.scheduledStart);
|
|
expect(result.task.scheduledEnd, task.scheduledEnd);
|
|
expect(result.task.stats.missedCount, 1);
|
|
expect(result.removedFromActivePlan, isFalse);
|
|
});
|
|
|
|
test('cancel and no-longer-relevant remain distinct states', () {
|
|
final task = requiredTask(
|
|
id: 'appointment',
|
|
type: TaskType.inflexible,
|
|
createdAt: now,
|
|
start: DateTime(2026, 6, 19, 9),
|
|
);
|
|
|
|
final cancelled = service.apply(
|
|
task: task,
|
|
action: RequiredTaskAction.cancel,
|
|
updatedAt: now,
|
|
);
|
|
final noLongerRelevant = service.apply(
|
|
task: task,
|
|
action: RequiredTaskAction.noLongerRelevant,
|
|
updatedAt: now,
|
|
);
|
|
|
|
expect(cancelled.task.status, TaskStatus.cancelled);
|
|
expect(cancelled.task.scheduledStart, isNull);
|
|
expect(cancelled.task.scheduledEnd, isNull);
|
|
expect(cancelled.task.stats.cancelledCount, 1);
|
|
expect(cancelled.removedFromActivePlan, isTrue);
|
|
|
|
expect(noLongerRelevant.task.status, TaskStatus.noLongerRelevant);
|
|
expect(noLongerRelevant.task.scheduledStart, isNull);
|
|
expect(noLongerRelevant.task.scheduledEnd, isNull);
|
|
expect(noLongerRelevant.task.stats.cancelledCount, 0);
|
|
expect(noLongerRelevant.removedFromActivePlan, isTrue);
|
|
});
|
|
|
|
test('required action service rejects flexible tasks', () {
|
|
final task = Task.quickCapture(
|
|
id: 'flexible',
|
|
title: 'Flexible',
|
|
createdAt: now,
|
|
).copyWith(status: TaskStatus.planned);
|
|
|
|
expect(
|
|
() => service.apply(
|
|
task: task,
|
|
action: RequiredTaskAction.done,
|
|
updatedAt: now,
|
|
),
|
|
throwsArgumentError,
|
|
);
|
|
});
|
|
|
|
test('required action service rejects locked tasks', () {
|
|
final task = requiredTask(
|
|
id: 'locked',
|
|
type: TaskType.locked,
|
|
createdAt: now,
|
|
start: DateTime(2026, 6, 19, 9),
|
|
);
|
|
|
|
expect(
|
|
() => service.apply(
|
|
task: task,
|
|
action: RequiredTaskAction.missed,
|
|
updatedAt: now,
|
|
),
|
|
throwsArgumentError,
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
Task requiredTask({
|
|
required String id,
|
|
required TaskType type,
|
|
required DateTime createdAt,
|
|
required DateTime start,
|
|
}) {
|
|
return Task(
|
|
id: id,
|
|
title: id,
|
|
projectId: 'required',
|
|
type: type,
|
|
status: TaskStatus.planned,
|
|
priority: PriorityLevel.high,
|
|
durationMinutes: 30,
|
|
scheduledStart: start,
|
|
scheduledEnd: start.add(const Duration(minutes: 30)),
|
|
createdAt: createdAt,
|
|
updatedAt: createdAt,
|
|
);
|
|
}
|