From da669bfc9bc1b893108d9e59cd82c3cdc09bf263 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 2 Jun 2026 01:45:57 -0400 Subject: [PATCH] fix(auth.apikeys): stamp schema version 2 to match donor gateway DBs; bump 0.1.2 The store was extracted from MxAccessGateway, whose deployed gateway-auth.db is at schema_version=2. The library capped at 1 and threw on a newer on-disk version -> gateway would fail to boot. Final schema is byte-identical since v1; stamp 2 so existing deployed DBs interoperate (no key re-issuance). +2 tests. --- ZB.MOM.WW.Auth/Directory.Build.props | 2 +- .../Sqlite/SqliteAuthSchema.cs | 11 ++++++++-- .../Sqlite/SqliteAuthStoreMigrator.cs | 7 +++++-- .../SqliteMigratorTests.cs | 21 +++++++++++++++++++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/ZB.MOM.WW.Auth/Directory.Build.props b/ZB.MOM.WW.Auth/Directory.Build.props index 5e21819..c48c525 100644 --- a/ZB.MOM.WW.Auth/Directory.Build.props +++ b/ZB.MOM.WW.Auth/Directory.Build.props @@ -5,7 +5,7 @@ enable enable latest - 0.1.1 + 0.1.2 true diff --git a/ZB.MOM.WW.Auth/src/ZB.MOM.WW.Auth.ApiKeys/Sqlite/SqliteAuthSchema.cs b/ZB.MOM.WW.Auth/src/ZB.MOM.WW.Auth.ApiKeys/Sqlite/SqliteAuthSchema.cs index f3afd55..86b87a5 100644 --- a/ZB.MOM.WW.Auth/src/ZB.MOM.WW.Auth.ApiKeys/Sqlite/SqliteAuthSchema.cs +++ b/ZB.MOM.WW.Auth/src/ZB.MOM.WW.Auth.ApiKeys/Sqlite/SqliteAuthSchema.cs @@ -5,8 +5,15 @@ namespace ZB.MOM.WW.Auth.ApiKeys.Sqlite; /// public static class SqliteAuthSchema { - /// The schema version this build creates and supports. - public const int CurrentVersion = 1; + /// + /// The schema version this build creates and supports. This is 2, not 1, + /// to match the deployed databases of the donor (MxAccessGateway) this store was + /// extracted from: that store reached its final shape via a v1→v2 history and stamps + /// version = 2 on disk. The final schema has been byte-identical since v1, so a + /// single-shot create stamped as 2 interoperates with existing gateway-auth.db + /// files (the migrator only refuses an on-disk version newer than this). + /// + public const int CurrentVersion = 2; /// Name of the single-row table tracking the applied schema version. public const string SchemaVersionTable = "schema_version"; diff --git a/ZB.MOM.WW.Auth/src/ZB.MOM.WW.Auth.ApiKeys/Sqlite/SqliteAuthStoreMigrator.cs b/ZB.MOM.WW.Auth/src/ZB.MOM.WW.Auth.ApiKeys/Sqlite/SqliteAuthStoreMigrator.cs index 416cad8..8234692 100644 --- a/ZB.MOM.WW.Auth/src/ZB.MOM.WW.Auth.ApiKeys/Sqlite/SqliteAuthStoreMigrator.cs +++ b/ZB.MOM.WW.Auth/src/ZB.MOM.WW.Auth.ApiKeys/Sqlite/SqliteAuthStoreMigrator.cs @@ -35,7 +35,7 @@ public sealed class SqliteAuthStoreMigrator(AuthSqliteConnectionFactory connecti $"Auth database schema version {existingVersion} is newer than supported version {SqliteAuthSchema.CurrentVersion}."); } - await ApplyVersionOneAsync(connection, transaction, cancellationToken).ConfigureAwait(false); + await ApplySchemaAsync(connection, transaction, cancellationToken).ConfigureAwait(false); await WriteSchemaVersionAsync(connection, transaction, cancellationToken).ConfigureAwait(false); await transaction.CommitAsync(cancellationToken).ConfigureAwait(false); @@ -78,7 +78,10 @@ public sealed class SqliteAuthStoreMigrator(AuthSqliteConnectionFactory connecti : Convert.ToInt32(version, CultureInfo.InvariantCulture); } - private static async Task ApplyVersionOneAsync( + // Single-shot create of the final schema (all DDL is CREATE ... IF NOT EXISTS, so it is + // idempotent against an already-provisioned database). The applied version is stamped + // separately by WriteSchemaVersionAsync. + private static async Task ApplySchemaAsync( SqliteConnection connection, SqliteTransaction transaction, CancellationToken cancellationToken) diff --git a/ZB.MOM.WW.Auth/tests/ZB.MOM.WW.Auth.ApiKeys.Tests/SqliteMigratorTests.cs b/ZB.MOM.WW.Auth/tests/ZB.MOM.WW.Auth.ApiKeys.Tests/SqliteMigratorTests.cs index c5e08c8..f7870d0 100644 --- a/ZB.MOM.WW.Auth/tests/ZB.MOM.WW.Auth.ApiKeys.Tests/SqliteMigratorTests.cs +++ b/ZB.MOM.WW.Auth/tests/ZB.MOM.WW.Auth.ApiKeys.Tests/SqliteMigratorTests.cs @@ -34,6 +34,27 @@ public sealed class SqliteMigratorTests : IDisposable Assert.Equal(1, await CountSchemaVersionRowsAsync()); } + [Fact] + public void CurrentVersion_Is2_ToMatchDonorGatewayDeployedSchema() => + // The store was extracted from MxAccessGateway, whose deployed gateway-auth.db is + // stamped version 2. The library must stamp 2 (not reset to 1) so it does not refuse + // those existing databases on first boot. Locking this invariant. + Assert.Equal(2, SqliteAuthSchema.CurrentVersion); + + [Fact] + public async Task MigrateAsync_AgainstExistingVersion2Db_DoesNotThrow_AndStaysAt2() + { + // The deployed-gateway scenario: a database already provisioned at version 2. + var migrator = new SqliteAuthStoreMigrator(Factory); + await migrator.MigrateAsync(CancellationToken.None); + await SetVersionAsync(2); + + await migrator.MigrateAsync(CancellationToken.None); // must not throw + + Assert.Equal(2, await ReadVersionAsync()); + Assert.True(await TableExistsAsync(SqliteAuthSchema.ApiKeysTable)); + } + [Fact] public async Task MigrateAsync_FutureSchemaVersion_Throws() {