feat(api): support task deadlines
This commit is contained in:
@@ -52,6 +52,7 @@ public class TasksController(ITaskRepository taskRepo, ILogger<TasksController>
|
|||||||
Category = request.Category,
|
Category = request.Category,
|
||||||
ParentTaskId = request.ParentTaskId,
|
ParentTaskId = request.ParentTaskId,
|
||||||
EstimatedMinutes = request.EstimatedMinutes,
|
EstimatedMinutes = request.EstimatedMinutes,
|
||||||
|
DueAt = request.DueAt,
|
||||||
Status = WorkTaskStatus.Pending
|
Status = WorkTaskStatus.Pending
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -173,6 +174,8 @@ public class TasksController(ITaskRepository taskRepo, ILogger<TasksController>
|
|||||||
if (request.Description is not null) task.Description = request.Description;
|
if (request.Description is not null) task.Description = request.Description;
|
||||||
if (request.Category is not null) task.Category = request.Category;
|
if (request.Category is not null) task.Category = request.Category;
|
||||||
if (request.EstimatedMinutes.HasValue) task.EstimatedMinutes = request.EstimatedMinutes;
|
if (request.EstimatedMinutes.HasValue) task.EstimatedMinutes = request.EstimatedMinutes;
|
||||||
|
if (request.ClearDueAt) task.DueAt = null;
|
||||||
|
else if (request.DueAt.HasValue) task.DueAt = request.DueAt;
|
||||||
if (request.ParentTaskId.HasValue)
|
if (request.ParentTaskId.HasValue)
|
||||||
{
|
{
|
||||||
var parent = await taskRepo.GetByIdAsync(request.ParentTaskId.Value);
|
var parent = await taskRepo.GetByIdAsync(request.ParentTaskId.Value);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||||
<PackageReference Include="xunit" Version="2.9.3" />
|
<PackageReference Include="xunit" Version="2.9.3" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||||
@@ -16,6 +17,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\TaskTracker.Api\TaskTracker.Api.csproj" />
|
||||||
<ProjectReference Include="..\TaskTracker.Core\TaskTracker.Core.csproj" />
|
<ProjectReference Include="..\TaskTracker.Core\TaskTracker.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\TaskTracker.Infrastructure\TaskTracker.Infrastructure.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
using TaskTracker.Api.Controllers;
|
||||||
|
using TaskTracker.Core.DTOs;
|
||||||
|
using TaskTracker.Core.Entities;
|
||||||
|
using TaskTracker.Infrastructure.Data;
|
||||||
|
using TaskTracker.Infrastructure.Repositories;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace TaskTracker.Tests;
|
||||||
|
|
||||||
|
public class TasksControllerDeadlineTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task Create_copies_due_at_including_offset_to_created_task()
|
||||||
|
{
|
||||||
|
await using var db = CreateDbContext();
|
||||||
|
var controller = CreateController(db);
|
||||||
|
var dueAt = DateTimeOffset.Parse("2026-08-14T11:00:00-04:00");
|
||||||
|
|
||||||
|
var result = await controller.Create(new CreateTaskRequest { Title = "deadline", DueAt = dueAt });
|
||||||
|
|
||||||
|
var created = Assert.IsType<CreatedAtActionResult>(result);
|
||||||
|
var response = Assert.IsType<ApiResponse<WorkTask>>(created.Value);
|
||||||
|
Assert.Equal(dueAt, response.Data!.DueAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Update_changes_due_at_when_value_is_supplied()
|
||||||
|
{
|
||||||
|
await using var db = CreateDbContext();
|
||||||
|
var task = await AddTaskAsync(db, DateTimeOffset.Parse("2026-08-14T11:00:00-04:00"));
|
||||||
|
var controller = CreateController(db);
|
||||||
|
var dueAt = DateTimeOffset.Parse("2026-08-15T09:30:00+02:00");
|
||||||
|
|
||||||
|
var result = await controller.Update(task.Id, new UpdateTaskRequest { DueAt = dueAt });
|
||||||
|
|
||||||
|
var ok = Assert.IsType<OkObjectResult>(result);
|
||||||
|
var response = Assert.IsType<ApiResponse<WorkTask>>(ok.Value);
|
||||||
|
Assert.Equal(dueAt, response.Data!.DueAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Update_clears_due_at_only_when_clear_due_at_is_true()
|
||||||
|
{
|
||||||
|
await using var db = CreateDbContext();
|
||||||
|
var task = await AddTaskAsync(db, DateTimeOffset.Parse("2026-08-14T11:00:00-04:00"));
|
||||||
|
var controller = CreateController(db);
|
||||||
|
|
||||||
|
var result = await controller.Update(task.Id, new UpdateTaskRequest { ClearDueAt = true });
|
||||||
|
|
||||||
|
var ok = Assert.IsType<OkObjectResult>(result);
|
||||||
|
var response = Assert.IsType<ApiResponse<WorkTask>>(ok.Value);
|
||||||
|
Assert.Null(response.Data!.DueAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Update_with_omitted_due_at_preserves_existing_due_at()
|
||||||
|
{
|
||||||
|
await using var db = CreateDbContext();
|
||||||
|
var dueAt = DateTimeOffset.Parse("2026-08-14T11:00:00-04:00");
|
||||||
|
var task = await AddTaskAsync(db, dueAt);
|
||||||
|
var controller = CreateController(db);
|
||||||
|
|
||||||
|
var result = await controller.Update(task.Id, new UpdateTaskRequest { Title = "renamed" });
|
||||||
|
|
||||||
|
var ok = Assert.IsType<OkObjectResult>(result);
|
||||||
|
var response = Assert.IsType<ApiResponse<WorkTask>>(ok.Value);
|
||||||
|
Assert.Equal(dueAt, response.Data!.DueAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TaskTrackerDbContext CreateDbContext()
|
||||||
|
{
|
||||||
|
var options = new DbContextOptionsBuilder<TaskTrackerDbContext>()
|
||||||
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
||||||
|
.Options;
|
||||||
|
return new TaskTrackerDbContext(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TasksController CreateController(TaskTrackerDbContext db) =>
|
||||||
|
new(new TaskRepository(db), NullLogger<TasksController>.Instance);
|
||||||
|
|
||||||
|
private static async Task<WorkTask> AddTaskAsync(TaskTrackerDbContext db, DateTimeOffset dueAt)
|
||||||
|
{
|
||||||
|
var task = new WorkTask { Title = "existing", DueAt = dueAt };
|
||||||
|
db.Tasks.Add(task);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user