c4366539ca
A key-store lookup that threw (shared SQL Server unreachable, SQLite I/O fault) escaped VerifyAsync despite the "only exception path is cancellation" contract, surfacing as an unaudited 500 / gRPC Unknown. ApiKeyFailure gains StoreUnavailable (appended; numeric values unchanged) and the lookup maps any non-cancellation exception to it. Unit theory and an offline refused-port SqlServerApiKeyStore test fail pre-fix. (scadaproj#46) Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
35 lines
1.4 KiB
C#
35 lines
1.4 KiB
C#
using ZB.MOM.WW.Auth.Abstractions.ApiKeys;
|
|
|
|
namespace ZB.MOM.WW.Auth.ApiKeys.SqlServer.Tests;
|
|
|
|
/// <summary>
|
|
/// Auth-010 (scadaproj#46), end to end through the real SQL Server store with no server: the shared
|
|
/// store being unreachable (failover, lost route) must come back from <c>VerifyAsync</c> as a
|
|
/// structured <see cref="ApiKeyFailure.StoreUnavailable"/>, not as a <c>SqlException</c> that
|
|
/// surfaces as an unaudited 500. Offline: it dials a refused local port.
|
|
/// </summary>
|
|
public sealed class SqlServerUnreachableVerifierTests
|
|
{
|
|
private sealed class Pepper : IApiKeyPepperProvider
|
|
{
|
|
public string? GetPepper() => "0123456789abcdef";
|
|
}
|
|
|
|
[Fact]
|
|
public async Task VerifyAsync_AgainstAnUnreachableSqlServer_ReturnsStoreUnavailable()
|
|
{
|
|
var factory = new AuthSqlServerConnectionFactory(new SqlServerApiKeyOptions
|
|
{
|
|
// Port 1 on loopback refuses immediately; no retries, so the test stays fast.
|
|
ConnectionString = "Server=tcp:127.0.0.1,1;Connect Timeout=3;ConnectRetryCount=0;TrustServerCertificate=true",
|
|
});
|
|
var verifier = new ApiKeyVerifier(
|
|
new ApiKeyOptions { TokenPrefix = "sbk" }, new SqlServerApiKeyStore(factory), new Pepper());
|
|
|
|
var result = await verifier.VerifyAsync("sbk_line1_supersecretvalue", CancellationToken.None);
|
|
|
|
Assert.False(result.Succeeded);
|
|
Assert.Equal(ApiKeyFailure.StoreUnavailable, result.Failure);
|
|
}
|
|
}
|