Verify mailer credentials against ProcessWeb API before showing E-Mail-Dienst as healthy
Playwright Tests / test (push) Canceled after 0s
Playwright Tests / test (push) Canceled after 0s
The Admin status pill previously turned green whenever the mailer was enabled in config, without proving the account/token actually work. Add IComService.VerifyCredentialsAsync (GET /mailer/fc?fn=verify, also passing serverid when configured so the API confirms this server belongs to the account) and wire it into a new "email" system-status probe, so the pill only goes green once the Mailer API itself confirms the credentials. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
f36f65789e
commit
107b7f54f1
@@ -476,6 +476,139 @@ public class ProcessWebComServiceTests
|
||||
Assert.Equal(0, handler.CallCount);
|
||||
}
|
||||
|
||||
// ── Credential verification (GET /mailer/fc?fn=verify) ─────────────────────
|
||||
private sealed class CapturingRequestHandler : HttpMessageHandler
|
||||
{
|
||||
private readonly HttpStatusCode _status;
|
||||
private readonly string _body;
|
||||
public HttpRequestMessage? LastRequest { get; private set; }
|
||||
|
||||
public CapturingRequestHandler(HttpStatusCode status, string body) { _status = status; _body = body; }
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
LastRequest = request;
|
||||
return Task.FromResult(new HttpResponseMessage(_status) { Content = new StringContent(_body) });
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task VerifyCredentialsAsync_MailerConfirms_ReturnsOkWithAccount()
|
||||
{
|
||||
var handler = new CapturingRequestHandler(HttpStatusCode.OK, "{\"ok\":true,\"account\":\"acc-123\"}");
|
||||
var svc = new ProcessWebComService(
|
||||
NullLogger<ProcessWebComService>.Instance, intranet: null!,
|
||||
Options.Create(new ProcessWebComSettings { Enabled = true, BaseUrl = "https://mailer.test", AccountId = "acct", Token = "tok" }),
|
||||
Options.Create(new FuchsEmailSettings()),
|
||||
new StubHttpClientFactory(handler));
|
||||
|
||||
var result = await svc.VerifyCredentialsAsync();
|
||||
|
||||
Assert.True(result.Ok);
|
||||
Assert.Equal("acc-123", result.Account);
|
||||
Assert.NotNull(handler.LastRequest);
|
||||
Assert.Equal(HttpMethod.Get, handler.LastRequest!.Method);
|
||||
Assert.Equal("https://mailer.test/mailer/fc?fn=verify", handler.LastRequest.RequestUri!.ToString());
|
||||
Assert.Equal("Basic", handler.LastRequest.Headers.Authorization!.Scheme);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task VerifyCredentialsAsync_ServerIdConfigured_IsSentAndEchoedOnSuccess()
|
||||
{
|
||||
var handler = new CapturingRequestHandler(HttpStatusCode.OK, "{\"ok\":true,\"account\":\"acc-123\",\"serverid\":\"srv-1\"}");
|
||||
var svc = new ProcessWebComService(
|
||||
NullLogger<ProcessWebComService>.Instance, intranet: null!,
|
||||
Options.Create(new ProcessWebComSettings { Enabled = true, BaseUrl = "https://mailer.test", AccountId = "acct", Token = "tok", ServerId = "srv-1" }),
|
||||
Options.Create(new FuchsEmailSettings()),
|
||||
new StubHttpClientFactory(handler));
|
||||
|
||||
var result = await svc.VerifyCredentialsAsync();
|
||||
|
||||
Assert.True(result.Ok);
|
||||
Assert.Equal("srv-1", result.ServerId);
|
||||
Assert.Equal("https://mailer.test/mailer/fc?fn=verify&serverid=srv-1", handler.LastRequest!.RequestUri!.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task VerifyCredentialsAsync_ServerIdNotOwnedByAccount_ReturnsNotOkWithServerIdDetail()
|
||||
{
|
||||
var handler = new CapturingRequestHandler(HttpStatusCode.OK, "{\"ok\":false,\"account\":\"acc-123\",\"serverid\":\"srv-wrong\"}");
|
||||
var svc = new ProcessWebComService(
|
||||
NullLogger<ProcessWebComService>.Instance, intranet: null!,
|
||||
Options.Create(new ProcessWebComSettings { Enabled = true, BaseUrl = "https://mailer.test", AccountId = "acct", Token = "tok", ServerId = "srv-wrong" }),
|
||||
Options.Create(new FuchsEmailSettings()),
|
||||
new StubHttpClientFactory(handler));
|
||||
|
||||
var result = await svc.VerifyCredentialsAsync();
|
||||
|
||||
Assert.False(result.Ok);
|
||||
Assert.Contains("srv-wrong", result.Detail);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task VerifyCredentialsAsync_MailerReturnsOkFalse_ReturnsNotOk()
|
||||
{
|
||||
var handler = new CapturingRequestHandler(HttpStatusCode.OK, "{\"ok\":false}");
|
||||
var svc = new ProcessWebComService(
|
||||
NullLogger<ProcessWebComService>.Instance, intranet: null!,
|
||||
Options.Create(new ProcessWebComSettings { Enabled = true, BaseUrl = "https://mailer.test", AccountId = "acct", Token = "tok" }),
|
||||
Options.Create(new FuchsEmailSettings()),
|
||||
new StubHttpClientFactory(handler));
|
||||
|
||||
var result = await svc.VerifyCredentialsAsync();
|
||||
|
||||
Assert.False(result.Ok);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task VerifyCredentialsAsync_Http401_ReturnsNotOkWithDetail()
|
||||
{
|
||||
var handler = new CapturingRequestHandler(HttpStatusCode.Unauthorized, "unauthorized");
|
||||
var svc = new ProcessWebComService(
|
||||
NullLogger<ProcessWebComService>.Instance, intranet: null!,
|
||||
Options.Create(new ProcessWebComSettings { Enabled = true, BaseUrl = "https://mailer.test", AccountId = "acct", Token = "wrong" }),
|
||||
Options.Create(new FuchsEmailSettings()),
|
||||
new StubHttpClientFactory(handler));
|
||||
|
||||
var result = await svc.VerifyCredentialsAsync();
|
||||
|
||||
Assert.False(result.Ok);
|
||||
Assert.Contains("401", result.Detail);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task VerifyCredentialsAsync_TransportException_ReturnsNotOkWithExceptionDetail()
|
||||
{
|
||||
var svc = new ProcessWebComService(
|
||||
NullLogger<ProcessWebComService>.Instance, intranet: null!,
|
||||
Options.Create(new ProcessWebComSettings { Enabled = true, BaseUrl = "https://mailer.test", AccountId = "acct", Token = "tok" }),
|
||||
Options.Create(new FuchsEmailSettings()),
|
||||
new StubHttpClientFactory(new ThrowingHandler()));
|
||||
|
||||
var result = await svc.VerifyCredentialsAsync();
|
||||
|
||||
Assert.False(result.Ok);
|
||||
Assert.Contains("simulated transport failure", result.Detail);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "tok")]
|
||||
[InlineData("acct", "")]
|
||||
public async Task VerifyCredentialsAsync_CredentialsNotConfigured_ReturnsNotOkWithoutCallingApi(string accountId, string token)
|
||||
{
|
||||
var handler = new CapturingRequestHandler(HttpStatusCode.OK, "{\"ok\":true}");
|
||||
var svc = new ProcessWebComService(
|
||||
NullLogger<ProcessWebComService>.Instance, intranet: null!,
|
||||
Options.Create(new ProcessWebComSettings { Enabled = true, BaseUrl = "https://mailer.test", AccountId = accountId, Token = token }),
|
||||
Options.Create(new FuchsEmailSettings()),
|
||||
new StubHttpClientFactory(handler));
|
||||
|
||||
var result = await svc.VerifyCredentialsAsync();
|
||||
|
||||
Assert.False(result.Ok);
|
||||
Assert.Null(handler.LastRequest);
|
||||
}
|
||||
|
||||
// ── Metric emission (performance indicator) ────────────────────────────────
|
||||
[Fact]
|
||||
public async Task SendEmailAsync_Success_IncrementsEmailsSentCounter()
|
||||
|
||||
@@ -60,13 +60,20 @@ public class SystemStatusServiceTests
|
||||
var env = new Mock<IHostEnvironment>();
|
||||
env.SetupGet(e => e.EnvironmentName).Returns("Development");
|
||||
|
||||
var comMock = com ?? new Mock<IComService>();
|
||||
if (com is null)
|
||||
{
|
||||
comMock.Setup(c => c.VerifyCredentialsAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new MailerCredentialsCheckResult { Ok = true, Account = "acc" });
|
||||
}
|
||||
|
||||
return new SystemStatusService(
|
||||
config ?? Config(),
|
||||
env.Object,
|
||||
provider ?? new ServiceCollection().BuildServiceProvider(),
|
||||
(blob ?? new Mock<IBlobStorageService>()).Object,
|
||||
(mfr ?? new Mock<IMfrClientFactory>()).Object,
|
||||
(com ?? new Mock<IComService>()).Object,
|
||||
comMock.Object,
|
||||
Options.Create(mailer ?? new ProcessWebComSettings { Enabled = true, Token = "tok", BaseUrl = "https://api.example", AccountId = "acc", ServerId = "srv" }),
|
||||
Options.Create(email ?? new FuchsEmailSettings()),
|
||||
Options.Create(blobSettings ?? new AzureBlobStorageSettings { Enabled = true }),
|
||||
@@ -277,6 +284,63 @@ public class SystemStatusServiceTests
|
||||
Assert.False(r.Ok);
|
||||
}
|
||||
|
||||
// ── Email probe ────────────────────────────────────────────────────────────
|
||||
[Fact]
|
||||
public async Task ProbeEmail_MailerDisabled_ReturnsDisabled()
|
||||
{
|
||||
var svc = Build(mailer: new ProcessWebComSettings { Enabled = false, AccountId = "acc", Token = "tok" });
|
||||
|
||||
var r = await svc.ProbeAsync("email");
|
||||
|
||||
Assert.Equal("email", r.Component);
|
||||
Assert.Equal("disabled", r.Status);
|
||||
Assert.False(r.Ok);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "tok")]
|
||||
[InlineData("acc", "")]
|
||||
public async Task ProbeEmail_CredentialsMissing_ReturnsUnconfigured(string accountId, string token)
|
||||
{
|
||||
var svc = Build(mailer: new ProcessWebComSettings { Enabled = true, AccountId = accountId, Token = token });
|
||||
|
||||
var r = await svc.ProbeAsync("email");
|
||||
|
||||
Assert.Equal("unconfigured", r.Status);
|
||||
Assert.False(r.Ok);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProbeEmail_MailerConfirms_ReturnsOkWithAccount()
|
||||
{
|
||||
var com = new Mock<IComService>();
|
||||
com.Setup(c => c.VerifyCredentialsAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new MailerCredentialsCheckResult { Ok = true, Account = "acc-123" });
|
||||
var svc = Build(com: com);
|
||||
|
||||
var r = await svc.ProbeAsync("email");
|
||||
|
||||
Assert.Equal("email", r.Component);
|
||||
Assert.Equal("ok", r.Status);
|
||||
Assert.True(r.Ok);
|
||||
Assert.Equal("acc-123", r.Detail);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProbeEmail_MailerRejectsCredentials_ReturnsError()
|
||||
{
|
||||
var com = new Mock<IComService>();
|
||||
com.Setup(c => c.VerifyCredentialsAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new MailerCredentialsCheckResult { Ok = false, Detail = "API-Fehler 401 (Unauthorized): {}" });
|
||||
var svc = Build(com: com);
|
||||
|
||||
var r = await svc.ProbeAsync("email");
|
||||
|
||||
Assert.Equal("error", r.Status);
|
||||
Assert.False(r.Ok);
|
||||
Assert.Equal("API-Fehler 401 (Unauthorized): {}", r.Detail);
|
||||
}
|
||||
|
||||
// ── Key Vault probe ────────────────────────────────────────────────────────
|
||||
[Fact]
|
||||
public async Task ProbeKeyVault_ClientNotRegistered_ReturnsUnconfigured()
|
||||
|
||||
Reference in New Issue
Block a user