diff --git a/pkg/migrations/op_add_column_test.go b/pkg/migrations/op_add_column_test.go index b9b08544..dee111fe 100644 --- a/pkg/migrations/op_add_column_test.go +++ b/pkg/migrations/op_add_column_test.go @@ -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{ @@ -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{ @@ -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{ @@ -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{ @@ -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{ diff --git a/pkg/migrations/op_change_type_test.go b/pkg/migrations/op_change_type_test.go index cea81bbc..3b51aa3f 100644 --- a/pkg/migrations/op_change_type_test.go +++ b/pkg/migrations/op_change_type_test.go @@ -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") }, }, { diff --git a/pkg/migrations/op_common_test.go b/pkg/migrations/op_common_test.go index 20025a74..feb19321 100644 --- a/pkg/migrations/op_common_test.go +++ b/pkg/migrations/op_common_test.go @@ -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) { @@ -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 @@ -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 { @@ -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() diff --git a/pkg/migrations/op_create_table_test.go b/pkg/migrations/op_create_table_test.go index c336404e..cc60cf34 100644 --- a/pkg/migrations/op_create_table_test.go +++ b/pkg/migrations/op_create_table_test.go @@ -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{ @@ -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{ @@ -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{ @@ -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{ diff --git a/pkg/migrations/op_set_check_test.go b/pkg/migrations/op_set_check_test.go index 8baae581..ae5cec81 100644 --- a/pkg/migrations/op_set_check_test.go +++ b/pkg/migrations/op_set_check_test.go @@ -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", @@ -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. @@ -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", @@ -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") }, }, }) diff --git a/pkg/migrations/op_set_fk_test.go b/pkg/migrations/op_set_fk_test.go index 5a9603bf..5e466c57 100644 --- a/pkg/migrations/op_set_fk_test.go +++ b/pkg/migrations/op_set_fk_test.go @@ -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", @@ -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", @@ -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") }, }, }) diff --git a/pkg/migrations/op_set_notnull_test.go b/pkg/migrations/op_set_notnull_test.go index 0f37880f..f15d2c60 100644 --- a/pkg/migrations/op_set_notnull_test.go +++ b/pkg/migrations/op_set_notnull_test.go @@ -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") }, }, { diff --git a/pkg/migrations/op_set_unique_test.go b/pkg/migrations/op_set_unique_test.go index d660c0f1..ae63cc37 100644 --- a/pkg/migrations/op_set_unique_test.go +++ b/pkg/migrations/op_set_unique_test.go @@ -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") }, }, })