fix(notification): save smtp sender without reentering password
Preserve the saved SMTP password when connection settings are unchanged and keep it out of admin forms. Ref: IT-1601
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
using System.Net;
|
||||
using HrynCo.NotificationService.DAL.Abstract.Providers;
|
||||
using HrynCo.NotificationService.DAL.Abstract.Repositories;
|
||||
using HrynCo.NotificationService.DAL.EF;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace HrynCo.NotificationService.Web.IntegrationTests;
|
||||
|
||||
public sealed class AdminSmtpSaveTests(TemplateDatabaseFixture fixture) : IClassFixture<TemplateDatabaseFixture>
|
||||
{
|
||||
[Fact]
|
||||
public async Task BlankPasswordKeepsSavedValueWhenOnlySenderChanges()
|
||||
{
|
||||
await using var context = await DefinitionApiTestContext.CreateAsync(fixture);
|
||||
var channel = await SeedChannel(context);
|
||||
using var client = context.App.CreateClient(new() { AllowAutoRedirect = false });
|
||||
var fields = TemplateEditorTestFields.Read(await client.GetStringAsync($"/admin/channels/{channel.Id}"));
|
||||
fields["UseSsl"] = "true";
|
||||
fields["Password"] = "";
|
||||
fields["FromName"] = "Updated sender";
|
||||
|
||||
var response = await client.PostAsync("/admin/channels/save", new FormUrlEncodedContent(fields));
|
||||
|
||||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||
var saved = await ReadSettings(context, channel.Id);
|
||||
Assert.Equal("saved-test-secret", saved.Password);
|
||||
Assert.Equal("Updated sender", saved.FromName);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Host", "other.example.invalid")]
|
||||
[InlineData("Port", "2525")]
|
||||
[InlineData("Username", "other-user")]
|
||||
[InlineData("UseSsl", "false")]
|
||||
public async Task ChangedConnectionWithBlankPasswordIsRejected(string field, string value)
|
||||
{
|
||||
await using var context = await DefinitionApiTestContext.CreateAsync(fixture);
|
||||
var channel = await SeedChannel(context);
|
||||
using var client = context.App.CreateClient(new() { AllowAutoRedirect = false });
|
||||
var fields = TemplateEditorTestFields.Read(await client.GetStringAsync($"/admin/channels/{channel.Id}"));
|
||||
fields["UseSsl"] = "true";
|
||||
fields["Password"] = "";
|
||||
fields[field] = value;
|
||||
|
||||
var response = await client.PostAsync("/admin/channels/save", new FormUrlEncodedContent(fields));
|
||||
var html = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Contains("Enter the password to save changed SMTP connection settings.", html);
|
||||
Assert.DoesNotContain("saved-test-secret", html);
|
||||
var saved = await ReadSettings(context, channel.Id);
|
||||
Assert.Equal("saved-test-secret", saved.Password);
|
||||
Assert.Equal("smtp.example.invalid", saved.Host);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExplicitPasswordCanReplaceSavedValue()
|
||||
{
|
||||
await using var context = await DefinitionApiTestContext.CreateAsync(fixture);
|
||||
var channel = await SeedChannel(context);
|
||||
using var client = context.App.CreateClient(new() { AllowAutoRedirect = false });
|
||||
var fields = TemplateEditorTestFields.Read(await client.GetStringAsync($"/admin/channels/{channel.Id}"));
|
||||
fields["UseSsl"] = "true";
|
||||
fields["Password"] = "replacement-test-secret";
|
||||
fields["Host"] = "other.example.invalid";
|
||||
|
||||
var response = await client.PostAsync("/admin/channels/save", new FormUrlEncodedContent(fields));
|
||||
|
||||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||
var saved = await ReadSettings(context, channel.Id);
|
||||
Assert.Equal("replacement-test-secret", saved.Password);
|
||||
Assert.Equal("other.example.invalid", saved.Host);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task NewChannelStillRequiresPassword()
|
||||
{
|
||||
await using var context = await DefinitionApiTestContext.CreateAsync(fixture);
|
||||
using var client = context.App.CreateClient(new() { AllowAutoRedirect = false });
|
||||
var fields = TemplateEditorTestFields.Read(await client.GetStringAsync("/admin/channels/create"));
|
||||
fields["ServiceName"] = "NewTestService";
|
||||
fields["Host"] = "smtp.example.invalid";
|
||||
fields["Username"] = "test-user";
|
||||
fields["FromEmail"] = "sender@example.invalid";
|
||||
fields["FromName"] = "Sender";
|
||||
fields["Password"] = "";
|
||||
|
||||
var response = await client.PostAsync("/admin/channels/save", new FormUrlEncodedContent(fields));
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Contains("The Password field is required.", await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvalidFormDoesNotRenderEnteredPassword()
|
||||
{
|
||||
await using var context = await DefinitionApiTestContext.CreateAsync(fixture);
|
||||
var channel = await SeedChannel(context);
|
||||
using var client = context.App.CreateClient(new() { AllowAutoRedirect = false });
|
||||
var fields = TemplateEditorTestFields.Read(await client.GetStringAsync($"/admin/channels/{channel.Id}"));
|
||||
fields["Password"] = "replacement-test-secret";
|
||||
fields["FromEmail"] = "invalid-address";
|
||||
|
||||
var response = await client.PostAsync("/admin/channels/save", new FormUrlEncodedContent(fields));
|
||||
var html = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.DoesNotContain("replacement-test-secret", html);
|
||||
Assert.Equal("saved-test-secret", (await ReadSettings(context, channel.Id)).Password);
|
||||
}
|
||||
|
||||
private static async Task<EmailChannel> SeedChannel(DefinitionApiTestContext context)
|
||||
{
|
||||
using var scope = context.App.Services.CreateScope();
|
||||
var channel = new EmailChannel
|
||||
{
|
||||
ServiceName = "TestService", EmailChannelType = EmailChannelType.Smtp,
|
||||
Settings = new SmtpChannelSettings
|
||||
{
|
||||
Host = "smtp.example.invalid", Port = 587, Username = "test-user",
|
||||
Password = "saved-test-secret", UseSsl = true,
|
||||
FromEmail = "sender@example.invalid", FromName = "Original sender"
|
||||
}
|
||||
};
|
||||
await scope.ServiceProvider.GetRequiredService<IEmailChannelRepository>().AddAsync(channel);
|
||||
await scope.ServiceProvider.GetRequiredService<NotificationDbContext>().SaveChangesAsync();
|
||||
return channel;
|
||||
}
|
||||
|
||||
private static async Task<SmtpChannelSettings> ReadSettings(DefinitionApiTestContext context, Guid id)
|
||||
{
|
||||
using var scope = context.App.Services.CreateScope();
|
||||
var channel = await scope.ServiceProvider.GetRequiredService<IEmailChannelRepository>().GetByIdAsync(id);
|
||||
return Assert.IsType<SmtpChannelSettings>(channel?.Settings);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user