package database_test import ( "net/http" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "sneak.berlin/go/webhooker/internal/database" "sneak.berlin/go/webhooker/internal/signature" ) // TestEntrypointSignatureColumnsMigrateToUnconfigured pins the // upgrade path for a deployment that already has entrypoints. // // The signature columns arrive through GORM's AutoMigrate, so every // row written before they existed acquires them with no value. That // has to land on "not configured", because the alternative is an // upgrade that rejects the traffic the operator was already // receiving — a self-inflicted outage on a receiver whose senders // cannot be told to start signing. // // The legacy schema is reproduced by dropping the columns from a // migrated database and writing a row through the old shape, so the // row really predates them rather than merely being blank. func TestEntrypointSignatureColumnsMigrateToUnconfigured(t *testing.T) { t.Parallel() db, lc := setupTestDB(t) lc.RequireStart() t.Cleanup(lc.RequireStop) for _, column := range []string{ "signature_scheme", "signature_secret", } { require.NoError( t, db.DB().Exec( "ALTER TABLE entrypoints DROP COLUMN "+column, ).Error, "dropping %s to reproduce the pre-upgrade schema", column, ) } const legacyID = "legacy-entrypoint" require.NoError( t, db.DB().Exec( `INSERT INTO entrypoints (id, created_at, updated_at, webhook_id, path, description, active) VALUES (?, ?, ?, ?, ?, ?, ?)`, legacyID, "2026-01-01 00:00:00", "2026-01-01 00:00:00", "legacy-webhook", "legacy-path", "predates signatures", true, ).Error, ) // The upgrade. require.NoError(t, db.Migrate()) var ep database.Entrypoint require.NoError( t, db.DB().Where("id = ?", legacyID).First(&ep).Error, "the migrated row must still load; a NULL landing in a "+ "string column would fail here", ) assert.Equal(t, database.SignatureSchemeNone, ep.SignatureScheme) assert.Empty(t, ep.SignatureSecret) assert.False(t, ep.SignatureConfigured()) assert.True(t, ep.Active, "the row's other columns survive") // The behaviour that actually matters: an unsigned request to // this entrypoint is still accepted. assert.NoError( t, signature.Verify(&ep, http.Header{}, []byte(`{"a":1}`)), ) }