135 lines
7.0 KiB
Rust
135 lines
7.0 KiB
Rust
// file: crates/ksp-store-postgres-lib/unit_tests/migration.rs
|
|
// version: 4
|
|
|
|
fn applied(version: i64, name: &str, checksum: &str) -> super::AppliedMigration {
|
|
return super::AppliedMigration { checksum: checksum.to_owned(), name: name.to_owned(), version };
|
|
}
|
|
|
|
fn embedded(version: i64, name: &'static str, resources: &'static [crate::SchemaResource]) -> super::EmbeddedMigration {
|
|
return super::EmbeddedMigration {
|
|
checksum: super::MigrationChecksum::Resources,
|
|
hook: super::MigrationHook::None,
|
|
name,
|
|
resources,
|
|
version,
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn pre_003_fix_001_embedded_registry_keeps_v000_checksum_and_uses_resource_owned_v001() {
|
|
assert_eq!(super::EMBEDDED_MIGRATIONS.len(), 2);
|
|
let v000 = &super::EMBEDDED_MIGRATIONS[0];
|
|
assert_eq!(v000.version, 0);
|
|
assert_eq!(v000.name, "bootstrap");
|
|
assert_eq!(v000.hook, super::MigrationHook::None);
|
|
assert_eq!(v000.resources.len(), 1);
|
|
assert!(v000.resources[0].sql.contains("CREATE TABLE ksp_store_schema_migrations"));
|
|
assert_eq!(super::migration_checksum(v000), "d29068b8c13b9dc0cc9ef6aaadd0fa12d41e0fe4c56541a1118c4bfc846a1450");
|
|
let v001 = &super::EMBEDDED_MIGRATIONS[1];
|
|
assert_eq!(v001.version, 1);
|
|
assert_eq!(v001.name, "raw_transaction");
|
|
assert_eq!(v001.hook, super::MigrationHook::StoreIdentity);
|
|
assert_eq!(v001.resources.len(), 40);
|
|
assert_eq!(super::migration_checksum(v001), "31488cda2f08f3f46c4cdbdbb6c18c243662fada02eac4487040c8735d72cc51");
|
|
assert!(super::validate_embedded_registry(super::EMBEDDED_MIGRATIONS).is_ok());
|
|
assert_eq!(crate::current_migration_version(), 1);
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_003_fix_001_v001_resource_order_and_api_bounds_are_exact() {
|
|
let v001 = &super::EMBEDDED_MIGRATIONS[1];
|
|
let ids = v001.resources.iter().map(|resource| return resource.id).collect::<std::vec::Vec<_>>();
|
|
assert_eq!(ids.iter().filter(|id| return id.starts_with("tables/")).count(), 4);
|
|
assert_eq!(ids.iter().filter(|id| return id.starts_with("constraints/")).count(), 35);
|
|
assert_eq!(ids.iter().filter(|id| return id.starts_with("indexes/")).count(), 1);
|
|
assert!(ids[..4].iter().all(|id| return id.starts_with("tables/")));
|
|
assert!(ids[4..39].iter().all(|id| return id.starts_with("constraints/")));
|
|
assert!(ids[39..].iter().all(|id| return id.starts_with("indexes/")));
|
|
let mut unique = std::collections::BTreeSet::<&str>::new();
|
|
for id in &ids {
|
|
assert!(unique.insert(id), "duplicate embedded migration resource: {id}");
|
|
}
|
|
let sql = v001.resources.iter().map(|resource| return resource.sql).collect::<std::vec::Vec<_>>().concat();
|
|
for required in [
|
|
"CREATE TABLE IF NOT EXISTS ksp_store_identity",
|
|
"CREATE TABLE IF NOT EXISTS ksp_raw_transactions",
|
|
"CREATE TABLE IF NOT EXISTS ksp_raw_transaction_observations",
|
|
"CREATE TABLE IF NOT EXISTS ksp_raw_transaction_archive_payloads",
|
|
"CREATE INDEX IF NOT EXISTS ix_ksp_raw_transactions_slot_signature",
|
|
"WHERE retention_state <> 'purged'",
|
|
"octet_length(signature) = 64",
|
|
"slot >= 0 AND slot <= 18446744073709551615",
|
|
"block_time_unix_millis >= 0 AND block_time_unix_millis <= 253402300799999",
|
|
"octet_length(content_hash) = 32",
|
|
"octet_length(payload) >= 1 AND octet_length(payload) <= 16777216",
|
|
"format_version >= 1 AND format_version <= 4294967295",
|
|
"received_at_unix_millis >= 0 AND received_at_unix_millis <= 253402300799999",
|
|
"source_payload_size_bytes >= 0 AND source_payload_size_bytes <= 67108864",
|
|
"origin = 'backfill' OR origin = 'import' OR origin = 'live' OR origin = 'repair' OR origin = 'replay'",
|
|
"retention_state = 'full' OR retention_state = 'archived' OR retention_state = 'purged'",
|
|
] {
|
|
assert!(sql.contains(required), "V001 physical contract is missing: {required}");
|
|
}
|
|
assert!(!sql.contains("compacted"));
|
|
assert!(!sql.contains("BIGSERIAL"));
|
|
assert!(!sql.contains("slot BIGINT"));
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_003_fix_001_ordered_registry_accepts_v000_prefix_and_full_v001_history() {
|
|
let v000 = super::EMBEDDED_MIGRATIONS[0];
|
|
let v001 = super::EMBEDDED_MIGRATIONS[1];
|
|
let v000_checksum = super::migration_checksum(&v000);
|
|
let prefix = [applied(0, v000.name, v000_checksum.as_str())];
|
|
assert_eq!(super::validate_history(&prefix, super::EMBEDDED_MIGRATIONS).ok(), std::option::Option::Some(1));
|
|
let v001_checksum = super::migration_checksum(&v001);
|
|
let full = [applied(0, v000.name, v000_checksum.as_str()), applied(1, v001.name, v001_checksum.as_str())];
|
|
assert_eq!(super::validate_history(&full, super::EMBEDDED_MIGRATIONS).ok(), std::option::Option::Some(2));
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_003_fix_001_registry_rejects_empty_nonzero_gap_empty_metadata_and_empty_resources() {
|
|
let empty: [super::EmbeddedMigration; 0] = [];
|
|
let starts_at_one = [embedded(1, "future", crate::V000_RESOURCES)];
|
|
let gap = [embedded(0, "bootstrap", crate::V000_RESOURCES), embedded(2, "future", crate::V000_RESOURCES)];
|
|
let empty_name = [embedded(0, "", crate::V000_RESOURCES)];
|
|
let empty_resources = [embedded(0, "bootstrap", &[])];
|
|
for registry in [&empty[..], &starts_at_one[..], &gap[..], &empty_name[..], &empty_resources[..]] {
|
|
let result = super::validate_embedded_registry(registry);
|
|
assert_eq!(result.err().map(|value| return value.kind()), std::option::Option::Some(crate::PostgresBackendErrorKind::MigrationMismatch));
|
|
}
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_003_fix_001_divergent_missing_or_gapped_history_is_terminal_mismatch() {
|
|
let v000 = super::EMBEDDED_MIGRATIONS[0];
|
|
let v001 = super::EMBEDDED_MIGRATIONS[1];
|
|
let v000_checksum = super::migration_checksum(&v000);
|
|
let v001_checksum = super::migration_checksum(&v001);
|
|
let wrong_name = [applied(0, "changed", v000_checksum.as_str())];
|
|
let wrong_checksum = [applied(0, v000.name, "00")];
|
|
let missing: [super::AppliedMigration; 0] = [];
|
|
let missing_v000 = [applied(1, v001.name, v001_checksum.as_str())];
|
|
for history in [&wrong_name[..], &wrong_checksum[..], &missing[..], &missing_v000[..]] {
|
|
let result = super::validate_history(history, super::EMBEDDED_MIGRATIONS);
|
|
assert_eq!(result.err().map(|value| return value.kind()), std::option::Option::Some(crate::PostgresBackendErrorKind::MigrationMismatch));
|
|
}
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_003_fix_001_newer_history_is_rejected_without_down_migration() {
|
|
let v000 = super::EMBEDDED_MIGRATIONS[0];
|
|
let v001 = super::EMBEDDED_MIGRATIONS[1];
|
|
let v000_checksum = super::migration_checksum(&v000);
|
|
let v001_checksum = super::migration_checksum(&v001);
|
|
let history = [applied(0, v000.name, v000_checksum.as_str()), applied(1, v001.name, v001_checksum.as_str()), applied(2, "future", "future-checksum")];
|
|
let result = super::validate_history(&history, super::EMBEDDED_MIGRATIONS);
|
|
assert_eq!(result.err().map(|value| return value.kind()), std::option::Option::Some(crate::PostgresBackendErrorKind::SchemaNewer));
|
|
return;
|
|
}
|