mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix(db): align desired schema bootstrap
Install pgcrypto before repository-owned desired-schema apply paths and retain the exact extension declaration in the desired schema. Distinguish the intermediate Invitation allocation from the final active object-kind catalog. Signed-off-by: Cea Stapleton Cordasco <261786559+cea@users.noreply.github.com>
This commit is contained in:
@@ -506,6 +506,9 @@ jobs:
|
||||
PGSCHEMA_PLAN_USER: buzz
|
||||
PGSCHEMA_PLAN_PASSWORD: buzz_dev
|
||||
run: |
|
||||
docker exec -e PGPASSWORD=buzz_dev buzz-postgres \
|
||||
psql -U buzz -d buzz -v ON_ERROR_STOP=1 \
|
||||
-c 'CREATE EXTENSION IF NOT EXISTS pgcrypto;'
|
||||
./bin/pgschema apply --file schema/schema.sql --auto-approve
|
||||
docker exec -i -e PGPASSWORD=buzz_dev buzz-postgres \
|
||||
psql -U buzz -d buzz -v ON_ERROR_STOP=1 < scripts/attach-schema-partitions.sql
|
||||
@@ -666,6 +669,9 @@ jobs:
|
||||
PGSCHEMA_PLAN_USER: buzz
|
||||
PGSCHEMA_PLAN_PASSWORD: buzz_dev
|
||||
run: |
|
||||
docker exec -e PGPASSWORD=buzz_dev buzz-postgres \
|
||||
psql -U buzz -d buzz -v ON_ERROR_STOP=1 \
|
||||
-c 'CREATE EXTENSION IF NOT EXISTS pgcrypto;'
|
||||
./bin/pgschema apply --file schema/schema.sql --auto-approve
|
||||
docker exec -i -e PGPASSWORD=buzz_dev buzz-postgres \
|
||||
psql -U buzz -d buzz -v ON_ERROR_STOP=1 < scripts/attach-schema-partitions.sql
|
||||
|
||||
@@ -5,8 +5,11 @@ use uuid::Uuid;
|
||||
|
||||
const INVITATION_OBJECT_MIGRATION: &str =
|
||||
include_str!("../../../../../migrations/0036_nip_fi_invitation_object_kind.sql");
|
||||
const ACTIVE_OBJECT_KINDS_MIGRATION: &str =
|
||||
include_str!("../../../../../migrations/0040_nip_fi_event_status_object_kinds.sql");
|
||||
const DESIRED_SCHEMA: &str = include_str!("../../../../../schema/schema.sql");
|
||||
const OBJECT_KIND_EXPRESSION: &str = "object_kind IN (1, 2, 3, 4, 5, 6, 9)";
|
||||
const INVITATION_OBJECT_KIND_EXPRESSION: &str = "object_kind IN (1, 2, 3, 4, 5, 6, 9)";
|
||||
const FINAL_OBJECT_KIND_EXPRESSION: &str = "object_kind IN (1, 2, 3, 4, 5, 6, 7, 8, 9)";
|
||||
const OBJECT_KIND_CHECKS: [(&str, &str); 3] = [
|
||||
(
|
||||
"authorization_admission_results",
|
||||
@@ -41,7 +44,12 @@ fn invitation_object_migration_and_desired_schema_are_exact() {
|
||||
assert_eq!(executable.matches("ADD CONSTRAINT").count(), 3);
|
||||
assert_eq!(executable.matches("NOT VALID").count(), 3);
|
||||
assert_eq!(executable.matches("VALIDATE CONSTRAINT").count(), 3);
|
||||
assert_eq!(executable.matches(OBJECT_KIND_EXPRESSION).count(), 3);
|
||||
assert_eq!(
|
||||
executable
|
||||
.matches(INVITATION_OBJECT_KIND_EXPRESSION)
|
||||
.count(),
|
||||
3
|
||||
);
|
||||
for forbidden in [
|
||||
"CREATE TABLE",
|
||||
"DROP TABLE",
|
||||
@@ -62,7 +70,7 @@ fn invitation_object_migration_and_desired_schema_are_exact() {
|
||||
for (table, constraint) in OBJECT_KIND_CHECKS {
|
||||
let drop = format!("ALTER TABLE {table}\n DROP CONSTRAINT {constraint};");
|
||||
let add = format!(
|
||||
"ALTER TABLE {table}\n ADD CONSTRAINT {constraint}\n CHECK ({OBJECT_KIND_EXPRESSION}) NOT VALID;"
|
||||
"ALTER TABLE {table}\n ADD CONSTRAINT {constraint}\n CHECK ({INVITATION_OBJECT_KIND_EXPRESSION}) NOT VALID;"
|
||||
);
|
||||
let validate = format!("ALTER TABLE {table}\n VALIDATE CONSTRAINT {constraint};");
|
||||
assert_eq!(executable.matches(&drop).count(), 1, "missing {drop}");
|
||||
@@ -73,7 +81,18 @@ fn invitation_object_migration_and_desired_schema_are_exact() {
|
||||
"missing {validate}"
|
||||
);
|
||||
|
||||
let desired = format!("CONSTRAINT {constraint} CHECK ({OBJECT_KIND_EXPRESSION})");
|
||||
let final_migration = format!(
|
||||
"ADD CONSTRAINT {constraint}\n CHECK ({FINAL_OBJECT_KIND_EXPRESSION}) NOT VALID;"
|
||||
);
|
||||
assert_eq!(
|
||||
ACTIVE_OBJECT_KINDS_MIGRATION
|
||||
.matches(&final_migration)
|
||||
.count(),
|
||||
1,
|
||||
"migration 0040 must allocate the final active kinds for {constraint}",
|
||||
);
|
||||
|
||||
let desired = format!("CONSTRAINT {constraint} CHECK ({FINAL_OBJECT_KIND_EXPRESSION})");
|
||||
assert_eq!(
|
||||
DESIRED_SCHEMA.matches(&desired).count(),
|
||||
1,
|
||||
@@ -83,7 +102,7 @@ fn invitation_object_migration_and_desired_schema_are_exact() {
|
||||
|
||||
for rejected in [7_i16, 8, 10] {
|
||||
assert!(
|
||||
!OBJECT_KIND_EXPRESSION
|
||||
!INVITATION_OBJECT_KIND_EXPRESSION
|
||||
.split(|character: char| !character.is_ascii_digit())
|
||||
.filter_map(|value| value.parse::<i16>().ok())
|
||||
.any(|value| value == rejected),
|
||||
@@ -92,7 +111,7 @@ fn invitation_object_migration_and_desired_schema_are_exact() {
|
||||
}
|
||||
}
|
||||
|
||||
async fn assert_exact_catalog(pool: &PgPool) {
|
||||
async fn assert_exact_catalog(pool: &PgPool, expected_codes: &[i16]) {
|
||||
let rows: Vec<(String, String, bool)> = sqlx::query_as(
|
||||
"SELECT c.conrelid::regclass::text,pg_get_constraintdef(c.oid,true),c.convalidated \
|
||||
FROM pg_constraint c \
|
||||
@@ -117,7 +136,7 @@ async fn assert_exact_catalog(pool: &PgPool) {
|
||||
.split(|character: char| !character.is_ascii_digit())
|
||||
.filter_map(|value| value.parse::<i16>().ok())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(codes, vec![1, 2, 3, 4, 5, 6, 9]);
|
||||
assert_eq!(codes, expected_codes);
|
||||
}
|
||||
|
||||
async fn create_probe_tables(connection: &mut PgConnection) {
|
||||
@@ -206,20 +225,25 @@ async fn probe_kind(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn assert_kind_behavior(connection: &mut PgConnection, invitation_is_allowed: bool) {
|
||||
async fn assert_kind_behavior(
|
||||
connection: &mut PgConnection,
|
||||
allowed_kinds: &[i16],
|
||||
rejected_kinds: &[i16],
|
||||
) {
|
||||
for table in [
|
||||
ProbeTable::AdmissionResults,
|
||||
ProbeTable::AuthorityEpochs,
|
||||
ProbeTable::ProtectedAuthority,
|
||||
] {
|
||||
let invitation = probe_kind(connection, table, 9).await;
|
||||
assert_eq!(
|
||||
invitation.is_ok(),
|
||||
invitation_is_allowed,
|
||||
"Invitation object kind has the wrong admission behavior: {invitation:?}",
|
||||
);
|
||||
for kind in [7, 8, 10] {
|
||||
let error = probe_kind(connection, table, kind)
|
||||
for kind in allowed_kinds {
|
||||
probe_kind(connection, table, *kind)
|
||||
.await
|
||||
.unwrap_or_else(|error| {
|
||||
panic!("active object kind {kind} was rejected: {error:?}")
|
||||
});
|
||||
}
|
||||
for kind in rejected_kinds {
|
||||
let error = probe_kind(connection, table, *kind)
|
||||
.await
|
||||
.expect_err("unallocated object kind must remain rejected");
|
||||
assert_eq!(
|
||||
@@ -247,7 +271,7 @@ async fn invitation_object_kind_is_exact_on_brownfield_and_fresh_catalogs() {
|
||||
.await
|
||||
.expect("acquire brownfield probe session");
|
||||
create_probe_tables(&mut connection).await;
|
||||
assert_kind_behavior(&mut connection, false).await;
|
||||
assert_kind_behavior(&mut connection, &[], &[7, 8, 9, 10]).await;
|
||||
drop_probe_tables(&mut connection).await;
|
||||
drop(connection);
|
||||
|
||||
@@ -256,13 +280,13 @@ async fn invitation_object_kind_is_exact_on_brownfield_and_fresh_catalogs() {
|
||||
.await
|
||||
.expect("upgrade brownfield catalog through 0036");
|
||||
assert_eq!(applied_versions(&pool).await.last().copied(), Some(36));
|
||||
assert_exact_catalog(&pool).await;
|
||||
assert_exact_catalog(&pool, &[1, 2, 3, 4, 5, 6, 9]).await;
|
||||
let mut connection = pool
|
||||
.acquire()
|
||||
.await
|
||||
.expect("acquire upgraded probe session");
|
||||
create_probe_tables(&mut connection).await;
|
||||
assert_kind_behavior(&mut connection, true).await;
|
||||
assert_kind_behavior(&mut connection, &[9], &[7, 8, 10]).await;
|
||||
drop_probe_tables(&mut connection).await;
|
||||
drop(connection);
|
||||
|
||||
@@ -270,5 +294,9 @@ async fn invitation_object_kind_is_exact_on_brownfield_and_fresh_catalogs() {
|
||||
run_migrations(&pool)
|
||||
.await
|
||||
.expect("apply Invitation object kind on a fresh database");
|
||||
assert_exact_catalog(&pool).await;
|
||||
assert_exact_catalog(&pool, &[1, 2, 3, 4, 5, 6, 7, 8, 9]).await;
|
||||
let mut connection = pool.acquire().await.expect("acquire final probe session");
|
||||
create_probe_tables(&mut connection).await;
|
||||
assert_kind_behavior(&mut connection, &[7, 8, 9], &[10]).await;
|
||||
drop_probe_tables(&mut connection).await;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
-- Dumped from database version PostgreSQL 17.10
|
||||
-- Dumped by pgschema version 1.7.4
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
|
||||
--
|
||||
-- Name: approval_status; Type: TYPE; Schema: -; Owner: -
|
||||
|
||||
@@ -97,6 +97,7 @@ psql_h -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
|
||||
export PGSCHEMA_PLAN_HOST=localhost PGSCHEMA_PLAN_PORT=${PG_PORT}
|
||||
export PGSCHEMA_PLAN_DB=buzz PGSCHEMA_PLAN_USER=buzz PGSCHEMA_PLAN_PASSWORD=buzz_dev
|
||||
export PGHOST=localhost PGPORT=${PG_PORT} PGUSER=buzz PGDATABASE=buzz
|
||||
psql_h -c 'CREATE EXTENSION IF NOT EXISTS pgcrypto;'
|
||||
./bin/pgschema apply --file schema/schema.sql --auto-approve
|
||||
psql_h < scripts/attach-schema-partitions.sql
|
||||
ok "Schema applied"
|
||||
|
||||
@@ -103,6 +103,9 @@ export PGSCHEMA_PLAN_DB=buzz
|
||||
export PGSCHEMA_PLAN_USER=buzz
|
||||
export PGSCHEMA_PLAN_PASSWORD=buzz_dev
|
||||
|
||||
docker exec -e PGPASSWORD="${PGPASSWORD}" buzz-postgres \
|
||||
psql -U "${PGUSER}" -d "${PGDATABASE}" -v ON_ERROR_STOP=1 \
|
||||
-c 'CREATE EXTENSION IF NOT EXISTS pgcrypto;'
|
||||
./bin/pgschema apply --file schema/schema.sql --auto-approve
|
||||
docker exec -i -e PGPASSWORD="${PGPASSWORD}" buzz-postgres \
|
||||
psql -U "${PGUSER}" -d "${PGDATABASE}" -v ON_ERROR_STOP=1 < scripts/attach-schema-partitions.sql
|
||||
|
||||
Reference in New Issue
Block a user