aba4173827
Share template keys across email and push with independent editors and revisions. Render localized actions and preserve version-one delivery. Ref: IT-1627
91 lines
4.1 KiB
C#
91 lines
4.1 KiB
C#
using System.Text.Json;
|
|
using HrynCo.NotificationService.Contracts.Messages;
|
|
using HrynCo.NotificationService.DAL.Abstract.Templates;
|
|
using HrynCo.NotificationService.Worker.Services.Push;
|
|
|
|
namespace HrynCo.NotificationService.Services.Tests.Push;
|
|
|
|
public sealed class PushTemplateRendererTests
|
|
{
|
|
[Fact]
|
|
public void RendersLocalizedLabelsAndOpaqueObjectIdsWithoutReinterpretingValues()
|
|
{
|
|
var result = PushTemplateRenderer.Render(Template(), Message());
|
|
Assert.Equal("Hello {{Injected}}", result.Title);
|
|
var button = Assert.Single(result.Buttons);
|
|
Assert.Equal("View {{Injected}}", button.Label);
|
|
Assert.Equal("need-42", button.ObjectId);
|
|
Assert.Equal("open", result.DefaultAction!.Action);
|
|
using var payload = JsonDocument.Parse(FirebasePushClient.BuildPayload(Guid.NewGuid(), result)["actions"]);
|
|
Assert.Equal("need-42", payload.RootElement.GetProperty("buttons")[0].GetProperty("objectId").GetString());
|
|
Assert.Equal("open", payload.RootElement.GetProperty("defaultAction").GetProperty("action").GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public void UsesEnglishFallbackAndRejectsMissingFallback()
|
|
{
|
|
var message = Message() with { LanguageCode = "uk" };
|
|
Assert.Equal("Hello {{Injected}}", PushTemplateRenderer.Render(Template(), message).Title);
|
|
var template = Template();
|
|
template.Languages = [];
|
|
Assert.Throws<EmailTemplateWriteException>(() => PushTemplateRenderer.Render(template, message));
|
|
}
|
|
|
|
[Fact]
|
|
public void MissingBindingOrConfiguredActionFailsInsteadOfSendingIncompleteNotification()
|
|
{
|
|
var message = Message();
|
|
Assert.Throws<EmailTemplateWriteException>(() => PushTemplateRenderer.Render(Template(), message with
|
|
{
|
|
TemplateData = message.TemplateData! with { Bindings = new Dictionary<string, string>() }
|
|
}));
|
|
Assert.Throws<EmailTemplateWriteException>(() => PushTemplateRenderer.Render(Template(), message with
|
|
{
|
|
TemplateData = message.TemplateData! with { Actions = new Dictionary<string, PushActionMetadata>() }
|
|
}));
|
|
}
|
|
|
|
[Fact]
|
|
public void TruncationKeepsUnicodeTextElementsIntact()
|
|
{
|
|
var message = Message() with
|
|
{
|
|
TemplateData = Message().TemplateData! with { Bindings = new Dictionary<string, string> { ["Name"] = string.Concat(Enumerable.Repeat("😀", 100)) } }
|
|
};
|
|
var result = PushTemplateRenderer.Render(Template(), message);
|
|
Assert.True(result.Title.Length <= 100);
|
|
Assert.EndsWith("…", result.Title);
|
|
Assert.False(char.IsHighSurrogate(result.Title[^2]));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("{\"buttons\":null}")]
|
|
[InlineData("{\"buttons\":[{\"action\":\"open\",\"label\":\"\"}]}")]
|
|
[InlineData("{\"buttons\":[{\"action\":\"open\",\"label\":\"Open\"},{\"action\":\"open\",\"label\":\"Again\"}]}")]
|
|
[InlineData("{\"defaultAction\":\"https://example.invalid\"}")]
|
|
[InlineData("{\"unknown\":true}")]
|
|
public void RejectsInvalidActionDefinitions(string json) =>
|
|
Assert.Throws<EmailTemplateWriteException>(() => PushTemplateActionValidation.Parse(json));
|
|
|
|
private static PushTemplateDefinition Template() => new()
|
|
{
|
|
Key = "Shared", Variables = [new EmailTemplateVariable { Name = "Name", Required = true }],
|
|
Languages = [new PushTemplateTranslation
|
|
{
|
|
LanguageCode = "en", Title = "Hello {{Name}}", Body = "Shared item",
|
|
ActionsJson = "{\"defaultAction\":\"open\",\"buttons\":[{\"action\":\"open\",\"label\":\"View {{Name}}\"}]}"
|
|
}]
|
|
};
|
|
|
|
private static SendPushMessageData Message() => new()
|
|
{
|
|
RegistrationId = Guid.NewGuid(), BindingId = Guid.NewGuid(), UserId = Guid.NewGuid(),
|
|
ServiceName = "Example", Version = 2, TemplateKey = "Shared", LanguageCode = "en", ExpiresAt = DateTimeOffset.UtcNow.AddMinutes(5),
|
|
TemplateData = new PushTemplateData
|
|
{
|
|
Bindings = new Dictionary<string, string> { ["Name"] = "{{Injected}}" },
|
|
Actions = new Dictionary<string, PushActionMetadata> { ["open"] = new() { ObjectId = "need-42" } }
|
|
}
|
|
};
|
|
}
|