diff --git a/tdd-todo-list.CSharp.Main/Task.cs b/tdd-todo-list.CSharp.Main/Task.cs new file mode 100644 index 00000000..9736c447 --- /dev/null +++ b/tdd-todo-list.CSharp.Main/Task.cs @@ -0,0 +1,59 @@ +public enum TaskStatus +{ + Incomplete, + Complete +} + +public enum Priority +{ + Low, + Medium, + High +} + +public enum Category +{ + Work, + Shopping, + Reading +} + +public class Task +{ + private static int _idCounter = 1; + public int Id { get; set; } + public string Title { get; set; } + public TaskStatus Status { get; set; } + public Priority Priority { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime? CompletedAt { get; set; } + public Category Category { get; set; } + + public Task(string title, Priority priority, Category category) + { + Id = _idCounter++; + Title = title; + Priority = priority; + Status = TaskStatus.Incomplete; + CreatedAt = DateTime.Now; + Category = category; + CompletedAt = null; + } + + public void MarkComplete() + { + Status = TaskStatus.Complete; + CompletedAt = DateTime.Now; + } + + public void MarkIncomplete() + { + Status = TaskStatus.Incomplete; + CompletedAt = null; + } + + public static void ResetIdCounter() + { + _idCounter = 1; + } +} diff --git a/tdd-todo-list.CSharp.Main/ToDoList.cs b/tdd-todo-list.CSharp.Main/ToDoList.cs index 835cb600..a1d12b0a 100644 --- a/tdd-todo-list.CSharp.Main/ToDoList.cs +++ b/tdd-todo-list.CSharp.Main/ToDoList.cs @@ -1,12 +1,211 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace tdd_todo_list.CSharp.Main +namespace tdd_todo_list.CSharp.Main { + public class TodoList { + private List ListOfTasks = new List(); + + public List GetAll() + { + return ListOfTasks; + } + + public List SortAscending() + { + return ListOfTasks.OrderBy(t => t.Title).ToList(); + } + + public List SortDescending() + { + return ListOfTasks.OrderByDescending(t => t.Title).ToList(); + } + + public Task FindByTitle(string title) + { + foreach (var task in ListOfTasks) + { + if (task.Title.Equals(title, StringComparison.OrdinalIgnoreCase)) + { + return task; + } + } + return null; + } + + // Extension + public Task FindById(int id) + { + foreach (var task in ListOfTasks) + { + if (task.Id == id) + { + return task; + } + } + return null; + } + + public bool Remove(int taskId) + { + var taskToRemove = ListOfTasks.FirstOrDefault(t => t.Id == taskId); + if (taskToRemove != null) + { + ListOfTasks.Remove(taskToRemove); + return true; + } + return false; + } + + // Extension + public bool UpdateTitle(int id, string newTitle) + { + foreach (var task in ListOfTasks) + { + if (task.Id == id) + { + task.Title = newTitle; + return true; + } + } + return false; + } + + // Extension + public bool ChangeStatus(int id, TaskStatus status) + { + foreach (var task in ListOfTasks) + { + if (task.Id == id) + { + task.Status = status; + return true; + } + } + return false; + } + + + public void Add(string title, Priority priority, Category category) + { + Task task = new Task(title, priority, category); + ListOfTasks.Add(task); + } + + public List GetCompleted() + { + List completedTasks = new List(); + foreach (var task in ListOfTasks) + { + if (task.Status == TaskStatus.Complete) + { + completedTasks.Add(task); + } + } + return completedTasks; + } + + public List GetInComplete() + { + List incompleteTasks = new List(); + foreach (var task in ListOfTasks) + { + if (task.Status == TaskStatus.Incomplete) + { + incompleteTasks.Add(task); + } + } + return incompleteTasks; + } + + public List ListByPriority(Priority priority) + { + List priorityTasks = new List(); + foreach (var task in ListOfTasks) + { + if (task.Priority == priority) + { + priorityTasks.Add(task); + } + } + return priorityTasks; + } + + + // Extension + public List ListByCategory(Category category) + { + List listByCategory = new List(); + foreach (var task in ListOfTasks) { + if (task.Category == category) + { + listByCategory.Add(task); + } + } + return listByCategory; + } + + + // Extension + public Task LongestTask() + { + TimeSpan timeToCompleted; + TimeSpan currentLongest = TimeSpan.Zero; + Task longestTask = null; + foreach (var task in ListOfTasks) + { + if (task.CompletedAt != null) + { + timeToCompleted = (TimeSpan)(task.CompletedAt - task.CreatedAt); + if (timeToCompleted > currentLongest) + { + currentLongest = timeToCompleted; + longestTask = task; + } + } + + } + return longestTask; + } + + // Extension + public Task ShortestTask() + { + TimeSpan timeToCompleted; + TimeSpan currentLongest = TimeSpan.MaxValue; + Task longestTask = null; + foreach (var task in ListOfTasks) + { + if (task.CompletedAt != null) + { + timeToCompleted = (TimeSpan)(task.CompletedAt - task.CreatedAt); + if (timeToCompleted < currentLongest) + { + currentLongest = timeToCompleted; + longestTask = task; + } + } + + } + return longestTask; + } + + // Extension + public List TasksLongerThan(TimeSpan duration) + { + TimeSpan timeToCompleted; + List listTasksLongerThanDuration = new List(); + foreach (var task in ListOfTasks) + { + if (task.CompletedAt != null) + { + timeToCompleted = (TimeSpan)(task.CompletedAt - task.CreatedAt); + if (timeToCompleted > duration) + { + listTasksLongerThanDuration.Add(task); + } + } + } + return listTasksLongerThanDuration; + } } } diff --git a/tdd-todo-list.CSharp.Main/domain-model.md b/tdd-todo-list.CSharp.Main/domain-model.md new file mode 100644 index 00000000..f695f1c6 --- /dev/null +++ b/tdd-todo-list.CSharp.Main/domain-model.md @@ -0,0 +1,27 @@ +| **Class** | **Method** / Property | **Scenario** | **Output** / **Description** | +| ---------- | ----------------------------------------- | ----------------------------------------------------- | ---------------------------- | +| `ToDoList` | `Add(string title, Priority priority)` | Add a task with a title and priority | `void` | +| `ToDoList` | `Remove(int taskId)` | Remove a task from the list | `bool` (success/fail) | +| `ToDoList` | `GetAll()` | View all tasks | `List` | +| `ToDoList` | `GetCompleted()` | View only completed tasks | `List` | +| `ToDoList` | `GetIncomplete()` | View only incomplete tasks | `List` | +| `ToDoList` | `FindByTitle(string title)` | Search for a task by its title | `Task` or `null` | +| `ToDoList` | `FindById(int id)` | Search for a task by its unique ID | `Task` or `null` | +| `ToDoList` | `UpdateTitle(int id, string newTitle)` | Update the name of a task by providing its ID | `bool` (success/fail) | +| `ToDoList` | `ChangeStatus(int id, TaskStatus status)` | Change the status of a task by providing its ID | `bool` (success/fail) | +| `ToDoList` | `SortAscending()` | View all tasks sorted alphabetically A → Z | `List` | +| `ToDoList` | `SortDescending()` | View all tasks sorted alphabetically Z → A | `List` | +| `ToDoList` | `ListByPriority(Priority priority)` | View tasks filtered by priority | `List` | +| `ToDoList` | `ListByCategory(Category category)` | View tasks filtered by category | `List` | +| `ToDoList` | `LongestTask()` | Find the task that took the longest time to complete | `Task` | +| `ToDoList` | `ShortestTask()` | Find the task that took the shortest time to complete | `Task` | +| `ToDoList` | `TasksLongerThan(TimeSpan duration)` | Find tasks that took longer than a specified duration | `List` | +| `Task` | `Id` | Unique identifier for each task | `int` | +| `Task` | `Title` | The name/title of the task | `string` | +| `Task` | `Status` | Current status (Complete/Incomplete) | `TaskStatus` | +| `Task` | `Priority` | Priority level of the task | `Priority` | +| `Task` | `Category` | Category of the task (e.g., Study, Work, Admin) | `Category` | +| `Task` | `CreatedAt` | Date and time the task was created | `DateTime` | +| `Task` | `CompletedAt` | Date and time the task was completed | `DateTime?` (nullable) | +| `Task` | `MarkComplete()` | Mark a task as complete and record completion time | `void` | +| `Task` | `MarkIncomplete()` | Mark a task as incomplete and reset completion time | `void` | diff --git a/tdd-todo-list.CSharp.Test/CoreTests.cs b/tdd-todo-list.CSharp.Test/CoreTests.cs index 084cce19..509a64a5 100644 --- a/tdd-todo-list.CSharp.Test/CoreTests.cs +++ b/tdd-todo-list.CSharp.Test/CoreTests.cs @@ -1,17 +1,184 @@ using tdd_todo_list.CSharp.Main; using NUnit.Framework; +using System.Linq; namespace tdd_todo_list.CSharp.Test { [TestFixture] public class CoreTests { + private TodoList todoList; + + [SetUp] + public void Setup() + { + Task.ResetIdCounter(); + todoList = new TodoList(); + todoList.Add("Homework", Priority.High, Category.Work); + todoList.Add("Laundry", Priority.Medium, Category.Shopping); + todoList.Add("Shopping", Priority.Low, Category.Shopping); + + var task = todoList.FindByTitle("Homework"); + task.MarkComplete(); + } + + [Test] + public void GetAll_ReturnsAllTasks() + { + var allTasks = todoList.GetAll(); + Assert.AreEqual(3, allTasks.Count); + } + + [Test] + public void Add_IncreasesTaskCount() + { + int initialCount = todoList.GetAll().Count; + todoList.Add("Gym", Priority.Medium, Category.Work); + Assert.AreEqual(initialCount + 1, todoList.GetAll().Count); + } + + [Test] + public void Remove_DecreasesTaskCount() + { + var taskToRemove = todoList.GetAll()[0]; + bool removed = todoList.Remove(taskToRemove.Id); + Assert.IsTrue(removed); + Assert.AreEqual(2, todoList.GetAll().Count); + } + + [Test] + public void SortAscending_SortsByTitle() + { + var sorted = todoList.SortAscending(); + Assert.AreEqual("Homework", sorted[0].Title); + Assert.AreEqual("Laundry", sorted[1].Title); + Assert.AreEqual("Shopping", sorted[2].Title); + } + + [Test] + public void SortDescending_SortsByTitle() + { + var sorted = todoList.SortDescending(); + Assert.AreEqual("Shopping", sorted[0].Title); + Assert.AreEqual("Laundry", sorted[1].Title); + Assert.AreEqual("Homework", sorted[2].Title); + } + + [Test] + public void FindByTitle_ReturnsCorrectTask() + { + var task = todoList.FindByTitle("Laundry"); + Assert.IsNotNull(task); + Assert.AreEqual("Laundry", task.Title); + } + + [Test] + public void FindByTitle_TaskNotFoundReturnsNull() + { + var task = todoList.FindByTitle("NonExisting"); + Assert.IsNull(task); + } + + [Test] + public void GetCompleted_ReturnsOnlyCompletedTasks() + { + var completed = todoList.GetCompleted(); + Assert.AreEqual(1, completed.Count); + Assert.AreEqual("Homework", completed[0].Title); + } [Test] - public void FirstTest() + public void GetInComplete_ReturnsOnlyIncompleteTasks() { - TodoList core = new TodoList(); - Assert.Pass(); + var incomplete = todoList.GetInComplete(); + Assert.AreEqual(2, incomplete.Count); + var titles = incomplete.Select(t => t.Title).ToList(); + CollectionAssert.AreEquivalent(new[] { "Laundry", "Shopping" }, titles); + } + + [Test] + public void ListByPriority_ReturnsTasksOfThatPriority() + { + var highPriority = todoList.ListByPriority(Priority.High); + Assert.AreEqual(1, highPriority.Count); + Assert.AreEqual("Homework", highPriority[0].Title); + + var mediumPriority = todoList.ListByPriority(Priority.Medium); + Assert.AreEqual(1, mediumPriority.Count); + Assert.AreEqual("Laundry", mediumPriority[0].Title); + } + + [Test] + public void FindById_ReturnsCorrectTask() + { + var task = todoList.FindById(2); + Assert.IsNotNull(task); + Assert.AreEqual("Laundry", task.Title); + } + + [Test] + public void UpdateTitle_ChangesTitleSuccessfully() + { + bool updated = todoList.UpdateTitle(2, "Wash Laundry"); + var task = todoList.FindById(2); + Assert.IsTrue(updated); + Assert.AreEqual("Wash Laundry", task.Title); + } + + [Test] + public void ChangeStatus_UpdatesTaskStatus() + { + bool changed = todoList.ChangeStatus(2, TaskStatus.Complete); + var task = todoList.FindById(2); + Assert.IsTrue(changed); + Assert.AreEqual(TaskStatus.Complete, task.Status); + } + + [Test] + public void ListByCategory_ReturnsTasksInCategory() + { + var shoppingTasks = todoList.ListByCategory(Category.Shopping); + Assert.AreEqual(2, shoppingTasks.Count); + CollectionAssert.AreEquivalent(new[] { "Laundry", "Shopping" }, shoppingTasks.Select(t => t.Title)); + } + + [Test] + public void LongestTask_ReturnsTaskWithMaxDuration() + { + // Adjust CompletedAt for more tasks + var shoppingTask = todoList.FindByTitle("Laundry"); + shoppingTask.MarkComplete(); + shoppingTask.CompletedAt = shoppingTask.CreatedAt.AddDays(7); + + var longest = todoList.LongestTask(); + Assert.AreEqual("Laundry", longest.Title); + } + + [Test] + public void ShortestTask_ReturnsTaskWithMinDuration() + { + var shoppingTask = todoList.FindByTitle("Laundry"); + shoppingTask.MarkComplete(); + shoppingTask.CompletedAt = shoppingTask.CreatedAt.AddDays(7); + + var homeworkTask = todoList.FindByTitle("Homework"); + var shortest = todoList.ShortestTask(); + Assert.AreEqual("Homework", shortest.Title); + } + + [Test] + public void TasksLongerThan_ReturnsTasksExceedingDuration() + { + var homeworkTask = todoList.FindByTitle("Homework"); + var shoppingTask = todoList.FindByTitle("Laundry"); + shoppingTask.MarkComplete(); + shoppingTask.CompletedAt = shoppingTask.CreatedAt.AddDays(7); + + TimeSpan duration = TimeSpan.FromDays(6); + var longTasks = todoList.TasksLongerThan(duration); + Assert.AreEqual(1, longTasks.Count); + Assert.AreEqual("Laundry", longTasks[0].Title); } } -} \ No newline at end of file +} +