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

146 lines
5.3 KiB
C#

using System.Globalization;
using ZB.MOM.WW.Auth.ApiKeys.Sqlite;
namespace ZB.MOM.WW.Auth.ApiKeys.SqlServer.Tests.Live;
/// <summary>
/// Live provisioning tests for <see cref="SqlServerApiKeyStoreMigrator"/>.
/// </summary>
/// <remarks>
/// Every test provisions its own throwaway schema — the concurrency case in particular needs a
/// virgin one, because the race the retry loop exists for only happens while the schema and tables
/// do not yet exist. All schemas created by the class are dropped in <see cref="DisposeAsync"/>.
/// </remarks>
[Collection("live-sqlserver")]
public sealed class SqlServerApiKeyStoreMigratorLiveTests : IAsyncLifetime
{
private readonly List<string> _schemas = [];
/// <inheritdoc />
public Task InitializeAsync() => Task.CompletedTask;
/// <inheritdoc />
public async Task DisposeAsync()
{
foreach (string schema in _schemas)
{
await LiveSqlServer.DropSchemaAsync(schema);
}
}
private string NewSchema()
{
string schema = LiveSqlServer.NewSchemaName();
_schemas.Add(schema);
return schema;
}
private static SqlServerApiKeyStoreMigrator MigratorFor(string schema) =>
new(new AuthSqlServerConnectionFactory(LiveSqlServer.OptionsFor(schema)));
[SkippableFact]
public async Task Migrate_OnEmptyDatabase_CreatesSchemaTablesAndVersionRow()
{
Skip.IfNot(LiveSqlServer.IsEnabled, LiveSqlServer.SkipReason);
string schema = NewSchema();
await MigratorFor(schema).MigrateAsync(CancellationToken.None);
Assert.Equal(
1,
Convert.ToInt32(
await LiveSqlServer.ScalarAsync(
$"SELECT COUNT(*) FROM sys.schemas WHERE name = N'{schema}'"),
CultureInfo.InvariantCulture));
foreach (string table in new[]
{
SqlServerAuthSchema.SchemaVersionTable,
SqlServerAuthSchema.ApiKeysTable,
SqlServerAuthSchema.ApiKeyAuditTable,
})
{
Assert.NotNull(
await LiveSqlServer.ScalarAsync($"SELECT OBJECT_ID(N'[{schema}].[{table}]', N'U')"));
}
Assert.Equal(
SqlServerAuthSchema.CurrentVersion,
Convert.ToInt32(
await LiveSqlServer.ScalarAsync(
$"SELECT version FROM [{schema}].[{SqlServerAuthSchema.SchemaVersionTable}] WHERE id = 1"),
CultureInfo.InvariantCulture));
}
[SkippableFact]
public async Task Migrate_Twice_IsIdempotent()
{
Skip.IfNot(LiveSqlServer.IsEnabled, LiveSqlServer.SkipReason);
string schema = NewSchema();
SqlServerApiKeyStoreMigrator migrator = MigratorFor(schema);
await migrator.MigrateAsync(CancellationToken.None);
await migrator.MigrateAsync(CancellationToken.None);
Assert.Equal(
1,
Convert.ToInt32(
await LiveSqlServer.ScalarAsync(
$"SELECT COUNT(*) FROM [{schema}].[{SqlServerAuthSchema.SchemaVersionTable}]"),
CultureInfo.InvariantCulture));
Assert.Equal(
SqlServerAuthSchema.CurrentVersion,
Convert.ToInt32(
await LiveSqlServer.ScalarAsync(
$"SELECT version FROM [{schema}].[{SqlServerAuthSchema.SchemaVersionTable}] WHERE id = 1"),
CultureInfo.InvariantCulture));
}
[SkippableFact]
public async Task Migrate_Concurrently_FromEightTasks_AllSucceed()
{
Skip.IfNot(LiveSqlServer.IsEnabled, LiveSqlServer.SkipReason);
// A virgin schema provisioned by eight nodes at once — the central-pair first boot, scaled
// up. Each task gets its own factory (and therefore its own connection) so the DDL really
// does run on eight sessions.
string schema = NewSchema();
await Task.WhenAll(Enumerable.Range(0, 8).Select(_ =>
Task.Run(() => MigratorFor(schema).MigrateAsync(CancellationToken.None))));
Assert.Equal(
1,
Convert.ToInt32(
await LiveSqlServer.ScalarAsync(
$"SELECT COUNT(*) FROM [{schema}].[{SqlServerAuthSchema.SchemaVersionTable}]"),
CultureInfo.InvariantCulture));
Assert.Equal(
SqlServerAuthSchema.CurrentVersion,
Convert.ToInt32(
await LiveSqlServer.ScalarAsync(
$"SELECT version FROM [{schema}].[{SqlServerAuthSchema.SchemaVersionTable}] WHERE id = 1"),
CultureInfo.InvariantCulture));
}
[SkippableFact]
public async Task Migrate_WhenStoredVersionIsNewer_Throws()
{
Skip.IfNot(LiveSqlServer.IsEnabled, LiveSqlServer.SkipReason);
string schema = NewSchema();
SqlServerApiKeyStoreMigrator migrator = MigratorFor(schema);
await migrator.MigrateAsync(CancellationToken.None);
await LiveSqlServer.ExecuteAsync(
$"UPDATE [{schema}].[{SqlServerAuthSchema.SchemaVersionTable}] SET version = 99 WHERE id = 1");
AuthStoreMigrationException exception = await Assert.ThrowsAsync<AuthStoreMigrationException>(
() => migrator.MigrateAsync(CancellationToken.None));
Assert.Contains("99", exception.Message, StringComparison.Ordinal);
}
}