Add admin email wizard and logging
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using PowderCoating.Application.Interfaces;
|
||||
using PowderCoating.Core.Entities;
|
||||
using PowderCoating.Core.Enums;
|
||||
using PowderCoating.Infrastructure.Data;
|
||||
using PowderCoating.Shared.Constants;
|
||||
using PowderCoating.Web.Controllers;
|
||||
|
||||
namespace PowderCoating.UnitTests;
|
||||
|
||||
public class EmailBroadcastControllerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Preview_RendersMergedSampleAndSanitizesHtml()
|
||||
{
|
||||
await using var context = CreateContext();
|
||||
context.Companies.Add(new Company
|
||||
{
|
||||
Id = 7,
|
||||
CompanyId = 7,
|
||||
CompanyName = "River City Powder",
|
||||
PrimaryContactName = "Jamie Rivera",
|
||||
PrimaryContactEmail = "jamie@example.com",
|
||||
IsActive = true
|
||||
});
|
||||
context.Users.Add(new ApplicationUser
|
||||
{
|
||||
Id = "admin-7",
|
||||
CompanyId = 7,
|
||||
CompanyRole = AppConstants.CompanyRoles.CompanyAdmin,
|
||||
FirstName = "Alex",
|
||||
LastName = "Admin",
|
||||
Email = "alex@example.com",
|
||||
UserName = "alex@example.com",
|
||||
IsActive = true
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var controller = CreateController(context);
|
||||
|
||||
var result = await controller.Preview(new AdminEmailSelectionModel
|
||||
{
|
||||
Subject = "Update for {{CompanyName}}",
|
||||
BodyHtml = "<p>Hi {{FirstName}}, contact {{CompanyAdminName}} at {{CompanyAdminEmail}}.</p><script>alert('x')</script>",
|
||||
CompanyIds = [7]
|
||||
});
|
||||
|
||||
var view = Assert.IsType<ViewResult>(result);
|
||||
var model = Assert.IsType<AdminEmailPreviewModel>(view.Model);
|
||||
|
||||
Assert.Equal("Update for River City Powder", model.SamplePreview.RenderedSubject);
|
||||
Assert.Contains("Hi Jamie", model.SamplePreview.RenderedHtmlBody);
|
||||
Assert.Contains("Alex Admin", model.SamplePreview.RenderedHtmlBody);
|
||||
Assert.Contains("alex@example.com", model.SamplePreview.RenderedHtmlBody);
|
||||
Assert.DoesNotContain("<script", model.SamplePreview.RenderedHtmlBody, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Send_WhenEmailSucceeds_WritesAdminEmailLogAndUsesPlatformReplyTo()
|
||||
{
|
||||
await using var context = CreateContext();
|
||||
context.Companies.Add(new Company
|
||||
{
|
||||
Id = 11,
|
||||
CompanyId = 11,
|
||||
CompanyName = "Summit Coatings",
|
||||
PrimaryContactName = "Morgan Lee",
|
||||
PrimaryContactEmail = "morgan@example.com",
|
||||
IsActive = true
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var emailService = new Mock<IEmailService>();
|
||||
emailService
|
||||
.Setup(x => x.SendEmailAsync(
|
||||
"morgan@example.com",
|
||||
"Morgan Lee",
|
||||
"Notice for Summit Coatings",
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"admin-notify@example.com",
|
||||
"Powder Coating Logix Admin"))
|
||||
.ReturnsAsync((true, (string?)null));
|
||||
|
||||
var platformSettings = new Mock<IPlatformSettingsService>();
|
||||
platformSettings
|
||||
.Setup(x => x.GetAsync(PlatformSettingKeys.AdminNotificationEmail))
|
||||
.ReturnsAsync("admin-notify@example.com,backup@example.com");
|
||||
|
||||
var controller = CreateController(context, emailService, platformSettings);
|
||||
|
||||
var result = await controller.Send(new AdminEmailSendRequest
|
||||
{
|
||||
Subject = "Notice for {{CompanyName}}",
|
||||
BodyHtml = "<p>Hello {{FirstName}},</p><p>Thanks for using {{CompanyName}}.</p>",
|
||||
CompanyIds = [11]
|
||||
});
|
||||
|
||||
var redirect = Assert.IsType<RedirectToActionResult>(result);
|
||||
Assert.Equal("Index", redirect.ActionName);
|
||||
|
||||
var log = await context.NotificationLogs.IgnoreQueryFilters().SingleAsync();
|
||||
Assert.Equal(NotificationType.AdminEmail, log.NotificationType);
|
||||
Assert.Equal(NotificationStatus.Sent, log.Status);
|
||||
Assert.Equal("morgan@example.com", log.Recipient);
|
||||
Assert.Equal("Notice for Summit Coatings", log.Subject);
|
||||
Assert.Contains("Hello Morgan", log.Message);
|
||||
emailService.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Send_WhenPrimaryContactEmailMissing_WritesSkippedLogWithoutSending()
|
||||
{
|
||||
await using var context = CreateContext();
|
||||
context.Companies.Add(new Company
|
||||
{
|
||||
Id = 13,
|
||||
CompanyId = 13,
|
||||
CompanyName = "No Inbox Inc",
|
||||
PrimaryContactName = "Taylor Noemail",
|
||||
PrimaryContactEmail = string.Empty,
|
||||
IsActive = true
|
||||
});
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
var emailService = new Mock<IEmailService>();
|
||||
var controller = CreateController(context, emailService);
|
||||
|
||||
var result = await controller.Send(new AdminEmailSendRequest
|
||||
{
|
||||
Subject = "Heads up for {{CompanyName}}",
|
||||
BodyHtml = "<p>Hello {{FirstName}}</p>",
|
||||
CompanyIds = [13]
|
||||
});
|
||||
|
||||
var redirect = Assert.IsType<RedirectToActionResult>(result);
|
||||
Assert.Equal("Index", redirect.ActionName);
|
||||
|
||||
var log = await context.NotificationLogs.IgnoreQueryFilters().SingleAsync();
|
||||
Assert.Equal(NotificationType.AdminEmail, log.NotificationType);
|
||||
Assert.Equal(NotificationStatus.Skipped, log.Status);
|
||||
Assert.Equal(string.Empty, log.Recipient);
|
||||
Assert.Equal("Company primary contact email is not configured.", log.ErrorMessage);
|
||||
emailService.Verify(
|
||||
x => x.SendEmailAsync(
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string?>(),
|
||||
It.IsAny<byte[]?>(),
|
||||
It.IsAny<string?>(),
|
||||
It.IsAny<string?>(),
|
||||
It.IsAny<string?>(),
|
||||
It.IsAny<string?>()),
|
||||
Times.Never);
|
||||
}
|
||||
|
||||
private static EmailBroadcastController CreateController(
|
||||
ApplicationDbContext context,
|
||||
Mock<IEmailService>? emailService = null,
|
||||
Mock<IPlatformSettingsService>? platformSettings = null)
|
||||
{
|
||||
var controller = new EmailBroadcastController(
|
||||
context,
|
||||
(emailService ?? new Mock<IEmailService>()).Object,
|
||||
(platformSettings ?? CreatePlatformSettings()).Object,
|
||||
Mock.Of<ILogger<EmailBroadcastController>>());
|
||||
|
||||
var httpContext = new DefaultHttpContext();
|
||||
controller.ControllerContext = new ControllerContext
|
||||
{
|
||||
HttpContext = httpContext
|
||||
};
|
||||
controller.TempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
private static Mock<IPlatformSettingsService> CreatePlatformSettings()
|
||||
{
|
||||
var settings = new Mock<IPlatformSettingsService>();
|
||||
settings.Setup(x => x.GetAsync(It.IsAny<string>())).ReturnsAsync((string?)null);
|
||||
return settings;
|
||||
}
|
||||
|
||||
private static ApplicationDbContext CreateContext()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
|
||||
return new ApplicationDbContext(options);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user