focus-flow/test/child_tasks_test.dart

141 lines
4.2 KiB
Dart

import 'package:adhd_scheduler_core/scheduler_core.dart';
import 'package:test/test.dart';
void main() {
group('Child task ownership', () {
final now = DateTime(2026, 6, 19, 12);
test('child references parent through parentTaskId', () {
final parent = task(id: 'parent', title: 'Clean kitchen', createdAt: now);
final child = task(
id: 'child',
title: 'Clear sink',
createdAt: now,
parentTaskId: parent.id,
);
final view = ChildTaskView(tasks: [parent, child]);
expect(child.parentTaskId, parent.id);
expect(view.isChildOf(child: child, parent: parent), isTrue);
expect(view.parentOf(child), same(parent));
});
test('parent can query and aggregate direct children', () {
final parent = task(id: 'parent', title: 'Clean kitchen', createdAt: now);
final plannedChild = task(
id: 'planned-child',
title: 'Clear sink',
createdAt: now,
parentTaskId: parent.id,
);
final completedChild = task(
id: 'completed-child',
title: 'Wipe counter',
createdAt: now,
status: TaskStatus.completed,
parentTaskId: parent.id,
);
final unrelatedChild = task(
id: 'unrelated-child',
title: 'Sort drawer',
createdAt: now,
parentTaskId: 'other-parent',
);
final view = ChildTaskView(
tasks: [parent, plannedChild, unrelatedChild, completedChild],
);
expect(view.childrenOf(parent).map((child) => child.id), [
'planned-child',
'completed-child',
]);
final summary = view.summaryFor(parent);
expect(summary.totalCount, 2);
expect(summary.plannedCount, 1);
expect(summary.completedCount, 1);
expect(summary.allChildrenCompleted, isFalse);
});
test('parent can remain incomplete while children are planned or completed',
() {
final parent = task(id: 'parent', title: 'Clean kitchen', createdAt: now);
final completedChild = task(
id: 'completed-child',
title: 'Clear sink',
createdAt: now,
status: TaskStatus.completed,
parentTaskId: parent.id,
);
final view = ChildTaskView(tasks: [parent, completedChild]);
expect(parent.status, TaskStatus.planned);
expect(view.summaryFor(parent).allChildrenCompleted, isTrue);
expect(view.parentShouldAutoComplete(parent), isFalse);
});
test('child ownership does not block normal scheduling behavior', () {
const engine = SchedulingEngine();
final parent = task(
id: 'parent',
title: 'Clean kitchen',
createdAt: now,
status: TaskStatus.backlog,
);
final child = task(
id: 'child',
title: 'Clear sink',
createdAt: now,
status: TaskStatus.backlog,
durationMinutes: 15,
parentTaskId: parent.id,
);
final result = engine.insertBacklogTaskIntoNextAvailableSlot(
input: SchedulingInput(
tasks: [parent, child],
window: SchedulingWindow(
start: DateTime(2026, 6, 19, 9),
end: DateTime(2026, 6, 19, 10),
),
),
taskId: child.id,
updatedAt: now,
);
final scheduledChild = taskById(result.tasks, child.id);
final unchangedParent = taskById(result.tasks, parent.id);
expect(scheduledChild.status, TaskStatus.planned);
expect(scheduledChild.parentTaskId, parent.id);
expect(scheduledChild.scheduledStart, DateTime(2026, 6, 19, 9));
expect(unchangedParent.status, TaskStatus.backlog);
expect(unchangedParent.scheduledStart, isNull);
});
});
}
Task task({
required String id,
required String title,
required DateTime createdAt,
TaskStatus status = TaskStatus.planned,
int? durationMinutes,
String? parentTaskId,
}) {
return Task(
id: id,
title: title,
projectId: 'home',
type: TaskType.flexible,
status: status,
priority: PriorityLevel.medium,
durationMinutes: durationMinutes,
parentTaskId: parentTaskId,
createdAt: createdAt,
updatedAt: createdAt,
);
}
Task taskById(List<Task> tasks, String id) {
return tasks.singleWhere((task) => task.id == id);
}