fix(notification-service): resolve NotificationService-014..018 — classify OAuth2 failures, fail on bad auth config, wire NotificationOptions fallback, disposable concurrency limiter

This commit is contained in:
Joseph Doherty
2026-05-17 03:18:33 -04:00
parent bf6bd8de5a
commit f5199e9da9
6 changed files with 454 additions and 41 deletions

View File

@@ -0,0 +1,45 @@
namespace ScadaLink.NotificationService.Tests;
/// <summary>
/// NS-016: <see cref="MailKitSmtpClientWrapper.AuthenticateAsync"/> must never
/// silently skip authentication for a misconfigured SMTP config — a missing
/// credential, an unrecognised auth type, or an unparseable Basic credential
/// must be a hard, surfaced error rather than an unauthenticated send.
/// </summary>
public class MailKitSmtpClientWrapperTests
{
[Fact]
public async Task Authenticate_EmptyCredentials_Throws()
{
// An AuthType of "basic"/"oauth2" with a null/empty Credentials value is a
// misconfigured row; the wrapper used to "return" and send unauthenticated.
var wrapper = new MailKitSmtpClientWrapper();
await Assert.ThrowsAsync<SmtpPermanentException>(
() => wrapper.AuthenticateAsync("basic", null));
await Assert.ThrowsAsync<SmtpPermanentException>(
() => wrapper.AuthenticateAsync("oauth2", ""));
}
[Fact]
public async Task Authenticate_UnknownAuthType_Throws()
{
// The switch had cases only for "basic"/"oauth2" and no default — any other
// value (typo, future "ntlm") fell through and sent unauthenticated.
var wrapper = new MailKitSmtpClientWrapper();
await Assert.ThrowsAsync<SmtpPermanentException>(
() => wrapper.AuthenticateAsync("ntlm", "user:pass"));
}
[Fact]
public async Task Authenticate_BasicCredentialWithoutColon_Throws()
{
// A "basic" credential string that does not split into exactly two parts was
// silently skipped — the connection then sent unauthenticated.
var wrapper = new MailKitSmtpClientWrapper();
await Assert.ThrowsAsync<SmtpPermanentException>(
() => wrapper.AuthenticateAsync("basic", "nocolon"));
}
}