Files
hrynco-notification-service/HrynCo.NotificationService.Services.Tests/EmailProcessing/EmailTemplateRenderingServiceTests.cs
T
agrynco 50033a5bd4 fix: escape html notification variables
Encode template variables in HTML bodies while preserving text and subjects.

Ref: IT-1115
2026-08-19 23:04:15 +03:00

89 lines
3.2 KiB
C#

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<string, string>
{
["AppName"] = "StoreMate",
["VerificationUrl"] = "https://example.invalid/verify"
});
RenderedEmail result = _service.Render(template, data);
Assert.Equal("Verify StoreMate", result.Subject);
Assert.Equal("<a href=\"https://example.invalid/verify\">Verify</a>", 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<string, string>
{
["AppName"] = "StoreMate"
});
InvalidDataException exception = Assert.Throws<InvalidDataException>(
() => _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<string, string>
{
["AppName"] = "Invemory <script>alert('xss')</script>",
["VerificationUrl"] = "https://example.invalid/verify?next=\" onclick=\"alert('xss')"
});
RenderedEmail result = _service.Render(template, data);
Assert.Equal("Verify Invemory <script>alert('xss')</script>", result.Subject);
Assert.Equal(
"<a href=\"https://example.invalid/verify?next=&quot; onclick=&quot;alert(&#39;xss&#39;)\">Verify</a>",
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 = "<a href=\"{{VerificationUrl}}\">Verify</a>",
TextBody = "Verify at {{VerificationUrl}}",
Variables =
[
new EmailTemplateVariable { Name = "AppName", Required = true },
new EmailTemplateVariable { Name = "VerificationUrl", Required = true }
]
};
private static SendEmailMessageData CreateData(IReadOnlyDictionary<string, string> variables) => new()
{
ServiceName = "StoreMate-Prod",
TemplateKey = "EmailVerification",
RecipientEmail = "owner@example.com",
RecipientName = "Owner",
LanguageCode = "uk",
Variables = variables
};
}