using FluentAssertions; using foodmarket.Api.Infrastructure.Email; using foodmarket.Application.Common.Email; using Xunit; namespace foodmarket.UnitTests; public class EmailTemplateRendererTests { [Fact] public void Substitutes_simple_placeholder() { var r = EmailTemplateRenderer.Render( "Привет, {{name}}!", new Dictionary { ["name"] = "Алия" }); r.Should().Be("Привет, Алия!"); } [Fact] public void Escapes_html_in_double_braces() { var r = EmailTemplateRenderer.Render( "

{{text}}

", new Dictionary { ["text"] = "" }); r.Should().Be("

<script>x</script>

"); } [Fact] public void Triple_braces_render_raw_html() { var r = EmailTemplateRenderer.Render( "
{{{table}}}
", new Dictionary { ["table"] = "
1
" }); r.Should().Be("
1
"); } [Fact] public void Conditional_block_shown_when_truthy() { var r = EmailTemplateRenderer.Render( "Hi{{#bonus}}, бонус: {{bonus}}{{/bonus}}!", new Dictionary { ["bonus"] = "100₸" }); r.Should().Be("Hi, бонус: 100₸!"); } [Fact] public void Conditional_block_hidden_when_falsy() { // null EmailTemplateRenderer.Render( "Hi{{#bonus}}, бонус!{{/bonus}}", new Dictionary { ["bonus"] = null }).Should().Be("Hi"); // empty EmailTemplateRenderer.Render( "Hi{{#bonus}}, бонус!{{/bonus}}", new Dictionary { ["bonus"] = "" }).Should().Be("Hi"); // "0" EmailTemplateRenderer.Render( "Hi{{#bonus}}, бонус!{{/bonus}}", new Dictionary { ["bonus"] = "0" }).Should().Be("Hi"); } [Fact] public void Missing_key_renders_empty() { EmailTemplateRenderer.Render( "Hi, {{nope}}!", new Dictionary()).Should().Be("Hi, !"); } [Fact] public void Invite_template_loads_and_substitutes() { var tpl = new EmailTemplates(); var (subject, html, text) = tpl.Render("invite", new Dictionary { ["organizationName"] = "ТОО Тест", ["employeeName"] = "Иван Иванов", ["email"] = "ivan@test.kz", ["temporaryPassword"] = "Pass123!", ["roleName"] = "Кассир", ["loginUrl"] = "https://admin.test.kz/login", }); subject.Should().Contain("ТОО Тест"); html.Should().Contain("Иван Иванов"); html.Should().Contain("Pass123!"); html.Should().Contain("Кассир"); html.Should().Contain("https://admin.test.kz/login"); text.Should().Contain("Иван Иванов"); text.Should().NotContain("", "plain-text fallback не должен содержать теги"); } [Fact] public void Low_stock_template_uses_raw_html_for_items() { var tpl = new EmailTemplates(); var itemsHtml = "
  • Молоко < 10
"; var (_, html, _) = tpl.Render("low-stock", new Dictionary { ["organizationName"] = "ТОО Тест", ["productCount"] = "3", ["itemsHtml"] = itemsHtml, ["stockUrl"] = "https://admin.test.kz/inventory/stock", }); // Тройные фигурные → вставка как есть, без двойного экранирования. html.Should().Contain(itemsHtml); } }