Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve constraint assertions #240

Merged
merged 4 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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