Files
jobtrackingapp/JobTrackerApi.Tests/JobApplicationsFollowUpDraftTests.cs

132 lines
5.6 KiB
C#

using System.Security.Claims;
using JobTrackerApi.Controllers;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using JobTrackerApi.Tests.TestSupport;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
namespace JobTrackerApi.Tests;
public sealed class JobApplicationsFollowUpDraftTests
{
[Fact]
public async Task Follow_up_draft_uses_imported_thread_and_saved_package_context()
{
await using var db = CreateDb();
var company = new Company
{
Name = "Acme",
RecruiterName = "Maria Recruiter",
RecruiterEmail = "maria@acme.test",
OwnerUserId = "user-1"
};
db.Companies.Add(company);
db.Users.Add(new ApplicationUser
{
Id = "user-1",
UserName = "user@example.test",
Email = "user@example.test",
DisplayName = "Casey Candidate"
});
await db.SaveChangesAsync();
var job = new JobApplication
{
JobTitle = "Backend Developer",
CompanyId = company.Id,
OwnerUserId = "user-1",
Status = "Waiting",
DateApplied = new DateTime(2026, 3, 1, 0, 0, 0, DateTimeKind.Utc),
Description = "Need .NET APIs and clear stakeholder communication.",
ShortSummary = "Strong overlap in backend API delivery.",
TailoredCvText = "Saved tailored CV",
CoverLetterText = "Saved cover letter",
RecruiterMessageDraft = "Saved recruiter message",
Notes = "Original notes\n\n<<<APPLICATION_ANSWER_DRAFT>>>\nSaved application answer\n<<<END_APPLICATION_ANSWER_DRAFT>>>"
};
db.JobApplications.Add(job);
await db.SaveChangesAsync();
db.Correspondences.Add(new Correspondence
{
JobApplicationId = job.Id,
From = "Company",
Subject = "Backend Developer application update",
ExternalThreadId = "thread-42",
ExternalFrom = "Maria Recruiter <maria@acme.test>",
ExternalTo = "user@example.test",
Content = "We're aligning interview times and want someone who can own the API layer.",
Date = new DateTime(2026, 3, 10, 0, 0, 0, DateTimeKind.Utc)
});
await db.SaveChangesAsync();
var summarizer = new Mock<ISummarizerService>();
summarizer
.Setup(service => service.SummarizeSectionAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
.ReturnsAsync((string instruction, string context, int _, int __) =>
{
if (instruction.Contains("List up to 4 concrete application-package signals", StringComparison.OrdinalIgnoreCase))
{
return "Keep the thread moving on interview timing.\nReuse the API-ownership language from the recruiter.";
}
if (instruction.Contains("follow-up email", StringComparison.OrdinalIgnoreCase))
{
return context.Contains("Saved cover letter", StringComparison.OrdinalIgnoreCase)
&& context.Contains("Backend Developer application update", StringComparison.OrdinalIgnoreCase)
&& context.Contains("Maria Recruiter", StringComparison.OrdinalIgnoreCase)
? "Hi Maria,\n\nI wanted to follow up on the Backend Developer thread and reiterate my fit for owning the API layer.\n\nThanks,\nCasey"
: "Generic follow-up";
}
return "Generated text";
});
var controller = CreateController(db, summarizer.Object, "user-1");
var result = await controller.GetFollowUpDraft(job.Id, "waiting-update", null, CancellationToken.None);
var ok = Assert.IsType<OkObjectResult>(result.Result);
var payload = Assert.IsType<JobApplicationsController.FollowUpDraftDto>(ok.Value);
Assert.Equal("Re: Backend Developer application update", payload.Subject);
Assert.Contains("Maria", payload.Body);
Assert.Contains("Saved application package material is available", payload.ContextSummary);
Assert.Contains(payload.ContextSignals, item => item.Contains("Saved cover letter available", StringComparison.OrdinalIgnoreCase));
Assert.Contains(payload.ContextSignals, item => item.Contains("thread participants", StringComparison.OrdinalIgnoreCase));
Assert.Equal("Backend Developer application update", payload.ThreadSubject);
Assert.Contains("Maria Recruiter", payload.LastCorrespondenceFrom ?? string.Empty);
}
private static JobApplicationsController CreateController(JobTrackerContext db, ISummarizerService summarizer, string userId)
{
var controller = new JobApplicationsController(db, summarizer, Mock.Of<IAppEmailSender>(), CreateUserManager().Object, NullLogger<JobApplicationsController>.Instance);
controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = new ClaimsPrincipal(new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier, userId)
}, "test"))
}
};
return controller;
}
private static JobTrackerContext CreateDb()
{
return TestHostFactory.CreateInMemoryDb();
}
private static Mock<UserManager<ApplicationUser>> CreateUserManager()
{
return TestHostFactory.CreateUserManager();
}
}