// file: crates/ksp-store-postgres-lib/unit_tests/schema.rs // version: 6 fn actual_column(name: &str, udt_name: &str, nullable: bool) -> super::ActualColumn { return super::ActualColumn { default: std::option::Option::None, generated: "NEVER".to_owned(), identity: "NO".to_owned(), name: name.to_owned(), nullable, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: udt_name.to_owned(), }; } #[test] fn pre_003_fix_001_v000_resource_is_relocated_without_changing_legacy_sql() { assert_eq!(crate::V000_RESOURCES.len(), 1); let resource = crate::V000_RESOURCES[0]; assert_eq!(resource.id, "tables/001_ksp_store_schema_migrations.sql"); assert_eq!( resource.sql, "CREATE TABLE ksp_store_schema_migrations (\n version BIGINT PRIMARY KEY,\n name TEXT NOT NULL,\n checksum TEXT NOT NULL,\n applied_at TIMESTAMPTZ NOT NULL\n);\n", ); return; } #[test] fn pre_003_fix_001_v001_resources_are_split_and_idempotent_by_object_family() { assert_eq!(crate::V001_RESOURCES.len(), 40); let table_resources = crate::V001_RESOURCES.iter().filter(|resource| return resource.id.starts_with("tables/")).collect::>(); let constraint_resources = crate::V001_RESOURCES.iter().filter(|resource| return resource.id.starts_with("constraints/")).collect::>(); let index_resources = crate::V001_RESOURCES.iter().filter(|resource| return resource.id.starts_with("indexes/")).collect::>(); assert_eq!(table_resources.len(), 4); assert_eq!(constraint_resources.len(), 35); assert_eq!(index_resources.len(), 1); for resource in table_resources { assert!(resource.sql.contains("CREATE TABLE IF NOT EXISTS")); assert!(resource.sql.contains("ADD COLUMN IF NOT EXISTS")); } for resource in constraint_resources { assert!(resource.sql.contains("IF NOT EXISTS")); } for resource in index_resources { assert!(resource.sql.contains("CREATE INDEX IF NOT EXISTS")); } return; } #[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); let ids = crate::V002_RESOURCES.iter().map(|resource| return resource.id).collect::>(); 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", ] ); let sql = crate::V002_RESOURCES.iter().map(|resource| return resource.sql).collect::>().concat(); let normalized_sql = sql.split_ascii_whitespace().collect::>().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", "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", ] { assert!(normalized_sql.contains(required), "V002 pre.002 physical foundation 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}"); } return; } #[test] fn pre_003_fix_001_external_extra_columns_are_accepted_only_when_they_cannot_block_ksp_inserts() { let nullable = actual_column("external_nullable", "text", true); assert!(nullable.is_non_blocking_extra()); let mut with_default = actual_column("external_default", "text", true); with_default.default = std::option::Option::Some("'x'::text".to_owned()); assert!(!with_default.is_non_blocking_extra()); let mut identity = actual_column("external_identity", "int8", true); identity.identity = "YES".to_owned(); assert!(!identity.is_non_blocking_extra()); let mut generated = actual_column("external_generated", "text", true); generated.generated = "ALWAYS".to_owned(); assert!(!generated.is_non_blocking_extra()); let blocking = actual_column("external_required", "text", false); assert!(!blocking.is_non_blocking_extra()); return; } #[test] fn pre_003_fix_001_required_column_matching_ignores_integer_precision_but_requires_numeric_20_0() { let expected_int = super::ColumnContract { name: "version", nullable: false, numeric_precision: std::option::Option::None, numeric_scale: std::option::Option::None, udt_name: "int8", }; let mut actual_int = actual_column("version", "int8", false); actual_int.numeric_precision = std::option::Option::Some(64); actual_int.numeric_scale = std::option::Option::Some(0); assert!(actual_int.matches(&expected_int)); let expected_numeric = super::ColumnContract { name: "slot", nullable: false, numeric_precision: std::option::Option::Some(20), numeric_scale: std::option::Option::Some(0), udt_name: "numeric", }; let mut actual_numeric = actual_column("slot", "numeric", false); actual_numeric.numeric_precision = std::option::Option::Some(20); actual_numeric.numeric_scale = std::option::Option::Some(0); assert!(actual_numeric.matches(&expected_numeric)); actual_numeric.numeric_precision = std::option::Option::Some(19); assert!(!actual_numeric.matches(&expected_numeric)); return; } #[test] fn pre_003_fix_001_catalog_normalization_and_resource_owned_constraint_definition_are_deterministic() { let normalized = super::normalize_catalog_sql("CHECK ((slot >= (0)::numeric) AND (slot <= (18446744073709551615)::numeric))"); assert_eq!(normalized, "checkslot>=0andslot<=18446744073709551615"); let postgres_17_normalized = super::normalize_catalog_sql("CHECK ((slot >= '0'::numeric) AND (slot <= '18446744073709551615'::numeric))"); assert_eq!(postgres_17_normalized, normalized); let bigint_normalized = super::normalize_catalog_sql( "CHECK ((block_time_unix_millis IS NULL) OR ((block_time_unix_millis >= '0'::bigint) AND (block_time_unix_millis <= '253402300799999'::bigint)))", ); let block_time_resource = crate::V001_RESOURCES.iter().find(|resource| return resource.id == "constraints/007_ck_ksp_raw_transactions_block_time.sql"); assert!(block_time_resource.is_some(), "V001 block-time constraint resource must remain embedded"); let block_time_resource = match block_time_resource { std::option::Option::Some(value) => value, std::option::Option::None => return, }; let block_time_expected = super::expected_constraint_definition(block_time_resource.sql, "ck_ksp_raw_transactions_block_time"); assert_eq!(block_time_expected.as_deref(), std::option::Option::Some(bigint_normalized.as_str())); let integer_alias = super::normalize_catalog_sql("CHECK (format_version <= '4294967295'::int8)"); assert_eq!(integer_alias, "checkformat_version<=4294967295"); let text_literal = super::normalize_catalog_sql("CHECK (retention_state = 'full'::text)"); assert_eq!(text_literal, "checkretention_state='full'"); let resource = crate::V001_RESOURCES.iter().find(|resource| return resource.id == "constraints/006_ck_ksp_raw_transactions_slot.sql"); assert!(resource.is_some(), "V001 slot constraint resource must remain embedded"); let resource = match resource { std::option::Option::Some(value) => value, std::option::Option::None => return, }; let expected = super::expected_constraint_definition(resource.sql, "ck_ksp_raw_transactions_slot"); assert_eq!(expected.as_deref(), std::option::Option::Some(normalized.as_str())); assert_ne!(expected.as_deref(), std::option::Option::Some("checkslot>=0andslot<=10")); return; }