From 853a6170a9cb4ef8cc81e09b4ae6d3b4f6068731 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Fri, 24 Nov 2023 13:20:24 +0100 Subject: [PATCH] BCF-2666 Add randomisation to testdb sequenced table columns (#11312) * Add randomisation to testdb sequenced table columns * Fix wrong import in pgtest.go * Move testdb sequence randomisation to shell testdb prepare * Use crypto random instead of math rand in test db alter sequences * Satisfy linter * Fix randomiseTestDBSequences err messages * Change randomiseTestDBSequences err msg * Add Error suffix to failedToRandomiseTestDBSequences custom err * Exclude unrelated tables from test db seq randomisation just in case * Rename randomiseTestDBSequences to randomizeTestDBSequences --- core/cmd/shell_local.go | 52 ++++++++++++++++++++++-- core/internal/testutils/pgtest/pgtest.go | 1 - 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index cbd0feccbf1..ada70736f49 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -2,6 +2,7 @@ package cmd import ( "context" + crand "crypto/rand" "database/sql" "fmt" "log" @@ -778,11 +779,13 @@ func (s *Shell) PrepareTestDatabase(c *cli.Context) error { if userOnly { fixturePath = "../store/fixtures/users_only_fixture.sql" } - if err := insertFixtures(dbUrl, fixturePath); err != nil { + if err = insertFixtures(dbUrl, fixturePath); err != nil { return s.errorOut(err) } - - return s.errorOut(dropDanglingTestDBs(s.Logger, db)) + if err = dropDanglingTestDBs(s.Logger, db); err != nil { + return s.errorOut(err) + } + return s.errorOut(randomizeTestDBSequences(db)) } func dropDanglingTestDBs(lggr logger.Logger, db *sqlx.DB) (err error) { @@ -822,6 +825,49 @@ func dropDanglingTestDBs(lggr logger.Logger, db *sqlx.DB) (err error) { return } +type failedToRandomizeTestDBSequencesError struct{} + +func (m *failedToRandomizeTestDBSequencesError) Error() string { + return "failed to randomize test db sequences" +} + +// randomizeTestDBSequences randomizes sequenced table columns sequence +// This is necessary as to avoid false positives in some test cases. +func randomizeTestDBSequences(db *sqlx.DB) error { + seqRows, err := db.Query(`SELECT sequence_schema, sequence_name FROM information_schema.sequences WHERE sequence_schema = $1`, "public") + if err != nil { + return fmt.Errorf("%s: error fetching sequences: %s", failedToRandomizeTestDBSequencesError{}, err) + } + + defer seqRows.Close() + for seqRows.Next() { + var sequenceSchema, sequenceName string + if err = seqRows.Scan(&sequenceSchema, &sequenceName); err != nil { + return fmt.Errorf("%s: failed scanning sequence rows: %s", failedToRandomizeTestDBSequencesError{}, err) + } + + if sequenceName == "goose_migrations_id_seq" || sequenceName == "configurations_id_seq" { + continue + } + + var randNum *big.Int + randNum, err = crand.Int(crand.Reader, utils.NewBigI(10000).ToInt()) + if err != nil { + return fmt.Errorf("%s: failed to generate random number", failedToRandomizeTestDBSequencesError{}) + } + + if _, err = db.Exec(fmt.Sprintf("ALTER SEQUENCE %s.%s RESTART WITH %d", sequenceSchema, sequenceName, randNum)); err != nil { + return fmt.Errorf("%s: failed to alter and restart %s sequence: %w", failedToRandomizeTestDBSequencesError{}, sequenceName, err) + } + } + + if err = seqRows.Err(); err != nil { + return fmt.Errorf("%s: failed to iterate through sequences: %w", failedToRandomizeTestDBSequencesError{}, err) + } + + return nil +} + // PrepareTestDatabaseUserOnly calls ResetDatabase then loads only user fixtures required for local // testing against testnets. Does not include fake chain fixtures. func (s *Shell) PrepareTestDatabaseUserOnly(c *cli.Context) error { diff --git a/core/internal/testutils/pgtest/pgtest.go b/core/internal/testutils/pgtest/pgtest.go index 1900fcc62b3..01024b39c37 100644 --- a/core/internal/testutils/pgtest/pgtest.go +++ b/core/internal/testutils/pgtest/pgtest.go @@ -34,7 +34,6 @@ func NewSqlxDB(t testing.TB) *sqlx.DB { db, err := sqlx.Open(string(dialects.TransactionWrappedPostgres), uuid.New().String()) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, db.Close()) }) - db.MapperFunc(reflectx.CamelToSnakeASCII) return db