feat(calendar): show recorded deadlines in agenda and month views
This commit is contained in:
@@ -4,6 +4,7 @@ using JobTrackerApi.Models;
|
||||
using JobTrackerApi.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
@@ -11,6 +12,75 @@ namespace JobTrackerApi.Tests;
|
||||
|
||||
public sealed class ApplicationCalendarControllerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Agenda_date_window_translates_on_sqlite_and_keeps_legacy_wall_clock_date()
|
||||
{
|
||||
await using var connection = new SqliteConnection("Data Source=:memory:");
|
||||
await connection.OpenAsync();
|
||||
var current = new Mock<ICurrentUserService>(); current.SetupGet(service => service.UserId).Returns("owner-1");
|
||||
await using var db = new JobTrackerContext(new DbContextOptionsBuilder<JobTrackerContext>().UseSqlite(connection).Options, current.Object);
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
var job = await SeedJobAsync(db, "owner-1");
|
||||
job.Status = "Saved"; job.FollowUpAt = null; job.Deadline = new DateTime(2026, 9, 30, 23, 59, 59);
|
||||
await db.SaveChangesAsync(); db.ChangeTracker.Clear();
|
||||
var controller = CreateController(db, "owner-1", new Mock<IGmailOAuthService>(MockBehavior.Strict).Object, new Mock<IMicrosoftGraphOAuthService>(MockBehavior.Strict).Object);
|
||||
var result = await controller.Agenda(new DateOnly(2026, 9, 1), new DateOnly(2026, 9, 30), default);
|
||||
var row = Assert.Single(Assert.IsAssignableFrom<IEnumerable<ApplicationCalendarController.CalendarAgendaItemDto>>(Assert.IsType<OkObjectResult>(result.Result).Value));
|
||||
Assert.Equal("2026-09-30", row.Date);
|
||||
Assert.Equal(new DateTime(2026, 9, 30, 23, 59, 59), row.RecordedAt);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Agenda_includes_saved_deadlines_and_preserves_recorded_dates_without_provider_access()
|
||||
{
|
||||
await using var db = CreateDb("owner-1");
|
||||
var job = await SeedJobAsync(db, "owner-1");
|
||||
job.Status = "Saved";
|
||||
job.FollowUpAt = new DateTime(2026, 9, 2, 0, 0, 0, DateTimeKind.Unspecified);
|
||||
job.Deadline = new DateTime(2026, 9, 15, 23, 30, 0, DateTimeKind.Unspecified);
|
||||
await db.SaveChangesAsync();
|
||||
var controller = CreateController(db, "owner-1", new Mock<IGmailOAuthService>(MockBehavior.Strict).Object, new Mock<IMicrosoftGraphOAuthService>(MockBehavior.Strict).Object);
|
||||
|
||||
var response = await controller.Agenda(new DateOnly(2026, 9, 1), new DateOnly(2026, 9, 30), default);
|
||||
var rows = Assert.IsAssignableFrom<IEnumerable<ApplicationCalendarController.CalendarAgendaItemDto>>(Assert.IsType<OkObjectResult>(response.Result).Value).ToList();
|
||||
|
||||
Assert.Equal(2, rows.Count);
|
||||
Assert.Equal("follow-up", rows[0].Kind);
|
||||
Assert.Equal("2026-09-02", rows[0].Date);
|
||||
Assert.Equal("deadline", rows[1].Kind);
|
||||
Assert.Equal("2026-09-15", rows[1].Date);
|
||||
Assert.Equal(job.Deadline, rows[1].RecordedAt);
|
||||
Assert.Equal(DateTimeKind.Unspecified, rows[1].RecordedAt.Kind);
|
||||
Assert.All(rows, row => { Assert.Equal(job.Id, row.JobApplicationId); Assert.Equal("Acme", row.CompanyName); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Agenda_excludes_other_owners_deleted_and_out_of_range_dates()
|
||||
{
|
||||
await using var db = CreateDb("owner-1");
|
||||
await SeedJobAsync(db, "owner-2");
|
||||
var deleted = await SeedJobAsync(db, "owner-1"); deleted.IsDeleted = true;
|
||||
var outside = await SeedJobAsync(db, "owner-1"); outside.FollowUpAt = new DateTime(2026, 8, 31); outside.Deadline = new DateTime(2026, 10, 1);
|
||||
var edge = await SeedJobAsync(db, "owner-1"); edge.FollowUpAt = null; edge.Deadline = new DateTime(2026, 9, 30, 23, 59, 59);
|
||||
await db.SaveChangesAsync();
|
||||
var controller = CreateController(db, "owner-1", Mock.Of<IGmailOAuthService>(), Mock.Of<IMicrosoftGraphOAuthService>());
|
||||
var response = await controller.Agenda(new DateOnly(2026, 9, 1), new DateOnly(2026, 9, 30), default);
|
||||
var rows = Assert.IsAssignableFrom<IEnumerable<ApplicationCalendarController.CalendarAgendaItemDto>>(Assert.IsType<OkObjectResult>(response.Result).Value);
|
||||
Assert.Equal(edge.Id, Assert.Single(rows).JobApplicationId);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("2026-09-30", "2026-09-01")]
|
||||
[InlineData("2026-01-01", "2026-12-31")]
|
||||
[InlineData("9999-12-01", "9999-12-31")]
|
||||
public async Task Agenda_rejects_unbounded_or_invalid_ranges(string from, string to)
|
||||
{
|
||||
await using var db = CreateDb("owner-1");
|
||||
var controller = CreateController(db, "owner-1", Mock.Of<IGmailOAuthService>(), Mock.Of<IMicrosoftGraphOAuthService>());
|
||||
var response = await controller.Agenda(DateOnly.Parse(from), DateOnly.Parse(to), default);
|
||||
Assert.IsType<BadRequestObjectResult>(response.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Status_distinguishes_connection_from_calendar_consent()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user