Files
scadaproj/ZB.MOM.WW.Auth/tests/ZB.MOM.WW.Auth.ApiKeys.SqlServer.Tests/Live/SqlServerApiKeyAuditStoreLiveTests.cs
T

75 lines
2.9 KiB
C#

using ZB.MOM.WW.Auth.Abstractions.ApiKeys;
namespace ZB.MOM.WW.Auth.ApiKeys.SqlServer.Tests.Live;
/// <summary>
/// Live tests for <see cref="SqlServerApiKeyAuditStore"/> that the ported SQLite suite does not
/// already cover: newest-first ordering under a limit, and the non-positive-limit short circuit.
/// </summary>
/// <remarks>
/// xUnit constructs a new test-class instance per test, so each test provisions and drops its own
/// throwaway schema.
/// </remarks>
[Collection("live-sqlserver")]
public sealed class SqlServerApiKeyAuditStoreLiveTests : IAsyncLifetime
{
private readonly string _schema = LiveSqlServer.NewSchemaName();
private SqlServerApiKeyAuditStore _audit = null!;
/// <inheritdoc />
public async Task InitializeAsync()
{
if (!LiveSqlServer.IsEnabled)
{
return;
}
var factory = new AuthSqlServerConnectionFactory(LiveSqlServer.OptionsFor(_schema));
await new SqlServerApiKeyStoreMigrator(factory).MigrateAsync(CancellationToken.None);
_audit = new SqlServerApiKeyAuditStore(factory);
}
/// <inheritdoc />
public Task DisposeAsync() => LiveSqlServer.DropSchemaAsync(_schema);
[SkippableFact]
public async Task Append_ThenListRecent_ReturnsNewestFirstWithinLimit()
{
Skip.IfNot(LiveSqlServer.IsEnabled, LiveSqlServer.SkipReason);
// Every row carries the SAME created_utc, so the newest-first order can only come from the
// IDENTITY audit_id the ORDER BY names — the timestamp cannot accidentally supply it.
var when = new DateTimeOffset(2026, 7, 1, 0, 0, 0, TimeSpan.Zero);
for (int i = 0; i < 6; i++)
{
await _audit.AppendAsync(
new ApiKeyAuditEntry("k", $"e{i}", "10.0.0.1", when, $"detail {i}"),
CancellationToken.None);
}
IReadOnlyList<ApiKeyAuditEntry> recent = await _audit.ListRecentAsync(3, CancellationToken.None);
Assert.Equal(3, recent.Count);
Assert.Equal(["e5", "e4", "e3"], recent.Select(e => e.EventType));
Assert.Equal("detail 5", recent[0].Details);
Assert.Equal(when, recent[0].CreatedUtc);
}
[SkippableFact]
public async Task ListRecent_NonPositiveLimit_ReturnsEmpty()
{
Skip.IfNot(LiveSqlServer.IsEnabled, LiveSqlServer.SkipReason);
await _audit.AppendAsync(
new ApiKeyAuditEntry("k", "created", null, DateTimeOffset.UtcNow, null),
CancellationToken.None);
// A non-positive limit short circuits WITHOUT touching the database — SELECT TOP (0) would
// be legal but SELECT TOP (-1) is a syntax error, so the guard is load-bearing here in a way
// it is not for SQLite's LIMIT.
Assert.Empty(await _audit.ListRecentAsync(0, CancellationToken.None));
Assert.Empty(await _audit.ListRecentAsync(-5, CancellationToken.None));
}
}