v0.3.3-pre.003
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/tests/dependency_boundary.rs
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -21,10 +21,10 @@ fn pre_005_backend_owns_exact_physical_runtime_dependencies_without_reverse_faca
|
||||
let migration = include_str!("../src/migration.rs");
|
||||
let bootstrap_sql = include_str!("../migrations/V000__bootstrap.sql");
|
||||
assert!(migration.contains("include_str!(\"../migrations/V000__bootstrap.sql\")"));
|
||||
assert!(migration.contains("include_str!(\"../migrations/V001__raw_transaction.sql\")"));
|
||||
assert!(bootstrap_sql.contains("ksp_store_schema_migrations"));
|
||||
for forbidden in ["RawTransaction", "RawAccountState", "raw_transaction", "raw_account", "CORE", "DECODE", "SPECIALIZED"] {
|
||||
assert!(!migration.contains(forbidden), "business migration implementation leaked into foundation: {forbidden}");
|
||||
assert!(!bootstrap_sql.contains(forbidden), "business schema leaked into foundation SQL: {forbidden}");
|
||||
assert!(!bootstrap_sql.contains(forbidden), "business schema leaked into immutable V000 SQL: {forbidden}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -84,17 +84,28 @@ fn pre_007_health_probe_remains_foundation_only_and_private_sql() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_002_migration_engine_is_registry_driven_and_network_hook_ready_without_v001_schema() {
|
||||
fn pre_003_migration_engine_embeds_v001_and_binds_network_without_repository_scope() {
|
||||
let migration = include_str!("../src/migration.rs");
|
||||
let v001 = include_str!("../migrations/V001__raw_transaction.sql");
|
||||
assert!(migration.contains("const EMBEDDED_MIGRATIONS: &[EmbeddedMigration]"));
|
||||
assert!(migration.contains("MigrationHook::None"));
|
||||
assert!(migration.contains("run_migration_hook(transaction, network, migration.hook, MigrationHookContext::AppliedNow).await"));
|
||||
assert!(migration.contains("network: &ksp_store_api::RawNetworkId"));
|
||||
assert!(migration.contains("MigrationHook::StoreIdentity"));
|
||||
assert!(migration.contains("MigrationHookContext::AppliedNow"));
|
||||
assert!(migration.contains("MigrationHookContext::Existing"));
|
||||
assert!(migration.contains("run_applied_migration_hooks(&transaction, network, next_index).await"));
|
||||
assert!(migration.contains("validate_history(history.as_slice(), EMBEDDED_MIGRATIONS)"));
|
||||
assert!(migration.contains("apply_pending_migrations(&transaction, network, next_index).await"));
|
||||
assert!(!migration.contains("V001__raw_transaction.sql"));
|
||||
assert!(!migration.contains("ksp_store_identity"));
|
||||
assert!(migration.contains("INSERT INTO ksp_store_identity (singleton, network) VALUES (1, $1)"));
|
||||
assert!(migration.contains("SELECT singleton, network FROM ksp_store_identity ORDER BY singleton LIMIT 2"));
|
||||
assert!(migration.contains("ksp_store_api::RawNetworkId::new(stored_network)"));
|
||||
for required in [
|
||||
"CREATE TABLE ksp_store_identity",
|
||||
"CREATE TABLE ksp_raw_transactions",
|
||||
"CREATE TABLE ksp_raw_transaction_observations",
|
||||
"CREATE TABLE ksp_raw_transaction_archive_payloads",
|
||||
"CREATE INDEX ix_ksp_raw_transactions_slot_signature",
|
||||
] {
|
||||
assert!(v001.contains(required), "missing V001 physical object: {required}");
|
||||
}
|
||||
for forbidden in ["impl ksp_store_api::RawTransaction", "repository", "sqlx", "RawAccountState"] {
|
||||
assert!(!migration.contains(forbidden), "repository/cross-scope implementation leaked into migration engine: {forbidden}");
|
||||
assert!(!v001.contains(forbidden), "forbidden V001 scope content detected: {forbidden}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/tests/hardening_completeness.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -115,6 +115,7 @@ fn pre_009_backend_modules_exports_and_manifest_dependencies_are_exact() {
|
||||
assert!(!crate_root.contains("pub mod "));
|
||||
let actual_exports = public_reexport_names(crate_root);
|
||||
let mut expected_exports = [
|
||||
"ERROR_CODE_POSTGRES_RETENTION_COMPACTION_UNSUPPORTED",
|
||||
"PostgresBackend",
|
||||
"PostgresBackendError",
|
||||
"PostgresBackendErrorKind",
|
||||
@@ -125,7 +126,7 @@ fn pre_009_backend_modules_exports_and_manifest_dependencies_are_exact() {
|
||||
];
|
||||
expected_exports.sort_unstable();
|
||||
assert_eq!(actual_exports.as_slice(), expected_exports.as_slice());
|
||||
assert_eq!(actual_exports.len(), 7);
|
||||
assert_eq!(actual_exports.len(), 8);
|
||||
let manifest = include_str!("../Cargo.toml");
|
||||
let actual_dependencies = manifest_dependency_names(manifest);
|
||||
let expected_dependencies = [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/tests/postgres_foundation_live.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -8,12 +8,30 @@
|
||||
//! Opt-in real PostgreSQL proof for the Store foundation runtime.
|
||||
//!
|
||||
//! The test reads one dedicated PostgreSQL URI from stdin, refuses to start
|
||||
//! when the KSP migration metadata table already exists, never prints the URI,
|
||||
//! creates no business table and cleans up only metadata it proved it created.
|
||||
//! when any KSP Store table managed by V000/V001 already exists, never prints
|
||||
//! the URI, and cleans up only the isolated schema it proved absent first. It
|
||||
//! validates migration/bootstrap behavior, not RawTransaction capabilities.
|
||||
|
||||
const LIVE_BOOTSTRAP_SQL: &str = include_str!("../migrations/V000__bootstrap.sql");
|
||||
const LIVE_BROKEN_CHECKSUM_A: &str = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
const LIVE_BROKEN_CHECKSUM_B: &str = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
|
||||
const LIVE_MANAGED_SCHEMA_DROP_SQL: &str = r#"DROP TABLE IF EXISTS ksp_raw_transaction_observations;
|
||||
DROP TABLE IF EXISTS ksp_raw_transaction_archive_payloads;
|
||||
DROP TABLE IF EXISTS ksp_raw_transactions;
|
||||
DROP TABLE IF EXISTS ksp_store_identity;
|
||||
DROP TABLE IF EXISTS ksp_store_schema_migrations;"#;
|
||||
const LIVE_MANAGED_SCHEMA_EXISTS_SQL: &str = r#"SELECT EXISTS (
|
||||
SELECT 1 FROM information_schema.tables
|
||||
WHERE table_schema = current_schema()
|
||||
AND table_name IN (
|
||||
'ksp_store_schema_migrations',
|
||||
'ksp_store_identity',
|
||||
'ksp_raw_transactions',
|
||||
'ksp_raw_transaction_observations',
|
||||
'ksp_raw_transaction_archive_payloads'
|
||||
)
|
||||
AND table_type = 'BASE TABLE'
|
||||
)"#;
|
||||
const LIVE_MAX_URI_BYTES: usize = 4_096;
|
||||
const LIVE_METADATA_EXISTS_SQL: &str = r#"SELECT EXISTS (
|
||||
SELECT 1 FROM information_schema.tables
|
||||
@@ -21,7 +39,6 @@ const LIVE_METADATA_EXISTS_SQL: &str = r#"SELECT EXISTS (
|
||||
AND table_name = 'ksp_store_schema_migrations'
|
||||
AND table_type = 'BASE TABLE'
|
||||
)"#;
|
||||
const LIVE_METADATA_DROP_SQL: &str = "DROP TABLE IF EXISTS ksp_store_schema_migrations";
|
||||
const LIVE_SENTINEL_CHECKSUM_SQL: &str = "SELECT checksum FROM ksp_store_schema_migrations WHERE version = 0";
|
||||
const LIVE_SENTINEL_INSERT_SQL: &str =
|
||||
"INSERT INTO ksp_store_schema_migrations (version, name, checksum, applied_at) VALUES (0, 'bootstrap', 'pre008_rollback_injected', CURRENT_TIMESTAMP)";
|
||||
@@ -83,13 +100,13 @@ async fn run_live_test(uri: &str) -> std::result::Result<(), LiveFailure> {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let preexisting_result = metadata_exists(&admin).await;
|
||||
let preexisting_result = managed_schema_exists(&admin).await;
|
||||
let preexisting = match preexisting_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if preexisting {
|
||||
return std::result::Result::Err(LiveFailure::new("metadata_preexisting_refusal"));
|
||||
return std::result::Result::Err(LiveFailure::new("managed_schema_preexisting_refusal"));
|
||||
}
|
||||
let major_result = postgres_major(&admin).await;
|
||||
let major = match major_result {
|
||||
@@ -100,16 +117,16 @@ async fn run_live_test(uri: &str) -> std::result::Result<(), LiveFailure> {
|
||||
return std::result::Result::Err(LiveFailure::new("postgres_major_unsupported"));
|
||||
}
|
||||
eprintln!("KSP Store PostgreSQL live proof: server major {major}");
|
||||
let mut owns_metadata = false;
|
||||
let scenario = run_foundation_scenario(&mut admin, uri, &mut owns_metadata).await;
|
||||
let cleanup = if owns_metadata { drop_metadata(&admin).await } else { std::result::Result::Ok(()) };
|
||||
let mut owns_schema = false;
|
||||
let scenario = run_foundation_scenario(&mut admin, uri, &mut owns_schema).await;
|
||||
let cleanup = if owns_schema { drop_managed_schema(&admin).await } else { std::result::Result::Ok(()) };
|
||||
if let std::result::Result::Err(error) = cleanup {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
if let std::result::Result::Err(error) = scenario {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let remains_result = metadata_exists(&admin).await;
|
||||
let remains_result = managed_schema_exists(&admin).await;
|
||||
let remains = match remains_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -120,7 +137,7 @@ async fn run_live_test(uri: &str) -> std::result::Result<(), LiveFailure> {
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
async fn run_foundation_scenario(admin: &mut tokio_postgres::Client, uri: &str, owns_metadata: &mut bool) -> std::result::Result<(), LiveFailure> {
|
||||
async fn run_foundation_scenario(admin: &mut tokio_postgres::Client, uri: &str, owns_schema: &mut bool) -> std::result::Result<(), LiveFailure> {
|
||||
let initial_result = open_backend(uri).await;
|
||||
let initial = match initial_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -134,9 +151,9 @@ async fn run_foundation_scenario(admin: &mut tokio_postgres::Client, uri: &str,
|
||||
if !created {
|
||||
return std::result::Result::Err(LiveFailure::new("initial_bootstrap_metadata"));
|
||||
}
|
||||
*owns_metadata = true;
|
||||
*owns_schema = true;
|
||||
let initial_health = initial.health().await;
|
||||
if !initial_health.is_ready() || initial_health.migration_version() != std::option::Option::Some(0) || initial_health.pending_migration_count() != 0 {
|
||||
if !initial_health.is_ready() || initial_health.migration_version() != std::option::Option::Some(1) || initial_health.pending_migration_count() != 0 {
|
||||
return std::result::Result::Err(LiveFailure::new("initial_health"));
|
||||
}
|
||||
let initial_close = close_backend(initial).await;
|
||||
@@ -150,7 +167,7 @@ async fn run_foundation_scenario(admin: &mut tokio_postgres::Client, uri: &str,
|
||||
};
|
||||
let idempotent_health = idempotent.health().await;
|
||||
if !idempotent_health.is_ready()
|
||||
|| idempotent_health.migration_version() != std::option::Option::Some(0)
|
||||
|| idempotent_health.migration_version() != std::option::Option::Some(1)
|
||||
|| idempotent_health.pending_migration_count() != 0
|
||||
{
|
||||
return std::result::Result::Err(LiveFailure::new("idempotent_health"));
|
||||
@@ -159,7 +176,7 @@ async fn run_foundation_scenario(admin: &mut tokio_postgres::Client, uri: &str,
|
||||
if let std::result::Result::Err(error) = idempotent_close {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let reset_result = drop_metadata(admin).await;
|
||||
let reset_result = drop_managed_schema(admin).await;
|
||||
if let std::result::Result::Err(error) = reset_result {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
@@ -213,7 +230,7 @@ async fn run_foundation_scenario(admin: &mut tokio_postgres::Client, uri: &str,
|
||||
if let std::result::Result::Err(error) = recovered_close {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let rollback_reset = drop_metadata(admin).await;
|
||||
let rollback_reset = drop_managed_schema(admin).await;
|
||||
if let std::result::Result::Err(error) = rollback_reset {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
@@ -233,7 +250,7 @@ async fn run_foundation_scenario(admin: &mut tokio_postgres::Client, uri: &str,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let final_health = final_backend.health().await;
|
||||
if !final_health.is_ready() || final_health.migration_version() != std::option::Option::Some(0) || final_health.pending_migration_count() != 0 {
|
||||
if !final_health.is_ready() || final_health.migration_version() != std::option::Option::Some(1) || final_health.pending_migration_count() != 0 {
|
||||
return std::result::Result::Err(LiveFailure::new("final_health"));
|
||||
}
|
||||
return close_backend(final_backend).await;
|
||||
@@ -354,6 +371,18 @@ async fn postgres_major(client: &tokio_postgres::Client) -> std::result::Result<
|
||||
return std::result::Result::Ok(version_num / 10_000);
|
||||
}
|
||||
|
||||
async fn managed_schema_exists(client: &tokio_postgres::Client) -> std::result::Result<bool, LiveFailure> {
|
||||
let row_result = client.query_one(LIVE_MANAGED_SCHEMA_EXISTS_SQL, &[]).await;
|
||||
let row = match row_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(LiveFailure::new("managed_schema_probe")),
|
||||
};
|
||||
return match row.try_get::<usize, bool>(0) {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||
std::result::Result::Err(_) => std::result::Result::Err(LiveFailure::new("managed_schema_probe_decode")),
|
||||
};
|
||||
}
|
||||
|
||||
async fn metadata_exists(client: &tokio_postgres::Client) -> std::result::Result<bool, LiveFailure> {
|
||||
let row_result = client.query_one(LIVE_METADATA_EXISTS_SQL, &[]).await;
|
||||
let row = match row_result {
|
||||
@@ -366,10 +395,10 @@ async fn metadata_exists(client: &tokio_postgres::Client) -> std::result::Result
|
||||
};
|
||||
}
|
||||
|
||||
async fn drop_metadata(client: &tokio_postgres::Client) -> std::result::Result<(), LiveFailure> {
|
||||
return match client.batch_execute(LIVE_METADATA_DROP_SQL).await {
|
||||
async fn drop_managed_schema(client: &tokio_postgres::Client) -> std::result::Result<(), LiveFailure> {
|
||||
return match client.batch_execute(LIVE_MANAGED_SCHEMA_DROP_SQL).await {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(()),
|
||||
std::result::Result::Err(_) => std::result::Result::Err(LiveFailure::new("metadata_cleanup")),
|
||||
std::result::Result::Err(_) => std::result::Result::Err(LiveFailure::new("managed_schema_cleanup")),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/tests/public_api.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -58,3 +58,13 @@ fn pre_007_backend_health_bridge_exposes_only_safe_snapshot_types() {
|
||||
let _health_probe = ksp_store_postgres_lib::PostgresBackend::health;
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_003_retention_compaction_error_code_matches_store_contract_value() {
|
||||
assert_eq!(ksp_store_postgres_lib::ERROR_CODE_POSTGRES_RETENTION_COMPACTION_UNSUPPORTED.domain(), "store");
|
||||
assert_eq!(
|
||||
ksp_store_postgres_lib::ERROR_CODE_POSTGRES_RETENTION_COMPACTION_UNSUPPORTED.code(),
|
||||
"postgres_retention_compaction_unsupported",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user