feat(calendar): add application date integrations
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
using JobTrackerApi.Controllers;
|
||||
using JobTrackerApi.Data;
|
||||
using JobTrackerApi.Models;
|
||||
using JobTrackerApi.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace JobTrackerApi.Tests;
|
||||
|
||||
public sealed class ApplicationCalendarControllerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Status_distinguishes_connection_from_calendar_consent()
|
||||
{
|
||||
await using var db = CreateDb("owner-1");
|
||||
var google = new Mock<IGmailOAuthService>();
|
||||
google.Setup(service => service.GetConnectionAsync("owner-1", It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new GmailConnection { OwnerUserId = "owner-1", Scope = GmailOAuthService.CalendarScope });
|
||||
var microsoft = new Mock<IMicrosoftGraphOAuthService>();
|
||||
microsoft.Setup(service => service.GetConnectionAsync("owner-1", It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new MicrosoftGraphConnection { OwnerUserId = "owner-1", Scope = "Mail.Read" });
|
||||
var controller = CreateController(db, "owner-1", google.Object, microsoft.Object);
|
||||
|
||||
var value = Assert.IsType<ApplicationCalendarController.CalendarStatusDto>(Assert.IsType<OkObjectResult>((await controller.Status(default)).Result).Value);
|
||||
|
||||
Assert.True(value.Google.Connected);
|
||||
Assert.True(value.Google.Writable);
|
||||
Assert.True(value.Microsoft.Connected);
|
||||
Assert.False(value.Microsoft.Writable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Creates_follow_up_in_selected_connected_calendar_with_stable_job_context()
|
||||
{
|
||||
await using var db = CreateDb("owner-1");
|
||||
var job = await SeedJobAsync(db, "owner-1");
|
||||
ExternalCalendarEventRequest? sent = null;
|
||||
var google = new Mock<IGmailOAuthService>();
|
||||
google.Setup(service => service.CreateCalendarEventAsync("owner-1", It.IsAny<ExternalCalendarEventRequest>(), It.IsAny<CancellationToken>()))
|
||||
.Callback<string, ExternalCalendarEventRequest, CancellationToken>((_, request, _) => sent = request)
|
||||
.ReturnsAsync(new ExternalCalendarEventResult("event-1", "https://calendar.google.test/event-1"));
|
||||
var controller = CreateController(db, "owner-1", google.Object, Mock.Of<IMicrosoftGraphOAuthService>());
|
||||
|
||||
var response = await controller.CreateEvent(job.Id, new("google", "follow-up"), default);
|
||||
|
||||
var value = Assert.IsType<ApplicationCalendarController.CalendarEventDto>(Assert.IsType<OkObjectResult>(response.Result).Value);
|
||||
Assert.Equal("google", value.Provider);
|
||||
Assert.Equal("event-1", value.Id);
|
||||
Assert.NotNull(sent);
|
||||
Assert.Equal("Follow up: Backend Engineer at Acme", sent.Summary);
|
||||
Assert.Equal(job.FollowUpAt, sent.StartsAtUtc.UtcDateTime);
|
||||
Assert.False(sent.AllDay);
|
||||
Assert.Equal(64, sent.StableId.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Rejects_another_owners_job_without_calling_provider()
|
||||
{
|
||||
await using var db = CreateDb("owner-1");
|
||||
var job = await SeedJobAsync(db, "owner-2");
|
||||
var google = new Mock<IGmailOAuthService>(MockBehavior.Strict);
|
||||
var controller = CreateController(db, "owner-1", google.Object, Mock.Of<IMicrosoftGraphOAuthService>());
|
||||
|
||||
var response = await controller.CreateEvent(job.Id, new("google", "follow-up"), default);
|
||||
|
||||
Assert.IsType<NotFoundResult>(response.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Downloads_portable_all_day_deadline_event_without_provider_credentials()
|
||||
{
|
||||
await using var db = CreateDb("owner-1");
|
||||
var job = await SeedJobAsync(db, "owner-1");
|
||||
var controller = CreateController(db, "owner-1", Mock.Of<IGmailOAuthService>(), Mock.Of<IMicrosoftGraphOAuthService>());
|
||||
|
||||
var response = Assert.IsType<FileContentResult>(await controller.DownloadEvent(job.Id, "deadline", default));
|
||||
var content = System.Text.Encoding.UTF8.GetString(response.FileContents);
|
||||
|
||||
Assert.Equal("text/calendar; charset=utf-8", response.ContentType);
|
||||
Assert.Contains("DTSTART;VALUE=DATE:20260915", content);
|
||||
Assert.Contains("DTEND;VALUE=DATE:20260916", content);
|
||||
Assert.Contains("SUMMARY:Application deadline: Backend Engineer at Acme", content);
|
||||
}
|
||||
|
||||
private static JobTrackerContext CreateDb(string owner)
|
||||
{
|
||||
var current = new Mock<ICurrentUserService>();
|
||||
current.SetupGet(service => service.UserId).Returns(owner);
|
||||
return new JobTrackerContext(new DbContextOptionsBuilder<JobTrackerContext>()
|
||||
.UseInMemoryDatabase(Guid.NewGuid().ToString()).Options, current.Object);
|
||||
}
|
||||
|
||||
private static async Task<JobApplication> SeedJobAsync(JobTrackerContext db, string owner)
|
||||
{
|
||||
var company = new Company { OwnerUserId = owner, Name = "Acme" };
|
||||
var job = new JobApplication
|
||||
{
|
||||
OwnerUserId = owner,
|
||||
Company = company,
|
||||
JobTitle = "Backend Engineer",
|
||||
Status = "Applied",
|
||||
FollowUpAt = new DateTime(2026, 9, 2, 10, 30, 0, DateTimeKind.Utc),
|
||||
Deadline = new DateTime(2026, 9, 15, 0, 0, 0, DateTimeKind.Utc),
|
||||
Location = "Oslo"
|
||||
};
|
||||
db.JobApplications.Add(job);
|
||||
await db.SaveChangesAsync();
|
||||
return job;
|
||||
}
|
||||
|
||||
private static ApplicationCalendarController CreateController(JobTrackerContext db, string owner, IGmailOAuthService google, IMicrosoftGraphOAuthService microsoft)
|
||||
{
|
||||
var current = new Mock<ICurrentUserService>();
|
||||
current.SetupGet(service => service.UserId).Returns(owner);
|
||||
return new ApplicationCalendarController(db, current.Object, google, microsoft);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user