v0.3.4-pre.003
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/unit_tests/migration.rs
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
fn applied(version: i64, name: &str, checksum: &str) -> super::AppliedMigration {
|
||||
return super::AppliedMigration { checksum: checksum.to_owned(), name: name.to_owned(), version };
|
||||
@@ -10,6 +10,7 @@ fn embedded(version: i64, name: &'static str, resources: &'static [crate::Schema
|
||||
checksum: super::MigrationChecksum::Resources,
|
||||
hook: super::MigrationHook::None,
|
||||
name,
|
||||
previous_checksums: &[],
|
||||
resources,
|
||||
version,
|
||||
};
|
||||
@@ -36,7 +37,7 @@ fn pre_003_fix_001_embedded_registry_keeps_v000_checksum_and_uses_resource_owned
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_002_v002_registry_is_additive_minimal_and_keeps_v000_v001_checksums_stable() {
|
||||
fn pre_003_v002_registry_is_complete_and_keeps_v000_v001_checksums_stable() {
|
||||
assert_eq!(super::EMBEDDED_MIGRATIONS.len(), 3);
|
||||
let v000 = &super::EMBEDDED_MIGRATIONS[0];
|
||||
let v001 = &super::EMBEDDED_MIGRATIONS[1];
|
||||
@@ -46,7 +47,9 @@ fn pre_002_v002_registry_is_additive_minimal_and_keeps_v000_v001_checksums_stabl
|
||||
assert_eq!(v002.version, 2);
|
||||
assert_eq!(v002.name, "raw_account_state");
|
||||
assert_eq!(v002.hook, super::MigrationHook::None);
|
||||
assert_eq!(v002.resources.len(), 5);
|
||||
assert_eq!(v002.previous_checksums, &[super::V002_PROVISIONAL_CHECKSUM_PRE_002]);
|
||||
assert_eq!(v002.resources.len(), 32);
|
||||
assert_eq!(super::migration_checksum(v002), "ff21605ed45f7ab4c0f92bbb692700b4118a9488b04d50a31d259ac59bdb550e");
|
||||
assert!(super::validate_embedded_registry(super::EMBEDDED_MIGRATIONS).is_ok());
|
||||
assert_eq!(crate::current_migration_version(), 2);
|
||||
let full = [
|
||||
@@ -54,7 +57,30 @@ fn pre_002_v002_registry_is_additive_minimal_and_keeps_v000_v001_checksums_stabl
|
||||
applied(1, v001.name, super::migration_checksum(v001).as_str()),
|
||||
applied(2, v002.name, super::migration_checksum(v002).as_str()),
|
||||
];
|
||||
assert_eq!(super::validate_history(&full, super::EMBEDDED_MIGRATIONS).ok(), std::option::Option::Some(3));
|
||||
assert_eq!(super::validate_history(&full, super::EMBEDDED_MIGRATIONS, false).ok(), std::option::Option::Some(3));
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_003_v002_provisional_checksum_requires_schema_autoupdate_and_rejects_unknown_history() {
|
||||
let v000 = &super::EMBEDDED_MIGRATIONS[0];
|
||||
let v001 = &super::EMBEDDED_MIGRATIONS[1];
|
||||
let v002 = &super::EMBEDDED_MIGRATIONS[2];
|
||||
let provisional = [
|
||||
applied(0, v000.name, super::migration_checksum(v000).as_str()),
|
||||
applied(1, v001.name, super::migration_checksum(v001).as_str()),
|
||||
applied(2, v002.name, super::V002_PROVISIONAL_CHECKSUM_PRE_002),
|
||||
];
|
||||
let strict = super::validate_history(&provisional, super::EMBEDDED_MIGRATIONS, false);
|
||||
assert_eq!(strict.err().map(|value| return value.kind()), std::option::Option::Some(crate::PostgresBackendErrorKind::MigrationMismatch));
|
||||
assert_eq!(super::validate_history(&provisional, super::EMBEDDED_MIGRATIONS, true).ok(), std::option::Option::Some(3));
|
||||
let unknown = [
|
||||
applied(0, v000.name, super::migration_checksum(v000).as_str()),
|
||||
applied(1, v001.name, super::migration_checksum(v001).as_str()),
|
||||
applied(2, v002.name, "unknown-v002-checksum"),
|
||||
];
|
||||
let unknown_result = super::validate_history(&unknown, super::EMBEDDED_MIGRATIONS, true);
|
||||
assert_eq!(unknown_result.err().map(|value| return value.kind()), std::option::Option::Some(crate::PostgresBackendErrorKind::MigrationMismatch));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -105,10 +131,10 @@ fn pre_003_fix_001_ordered_registry_accepts_v000_prefix_and_full_v001_history()
|
||||
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));
|
||||
assert_eq!(super::validate_history(&prefix, super::EMBEDDED_MIGRATIONS, false).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));
|
||||
assert_eq!(super::validate_history(&full, super::EMBEDDED_MIGRATIONS, false).ok(), std::option::Option::Some(2));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -137,7 +163,7 @@ fn pre_003_fix_001_divergent_missing_or_gapped_history_is_terminal_mismatch() {
|
||||
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);
|
||||
let result = super::validate_history(history, super::EMBEDDED_MIGRATIONS, false);
|
||||
assert_eq!(result.err().map(|value| return value.kind()), std::option::Option::Some(crate::PostgresBackendErrorKind::MigrationMismatch));
|
||||
}
|
||||
return;
|
||||
@@ -157,7 +183,7 @@ fn pre_003_fix_001_newer_history_is_rejected_without_down_migration() {
|
||||
applied(2, v002.name, v002_checksum.as_str()),
|
||||
applied(3, "future", "future-checksum"),
|
||||
];
|
||||
let result = super::validate_history(&history, super::EMBEDDED_MIGRATIONS);
|
||||
let result = super::validate_history(&history, super::EMBEDDED_MIGRATIONS, false);
|
||||
assert_eq!(result.err().map(|value| return value.kind()), std::option::Option::Some(crate::PostgresBackendErrorKind::SchemaNewer));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/unit_tests/schema.rs
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
fn actual_column(name: &str, udt_name: &str, nullable: bool) -> super::ActualColumn {
|
||||
return super::ActualColumn {
|
||||
@@ -49,34 +49,96 @@ fn pre_003_fix_001_v001_resources_are_split_and_idempotent_by_object_family() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_002_v002_resources_are_exactly_two_tables_plus_base_pk_fk_without_indexes_or_domain_checks() {
|
||||
assert_eq!(crate::V002_RESOURCES.len(), 5);
|
||||
fn pre_003_v002_resources_are_complete_bounded_and_have_one_unfiltered_navigation_index() {
|
||||
assert_eq!(crate::V002_RESOURCES.len(), 32);
|
||||
let ids = crate::V002_RESOURCES.iter().map(|resource| return resource.id).collect::<std::vec::Vec<_>>();
|
||||
assert_eq!(ids[..2], ["tables/001_ksp_raw_account_states.sql", "tables/002_ksp_raw_account_observations.sql"]);
|
||||
assert_eq!(
|
||||
ids[2..],
|
||||
[
|
||||
"constraints/001_pk_ksp_raw_account_states.sql",
|
||||
"constraints/002_pk_ksp_raw_account_observations.sql",
|
||||
"constraints/003_fk_ksp_raw_account_observations_state.sql",
|
||||
]
|
||||
);
|
||||
assert_eq!(ids.iter().filter(|id| return id.starts_with("tables/")).count(), 2);
|
||||
assert_eq!(ids.iter().filter(|id| return id.starts_with("constraints/")).count(), 29);
|
||||
assert_eq!(ids.iter().filter(|id| return id.starts_with("indexes/")).count(), 1);
|
||||
assert!(ids[..2].iter().all(|id| return id.starts_with("tables/")));
|
||||
assert!(ids[2..31].iter().all(|id| return id.starts_with("constraints/")));
|
||||
assert_eq!(ids[31], "indexes/001_ix_ksp_raw_account_states_slot_pubkey_state_hash.sql");
|
||||
let mut unique = std::collections::BTreeSet::<&str>::new();
|
||||
for id in &ids {
|
||||
assert!(unique.insert(id), "duplicate V002 resource: {id}");
|
||||
}
|
||||
let sql = crate::V002_RESOURCES.iter().map(|resource| return resource.sql).collect::<std::vec::Vec<_>>().concat();
|
||||
let normalized_sql = sql.split_ascii_whitespace().collect::<std::vec::Vec<_>>().join(" ");
|
||||
for required in [
|
||||
"CREATE TABLE IF NOT EXISTS ksp_raw_account_states",
|
||||
"CREATE TABLE IF NOT EXISTS ksp_raw_account_observations",
|
||||
"slot NUMERIC(20, 0) NOT NULL",
|
||||
"lamports NUMERIC(20, 0) NOT NULL",
|
||||
"rent_epoch NUMERIC(20, 0) NOT NULL",
|
||||
"write_version NUMERIC(20, 0) NULL",
|
||||
"PRIMARY KEY (pubkey, slot, state_hash)",
|
||||
"PRIMARY KEY (observation_key)",
|
||||
"FOREIGN KEY (account_pubkey, account_slot, account_state_hash) REFERENCES ksp_raw_account_states(pubkey, slot, state_hash) ON DELETE RESTRICT",
|
||||
"octet_length(pubkey) = 32",
|
||||
"slot >= 0 AND slot <= 18446744073709551615",
|
||||
"octet_length(state_hash) = 32",
|
||||
"lamports >= 0 AND lamports <= 18446744073709551615",
|
||||
"octet_length(owner) = 32",
|
||||
"rent_epoch >= 0 AND rent_epoch <= 18446744073709551615",
|
||||
"octet_length(data) <= 16777216",
|
||||
"octet_length(observation_key) = 32",
|
||||
"octet_length(account_pubkey) = 32",
|
||||
"account_slot >= 0 AND account_slot <= 18446744073709551615",
|
||||
"octet_length(account_state_hash) = 32",
|
||||
"octet_length(provider) >= 1 AND octet_length(provider) <= 128",
|
||||
"octet_length(protocol) >= 1 AND octet_length(protocol) <= 128",
|
||||
"octet_length(acquisition_method) >= 1 AND octet_length(acquisition_method) <= 128",
|
||||
"origin = 'backfill' OR origin = 'import' OR origin = 'live' OR origin = 'repair' OR origin = 'replay'",
|
||||
"received_at_unix_millis >= 0 AND received_at_unix_millis <= 253402300799999",
|
||||
"observed_at_unix_millis IS NULL OR observed_at_unix_millis <= received_at_unix_millis",
|
||||
"source_payload_size_bytes IS NULL OR source_payload_size_bytes >= 0 AND source_payload_size_bytes <= 67108864",
|
||||
"transaction_signature IS NULL OR octet_length(transaction_signature) = 64",
|
||||
"write_version IS NULL OR write_version >= 0 AND write_version <= 18446744073709551615",
|
||||
"CREATE INDEX IF NOT EXISTS ix_ksp_raw_account_states_slot_pubkey_state_hash ON ksp_raw_account_states (slot, pubkey, state_hash)",
|
||||
] {
|
||||
assert!(normalized_sql.contains(required), "V002 pre.002 physical foundation is missing: {required}");
|
||||
assert!(normalized_sql.contains(required), "V002 final physical contract is missing: {required}");
|
||||
}
|
||||
for forbidden in ["CREATE INDEX", " CHECK ", "ON DELETE CASCADE", "BIGSERIAL", "slot BIGINT", "data TEXT"] {
|
||||
assert!(!normalized_sql.contains(forbidden), "pre.002 advanced V002 beyond the minimal table/PK/FK scope: {forbidden}");
|
||||
for forbidden in [
|
||||
"ON DELETE CASCADE",
|
||||
"BIGSERIAL",
|
||||
"slot BIGINT",
|
||||
"lamports BIGINT",
|
||||
"rent_epoch BIGINT",
|
||||
"write_version BIGINT",
|
||||
"data TEXT",
|
||||
"CREATE UNIQUE INDEX",
|
||||
] {
|
||||
assert!(!normalized_sql.contains(forbidden), "V002 contains forbidden physical policy/index shape: {forbidden}");
|
||||
}
|
||||
let index_resource = &crate::V002_RESOURCES[31];
|
||||
let contract = match index_resource.object {
|
||||
super::SchemaObjectContract::Index(value) => value,
|
||||
super::SchemaObjectContract::Constraint(_) | super::SchemaObjectContract::Table(_) => panic!("V002 final resource must be the navigation index"),
|
||||
};
|
||||
assert_eq!(contract.key_fragment, "slot,pubkey,state_hash");
|
||||
assert_eq!(contract.predicate_fragment, std::option::Option::None);
|
||||
assert!(!contract.unique);
|
||||
assert!(!index_resource.sql.contains("WHERE"));
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_003_v002_external_compatibility_recognizes_only_owned_constraints() {
|
||||
assert!(super::is_expected_constraint(crate::V002_RESOURCES, "ksp_raw_account_states", "ck_ksp_raw_account_states_slot"));
|
||||
assert!(super::is_expected_constraint(crate::V002_RESOURCES, "ksp_raw_account_observations", "fk_ksp_raw_account_observations_state",));
|
||||
assert!(!super::is_expected_constraint(crate::V002_RESOURCES, "ksp_raw_account_states", "external_check"));
|
||||
let slot_resource = crate::V002_RESOURCES.iter().find(|resource| return resource.id == "constraints/005_ck_ksp_raw_account_states_slot.sql");
|
||||
let slot_resource = match slot_resource {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => panic!("V002 slot constraint must remain embedded"),
|
||||
};
|
||||
let expected = super::expected_constraint_definition(slot_resource.sql, "ck_ksp_raw_account_states_slot");
|
||||
let expected = match expected {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => panic!("V002 slot constraint definition must be extractable"),
|
||||
};
|
||||
assert!(super::matches_expected_constraint_definition(crate::V002_RESOURCES, "ksp_raw_account_states", "c", expected.as_str()));
|
||||
assert!(!super::matches_expected_constraint_definition(crate::V001_RESOURCES, "ksp_raw_account_states", "c", expected.as_str()));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user