204 lines
7.4 KiB
C#
204 lines
7.4 KiB
C#
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);
|
|
}
|
|
}
|