fix(email): preserve draft send identity
CI and Deploy / test (pull_request) Successful in 4m15s
CI and Deploy / deploy (pull_request) Has been skipped

Persist and export each draft's idempotency UUID so refresh and edits cannot create a fresh delivery identity. Add reversible provider-specific migration SQL.
This commit is contained in:
cesnimda
2026-08-10 10:11:04 +02:00
parent 108440d361
commit 80b5532c2f
12 changed files with 2778 additions and 4 deletions
@@ -40,6 +40,7 @@ public sealed class EmailDraftsControllerTests
Assert.Equal("gmail", created.Provider);
Assert.Equal(1, created.Revision);
Assert.Empty(created.To);
Assert.True(Guid.TryParse(created.ClientRequestId, out _));
Assert.IsType<BadRequestObjectResult>((await controller.Create(
new EmailDraftsController.CreateDraftRequest(fixture.UserOneJobId, "unknown", "", "", "", null), default)).Result);
@@ -63,6 +64,9 @@ public sealed class EmailDraftsControllerTests
var ownList = Assert.IsAssignableFrom<IReadOnlyList<EmailDraftsController.DraftDto>>(
Assert.IsType<OkObjectResult>((await controller.List(fixture.UserOneJobId, default)).Result).Value);
Assert.Equal(fixture.UserOneDraftId, Assert.Single(ownList).Id);
var allOwnDrafts = Assert.IsAssignableFrom<IReadOnlyList<EmailDraftsController.DraftDto>>(
Assert.IsType<OkObjectResult>((await controller.List(null, default)).Result).Value);
Assert.Equal(fixture.UserOneDraftId, Assert.Single(allOwnDrafts).Id);
}
[Fact]
@@ -72,11 +76,13 @@ public sealed class EmailDraftsControllerTests
await using (var userOneDb = fixture.Context("user-1"))
{
var userOne = Controller(userOneDb, "user-1");
var originalRequestId = (await userOneDb.EmailDrafts.AsNoTracking().SingleAsync()).ClientRequestId;
var updatedResult = await userOne.Update(fixture.UserOneDraftId,
new EmailDraftsController.UpdateDraftRequest(1, "new@example.test", "Updated", "Updated body"), default);
var updated = Assert.IsType<EmailDraftsController.DraftDto>(Assert.IsType<OkObjectResult>(updatedResult.Result).Value);
Assert.Equal(2, updated.Revision);
Assert.Equal("Updated body", updated.BodyText);
Assert.Equal(originalRequestId, updated.ClientRequestId);
Assert.IsType<ConflictObjectResult>((await userOne.Update(fixture.UserOneDraftId,
new EmailDraftsController.UpdateDraftRequest(1, "stale@example.test", "Stale", "Stale body"), default)).Result);
@@ -190,6 +196,7 @@ public sealed class EmailDraftsControllerTests
To = recipient,
Subject = "Synthetic",
BodyText = "Synthetic private draft.",
ClientRequestId = Guid.NewGuid().ToString("D"),
CreatedAtUtc = DateTime.UtcNow,
UpdatedAtUtc = DateTime.UtcNow,
};