114 lines
4.0 KiB
C#
114 lines
4.0 KiB
C#
using Microsoft.Data.Sqlite;
|
|
using ZB.MOM.WW.Auth.ApiKeys.Sqlite;
|
|
|
|
namespace ZB.MOM.WW.Auth.ApiKeys.Tests;
|
|
|
|
public sealed class SqliteMigratorTests : IDisposable
|
|
{
|
|
private readonly string _dbPath =
|
|
Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".db");
|
|
|
|
private AuthSqliteConnectionFactory Factory => new(_dbPath);
|
|
|
|
[Fact]
|
|
public async Task MigrateAsync_CreatesAllThreeTables()
|
|
{
|
|
var migrator = new SqliteAuthStoreMigrator(Factory);
|
|
|
|
await migrator.MigrateAsync(CancellationToken.None);
|
|
|
|
Assert.True(await TableExistsAsync(SqliteAuthSchema.ApiKeysTable));
|
|
Assert.True(await TableExistsAsync(SqliteAuthSchema.ApiKeyAuditTable));
|
|
Assert.True(await TableExistsAsync(SqliteAuthSchema.SchemaVersionTable));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task MigrateAsync_RunTwice_IsIdempotentAndRecordsCurrentVersion()
|
|
{
|
|
var migrator = new SqliteAuthStoreMigrator(Factory);
|
|
|
|
await migrator.MigrateAsync(CancellationToken.None);
|
|
await migrator.MigrateAsync(CancellationToken.None);
|
|
|
|
Assert.Equal(SqliteAuthSchema.CurrentVersion, await ReadVersionAsync());
|
|
Assert.Equal(1, await CountSchemaVersionRowsAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task MigrateAsync_FutureSchemaVersion_Throws()
|
|
{
|
|
var migrator = new SqliteAuthStoreMigrator(Factory);
|
|
await migrator.MigrateAsync(CancellationToken.None);
|
|
|
|
await SetVersionAsync(99);
|
|
|
|
await Assert.ThrowsAsync<AuthStoreMigrationException>(
|
|
() => migrator.MigrateAsync(CancellationToken.None));
|
|
}
|
|
|
|
private async Task<bool> TableExistsAsync(string tableName)
|
|
{
|
|
await using SqliteConnection connection =
|
|
await Factory.OpenConnectionAsync(CancellationToken.None);
|
|
await using SqliteCommand command = connection.CreateCommand();
|
|
command.CommandText =
|
|
"SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = $name;";
|
|
command.Parameters.AddWithValue("$name", tableName);
|
|
long count = (long)(await command.ExecuteScalarAsync(CancellationToken.None) ?? 0L);
|
|
return count == 1;
|
|
}
|
|
|
|
private async Task<int> ReadVersionAsync()
|
|
{
|
|
await using SqliteConnection connection =
|
|
await Factory.OpenConnectionAsync(CancellationToken.None);
|
|
await using SqliteCommand command = connection.CreateCommand();
|
|
command.CommandText = "SELECT version FROM schema_version WHERE id = 1;";
|
|
object? value = await command.ExecuteScalarAsync(CancellationToken.None);
|
|
return Convert.ToInt32(value, System.Globalization.CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
private async Task<int> CountSchemaVersionRowsAsync()
|
|
{
|
|
await using SqliteConnection connection =
|
|
await Factory.OpenConnectionAsync(CancellationToken.None);
|
|
await using SqliteCommand command = connection.CreateCommand();
|
|
command.CommandText = "SELECT COUNT(*) FROM schema_version;";
|
|
long count = (long)(await command.ExecuteScalarAsync(CancellationToken.None) ?? 0L);
|
|
return (int)count;
|
|
}
|
|
|
|
private async Task SetVersionAsync(int version)
|
|
{
|
|
await using SqliteConnection connection =
|
|
await Factory.OpenConnectionAsync(CancellationToken.None);
|
|
await using SqliteCommand command = connection.CreateCommand();
|
|
command.CommandText = "UPDATE schema_version SET version = $version WHERE id = 1;";
|
|
command.Parameters.AddWithValue("$version", version);
|
|
await command.ExecuteNonQueryAsync(CancellationToken.None);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
SqliteConnection.ClearAllPools();
|
|
TryDelete(_dbPath);
|
|
TryDelete(_dbPath + "-wal");
|
|
TryDelete(_dbPath + "-shm");
|
|
}
|
|
|
|
private static void TryDelete(string path)
|
|
{
|
|
try
|
|
{
|
|
if (File.Exists(path))
|
|
{
|
|
File.Delete(path);
|
|
}
|
|
}
|
|
catch (IOException)
|
|
{
|
|
// Best-effort cleanup of the per-test temp database.
|
|
}
|
|
}
|
|
}
|