Files
jobtrackingapp/JobTrackerApi.Tests/EmailDraftPersistenceTests.cs
T
cesnimda 80b5532c2f
CI and Deploy / test (pull_request) Successful in 4m15s
CI and Deploy / deploy (pull_request) Has been skipped
fix(email): preserve draft send identity
Persist and export each draft's idempotency UUID so refresh and edits cannot create a fresh delivery identity. Add reversible provider-specific migration SQL.
2026-08-10 10:11:04 +02:00

79 lines
3.2 KiB
C#

using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Moq;
using Xunit;
namespace JobTrackerApi.Tests;
public sealed class EmailDraftPersistenceTests
{
[Fact]
public async Task Drafts_are_owner_filtered_and_follow_the_owned_job_lifecycle()
{
await using var connection = new SqliteConnection("Data Source=:memory:");
await connection.OpenAsync();
var options = new DbContextOptionsBuilder<JobTrackerContext>().UseSqlite(connection).Options;
int userOneJobId;
int userTwoJobId;
await using (var seed = Context(options, null))
{
await seed.Database.EnsureCreatedAsync();
var companyOne = new Company { Name = "One", OwnerUserId = "user-1" };
var companyTwo = new Company { Name = "Two", OwnerUserId = "user-2" };
seed.Companies.AddRange(companyOne, companyTwo);
await seed.SaveChangesAsync();
var jobOne = new JobApplication { JobTitle = "Role one", CompanyId = companyOne.Id, OwnerUserId = "user-1" };
var jobTwo = new JobApplication { JobTitle = "Role two", CompanyId = companyTwo.Id, OwnerUserId = "user-2" };
seed.JobApplications.AddRange(jobOne, jobTwo);
await seed.SaveChangesAsync();
userOneJobId = jobOne.Id;
userTwoJobId = jobTwo.Id;
seed.EmailDrafts.AddRange(
Draft("user-1", jobOne.Id, "one@example.test"),
Draft("user-2", jobTwo.Id, "two@example.test"));
await seed.SaveChangesAsync();
}
await using (var userOne = Context(options, "user-1"))
{
var visible = Assert.Single(await userOne.EmailDrafts.AsNoTracking().ToListAsync());
Assert.Equal(userOneJobId, visible.JobApplicationId);
Assert.Equal("one@example.test", visible.To);
var job = await userOne.JobApplications.SingleAsync(item => item.Id == userOneJobId);
userOne.JobApplications.Remove(job);
await userOne.SaveChangesAsync();
}
await using var verify = Context(options, null);
var remaining = Assert.Single(await verify.EmailDrafts.IgnoreQueryFilters().AsNoTracking().ToListAsync());
Assert.Equal("user-2", remaining.OwnerUserId);
Assert.Equal(userTwoJobId, remaining.JobApplicationId);
}
private static EmailDraft Draft(string ownerUserId, int jobApplicationId, string recipient) => new()
{
Id = Guid.NewGuid(),
OwnerUserId = ownerUserId,
JobApplicationId = jobApplicationId,
Provider = "gmail",
To = recipient,
Subject = "Synthetic draft",
BodyText = "Synthetic private draft body.",
ThreadId = "thread-1",
ClientRequestId = Guid.NewGuid().ToString("D"),
CreatedAtUtc = DateTime.UtcNow,
UpdatedAtUtc = DateTime.UtcNow,
};
private static JobTrackerContext Context(DbContextOptions<JobTrackerContext> options, string? userId)
{
var currentUser = new Mock<ICurrentUserService>();
currentUser.SetupGet(service => service.UserId).Returns(userId);
return new JobTrackerContext(options, currentUser.Object);
}
}