feat(email): include drafts in user exports
CI and Deploy / test (pull_request) Successful in 4m14s
CI and Deploy / deploy (pull_request) Has been skipped

Export readable private draft content only through existing owner-filtered encrypted and daily export boundaries, with cross-tenant regression coverage.
This commit is contained in:
cesnimda
2026-08-10 10:00:34 +02:00
parent 10740fd8c3
commit 2fa4e38ba9
5 changed files with 98 additions and 1 deletions
@@ -121,6 +121,9 @@ public sealed class BackgroundWorkerTenantTests
db.EmailSendAttempts.AddRange(
new EmailSendAttempt { Id = Guid.NewGuid(), OwnerUserId = "user-1", JobApplicationId = jobs[0].Id, Provider = "gmail", ClientRequestId = Guid.NewGuid().ToString(), PayloadHash = new string('a', 64), Status = EmailSendStatuses.Sent, CreatedAtUtc = DateTime.UtcNow },
new EmailSendAttempt { Id = Guid.NewGuid(), OwnerUserId = "user-2", JobApplicationId = jobs[1].Id, Provider = "microsoft", ClientRequestId = Guid.NewGuid().ToString(), PayloadHash = new string('b', 64), Status = EmailSendStatuses.Failed, FailureCategory = "rejected", CreatedAtUtc = DateTime.UtcNow });
db.EmailDrafts.AddRange(
Draft("user-1", jobs[0].Id, "one@example.test", "Private draft one"),
Draft("user-2", jobs[1].Id, "two@example.test", "Private draft two"));
await db.SaveChangesAsync();
}
var environment = new Mock<IHostEnvironment>();
@@ -148,6 +151,10 @@ public sealed class BackgroundWorkerTenantTests
var attempt = Assert.Single(rootElement.GetProperty("EmailSendAttempts").EnumerateArray());
Assert.Equal(owner == "user-1" ? "gmail" : "microsoft", attempt.GetProperty("Provider").GetString());
Assert.False(attempt.TryGetProperty("PayloadHash", out _));
var draft = Assert.Single(rootElement.GetProperty("EmailDrafts").EnumerateArray());
Assert.Equal(owner == "user-1" ? "one@example.test" : "two@example.test", draft.GetProperty("To").GetString());
Assert.Equal(owner == "user-1" ? "Private draft one" : "Private draft two", draft.GetProperty("BodyText").GetString());
Assert.Equal(1, draft.GetProperty("Revision").GetInt64());
}
owners.Sort(StringComparer.Ordinal);
Assert.Equal(new[] { "user-1", "user-2" }, owners);
@@ -159,6 +166,20 @@ public sealed class BackgroundWorkerTenantTests
}
}
private static EmailDraft Draft(string ownerUserId, int jobApplicationId, string recipient, string body) => new()
{
Id = Guid.NewGuid(),
OwnerUserId = ownerUserId,
JobApplicationId = jobApplicationId,
Provider = ownerUserId == "user-1" ? "gmail" : "microsoft",
To = recipient,
Subject = "Synthetic export subject",
BodyText = body,
ThreadId = "synthetic-thread",
CreatedAtUtc = DateTime.UtcNow.AddMinutes(-5),
UpdatedAtUtc = DateTime.UtcNow,
};
[Fact]
public async Task Enrichment_processes_both_owners_only_through_fake_ai()
{