feat(smtp): UpdateSmtpConfigCommand carries TlsMode + Credentials
Add two optional nullable fields (TlsMode, Credentials) to the UpdateSmtpConfigCommand record. The handler applies preserve-if-null semantics: an update that omits a field leaves the existing value intact, so existing 5-arg callers remain non-breaking.
This commit is contained in:
@@ -6,4 +6,4 @@ public record CreateNotificationListCommand(string Name, IReadOnlyList<string> R
|
|||||||
public record UpdateNotificationListCommand(int NotificationListId, string Name, IReadOnlyList<string> RecipientEmails);
|
public record UpdateNotificationListCommand(int NotificationListId, string Name, IReadOnlyList<string> RecipientEmails);
|
||||||
public record DeleteNotificationListCommand(int NotificationListId);
|
public record DeleteNotificationListCommand(int NotificationListId);
|
||||||
public record ListSmtpConfigsCommand;
|
public record ListSmtpConfigsCommand;
|
||||||
public record UpdateSmtpConfigCommand(int SmtpConfigId, string Server, int Port, string AuthMode, string FromAddress);
|
public record UpdateSmtpConfigCommand(int SmtpConfigId, string Server, int Port, string AuthMode, string FromAddress, string? TlsMode = null, string? Credentials = null);
|
||||||
|
|||||||
@@ -1124,6 +1124,10 @@ public class ManagementActor : ReceiveActor
|
|||||||
config.Port = cmd.Port;
|
config.Port = cmd.Port;
|
||||||
config.AuthType = cmd.AuthMode;
|
config.AuthType = cmd.AuthMode;
|
||||||
config.FromAddress = cmd.FromAddress;
|
config.FromAddress = cmd.FromAddress;
|
||||||
|
// Preserve-if-null: an update that omits TlsMode/Credentials leaves the
|
||||||
|
// existing values intact (non-breaking for callers that do not send them).
|
||||||
|
if (cmd.TlsMode is not null) config.TlsMode = cmd.TlsMode;
|
||||||
|
if (cmd.Credentials is not null) config.Credentials = cmd.Credentials;
|
||||||
await repo.UpdateSmtpConfigurationAsync(config);
|
await repo.UpdateSmtpConfigurationAsync(config);
|
||||||
await repo.SaveChangesAsync();
|
await repo.SaveChangesAsync();
|
||||||
await AuditAsync(sp, user, "Update", "SmtpConfiguration", config.Id.ToString(), config.Host, config);
|
await AuditAsync(sp, user, "Update", "SmtpConfiguration", config.Id.ToString(), config.Host, config);
|
||||||
|
|||||||
@@ -1002,6 +1002,72 @@ public class ManagementActorTests : TestKit, IDisposable
|
|||||||
Assert.Contains(envelope.CorrelationId, response.Error);
|
Assert.Contains(envelope.CorrelationId, response.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// UpdateSmtpConfig — TlsMode + Credentials plumbing (preserve-if-null)
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void UpdateSmtpConfig_WithTlsModeAndCredentials_PersistsThem()
|
||||||
|
{
|
||||||
|
var notifRepo = Substitute.For<INotificationRepository>();
|
||||||
|
var existing = new Commons.Entities.Notifications.SmtpConfiguration(
|
||||||
|
"old.example.com", "OAuth2", "old@example.com")
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Port = 25,
|
||||||
|
TlsMode = "StartTLS",
|
||||||
|
Credentials = "old-secret",
|
||||||
|
};
|
||||||
|
notifRepo.GetSmtpConfigurationByIdAsync(1, Arg.Any<CancellationToken>()).Returns(existing);
|
||||||
|
_services.AddScoped(_ => notifRepo);
|
||||||
|
|
||||||
|
var actor = CreateActor();
|
||||||
|
var envelope = Envelope(
|
||||||
|
new UpdateSmtpConfigCommand(1, "new.example.com", 465, "Basic", "new@example.com", "SSL", "user:pass"),
|
||||||
|
"Design");
|
||||||
|
|
||||||
|
actor.Tell(envelope);
|
||||||
|
|
||||||
|
var response = ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(5));
|
||||||
|
Assert.Equal(envelope.CorrelationId, response.CorrelationId);
|
||||||
|
Assert.Equal("SSL", existing.TlsMode);
|
||||||
|
Assert.Equal("user:pass", existing.Credentials);
|
||||||
|
Assert.Equal("new.example.com", existing.Host);
|
||||||
|
Assert.Equal("Basic", existing.AuthType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void UpdateSmtpConfig_WithNullTlsModeAndCredentials_PreservesExistingValues()
|
||||||
|
{
|
||||||
|
var notifRepo = Substitute.For<INotificationRepository>();
|
||||||
|
var existing = new Commons.Entities.Notifications.SmtpConfiguration(
|
||||||
|
"old.example.com", "OAuth2", "old@example.com")
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Port = 25,
|
||||||
|
TlsMode = "StartTLS",
|
||||||
|
Credentials = "old-secret",
|
||||||
|
};
|
||||||
|
notifRepo.GetSmtpConfigurationByIdAsync(1, Arg.Any<CancellationToken>()).Returns(existing);
|
||||||
|
_services.AddScoped(_ => notifRepo);
|
||||||
|
|
||||||
|
var actor = CreateActor();
|
||||||
|
var envelope = Envelope(
|
||||||
|
new UpdateSmtpConfigCommand(1, "new.example.com", 465, "Basic", "new@example.com"),
|
||||||
|
"Design");
|
||||||
|
|
||||||
|
actor.Tell(envelope);
|
||||||
|
|
||||||
|
var response = ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(5));
|
||||||
|
Assert.Equal(envelope.CorrelationId, response.CorrelationId);
|
||||||
|
// Omitted fields are preserved, not nulled.
|
||||||
|
Assert.Equal("StartTLS", existing.TlsMode);
|
||||||
|
Assert.Equal("old-secret", existing.Credentials);
|
||||||
|
// Provided fields are still updated.
|
||||||
|
Assert.Equal("new.example.com", existing.Host);
|
||||||
|
Assert.Equal("Basic", existing.AuthType);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CuratedHandlerFailure_SurfacesTheCuratedMessage()
|
public void CuratedHandlerFailure_SurfacesTheCuratedMessage()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user