using TaskTracker.Api.Pages; using TaskTracker.Core.Entities; using TaskTracker.Core.Enums; using Xunit; namespace TaskTracker.Tests; public class BoardUpcomingTasksTests { [Fact] public void SelectUpcomingTasks_excludes_completed_abandoned_and_tasks_without_a_deadline() { var dueAt = DateTimeOffset.Parse("2026-08-14T09:00:00Z"); var tasks = new[] { Task("pending", WorkTaskStatus.Pending, dueAt), Task("completed", WorkTaskStatus.Completed, dueAt.AddHours(-1)), Task("abandoned", WorkTaskStatus.Abandoned, dueAt.AddHours(1)), Task("no deadline", WorkTaskStatus.Active), }; var upcoming = BoardModel.SelectUpcomingTasks(tasks, category: null, hasSubtasks: false); var task = Assert.Single(upcoming); Assert.Equal("pending", task.Title); } [Fact] public void SelectUpcomingTasks_includes_top_level_tasks_and_subtasks_in_due_date_order() { var parent = Task("top level", WorkTaskStatus.Pending, DateTimeOffset.Parse("2026-08-14T12:00:00Z")); var subtask = Task("subtask", WorkTaskStatus.Active, DateTimeOffset.Parse("2026-08-14T10:00:00Z"), parentTaskId: 1); var later = Task("later", WorkTaskStatus.Paused, DateTimeOffset.Parse("2026-08-14T14:00:00Z")); var upcoming = BoardModel.SelectUpcomingTasks([parent, subtask, later], category: null, hasSubtasks: false); Assert.Equal(["subtask", "top level", "later"], upcoming.Select(task => task.Title)); } [Fact] public void SelectUpcomingTasks_limits_results_to_eight_earliest_deadlines() { var start = DateTimeOffset.Parse("2026-08-14T09:00:00Z"); var tasks = Enumerable.Range(0, 9) .Select(index => Task($"task {index}", WorkTaskStatus.Pending, start.AddHours(index))) .ToList(); var upcoming = BoardModel.SelectUpcomingTasks(tasks, category: null, hasSubtasks: false); Assert.Equal(8, upcoming.Count); Assert.DoesNotContain(upcoming, task => task.Title == "task 8"); } [Fact] public void SelectUpcomingTasks_honors_active_category_and_has_subtasks_filters() { var dueAt = DateTimeOffset.Parse("2026-08-14T09:00:00Z"); var developmentParent = Task("development parent", WorkTaskStatus.Pending, dueAt, category: "Development"); developmentParent.SubTasks.Add(Task("development child", WorkTaskStatus.Pending, dueAt.AddHours(1), parentTaskId: 1, category: "Development")); var developmentLeaf = Task("development leaf", WorkTaskStatus.Pending, dueAt.AddHours(2), category: "Development"); var researchParent = Task("research parent", WorkTaskStatus.Pending, dueAt.AddHours(-1), category: "Research"); researchParent.SubTasks.Add(Task("research child", WorkTaskStatus.Pending, dueAt, parentTaskId: 2, category: "Research")); var upcoming = BoardModel.SelectUpcomingTasks( [developmentParent, developmentParent.SubTasks[0], developmentLeaf, researchParent, researchParent.SubTasks[0]], category: "development", hasSubtasks: true); var task = Assert.Single(upcoming); Assert.Equal("development parent", task.Title); } private static WorkTask Task(string title, WorkTaskStatus status, DateTimeOffset? dueAt = null, int? parentTaskId = null, string? category = null) => new() { Title = title, Status = status, DueAt = dueAt, ParentTaskId = parentTaskId, Category = category, }; }