Files
hrynco-notification-service/HrynCo.NotificationService.Web.IntegrationTests/EmailChannelServiceLinkTests.cs
agrynco 1631786ce2 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
2026-10-24 12:31:35 +03:00

113 lines
6.2 KiB
C#

using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using HrynCo.NotificationService.DAL.Abstract.Providers;
using HrynCo.NotificationService.DAL.Abstract.Repositories;
using HrynCo.NotificationService.DAL.Abstract.Templates;
using Microsoft.Extensions.DependencyInjection;
namespace HrynCo.NotificationService.Web.IntegrationTests;
public sealed class EmailChannelServiceLinkTests(TemplateDatabaseFixture fixture) : IClassFixture<TemplateDatabaseFixture>
{
[Fact]
public async Task RenameUpdatesAllReadPathsAndResolvesChannelsByCurrentName()
{
await using var context = await DefinitionApiTestContext.CreateAsync(fixture);
var channel = await SeedAsync(context);
using (var scope = context.App.Services.CreateScope())
{
var services = scope.ServiceProvider.GetRequiredService<INotificationServiceRepository>();
var service = (await services.GetAsync(context.ServiceId))!;
service.Name = "Invemory-Renamed";
await services.SaveAsync(service, false);
}
var read = (await context.Client.GetFromJsonAsync<JsonElement>($"/api/v1/email-channels/{channel.Id}")).GetProperty("data");
Assert.Equal(context.ServiceId, read.GetProperty("serviceId").GetGuid());
Assert.Equal("Invemory-Renamed", read.GetProperty("serviceName").GetString());
var current = await context.Client.GetFromJsonAsync<JsonElement>("/api/v1/email-channels?serviceName=Invemory-Renamed");
Assert.Single(current.GetProperty("data").EnumerateArray());
var previous = await context.Client.GetFromJsonAsync<JsonElement>("/api/v1/email-channels?serviceName=TestService");
Assert.Empty(previous.GetProperty("data").EnumerateArray());
Assert.Contains("Invemory-Renamed", await context.Client.GetStringAsync("/admin/channels"));
Assert.Contains("Invemory-Renamed", await context.Client.GetStringAsync($"/admin/channels/{channel.Id}"));
}
[Fact]
public async Task EditorAllowsReassignmentWithoutReplacingPasswordOrUsage()
{
await using var context = await DefinitionApiTestContext.CreateAsync(fixture);
var channel = await SeedAsync(context);
using var scope = context.App.Services.CreateScope();
var destination = new NotificationServiceInfo { Name = "Invemory-Other" };
await scope.ServiceProvider.GetRequiredService<INotificationServiceRepository>().SaveAsync(destination, true);
using var client = context.App.CreateClient(new() { AllowAutoRedirect = false });
var html = await client.GetStringAsync($"/admin/channels/{channel.Id}");
Assert.Contains("<select", html);
Assert.Contains("name=\"ServiceId\"", html);
Assert.Contains("Invemory-Other", html);
Assert.DoesNotContain("name=\"ServiceName\"", html);
var fields = TemplateEditorTestFields.Read(html);
fields["ServiceId"] = destination.Id.ToString();
fields["Password"] = "";
fields["UseSsl"] = "true";
fields["IsActive"] = "true";
var response = await client.PostAsync("/admin/channels/save", new FormUrlEncodedContent(fields));
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
using var readScope = context.App.Services.CreateScope();
var channels = readScope.ServiceProvider.GetRequiredService<IEmailChannelRepository>();
var saved = (await channels.GetByIdAsync(channel.Id))!;
Assert.Equal(destination.Id, saved.ServiceId);
Assert.Equal("Invemory-Other", saved.ServiceName);
Assert.Equal("test-only-password", Assert.IsType<SmtpChannelSettings>(saved.Settings).Password);
Assert.Equal(25, saved.DailyLimit);
Assert.Equal(250, saved.MonthlyLimit);
Assert.Equal(2, saved.Priority);
var summary = Assert.Single(await channels.GetAllWithUsageSummaryAsync(DateOnly.FromDateTime(DateTime.UtcNow)));
Assert.Equal(1, summary.DailySent);
Assert.Empty(await channels.GetByServiceAsync(context.ServiceId));
Assert.Single(await channels.GetByServiceAsync(destination.Id));
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task UpdateRejectsMissingServiceWithoutChangingChannel(bool empty)
{
await using var context = await DefinitionApiTestContext.CreateAsync(fixture);
var channel = await SeedAsync(context);
var response = await context.Client.PutAsJsonAsync($"/api/v1/email-channels/{channel.Id}", new
{
serviceId = empty ? Guid.Empty : Guid.NewGuid(), priority = 9,
settings = (SmtpChannelSettings)channel.Settings, dailyLimit = 1, monthlyLimit = 2, warnThresholdPercent = 80, isActive = false
});
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
var result = await response.Content.ReadFromJsonAsync<JsonElement>();
Assert.Equal("Select an existing service.", result.GetProperty("error").GetProperty("message").GetString());
using var scope = context.App.Services.CreateScope();
var saved = (await scope.ServiceProvider.GetRequiredService<IEmailChannelRepository>().GetByIdAsync(channel.Id))!;
Assert.Equal(context.ServiceId, saved.ServiceId);
Assert.Equal(2, saved.Priority);
Assert.Equal(25, saved.DailyLimit);
}
private static async Task<EmailChannel> SeedAsync(DefinitionApiTestContext context)
{
using var scope = context.App.Services.CreateScope();
var channel = new EmailChannel
{
ServiceId = context.ServiceId, Priority = 2, EmailChannelType = EmailChannelType.Smtp,
DailyLimit = 25, MonthlyLimit = 250, IsActive = true,
Settings = new SmtpChannelSettings
{
Host = "smtp.invalid", Port = 587, Username = "test-user", Password = "test-only-password",
UseSsl = true, FromEmail = "sender@example.invalid", FromName = "Invemory Test"
}
};
await scope.ServiceProvider.GetRequiredService<IEmailChannelRepository>().AddAsync(channel);
await scope.ServiceProvider.GetRequiredService<IEmailChannelUsageRepository>()
.IncrementUsageAsync(channel.Id, DateOnly.FromDateTime(DateTime.UtcNow));
return channel;
}
}