Skip to content

Commit

Permalink
Improve constraint assertions (#240)
Browse files Browse the repository at this point in the history
Add more precise test assertions for constraints and foreign key
existence.

Previously there was one `ConstraintMustExist` assertion that served for
all kinds constraints. This PR adds:

* `CheckConstraintMustExist`
* `ValidatedForeignKeyMustExist`
* `NotValidatedForeignKeyMustExist`

and updates tests to use each, as appropriate.
  • Loading branch information
andrew-farries committed Jan 17, 2024
1 parent 4928cf1 commit 7b5fafd
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 25 deletions.
12 changes: 6 additions & 6 deletions pkg/migrations/op_add_column_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func TestAddForeignKeyColumn(t *testing.T) {
},
afterStart: func(t *testing.T, db *sql.DB) {
// The foreign key constraint exists on the new table.
ConstraintMustExist(t, db, "public", "orders", "fk_users_id")
ValidatedForeignKeyMustExist(t, db, "public", "orders", "fk_users_id")

// Inserting a row into the referenced table succeeds.
MustInsert(t, db, "public", "01_create_table", "users", map[string]string{
Expand All @@ -194,7 +194,7 @@ func TestAddForeignKeyColumn(t *testing.T) {
},
afterComplete: func(t *testing.T, db *sql.DB) {
// The foreign key constraint still exists on the new table
ConstraintMustExist(t, db, "public", "orders", "fk_users_id")
ValidatedForeignKeyMustExist(t, db, "public", "orders", "fk_users_id")

// Inserting a row into the referenced table succeeds.
MustInsert(t, db, "public", "02_add_column", "users", map[string]string{
Expand Down Expand Up @@ -273,7 +273,7 @@ func TestAddForeignKeyColumn(t *testing.T) {
},
afterStart: func(t *testing.T, db *sql.DB) {
// The foreign key constraint exists on the new table.
ConstraintMustExist(t, db, "public", "orders", "fk_users_id")
ValidatedForeignKeyMustExist(t, db, "public", "orders", "fk_users_id")

// Inserting a row into the referenced table succeeds.
MustInsert(t, db, "public", "01_create_table", "users", map[string]string{
Expand All @@ -297,7 +297,7 @@ func TestAddForeignKeyColumn(t *testing.T) {
},
afterComplete: func(t *testing.T, db *sql.DB) {
// The foreign key constraint still exists on the new table
ConstraintMustExist(t, db, "public", "orders", "fk_users_id")
ValidatedForeignKeyMustExist(t, db, "public", "orders", "fk_users_id")

// Inserting a row into the referenced table succeeds.
MustInsert(t, db, "public", "02_add_column", "users", map[string]string{
Expand Down Expand Up @@ -547,12 +547,12 @@ func TestAddNotNullColumnWithNoDefault(t *testing.T) {
afterRollback: func(t *testing.T, db *sql.DB) {
// the check constraint has been dropped.
constraintName := migrations.NotNullConstraintName("description")
ConstraintMustNotExist(t, db, "public", "products", constraintName)
CheckConstraintMustNotExist(t, db, "public", "products", constraintName)
},
afterComplete: func(t *testing.T, db *sql.DB) {
// the check constraint has been dropped.
constraintName := migrations.NotNullConstraintName("description")
ConstraintMustNotExist(t, db, "public", "products", constraintName)
CheckConstraintMustNotExist(t, db, "public", "products", constraintName)

// can't insert a null description into the new view; the column now has a NOT NULL constraint.
MustNotInsert(t, db, "public", "02_add_column", "products", map[string]string{
Expand Down
4 changes: 2 additions & 2 deletions pkg/migrations/op_change_type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,13 +213,13 @@ func TestChangeColumnType(t *testing.T) {
},
afterStart: func(t *testing.T, db *sql.DB) {
// A temporary FK constraint has been created on the temporary column
ConstraintMustExist(t, db, "public", "employees", migrations.TemporaryName("fk_employee_department"))
ValidatedForeignKeyMustExist(t, db, "public", "employees", migrations.TemporaryName("fk_employee_department"))
},
afterRollback: func(t *testing.T, db *sql.DB) {
},
afterComplete: func(t *testing.T, db *sql.DB) {
// The foreign key constraint still exists on the column
ConstraintMustExist(t, db, "public", "employees", "fk_employee_department")
ValidatedForeignKeyMustExist(t, db, "public", "employees", "fk_employee_department")
},
},
{
Expand Down
46 changes: 41 additions & 5 deletions pkg/migrations/op_common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,20 +195,34 @@ func TriggerMustExist(t *testing.T, db *sql.DB, schema, table, trigger string) {
}
}

func ConstraintMustNotExist(t *testing.T, db *sql.DB, schema, table, constraint string) {
func CheckConstraintMustNotExist(t *testing.T, db *sql.DB, schema, table, constraint string) {
t.Helper()
if constraintExists(t, db, schema, table, constraint) {
if checkConstraintExists(t, db, schema, table, constraint) {
t.Fatalf("Expected constraint %q to not exist", constraint)
}
}

func ConstraintMustExist(t *testing.T, db *sql.DB, schema, table, constraint string) {
func CheckConstraintMustExist(t *testing.T, db *sql.DB, schema, table, constraint string) {
t.Helper()
if !constraintExists(t, db, schema, table, constraint) {
if !checkConstraintExists(t, db, schema, table, constraint) {
t.Fatalf("Expected constraint %q to exist", constraint)
}
}

func ValidatedForeignKeyMustExist(t *testing.T, db *sql.DB, schema, table, constraint string) {
t.Helper()
if !foreignKeyExists(t, db, schema, table, constraint, true) {
t.Fatalf("Expected validated foreign key %q to exist", constraint)
}
}

func NotValidatedForeignKeyMustExist(t *testing.T, db *sql.DB, schema, table, constraint string) {
t.Helper()
if !foreignKeyExists(t, db, schema, table, constraint, false) {
t.Fatalf("Expected not validated foreign key %q to exist", constraint)
}
}

func IndexMustExist(t *testing.T, db *sql.DB, schema, table, index string) {
t.Helper()
if !indexExists(t, db, schema, table, index) {
Expand Down Expand Up @@ -264,7 +278,7 @@ func indexExists(t *testing.T, db *sql.DB, schema, table, index string) bool {
return exists
}

func constraintExists(t *testing.T, db *sql.DB, schema, table, constraint string) bool {
func checkConstraintExists(t *testing.T, db *sql.DB, schema, table, constraint string) bool {
t.Helper()

var exists bool
Expand All @@ -274,6 +288,7 @@ func constraintExists(t *testing.T, db *sql.DB, schema, table, constraint string
FROM pg_catalog.pg_constraint
WHERE conrelid = $1::regclass
AND conname = $2
AND contype = 'c'
)`,
fmt.Sprintf("%s.%s", schema, table), constraint).Scan(&exists)
if err != nil {
Expand All @@ -283,6 +298,27 @@ func constraintExists(t *testing.T, db *sql.DB, schema, table, constraint string
return exists
}

func foreignKeyExists(t *testing.T, db *sql.DB, schema, table, constraint string, validated bool) bool {
t.Helper()

var exists bool
err := db.QueryRow(`
SELECT EXISTS (
SELECT 1
FROM pg_catalog.pg_constraint
WHERE conrelid = $1::regclass
AND conname = $2
AND contype = 'f'
AND convalidated = $3
)`,
fmt.Sprintf("%s.%s", schema, table), constraint, validated).Scan(&exists)
if err != nil {
t.Fatal(err)
}

return exists
}

func triggerExists(t *testing.T, db *sql.DB, schema, table, trigger string) bool {
t.Helper()

Expand Down
8 changes: 4 additions & 4 deletions pkg/migrations/op_create_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func TestCreateTable(t *testing.T) {
},
afterStart: func(t *testing.T, db *sql.DB) {
// The foreign key constraint exists on the new table.
ConstraintMustExist(t, db, "public", migrations.TemporaryName("orders"), "fk_users_id")
ValidatedForeignKeyMustExist(t, db, "public", migrations.TemporaryName("orders"), "fk_users_id")

// Inserting a row into the referenced table succeeds.
MustInsert(t, db, "public", "01_create_table", "users", map[string]string{
Expand All @@ -151,7 +151,7 @@ func TestCreateTable(t *testing.T) {
// The table has been dropped, so the foreign key constraint is gone.
},
afterComplete: func(t *testing.T, db *sql.DB) {
ConstraintMustExist(t, db, "public", "orders", "fk_users_id")
ValidatedForeignKeyMustExist(t, db, "public", "orders", "fk_users_id")

// Inserting a row into the referenced table succeeds.
MustInsert(t, db, "public", "02_create_table_with_fk", "users", map[string]string{
Expand Down Expand Up @@ -200,7 +200,7 @@ func TestCreateTable(t *testing.T) {
},
afterStart: func(t *testing.T, db *sql.DB) {
// The check constraint exists on the new table.
ConstraintMustExist(t, db, "public", migrations.TemporaryName("users"), "check_name_length")
CheckConstraintMustExist(t, db, "public", migrations.TemporaryName("users"), "check_name_length")

// Inserting a row into the table succeeds when the check constraint is satisfied.
MustInsert(t, db, "public", "01_create_table", "users", map[string]string{
Expand All @@ -217,7 +217,7 @@ func TestCreateTable(t *testing.T) {
},
afterComplete: func(t *testing.T, db *sql.DB) {
// The check constraint exists on the new table.
ConstraintMustExist(t, db, "public", "users", "check_name_length")
CheckConstraintMustExist(t, db, "public", "users", "check_name_length")

// Inserting a row into the table succeeds when the check constraint is satisfied.
MustInsert(t, db, "public", "01_create_table", "users", map[string]string{
Expand Down
13 changes: 11 additions & 2 deletions pkg/migrations/op_set_check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ func TestSetCheckConstraint(t *testing.T) {
// The new (temporary) `title` column should exist on the underlying table.
ColumnMustExist(t, db, "public", "posts", migrations.TemporaryName("title"))

// A check constraint has been added to the temporary column
CheckConstraintMustExist(t, db, "public", "posts", "check_title_length")

// Inserting a row that meets the check constraint into the old view works.
MustInsert(t, db, "public", "01_add_table", "posts", map[string]string{
"title": "post by alice",
Expand Down Expand Up @@ -96,6 +99,9 @@ func TestSetCheckConstraint(t *testing.T) {
// The new (temporary) `title` column should not exist on the underlying table.
ColumnMustNotExist(t, db, "public", "posts", migrations.TemporaryName("title"))

// The check constraint no longer exists.
CheckConstraintMustNotExist(t, db, "public", "posts", "check_title_length")

// The up function no longer exists.
FunctionMustNotExist(t, db, "public", migrations.TriggerFunctionName("posts", "title"))
// The down function no longer exists.
Expand All @@ -107,6 +113,9 @@ func TestSetCheckConstraint(t *testing.T) {
TriggerMustNotExist(t, db, "public", "posts", migrations.TriggerName("posts", migrations.TemporaryName("title")))
},
afterComplete: func(t *testing.T, db *sql.DB) {
// The check constraint exists on the new table.
CheckConstraintMustExist(t, db, "public", "posts", "check_title_length")

// Inserting a row that meets the check constraint into the new view works.
MustInsert(t, db, "public", "02_add_check_constraint", "posts", map[string]string{
"title": "post by dana",
Expand Down Expand Up @@ -275,13 +284,13 @@ func TestSetCheckConstraint(t *testing.T) {
},
afterStart: func(t *testing.T, db *sql.DB) {
// A temporary FK constraint has been created on the temporary column
ConstraintMustExist(t, db, "public", "employees", migrations.TemporaryName("fk_employee_department"))
ValidatedForeignKeyMustExist(t, db, "public", "employees", migrations.TemporaryName("fk_employee_department"))
},
afterRollback: func(t *testing.T, db *sql.DB) {
},
afterComplete: func(t *testing.T, db *sql.DB) {
// The foreign key constraint still exists on the column
ConstraintMustExist(t, db, "public", "employees", "fk_employee_department")
ValidatedForeignKeyMustExist(t, db, "public", "employees", "fk_employee_department")
},
},
})
Expand Down
10 changes: 8 additions & 2 deletions pkg/migrations/op_set_fk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ func TestSetForeignKey(t *testing.T) {
// The new (temporary) `user_id` column should exist on the underlying table.
ColumnMustExist(t, db, "public", "posts", migrations.TemporaryName("user_id"))

// A temporary FK constraint has been created on the temporary column
NotValidatedForeignKeyMustExist(t, db, "public", "posts", migrations.TemporaryName("fk_users_id"))

// Inserting some data into the `users` table works.
MustInsert(t, db, "public", "02_add_fk_constraint", "users", map[string]string{
"name": "alice",
Expand Down Expand Up @@ -140,6 +143,9 @@ func TestSetForeignKey(t *testing.T) {
// The new (temporary) `user_id` column should not exist on the underlying table.
ColumnMustNotExist(t, db, "public", "posts", migrations.TemporaryName("user_id"))

// A validated foreign key constraint exists on the underlying table.
ValidatedForeignKeyMustExist(t, db, "public", "posts", "fk_users_id")

// Inserting data into the new `posts` view with a valid user reference works.
MustInsert(t, db, "public", "02_add_fk_constraint", "posts", map[string]string{
"title": "another post by alice",
Expand Down Expand Up @@ -341,13 +347,13 @@ func TestSetForeignKey(t *testing.T) {
},
afterStart: func(t *testing.T, db *sql.DB) {
// A temporary FK constraint has been created on the temporary column
ConstraintMustExist(t, db, "public", "posts", migrations.TemporaryName("fk_users_id_1"))
ValidatedForeignKeyMustExist(t, db, "public", "posts", migrations.TemporaryName("fk_users_id_1"))
},
afterRollback: func(t *testing.T, db *sql.DB) {
},
afterComplete: func(t *testing.T, db *sql.DB) {
// The foreign key constraint still exists on the column
ConstraintMustExist(t, db, "public", "posts", "fk_users_id_1")
ValidatedForeignKeyMustExist(t, db, "public", "posts", "fk_users_id_1")
},
},
})
Expand Down
4 changes: 2 additions & 2 deletions pkg/migrations/op_set_notnull_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,13 +290,13 @@ func TestSetNotNull(t *testing.T) {
},
afterStart: func(t *testing.T, db *sql.DB) {
// A temporary FK constraint has been created on the temporary column
ConstraintMustExist(t, db, "public", "employees", migrations.TemporaryName("fk_employee_department"))
ValidatedForeignKeyMustExist(t, db, "public", "employees", migrations.TemporaryName("fk_employee_department"))
},
afterRollback: func(t *testing.T, db *sql.DB) {
},
afterComplete: func(t *testing.T, db *sql.DB) {
// The foreign key constraint still exists on the column
ConstraintMustExist(t, db, "public", "employees", "fk_employee_department")
ValidatedForeignKeyMustExist(t, db, "public", "employees", "fk_employee_department")
},
},
{
Expand Down
4 changes: 2 additions & 2 deletions pkg/migrations/op_set_unique_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,13 +329,13 @@ func TestSetColumnUnique(t *testing.T) {
},
afterStart: func(t *testing.T, db *sql.DB) {
// A temporary FK constraint has been created on the temporary column
ConstraintMustExist(t, db, "public", "employees", migrations.TemporaryName("fk_employee_department"))
ValidatedForeignKeyMustExist(t, db, "public", "employees", migrations.TemporaryName("fk_employee_department"))
},
afterRollback: func(t *testing.T, db *sql.DB) {
},
afterComplete: func(t *testing.T, db *sql.DB) {
// The foreign key constraint still exists on the column
ConstraintMustExist(t, db, "public", "employees", "fk_employee_department")
ValidatedForeignKeyMustExist(t, db, "public", "employees", "fk_employee_department")
},
},
})
Expand Down

0 comments on commit 7b5fafd

Please sign in to comment.