feat(push): add service-owned firebase channels and delivery

Add push contracts, provider delivery and channel diagnostics. Link email channels to services and document migration and validation.

Ref: IT-1074
This commit is contained in:
2026-10-24 12:31:35 +03:00
parent b30dba93e8
commit 1631786ce2
105 changed files with 3838 additions and 147 deletions
@@ -23,12 +23,12 @@ public sealed class SendEmailServiceTests
Password = "not-a-real-secret",
UseSsl = true,
FromEmail = "notifications@example.com",
FromName = "StoreMate"
FromName = "Invemory"
};
var channel = new EmailChannel
{
Id = channelId,
ServiceName = "StoreMate-Prod",
ServiceId = Guid.NewGuid(),
Priority = 1,
EmailChannelType = EmailChannelType.Smtp,
Settings = settings,
@@ -36,7 +36,7 @@ public sealed class SendEmailServiceTests
};
var template = new EmailTemplate
{
ServiceName = "StoreMate-Prod",
ServiceName = "Invemory-Prod",
Key = "EmailVerification",
LanguageCode = "uk",
Subject = "Verify {{AppName}}",
@@ -46,17 +46,21 @@ public sealed class SendEmailServiceTests
};
IEmailChannelRepository channels = Substitute.For<IEmailChannelRepository>();
channels.GetByServiceAsync("StoreMate-Prod", Arg.Any<CancellationToken>())
channels.GetByServiceAsync(channel.ServiceId, Arg.Any<CancellationToken>())
.Returns([channel]);
var services = Substitute.For<INotificationServiceRepository>();
services.GetByNameAsync("Invemory-Prod", Arg.Any<CancellationToken>())
.Returns(new NotificationServiceInfo { Id = channel.ServiceId, Name = "Invemory-Prod" });
IEmailChannelUsageRepository usage = Substitute.For<IEmailChannelUsageRepository>();
IEmailTemplateService templates = Substitute.For<IEmailTemplateService>();
templates.GetAsync("StoreMate-Prod", "EmailVerification", "uk", Arg.Any<CancellationToken>())
templates.GetAsync("Invemory-Prod", "EmailVerification", "uk", Arg.Any<CancellationToken>())
.Returns(template);
var renderer = new EmailTemplateRenderingService();
var smtp = new RecordingSmtpEmailSender();
INotificationResultPublisher resultPublisher = Substitute.For<INotificationResultPublisher>();
var service = new SendEmailService(
channels,
services,
usage,
templates,
renderer,
@@ -71,12 +75,12 @@ public sealed class SendEmailServiceTests
},
Data = new SendEmailMessageData
{
ServiceName = "StoreMate-Prod",
ServiceName = "Invemory-Prod",
TemplateKey = "EmailVerification",
RecipientEmail = "owner@example.com",
RecipientName = "Owner",
LanguageCode = "uk",
Variables = new Dictionary<string, string> { ["AppName"] = "StoreMate" }
Variables = new Dictionary<string, string> { ["AppName"] = "Invemory" }
}
};
@@ -84,7 +88,7 @@ public sealed class SendEmailServiceTests
Assert.Equal(1, smtp.SendCount);
Assert.Same(settings, smtp.Settings);
Assert.Equal("Verify StoreMate", smtp.Email?.Subject);
Assert.Equal("Verify Invemory", smtp.Email?.Subject);
Assert.Equal("owner@example.com", smtp.RecipientEmail);
Assert.Equal("Owner", smtp.RecipientName);
await usage.Received(1).IncrementUsageAsync(