Skip to content

Commit

Permalink
Add and use a table cleanup assertion function (#493)
Browse files Browse the repository at this point in the history
Reduce repetition in test code by extracting the common assertions for
ensuring a table is cleaned up after rollback (temporary columns,
triggers and trigger functions all removed) into a
`TableMustBeCleanedUp` assertion.
  • Loading branch information
andrew-farries authored Nov 26, 2024
1 parent 1279b7d commit 86e02c5
Show file tree
Hide file tree
Showing 10 changed files with 80 additions and 261 deletions.
18 changes: 4 additions & 14 deletions pkg/migrations/op_change_type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,24 +101,14 @@ func TestChangeColumnType(t *testing.T) {
}, rows)
},
afterRollback: func(t *testing.T, db *sql.DB, schema string) {
// The new (temporary) `rating` column should not exist on the underlying table.
ColumnMustNotExist(t, db, schema, "reviews", migrations.TemporaryName("rating"))

// The up function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("reviews", "rating"))
// The down function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("reviews", migrations.TemporaryName("rating")))

// The up trigger no longer exists.
TriggerMustNotExist(t, db, schema, "reviews", migrations.TriggerName("reviews", "rating"))
// The down trigger no longer exists.
TriggerMustNotExist(t, db, schema, "reviews", migrations.TriggerName("reviews", migrations.TemporaryName("rating")))
// The table is cleaned up; temporary columns, trigger functions and triggers no longer exist.
TableMustBeCleanedUp(t, db, schema, "reviews", "rating")
},
afterComplete: func(t *testing.T, db *sql.DB, schema string) {
newVersionSchema := roll.VersionedSchemaName(schema, "02_change_type")

// The new (temporary) `rating` column should not exist on the underlying table.
ColumnMustNotExist(t, db, schema, "reviews", migrations.TemporaryName("rating"))
// The table is cleaned up; temporary columns, trigger functions and triggers no longer exist.
TableMustBeCleanedUp(t, db, schema, "reviews", "rating")

// The `rating` column in the new view must have the correct type.
ColumnMustHaveType(t, db, newVersionSchema, "reviews", "rating", "integer")
Expand Down
26 changes: 26 additions & 0 deletions pkg/migrations/op_common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,32 @@ func MustSelect(t *testing.T, db *sql.DB, schema, version, table string) []map[s
return res
}

// TableMustBeCleanedUp asserts that the `columns` on `table` in `schema` have
// been cleaned up after migration rollback or completion. This means:
// - The temporary columns should not exist on the underlying table.
// - The up functions for the columns no longer exist.
// - The down functions for the columns no longer exist.
// - The up triggers for the columns no longer exist.
// - The down triggers for the columns no longer exist.
func TableMustBeCleanedUp(t *testing.T, db *sql.DB, schema, table string, columns ...string) {
t.Helper()

for _, column := range columns {
// The temporary column should not exist on the underlying table.
ColumnMustNotExist(t, db, schema, table, migrations.TemporaryName(column))

// The up function for the column no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName(table, column))
// The down function for the column no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName(table, migrations.TemporaryName(column)))

// The up trigger for the column no longer exists.
TriggerMustNotExist(t, db, schema, table, migrations.TriggerName(table, column))
// The down trigger for the column no longer exists.
TriggerMustNotExist(t, db, schema, table, migrations.TriggerName(table, migrations.TemporaryName(column)))
}
}

func mustSetSearchPath(t *testing.T, db *sql.DB, schema string) {
t.Helper()

Expand Down
43 changes: 14 additions & 29 deletions pkg/migrations/op_create_constraint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ func TestCreateConstraint(t *testing.T) {
IndexMustNotExist(t, db, schema, "users", "uniue_name")

// Functions, triggers and temporary columns are dropped.
tableCleanedUp(t, db, schema, "users", "name")
TableMustBeCleanedUp(t, db, schema, "users", "name")
},
afterComplete: func(t *testing.T, db *sql.DB, schema string) {
// Functions, triggers and temporary columns are dropped.
tableCleanedUp(t, db, schema, "users", "name")
TableMustBeCleanedUp(t, db, schema, "users", "name")

// Inserting values into the new schema that violate uniqueness should fail.
MustInsert(t, db, schema, "02_create_constraint", "users", map[string]string{
Expand Down Expand Up @@ -161,11 +161,11 @@ func TestCreateConstraint(t *testing.T) {
},
afterRollback: func(t *testing.T, db *sql.DB, schema string) {
// Functions, triggers and temporary columns are dropped.
tableCleanedUp(t, db, schema, "users", "name")
TableMustBeCleanedUp(t, db, schema, "users", "name")
},
afterComplete: func(t *testing.T, db *sql.DB, schema string) {
// Functions, triggers and temporary columns are dropped.
tableCleanedUp(t, db, schema, "users", "name")
TableMustBeCleanedUp(t, db, schema, "users", "name")

// Inserting values into the new schema that violate the check constraint should fail.
MustNotInsert(t, db, schema, "02_create_constraint", "users", map[string]string{
Expand Down Expand Up @@ -250,8 +250,8 @@ func TestCreateConstraint(t *testing.T) {
IndexMustNotExist(t, db, schema, "users", "unique_name_email")

// Functions, triggers and temporary columns are dropped.
tableCleanedUp(t, db, schema, "users", "name")
tableCleanedUp(t, db, schema, "users", "email")
TableMustBeCleanedUp(t, db, schema, "users", "name")
TableMustBeCleanedUp(t, db, schema, "users", "email")
},
afterComplete: func(t *testing.T, db *sql.DB, schema string) {
// Complete is a no-op.
Expand Down Expand Up @@ -334,13 +334,13 @@ func TestCreateConstraint(t *testing.T) {
// The check constraint must not exists on the table.
CheckConstraintMustNotExist(t, db, schema, "users", "check_name_email")
// Functions, triggers and temporary columns are dropped.
tableCleanedUp(t, db, schema, "users", "name")
tableCleanedUp(t, db, schema, "users", "email")
TableMustBeCleanedUp(t, db, schema, "users", "name")
TableMustBeCleanedUp(t, db, schema, "users", "email")
},
afterComplete: func(t *testing.T, db *sql.DB, schema string) {
// Functions, triggers and temporary columns are dropped.
tableCleanedUp(t, db, schema, "users", "name")
tableCleanedUp(t, db, schema, "users", "email")
TableMustBeCleanedUp(t, db, schema, "users", "name")
TableMustBeCleanedUp(t, db, schema, "users", "email")

// Inserting values into the new schema that the violate the check constraint must fail.
MustNotInsert(t, db, schema, "02_create_constraint", "users", map[string]string{
Expand Down Expand Up @@ -474,13 +474,13 @@ func TestCreateConstraint(t *testing.T) {
// The check constraint must not exists on the table.
CheckConstraintMustNotExist(t, db, schema, "reports", "fk_users")
// Functions, triggers and temporary columns are dropped.
tableCleanedUp(t, db, schema, "reports", "users_id")
tableCleanedUp(t, db, schema, "reports", "users_zip")
TableMustBeCleanedUp(t, db, schema, "reports", "users_id")
TableMustBeCleanedUp(t, db, schema, "reports", "users_zip")
},
afterComplete: func(t *testing.T, db *sql.DB, schema string) {
// Functions, triggers and temporary columns are dropped.
tableCleanedUp(t, db, schema, "reports", "users_id")
tableCleanedUp(t, db, schema, "reports", "users_zip")
TableMustBeCleanedUp(t, db, schema, "reports", "users_id")
TableMustBeCleanedUp(t, db, schema, "reports", "users_zip")

// Inserting values into the new schema that the violate the check constraint must fail.
MustNotInsert(t, db, schema, "02_create_constraint", "reports", map[string]string{
Expand Down Expand Up @@ -683,18 +683,3 @@ func TestCreateConstraint(t *testing.T) {
},
})
}

func tableCleanedUp(t *testing.T, db *sql.DB, schema, table, column string) {
// The new, temporary column should not exist on the underlying table.
ColumnMustNotExist(t, db, schema, table, migrations.TemporaryName(column))

// The up function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName(table, column))
// The down function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName(table, migrations.TemporaryName(column)))

// The up trigger no longer exists.
TriggerMustNotExist(t, db, schema, table, migrations.TriggerName(table, column))
// The down trigger no longer exists.
TriggerMustNotExist(t, db, schema, table, migrations.TriggerName(table, migrations.TemporaryName(column)))
}
68 changes: 12 additions & 56 deletions pkg/migrations/op_drop_constraint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,8 @@ func TestDropConstraint(t *testing.T) {
}, rows)
},
afterRollback: func(t *testing.T, db *sql.DB, schema string) {
// The new (temporary) `title` column should not exist on the underlying table.
ColumnMustNotExist(t, db, schema, "posts", migrations.TemporaryName("title"))

// The up function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("posts", "title"))
// The down function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("posts", migrations.TemporaryName("title")))

// The up trigger no longer exists.
TriggerMustNotExist(t, db, schema, "posts", migrations.TriggerName("posts", "title"))
// The down trigger no longer exists.
TriggerMustNotExist(t, db, schema, "posts", migrations.TriggerName("posts", migrations.TemporaryName("title")))
// The table is cleaned up; temporary columns, trigger functions and triggers no longer exist.
TableMustBeCleanedUp(t, db, schema, "posts", "title")
},
afterComplete: func(t *testing.T, db *sql.DB, schema string) {
// Inserting a row that does not meet the check constraint into the new view works.
Expand All @@ -134,15 +124,8 @@ func TestDropConstraint(t *testing.T) {
{"id": 5, "title": "e"},
}, rows)

// The up function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("posts", "title"))
// The down function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("posts", migrations.TemporaryName("title")))

// The up trigger no longer exists.
TriggerMustNotExist(t, db, schema, "posts", migrations.TriggerName("posts", "title"))
// The down trigger no longer exists.
TriggerMustNotExist(t, db, schema, "posts", migrations.TriggerName("posts", migrations.TemporaryName("title")))
// The table is cleaned up; temporary columns, trigger functions and triggers no longer exist.
TableMustBeCleanedUp(t, db, schema, "posts", "title")
},
},
{
Expand Down Expand Up @@ -332,18 +315,8 @@ func TestDropConstraint(t *testing.T) {
}, rows)
},
afterRollback: func(t *testing.T, db *sql.DB, schema string) {
// The new (temporary) `user_id` column should not exist on the underlying table.
ColumnMustNotExist(t, db, schema, "posts", migrations.TemporaryName("user_id"))

// The up function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("posts", "user_id"))
// The down function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("posts", migrations.TemporaryName("user_id")))

// The up trigger no longer exists.
TriggerMustNotExist(t, db, schema, "posts", migrations.TriggerName("posts", "user_id"))
// The down trigger no longer exists.
TriggerMustNotExist(t, db, schema, "posts", migrations.TriggerName("posts", migrations.TemporaryName("user_id")))
// The table is cleaned up; temporary columns, trigger functions and triggers no longer exist.
TableMustBeCleanedUp(t, db, schema, "posts", "user_id")
},
afterComplete: func(t *testing.T, db *sql.DB, schema string) {
// The new (temporary) `user_id` column should not exist on the underlying table.
Expand All @@ -364,15 +337,8 @@ func TestDropConstraint(t *testing.T) {
{"id": 5, "title": "another post by an unknown user", "user_id": 4},
}, rows)

// The up function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("posts", "user_id"))
// The down function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("posts", migrations.TemporaryName("user_id")))

// The up trigger no longer exists.
TriggerMustNotExist(t, db, schema, "posts", migrations.TriggerName("posts", "user_id"))
// The down trigger no longer exists.
TriggerMustNotExist(t, db, schema, "posts", migrations.TriggerName("posts", migrations.TemporaryName("user_id")))
// The table is cleaned up; temporary columns, trigger functions and triggers no longer exist.
TableMustBeCleanedUp(t, db, schema, "posts", "user_id")
},
},
{
Expand Down Expand Up @@ -430,22 +396,12 @@ func TestDropConstraint(t *testing.T) {
})
},
afterRollback: func(t *testing.T, db *sql.DB, schema string) {
// The new (temporary) `name` column should not exist on the underlying table.
ColumnMustNotExist(t, db, schema, "users", migrations.TemporaryName("name"))

// The up function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("users", "name"))
// The down function no longer exists.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("users", migrations.TemporaryName("name")))

// The up trigger no longer exists.
TriggerMustNotExist(t, db, schema, "users", migrations.TriggerName("users", "name"))
// The down trigger no longer exists.
TriggerMustNotExist(t, db, schema, "users", migrations.TriggerName("users", migrations.TemporaryName("name")))
// The table is cleaned up; temporary columns, trigger functions and triggers no longer exist.
TableMustBeCleanedUp(t, db, schema, "users", "name")
},
afterComplete: func(t *testing.T, db *sql.DB, schema string) {
// The new (temporary) `name` column should not exist on the underlying table.
ColumnMustNotExist(t, db, schema, "users", migrations.TemporaryName("name"))
// The table is cleaned up; temporary columns, trigger functions and triggers no longer exist.
TableMustBeCleanedUp(t, db, schema, "users", "name")

// Inserting a row that does not meet the unique constraint into the new view works.
MustInsert(t, db, schema, "02_drop_unique_constraint", "users", map[string]string{
Expand Down
63 changes: 6 additions & 57 deletions pkg/migrations/op_drop_multicolumn_constraint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,25 +124,8 @@ func TestDropMultiColumnConstraint(t *testing.T) {
}, rows)
},
afterRollback: func(t *testing.T, db *sql.DB, schema string) {
// The temporary columns no longer exist.
ColumnMustNotExist(t, db, schema, "products", migrations.TemporaryName("price"))
ColumnMustNotExist(t, db, schema, "products", migrations.TemporaryName("discount"))

// The up functions no longer exist.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("products", "price"))
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("products", "discount"))

// The down functions no longer exist.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("products", migrations.TemporaryName("price")))
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("products", migrations.TemporaryName("discount")))

// The up triggers no longer exist.
TriggerMustNotExist(t, db, schema, "products", migrations.TriggerName("products", "price"))
TriggerMustNotExist(t, db, schema, "products", migrations.TriggerName("products", "discount"))

// The down triggers no longer exist.
TriggerMustNotExist(t, db, schema, "products", migrations.TriggerName("products", migrations.TemporaryName("price")))
TriggerMustNotExist(t, db, schema, "products", migrations.TriggerName("products", migrations.TemporaryName("discount")))
// The table is cleaned up; temporary columns, trigger functions and triggers no longer exist.
TableMustBeCleanedUp(t, db, schema, "products", "price", "discount")
},
afterComplete: func(t *testing.T, db *sql.DB, schema string) {
// Inserting a row into the new schema that violates the check constraint succeeds.
Expand Down Expand Up @@ -257,25 +240,8 @@ func TestDropMultiColumnConstraint(t *testing.T) {
}, rows)
},
afterRollback: func(t *testing.T, db *sql.DB, schema string) {
// The temporary columns no longer exist.
ColumnMustNotExist(t, db, schema, "products", migrations.TemporaryName("name"))
ColumnMustNotExist(t, db, schema, "products", migrations.TemporaryName("description"))

// The up functions no longer exist.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("products", "name"))
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("products", "description"))

// The down functions no longer exist.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("products", migrations.TemporaryName("name")))
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("products", migrations.TemporaryName("description")))

// The up triggers no longer exist.
TriggerMustNotExist(t, db, schema, "products", migrations.TriggerName("products", "name"))
TriggerMustNotExist(t, db, schema, "products", migrations.TriggerName("products", "description"))

// The down triggers no longer exist.
TriggerMustNotExist(t, db, schema, "products", migrations.TriggerName("products", migrations.TemporaryName("name")))
TriggerMustNotExist(t, db, schema, "products", migrations.TriggerName("products", migrations.TemporaryName("description")))
// The table is cleaned up; temporary columns, trigger functions and triggers no longer exist.
TableMustBeCleanedUp(t, db, schema, "products", "name", "description")
},
afterComplete: func(t *testing.T, db *sql.DB, schema string) {
// Inserting a row into the new schema that violates the unique constraint succeeds.
Expand Down Expand Up @@ -423,25 +389,8 @@ func TestDropMultiColumnConstraint(t *testing.T) {
}, rows)
},
afterRollback: func(t *testing.T, db *sql.DB, schema string) {
// The temporary columns no longer exist.
ColumnMustNotExist(t, db, schema, "reports", migrations.TemporaryName("user_id"))
ColumnMustNotExist(t, db, schema, "reports", migrations.TemporaryName("user_zip"))

// The up functions no longer exist.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("reports", "user_id"))
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("reports", "user_zip"))

// The down functions no longer exist.
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("reports", migrations.TemporaryName("user_id")))
FunctionMustNotExist(t, db, schema, migrations.TriggerFunctionName("reports", migrations.TemporaryName("user_zip")))

// The up triggers no longer exist.
TriggerMustNotExist(t, db, schema, "reports", migrations.TriggerName("reports", "user_id"))
TriggerMustNotExist(t, db, schema, "reports", migrations.TriggerName("reports", "user_zip"))

// The down triggers no longer exist.
TriggerMustNotExist(t, db, schema, "reports", migrations.TriggerName("reports", migrations.TemporaryName("user_id")))
TriggerMustNotExist(t, db, schema, "reports", migrations.TriggerName("reports", migrations.TemporaryName("user_zip")))
// The table is cleaned up; temporary columns, trigger functions and triggers no longer exist.
TableMustBeCleanedUp(t, db, schema, "reports", "user_id", "user_zip")
},
afterComplete: func(t *testing.T, db *sql.DB, schema string) {
// Inserting a row that violates the FK constraint into the new schema succeeds.
Expand Down
Loading

0 comments on commit 86e02c5

Please sign in to comment.