aba4173827
Share template keys across email and push with independent editors and revisions. Render localized actions and preserve version-one delivery. Ref: IT-1627
106 lines
6.0 KiB
C#
106 lines
6.0 KiB
C#
using System.Text.Json;
|
|
using HrynCo.NotificationService.Contracts.Messages;
|
|
using HrynCo.NotificationService.DAL.Abstract.Repositories;
|
|
using HrynCo.NotificationService.DAL.Abstract.PushChannels;
|
|
using HrynCo.NotificationService.DAL.Abstract.Templates;
|
|
using HrynCo.NotificationService.Worker.Services.Push;
|
|
using Hrynco.RabbitMq;
|
|
using NSubstitute;
|
|
|
|
namespace HrynCo.NotificationService.Services.Tests.Push;
|
|
|
|
public sealed class PushPayloadVersionTests
|
|
{
|
|
[Theory]
|
|
[InlineData(false)]
|
|
[InlineData(true)]
|
|
public void OriginalPayloadDeserializesWithoutTemplateFieldsAndKeepsExactLegacyOutput(bool explicitVersion)
|
|
{
|
|
var json = JsonSerializer.SerializeToNode(new
|
|
{
|
|
registrationId = Guid.NewGuid(), bindingId = Guid.NewGuid(), userId = Guid.NewGuid(),
|
|
serviceName = "Invemory", title = "Original title", body = "Original body",
|
|
token = "device-token", tokenHash = new string('a', 64), expiresAt = DateTimeOffset.UtcNow.AddMinutes(5)
|
|
})!;
|
|
if (explicitVersion) json["version"] = 1;
|
|
var data = json.Deserialize<SendPushMessageData>(new JsonSerializerOptions(JsonSerializerDefaults.Web))!;
|
|
var message = new SendPushMessage { Data = data, CorrelationContext = new CorrelationContext { CorrelationId = Guid.NewGuid().ToString() } };
|
|
Assert.True(PushMessageValidator.IsValid(message));
|
|
Assert.Null(data.TemplateData);
|
|
var id = Guid.NewGuid();
|
|
var payload = FirebasePushClient.BuildPayload(id, new RenderedPushMessageData(data, data.Title!, data.Body!, null, []));
|
|
Assert.Equal(7, payload.Count);
|
|
Assert.Equal("1", payload["version"]);
|
|
Assert.Equal("/home", payload["route"]);
|
|
Assert.Equal(id.ToString("D"), payload["messageId"]);
|
|
Assert.Equal(data.BindingId.ToString("D"), payload["bindingId"]);
|
|
Assert.Equal("Original title", payload["title"]);
|
|
Assert.Equal("Original body", payload["body"]);
|
|
Assert.False(payload.ContainsKey("actions"));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(0)]
|
|
[InlineData(3)]
|
|
[InlineData(-1)]
|
|
public async Task UnsupportedVersionNeverFallsBackOrContactsFirebase(int version)
|
|
{
|
|
var data = Data() with { Version = version, Title = "Legacy", Body = "Legacy" };
|
|
Assert.False(PushMessageValidator.IsValidContent(data));
|
|
var client = Substitute.For<IFirebasePushClient>();
|
|
var sender = new FcmPushSender(Substitute.For<INotificationServiceRepository>(), Substitute.For<IPushChannelRepository>(), client, Substitute.For<IPushTemplateDefinitionRepository>());
|
|
var result = await sender.SendAsync(Guid.NewGuid(), data, default);
|
|
Assert.Contains("Unsupported push payload version", result.Error);
|
|
await client.DidNotReceiveWithAnyArgs().SendAsync(default, default!, default!, default);
|
|
Assert.Throws<ArgumentOutOfRangeException>(() => FirebasePushClient.BuildPayload(Guid.NewGuid(), new RenderedPushMessageData(data, "Title", "Body", null, [])));
|
|
}
|
|
|
|
[Fact]
|
|
public void SectionActionHasNoObjectIdAndVersionTwoNeverContainsLegacyRoute()
|
|
{
|
|
var template = new PushTemplateDefinition { Key = "Section", Languages = [new PushTemplateTranslation
|
|
{
|
|
LanguageCode = "uk", Title = "Зміни", Body = "Перегляньте зміни",
|
|
ActionsJson = "{\"defaultAction\":\"openBasket\",\"buttons\":[{\"action\":\"openBasket\",\"label\":\"Перейти в розділ\"}]}"
|
|
}] };
|
|
var data = Data();
|
|
var rendered = PushTemplateRenderer.Render(template, data);
|
|
Assert.Null(Assert.Single(rendered.Buttons).ObjectId);
|
|
var payload = FirebasePushClient.BuildPayload(Guid.NewGuid(), rendered);
|
|
Assert.Equal("2", payload["version"]);
|
|
Assert.False(payload.ContainsKey("route"));
|
|
using var actions = JsonDocument.Parse(payload["actions"]);
|
|
var button = actions.RootElement.GetProperty("buttons")[0];
|
|
Assert.Equal("openBasket", button.GetProperty("action").GetString());
|
|
Assert.Equal("Перейти в розділ", button.GetProperty("label").GetString());
|
|
Assert.False(button.TryGetProperty("objectId", out _));
|
|
Assert.Throws<EmailTemplateWriteException>(() => PushTemplateRenderer.Render(template, data with { TemplateData = data.TemplateData! with { Actions = new Dictionary<string, PushActionMetadata>() } }));
|
|
}
|
|
|
|
[Fact]
|
|
public void TestSendBuildsActualButtonPayloadWithRealBindingAndPreservesSimpleLegacyTest()
|
|
{
|
|
var binding = Guid.NewGuid();
|
|
var test = new PushChannelTestMessage { Token = "test-token", Version = 2, BindingId = binding, ActionKey = "openBasket", ButtonLabel = "Перейти в розділ" };
|
|
var current = FirebasePushClient.BuildTestMessage(test);
|
|
Assert.Null(current.Notification);
|
|
Assert.Equal("2", current.Data["version"]);
|
|
Assert.Equal(binding.ToString("D"), current.Data["bindingId"]);
|
|
using var actions = JsonDocument.Parse(current.Data["actions"]);
|
|
Assert.Equal("openBasket", actions.RootElement.GetProperty("buttons")[0].GetProperty("action").GetString());
|
|
Assert.False(actions.RootElement.GetProperty("buttons")[0].TryGetProperty("objectId", out _));
|
|
var legacy = FirebasePushClient.BuildTestMessage(test with { Version = 1, BindingId = null });
|
|
Assert.NotNull(legacy.Notification);
|
|
Assert.True(legacy.Data is null || legacy.Data.Count == 0);
|
|
Assert.NotNull((test with { BindingId = null }).Validate());
|
|
Assert.NotNull((test with { Version = 9 }).Validate());
|
|
}
|
|
|
|
private static SendPushMessageData Data() => new()
|
|
{
|
|
RegistrationId = Guid.NewGuid(), BindingId = Guid.NewGuid(), UserId = Guid.NewGuid(), ServiceName = "Invemory",
|
|
Version = 2, TemplateKey = "Section", LanguageCode = "uk", ExpiresAt = DateTimeOffset.UtcNow.AddMinutes(5),
|
|
TemplateData = new PushTemplateData { Bindings = new Dictionary<string, string>(), Actions = new Dictionary<string, PushActionMetadata> { ["openBasket"] = new() } }
|
|
};
|
|
}
|