Skip to content
Open
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
64 changes: 63 additions & 1 deletion crates/buzz-db/src/runtime/migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,7 @@ mod postgres_tests {
let mut migrations: Vec<_> = MIGRATOR.iter().collect();
migrations.sort_by_key(|migration| migration.version);

assert_eq!(migrations.len(), 88);
assert_eq!(migrations.len(), 89);
assert_eq!(migrations[87].version, 88);
assert!(migrations[87]
.sql
Expand Down Expand Up @@ -992,6 +992,34 @@ mod postgres_tests {
.sql
.as_str()
.contains("CREATE FUNCTION profile_name_fields"));
// The app owns the NIP-RS watermark, retention-free purge and
// mention liveness, so 0089 drops the migration-only triggers that
// 0009, 0010, 0011 and 0019 installed. Those files stay frozen.
assert_eq!(migrations[88].version, 89);
let retire_nip_rs = strip_sql_comments(migrations[88].sql.as_str());
for statement in [
"SET LOCAL lock_timeout = '5s';",
"DROP TRIGGER trg_events_nip_rs_watermark ON events;",
"DROP TRIGGER trg_events_guard_nip_rs_hard_delete ON events;",
"DROP TRIGGER trg_events_purge_soft_deleted_nip_rs ON events;",
"DROP TRIGGER trg_events_purge_soft_deleted_buzz_mesh_status ON events;",
"DROP TRIGGER trg_event_mentions_require_live_event ON event_mentions;",
"DROP FUNCTION guard_nip_rs_watermark();",
"DROP FUNCTION guard_nip_rs_hard_delete();",
"DROP FUNCTION purge_soft_deleted_nip_rs();",
"DROP FUNCTION purge_soft_deleted_buzz_mesh_status();",
"DROP FUNCTION guard_event_mention_live();",
] {
assert!(
retire_nip_rs.contains(statement),
"0089 must run {statement}"
);
}
assert!(
retire_nip_rs.find("SET LOCAL lock_timeout").unwrap()
< retire_nip_rs.find("DROP TRIGGER").unwrap(),
"0089 must bound its ACCESS EXCLUSIVE lock on events before the first DROP"
);
assert_eq!(migrations[55].version, 56);
assert!(migrations[55]
.sql
Expand Down Expand Up @@ -2928,6 +2956,10 @@ mod postgres_tests {
/// drift this test guards against. `indoption` (not just `indexdef` text) is
/// asserted so a resurrected `NULLS FIRST` in a migration that `pgschema`
/// cannot represent is caught.
///
/// It also asserts the fully migrated `events` and
/// `event_mentions` row triggers match `schema/` exactly, so no
/// migration-only trigger survives on the event write path.
#[tokio::test]
#[ignore = "requires Postgres"]
async fn admin_schema_parity_between_desired_state_and_migrations() {
Expand Down Expand Up @@ -3007,6 +3039,25 @@ mod postgres_tests {
.expect("read events key definitions")
}

// Row triggers on a parent table (partition clones follow the parent):
// name, function, enable mode and the tgtype bitmask.
async fn trigger_shapes(pool: &PgPool, table: &str) -> Vec<(String, String, String, i16)> {
sqlx::query_as(
"SELECT trigger.tgname::text, procedure.proname::text, \
trigger.tgenabled::text, trigger.tgtype \
FROM pg_trigger trigger \
JOIN pg_class c ON c.oid = trigger.tgrelid \
JOIN pg_namespace n ON n.oid = c.relnamespace \
JOIN pg_proc procedure ON procedure.oid = trigger.tgfoid \
WHERE n.nspname = 'public' AND c.relname = $1 AND NOT trigger.tgisinternal \
ORDER BY trigger.tgname",
)
.bind(table)
.fetch_all(pool)
.await
.expect("read trigger shapes")
}

let base_url = std::env::var("BUZZ_TEST_DATABASE_URL")
.or_else(|_| std::env::var("DATABASE_URL"))
.unwrap_or_else(|_| TEST_DB_URL.to_owned());
Expand Down Expand Up @@ -3128,6 +3179,17 @@ mod postgres_tests {
"constraint parity mismatch for {table}"
);
}

// Every migration-only trigger on the event tables is retired, so a
// fully migrated database carries exactly the desired-state triggers.
for table in ["events", "event_mentions"] {
assert_eq!(
trigger_shapes(&desired, table).await,
trigger_shapes(&migrated, table).await,
"trigger parity mismatch for {table}: a migration installs a trigger that \
schema/ does not declare (or the reverse)"
);
}
}

/// Re-applying `schema/schema.sql` to a database bootstrapped the way every
Expand Down
Loading
Loading