fix(configdb): make ResyncLdapGroupMappingSeed migration idempotent (guarded insert) (#70)

This commit is contained in:
Joseph Doherty
2026-06-19 01:51:20 -04:00
parent f4e03ce8f7
commit 3d4521f250
2 changed files with 188 additions and 4 deletions
@@ -7,18 +7,56 @@ namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Migrations
/// <inheritdoc />
public partial class ResyncLdapGroupMappingSeed : Migration
{
/// <summary>
/// SQLite idempotent insert for the Id=5 (SCADA-Viewers → Viewer) seed row.
/// <c>INSERT OR IGNORE</c> skips the row when the <c>Id</c> primary key already
/// exists, so replaying this migration against a DB that already carries Id=5
/// (e.g. one seeded by a newer HasData baseline) is a no-op rather than a PK
/// violation. Exposed as a constant so the idempotency test can exercise the
/// exact SQL the migration emits on the SQLite branch.
/// </summary>
internal const string SqliteInsertSql =
"INSERT OR IGNORE INTO \"LdapGroupMappings\" (\"Id\", \"LdapGroupName\", \"Role\") " +
"VALUES (5, 'SCADA-Viewers', 'Viewer');";
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: "LdapGroupMappings",
columns: new[] { "Id", "LdapGroupName", "Role" },
values: new object[] { 5, "SCADA-Viewers", "Viewer" });
// #70: this seed insert must be idempotent. The original unconditional
// InsertData threw a PK violation (and crash-looped startup, since dev
// auto-applies migrations) on any pre-existing DB that already had Id=5
// — e.g. one seeded via the old HasData baseline. Guard the insert so it
// is a no-op when the row already exists. Provider-aware because the
// production central DB is SQL Server while dev/test run on SQLite, and
// the two speak different conditional-insert dialects (mirrors the
// NotificationOutboxRepository.InsertIfNotExists pattern, #286).
if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.Sqlite")
{
// SQLite: INSERT OR IGNORE silently skips the row on a PK clash.
migrationBuilder.Sql(SqliteInsertSql);
}
else
{
// SQL Server: guard with IF NOT EXISTS. LdapGroupMappings.Id is an
// IDENTITY column (InitialSchema declares SqlServer:Identity "1, 1"),
// so an explicit Id value requires SET IDENTITY_INSERT ON/OFF around
// the INSERT — exactly what EF's InsertData emitted for this row.
migrationBuilder.Sql(@"
IF NOT EXISTS (SELECT 1 FROM [LdapGroupMappings] WHERE [Id] = 5)
BEGIN
SET IDENTITY_INSERT [LdapGroupMappings] ON;
INSERT INTO [LdapGroupMappings] ([Id], [LdapGroupName], [Role])
VALUES (5, N'SCADA-Viewers', N'Viewer');
SET IDENTITY_INSERT [LdapGroupMappings] OFF;
END");
}
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
// Idempotent delete: removes the Id=5 row if present, no-op otherwise.
// Unchanged in behaviour from the original DeleteData.
migrationBuilder.DeleteData(
table: "LdapGroupMappings",
keyColumn: "Id",