namespace HrynCo.NotificationService.Services.Tests.Push; using FirebaseAdmin.Messaging; using HrynCo.NotificationService.Contracts.Messages; using HrynCo.NotificationService.DAL.Abstract.Push; using HrynCo.NotificationService.Worker.Services.Push; using Hrynco.RabbitMq; using NSubstitute; public sealed class PushDeliveryTests { [Theory] [InlineData(MessagingErrorCode.Unregistered, true)] [InlineData(MessagingErrorCode.SenderIdMismatch, false)] [InlineData(MessagingErrorCode.InvalidArgument, false)] public void OnlyUnregisteredInvalidatesTheInstallation(MessagingErrorCode code, bool invalid) { var result = FirebasePushClient.Classify(code); Assert.Equal(invalid, result.InvalidRegistration); Assert.NotNull(result.Error); } [Theory] [InlineData(MessagingErrorCode.Internal)] [InlineData(MessagingErrorCode.Unavailable)] [InlineData(MessagingErrorCode.QuotaExceeded)] public void TransientFailuresPreserveRetryAfter(MessagingErrorCode code) { var exception = Assert.Throws(() => FirebasePushClient.Classify(code, TimeSpan.FromMinutes(3))); Assert.Equal(TimeSpan.FromMinutes(3), exception.RetryAfter); Assert.True(PushDeliveryService.RetryDelay(0, exception.RetryAfter) >= TimeSpan.FromMinutes(3)); Assert.True(PushDeliveryService.RetryDelay(1, TimeSpan.Zero) >= TimeSpan.FromMinutes(2)); } [Fact] public async Task ExhaustedReplyRetriesReplaySavedSuccessWithoutSendingAgain() { var repository = Substitute.For(); var sender = Substitute.For(); var publisher = Substitute.For(); repository.DeliverOnceAsync(Arg.Any(), Arg.Any(), Arg.Any>>(), Arg.Any()) .Returns(new PushDeliveryOutcome(null)); var service = new PushDeliveryService(repository, sender, publisher); var id = Guid.NewGuid(); var message = new SendPushMessage { CorrelationContext = new CorrelationContext { CorrelationId = id.ToString(), ReplyTo = "results" }, Data = new SendPushMessageData { RegistrationId = Guid.NewGuid(), BindingId = Guid.NewGuid(), UserId = Guid.NewGuid(), ExpiresAt = DateTimeOffset.UtcNow.AddMinutes(10), ServiceName = "Invemory", Title = "Invemory", Body = "Test", TokenHash = new string('a', 64) } }; await service.CompleteFailureAsync(id, message, default); await publisher.Received(1).PublishAsync("results", Arg.Is(x => x.Data.IsSuccess && x.Data.PushBindingId == message.Data.BindingId), default); await sender.DidNotReceive().SendAsync(Arg.Any(), Arg.Any(), Arg.Any()); } }