v0.3.8-pre.004
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/tests/dependency_boundary.rs
|
||||
// version: 24
|
||||
// version: 25
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -408,13 +408,14 @@ fn pre_007_raw_retention_is_atomic_compare_and_transition_without_fake_compactio
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_008_backend_trait_implementations_cover_exact_ten_raw_capabilities_in_runtime_bridge() {
|
||||
fn v0_3_8_pre_004_backend_trait_implementations_cover_exact_eleven_raw_capabilities_in_runtime_bridge() {
|
||||
let runtime = include_str!("../src/runtime.rs");
|
||||
for implementation in [
|
||||
"impl ksp_store_api::RawAccountObservationRead for PostgresBackend",
|
||||
"impl ksp_store_api::RawAccountObservationWrite for PostgresBackend",
|
||||
"impl ksp_store_api::RawAccountStateRead for PostgresBackend",
|
||||
"impl ksp_store_api::RawAccountStateWrite for PostgresBackend",
|
||||
"impl ksp_store_api::RawTransactionInspectionRead for PostgresBackend",
|
||||
"impl ksp_store_api::RawTransactionObservationRead for PostgresBackend",
|
||||
"impl ksp_store_api::RawTransactionObservationWrite for PostgresBackend",
|
||||
"impl ksp_store_api::RawTransactionRead for PostgresBackend",
|
||||
@@ -424,6 +425,6 @@ fn pre_008_backend_trait_implementations_cover_exact_ten_raw_capabilities_in_run
|
||||
] {
|
||||
assert_eq!(runtime.matches(implementation).count(), 1, "unexpected PostgreSQL RAW capability inventory: {implementation}");
|
||||
}
|
||||
assert_eq!(runtime.matches("impl ksp_store_api::Raw").count(), 10);
|
||||
assert_eq!(runtime.matches("impl ksp_store_api::Raw").count(), 11);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/tests/hardening_completeness.rs
|
||||
// version: 17
|
||||
// version: 18
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -281,6 +281,7 @@ fn pre_009_live_raw_transaction_proof_is_opt_in_isolated_and_secret_safe() {
|
||||
"prove_concurrent_identical_insert",
|
||||
"prove_concurrent_divergent_insert",
|
||||
"prove_pagination",
|
||||
"prove_inspection",
|
||||
"prove_retention_and_rehydrate",
|
||||
"prove_retention_races",
|
||||
"prove_cancellation_rollback",
|
||||
@@ -298,13 +299,14 @@ fn pre_009_live_raw_transaction_proof_is_opt_in_isolated_and_secret_safe() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_010_raw_capability_implementation_inventory_is_exactly_ten() {
|
||||
fn v0_3_8_pre_004_raw_capability_implementation_inventory_is_exactly_eleven() {
|
||||
let runtime = include_str!("../src/runtime.rs");
|
||||
let capability_impls = [
|
||||
"impl ksp_store_api::RawAccountObservationRead for PostgresBackend",
|
||||
"impl ksp_store_api::RawAccountObservationWrite for PostgresBackend",
|
||||
"impl ksp_store_api::RawAccountStateRead for PostgresBackend",
|
||||
"impl ksp_store_api::RawAccountStateWrite for PostgresBackend",
|
||||
"impl ksp_store_api::RawTransactionInspectionRead for PostgresBackend",
|
||||
"impl ksp_store_api::RawTransactionObservationRead for PostgresBackend",
|
||||
"impl ksp_store_api::RawTransactionObservationWrite for PostgresBackend",
|
||||
"impl ksp_store_api::RawTransactionRead for PostgresBackend",
|
||||
@@ -315,7 +317,7 @@ fn pre_010_raw_capability_implementation_inventory_is_exactly_ten() {
|
||||
for implementation in capability_impls {
|
||||
assert_eq!(runtime.matches(implementation).count(), 1, "unexpected PostgreSQL capability implementation inventory: {implementation}");
|
||||
}
|
||||
assert_eq!(runtime.matches("impl ksp_store_api::Raw").count(), 10);
|
||||
assert_eq!(runtime.matches("impl ksp_store_api::Raw").count(), 11);
|
||||
let migration = include_str!("../src/migration.rs");
|
||||
assert!(migration.contains("raw_account_state"));
|
||||
assert!(migration.contains("crate::V002_RESOURCES"));
|
||||
@@ -324,25 +326,77 @@ fn pre_010_raw_capability_implementation_inventory_is_exactly_ten() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_010_raw_transaction_private_sql_keeps_keyset_navigation_and_bounded_statement_surface() {
|
||||
fn v0_3_8_pre_004_raw_transaction_keyset_sql_remains_offset_free_and_unchanged_in_role() {
|
||||
let source = include_str!("../src/raw_transaction.rs");
|
||||
for required in [
|
||||
"ORDER BY slot ASC, signature ASC",
|
||||
"ORDER BY slot DESC, signature DESC",
|
||||
"LIMIT $5",
|
||||
"FOR UPDATE",
|
||||
"ON CONFLICT (signature) DO NOTHING",
|
||||
"ON CONFLICT (observation_key) DO NOTHING",
|
||||
"ksp_raw_transaction_archive_payloads",
|
||||
] {
|
||||
let ascending = source.lines().find(|line| line.starts_with("const LIST_TRANSACTIONS_ASC_SQL"));
|
||||
let ascending = match ascending {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => panic!("missing canonical ascending keyset SQL"),
|
||||
};
|
||||
let descending = source.lines().find(|line| line.starts_with("const LIST_TRANSACTIONS_DESC_SQL"));
|
||||
let descending = match descending {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => panic!("missing canonical descending keyset SQL"),
|
||||
};
|
||||
for statement in [ascending, descending] {
|
||||
assert!(statement.contains("retention_state <> 'purged'"));
|
||||
assert!(statement.contains("LIMIT $5"));
|
||||
assert!(!statement.contains(" OFFSET "));
|
||||
}
|
||||
assert!(ascending.contains("(slot, signature) >"));
|
||||
assert!(ascending.contains("ORDER BY slot ASC, signature ASC"));
|
||||
assert!(descending.contains("(slot, signature) <"));
|
||||
assert!(descending.contains("ORDER BY slot DESC, signature DESC"));
|
||||
for required in ["FOR UPDATE", "ON CONFLICT (signature) DO NOTHING", "ON CONFLICT (observation_key) DO NOTHING", "ksp_raw_transaction_archive_payloads"] {
|
||||
assert!(source.contains(required), "required hardened RawTransaction SQL contract missing: {required}");
|
||||
}
|
||||
for forbidden in [" OFFSET ", "SELECT *", "ON CONFLICT DO UPDATE", "processing_state", "batch_size", "priority"] {
|
||||
for forbidden in ["SELECT *", "ON CONFLICT DO UPDATE", "processing_state", "batch_size", "priority"] {
|
||||
assert!(!source.contains(forbidden), "forbidden RawTransaction scope/policy SQL detected: {forbidden}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v0_3_8_pre_004_transaction_inspection_sql_is_single_statement_payload_free_counted_and_random_access() {
|
||||
let source = include_str!("../src/raw_transaction.rs");
|
||||
let ascending = source.lines().find(|line| line.starts_with("const INSPECT_TRANSACTIONS_ASC_SQL"));
|
||||
let ascending = match ascending {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => panic!("missing ascending inspection SQL"),
|
||||
};
|
||||
let descending = source.lines().find(|line| line.starts_with("const INSPECT_TRANSACTIONS_DESC_SQL"));
|
||||
let descending = match descending {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => panic!("missing descending inspection SQL"),
|
||||
};
|
||||
for statement in [ascending, descending] {
|
||||
for required in [
|
||||
"COUNT(*)::TEXT AS filtered_count_text",
|
||||
"COUNT(*)::TEXT FROM ksp_raw_transactions",
|
||||
"LEFT JOIN LATERAL",
|
||||
"LEFT JOIN ksp_raw_transaction_archive_payloads",
|
||||
"OCTET_LENGTH(transaction_row.payload)::BIGINT",
|
||||
"OCTET_LENGTH(archive_row.payload)::BIGINT",
|
||||
"LIMIT $3 OFFSET $4",
|
||||
"payload_size_bytes",
|
||||
"hot_payload_present",
|
||||
"archive_payload_present",
|
||||
] {
|
||||
assert!(statement.contains(required), "missing transaction inspection SQL contract: {required}");
|
||||
}
|
||||
assert!(!statement.contains("transaction_row.payload, transaction_row.retention_state"));
|
||||
assert!(!statement.contains("archive_row.payload AS"));
|
||||
assert!(!statement.contains("retention_state <> 'purged'"));
|
||||
}
|
||||
assert!(ascending.contains("ORDER BY transaction_row.slot ASC, transaction_row.signature ASC"));
|
||||
assert!(descending.contains("ORDER BY transaction_row.slot DESC, transaction_row.signature DESC"));
|
||||
assert_eq!(source.matches(" OFFSET ").count(), 2);
|
||||
assert!(source.contains("raw_transaction_inspection_sql_window(query.page())"));
|
||||
assert!(source.contains("raw_transaction_inspection_offset"));
|
||||
assert!(source.contains("raw_transaction_inspection_limit"));
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_010_raw_account_private_sql_is_non_destructive_keyset_and_family_local() {
|
||||
let source = include_str!("../src/raw_account.rs");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/tests/postgres_raw_transaction_live.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -183,6 +183,7 @@ async fn run_raw_transaction_scenario(admin: &mut tokio_postgres::Client, uri: &
|
||||
prove_atomic_insert_and_reads(&backend).await,
|
||||
prove_additional_observation_and_atomic_rollback(&backend).await,
|
||||
prove_pagination(&backend).await,
|
||||
prove_inspection(&backend).await,
|
||||
prove_retention_and_rehydrate(&backend).await,
|
||||
] {
|
||||
if let std::result::Result::Err(error) = proof {
|
||||
@@ -476,6 +477,57 @@ async fn prove_pagination(backend: &ksp_store_postgres_lib::PostgresBackend) ->
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
async fn prove_inspection(backend: &ksp_store_postgres_lib::PostgresBackend) -> std::result::Result<(), LiveFailure> {
|
||||
let ascending_query =
|
||||
match inspection_query(ksp_store_api::RawSortDirection::Ascending, 1, 2, std::option::Option::Some(9_000), std::option::Option::Some(9_002)) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let ascending = match backend.inspect_raw_transactions(&ascending_query).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(LiveFailure::new("inspection_ascending_query")),
|
||||
};
|
||||
if ascending.filtered_items() != 5 || ascending.total_items() < ascending.filtered_items() || ascending.items().len() != 2 {
|
||||
return std::result::Result::Err(LiveFailure::new("inspection_ascending_counts"));
|
||||
}
|
||||
if ascending.items()[0].reference().signature().as_bytes()[0] != 51 || ascending.items()[1].reference().signature().as_bytes()[0] != 52 {
|
||||
return std::result::Result::Err(LiveFailure::new("inspection_ascending_order"));
|
||||
}
|
||||
for item in ascending.items() {
|
||||
if item.payload_size_bytes() != std::option::Option::Some(4) || item.retention_state() != ksp_store_api::RawRetentionState::Full {
|
||||
return std::result::Result::Err(LiveFailure::new("inspection_ascending_summary"));
|
||||
}
|
||||
}
|
||||
let descending_query =
|
||||
match inspection_query(ksp_store_api::RawSortDirection::Descending, 1, 2, std::option::Option::Some(9_000), std::option::Option::Some(9_002)) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let descending = match backend.inspect_raw_transactions(&descending_query).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(LiveFailure::new("inspection_descending_query")),
|
||||
};
|
||||
if descending.filtered_items() != 5 || descending.items().len() != 2 {
|
||||
return std::result::Result::Err(LiveFailure::new("inspection_descending_counts"));
|
||||
}
|
||||
if descending.items()[0].reference().signature().as_bytes()[0] != 53 || descending.items()[1].reference().signature().as_bytes()[0] != 52 {
|
||||
return std::result::Result::Err(LiveFailure::new("inspection_descending_order"));
|
||||
}
|
||||
let empty_query =
|
||||
match inspection_query(ksp_store_api::RawSortDirection::Ascending, 99, 25, std::option::Option::Some(9_000), std::option::Option::Some(9_002)) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let empty = match backend.inspect_raw_transactions(&empty_query).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(LiveFailure::new("inspection_empty_query")),
|
||||
};
|
||||
if empty.filtered_items() != 5 || !empty.items().is_empty() {
|
||||
return std::result::Result::Err(LiveFailure::new("inspection_empty_page"));
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
async fn prove_retention_and_rehydrate(backend: &ksp_store_postgres_lib::PostgresBackend) -> std::result::Result<(), LiveFailure> {
|
||||
let transaction = match raw_transaction(70, 7_000, 70) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -750,6 +802,29 @@ async fn collect_pages(
|
||||
return std::result::Result::Ok(output);
|
||||
}
|
||||
|
||||
fn inspection_query(
|
||||
direction: ksp_store_api::RawSortDirection,
|
||||
offset: u64,
|
||||
limit: u64,
|
||||
start: std::option::Option<u64>,
|
||||
end: std::option::Option<u64>,
|
||||
) -> std::result::Result<ksp_store_api::RawTransactionInspectionQuery, LiveFailure> {
|
||||
let network = match network("devnet") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let slots = match ksp_store_api::RawSlotRange::new(start, end) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(LiveFailure::new("inspection_slot_range")),
|
||||
};
|
||||
let limit = match ksp_store_api::RawPageLimit::new(limit) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(LiveFailure::new("inspection_page_limit")),
|
||||
};
|
||||
let page = ksp_store_api::RawInspectionPageRequest::new(offset, limit);
|
||||
return std::result::Result::Ok(ksp_store_api::RawTransactionInspectionQuery::new(network, slots, direction, page));
|
||||
}
|
||||
|
||||
fn page_query(
|
||||
direction: ksp_store_api::RawSortDirection,
|
||||
cursor: std::option::Option<ksp_store_api::RawPageCursor>,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-store-postgres-lib/tests/public_api.rs
|
||||
// version: 14
|
||||
// version: 15
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -117,8 +117,9 @@ fn pre_005_raw_write_bridge_uses_only_backend_independent_models_and_outcomes()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_006_raw_list_bridge_uses_backend_independent_query_page_and_reference_models() {
|
||||
fn v0_3_8_pre_004_raw_list_and_inspection_bridges_use_only_backend_independent_models() {
|
||||
let _list = ksp_store_postgres_lib::PostgresBackend::list_raw_transactions;
|
||||
let _inspection = ksp_store_postgres_lib::PostgresBackend::inspect_raw_transactions;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -134,6 +135,7 @@ where
|
||||
+ ksp_store_api::RawAccountObservationWrite
|
||||
+ ksp_store_api::RawAccountStateRead
|
||||
+ ksp_store_api::RawAccountStateWrite
|
||||
+ ksp_store_api::RawTransactionInspectionRead
|
||||
+ ksp_store_api::RawTransactionObservationRead
|
||||
+ ksp_store_api::RawTransactionObservationWrite
|
||||
+ ksp_store_api::RawTransactionRead
|
||||
@@ -146,7 +148,7 @@ where
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_008_postgres_backend_implements_exact_raw_capability_set_10_of_10() {
|
||||
fn v0_3_8_pre_004_postgres_backend_implements_transaction_inspection_as_capability_11_of_11() {
|
||||
assert_raw_capabilities::<ksp_store_postgres_lib::PostgresBackend>();
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user