feat(email): expose provider-neutral reads
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
using System.Security.Claims;
|
||||
using JobTrackerApi.Controllers;
|
||||
using JobTrackerApi.Services.EmailProviders;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Xunit;
|
||||
|
||||
namespace JobTrackerApi.Tests;
|
||||
|
||||
public sealed class EmailControllerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Provider_status_is_owner_scoped_and_does_not_claim_send_support()
|
||||
{
|
||||
var gmail = new FakeProvider("gmail", new EmailConnectionInfo("gmail", "owner@gmail.test"));
|
||||
var outlook = new FakeProvider("microsoft", null);
|
||||
var controller = CreateController(gmail, outlook);
|
||||
|
||||
var result = await controller.GetProviders(CancellationToken.None);
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result.Result);
|
||||
var statuses = Assert.IsAssignableFrom<IReadOnlyList<EmailController.ProviderStatus>>(ok.Value);
|
||||
Assert.Equal("user-1", gmail.LastOwnerUserId);
|
||||
Assert.Collection(statuses,
|
||||
status =>
|
||||
{
|
||||
Assert.Equal("Gmail", status.DisplayName);
|
||||
Assert.True(status.Connected);
|
||||
Assert.True(status.CanRead);
|
||||
Assert.False(status.CanSend);
|
||||
},
|
||||
status =>
|
||||
{
|
||||
Assert.Equal("Outlook", status.DisplayName);
|
||||
Assert.False(status.Connected);
|
||||
Assert.False(status.CanRead);
|
||||
Assert.False(status.CanSend);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Search_rejects_unknown_or_disconnected_provider()
|
||||
{
|
||||
var controller = CreateController(new FakeProvider("gmail", null));
|
||||
|
||||
var unknown = await controller.Search("unknown", null, 25, CancellationToken.None);
|
||||
Assert.IsType<BadRequestObjectResult>(unknown.Result);
|
||||
|
||||
var disconnected = await controller.Search("gmail", null, 25, CancellationToken.None);
|
||||
Assert.IsType<ConflictObjectResult>(disconnected.Result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Message_detail_returns_plain_text_without_provider_html()
|
||||
{
|
||||
var provider = new FakeProvider("gmail", new EmailConnectionInfo("gmail", "owner@gmail.test"));
|
||||
var controller = CreateController(provider);
|
||||
|
||||
var result = await controller.GetMessage("gmail", "message-1", CancellationToken.None);
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result.Result);
|
||||
var detail = Assert.IsType<EmailController.MessageDetail>(ok.Value);
|
||||
Assert.Equal("Safe plain text", detail.BodyText);
|
||||
Assert.DoesNotContain("script", System.Text.Json.JsonSerializer.Serialize(detail), StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Equal("user-1", provider.LastOwnerUserId);
|
||||
}
|
||||
|
||||
private static EmailController CreateController(params IEmailProvider[] providers)
|
||||
{
|
||||
var controller = new EmailController(new EmailProviderRegistry(providers));
|
||||
controller.ControllerContext = new ControllerContext
|
||||
{
|
||||
HttpContext = new DefaultHttpContext
|
||||
{
|
||||
User = new ClaimsPrincipal(new ClaimsIdentity(
|
||||
new[] { new Claim(ClaimTypes.NameIdentifier, "user-1") },
|
||||
"test"))
|
||||
}
|
||||
};
|
||||
return controller;
|
||||
}
|
||||
|
||||
private sealed class FakeProvider(string providerKey, EmailConnectionInfo? connection) : IEmailProvider
|
||||
{
|
||||
public string ProviderKey => providerKey;
|
||||
public string? LastOwnerUserId { get; private set; }
|
||||
|
||||
public Task<EmailConnectionInfo?> GetConnectionAsync(string ownerUserId, CancellationToken cancellationToken)
|
||||
{
|
||||
LastOwnerUserId = ownerUserId;
|
||||
return Task.FromResult(connection);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<EmailMessageSummary>> SearchAsync(string ownerUserId, string? query, int maxResults, CancellationToken cancellationToken)
|
||||
=> Task.FromResult<IReadOnlyList<EmailMessageSummary>>(Array.Empty<EmailMessageSummary>());
|
||||
|
||||
public Task<IReadOnlyList<EmailMessageSummary>> ListThreadMessagesAsync(string ownerUserId, string threadId, CancellationToken cancellationToken)
|
||||
=> Task.FromResult<IReadOnlyList<EmailMessageSummary>>(Array.Empty<EmailMessageSummary>());
|
||||
|
||||
public Task<EmailMessageDetail> GetMessageAsync(string ownerUserId, string messageId, CancellationToken cancellationToken)
|
||||
{
|
||||
LastOwnerUserId = ownerUserId;
|
||||
return Task.FromResult(new EmailMessageDetail(
|
||||
messageId,
|
||||
"thread-1",
|
||||
"Interview",
|
||||
"recruiter@example.test",
|
||||
"owner@gmail.test",
|
||||
DateTimeOffset.UtcNow,
|
||||
"Snippet",
|
||||
"Safe plain text",
|
||||
"<script>unsafe()</script>",
|
||||
Array.Empty<string>(),
|
||||
Array.Empty<EmailAttachmentRef>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user