namespace HrynCo.NotificationService.Services.Tests.EmailProcessing; using HrynCo.NotificationService.Contracts.Messages; using HrynCo.NotificationService.DAL.Abstract.Templates; using HrynCo.NotificationService.Worker.Services.EmailProcessing; public sealed class EmailTemplateRenderingServiceTests { private readonly EmailTemplateRenderingService _service = new(); [Fact] public void Render_InterpolatesItemTrackerVariables() { EmailTemplate template = CreateTemplate(); SendEmailMessageData data = CreateData(new Dictionary { ["AppName"] = "StoreMate", ["VerificationUrl"] = "https://example.invalid/verify" }); RenderedEmail result = _service.Render(template, data); Assert.Equal("Verify StoreMate", result.Subject); Assert.Equal("Verify", result.HtmlBody); Assert.Equal("Verify at https://example.invalid/verify", result.TextBody); } [Fact] public void Render_WhenRequiredVariableIsMissing_ThrowsObservableFailure() { EmailTemplate template = CreateTemplate(); SendEmailMessageData data = CreateData(new Dictionary { ["AppName"] = "StoreMate" }); InvalidDataException exception = Assert.Throws( () => _service.Render(template, data)); Assert.Equal("Required template variables are missing: VerificationUrl.", exception.Message); } [Fact] public void Render_HtmlEncodesVariablesWithoutChangingSubjectOrTextBody() { EmailTemplate template = CreateTemplate(); SendEmailMessageData data = CreateData(new Dictionary { ["AppName"] = "Invemory ", ["VerificationUrl"] = "https://example.invalid/verify?next=\" onclick=\"alert('xss')" }); RenderedEmail result = _service.Render(template, data); Assert.Equal("Verify Invemory ", result.Subject); Assert.Equal( "Verify", result.HtmlBody); Assert.Equal( "Verify at https://example.invalid/verify?next=\" onclick=\"alert('xss')", result.TextBody); } private static EmailTemplate CreateTemplate() => new() { ServiceName = "StoreMate-Prod", Key = "EmailVerification", LanguageCode = "uk", Subject = "Verify {{AppName}}", HtmlBody = "Verify", TextBody = "Verify at {{VerificationUrl}}", Variables = [ new EmailTemplateVariable { Name = "AppName", Required = true }, new EmailTemplateVariable { Name = "VerificationUrl", Required = true } ] }; private static SendEmailMessageData CreateData(IReadOnlyDictionary variables) => new() { ServiceName = "StoreMate-Prod", TemplateKey = "EmailVerification", RecipientEmail = "owner@example.com", RecipientName = "Owner", LanguageCode = "uk", Variables = variables }; }