257 lines
12 KiB
Rust
257 lines
12 KiB
Rust
// file: crates/ksp-store-api/tests/release_completeness.rs
|
|
// version: 1
|
|
|
|
//! Release-level boundary and completeness canaries for the `0.3.1` Store API RAW surface.
|
|
|
|
#[test]
|
|
fn pre_007_exact_crate_root_export_inventory_is_stable() {
|
|
let crate_root = include_str!("../src/lib.rs");
|
|
let mut actual = std::vec::Vec::new();
|
|
for line in crate_root.lines() {
|
|
let trimmed = line.trim();
|
|
if trimmed.starts_with("pub use ") {
|
|
actual.push(trimmed);
|
|
}
|
|
}
|
|
actual.sort_unstable();
|
|
let mut expected = std::vec![
|
|
"pub use ksp_core_lib::Error;",
|
|
"pub use ksp_core_lib::ErrorCode;",
|
|
"pub use ksp_core_lib::ErrorContext;",
|
|
"pub use ksp_core_lib::Pubkey;",
|
|
"pub use ksp_core_lib::Result;",
|
|
"pub use self::capability::StoreApiFuture;",
|
|
"pub use self::capability::raw_account::RawAccountObservationRead;",
|
|
"pub use self::capability::raw_account::RawAccountObservationWrite;",
|
|
"pub use self::capability::raw_account::RawAccountStateRead;",
|
|
"pub use self::capability::raw_account::RawAccountStateWrite;",
|
|
"pub use self::capability::raw_retention::RawTransactionRetentionRead;",
|
|
"pub use self::capability::raw_retention::RawTransactionRetentionWrite;",
|
|
"pub use self::capability::raw_transaction::RawTransactionObservationRead;",
|
|
"pub use self::capability::raw_transaction::RawTransactionObservationWrite;",
|
|
"pub use self::capability::raw_transaction::RawTransactionRead;",
|
|
"pub use self::capability::raw_transaction::RawTransactionWrite;",
|
|
"pub use self::error::ERROR_CODE_RAW_CONFLICT;",
|
|
"pub use self::error::ERROR_CODE_RAW_MODEL_INVALID;",
|
|
"pub use self::error::ERROR_CODE_RAW_PAYLOAD_INVALID;",
|
|
"pub use self::error::ERROR_CODE_RAW_PROVENANCE_INVALID;",
|
|
"pub use self::error::ERROR_CODE_RAW_QUERY_INVALID;",
|
|
"pub use self::error::ERROR_CODE_RAW_RETENTION_INVALID;",
|
|
"pub use self::model::raw_account::RawAccountObservation;",
|
|
"pub use self::model::raw_account::RawAccountState;",
|
|
"pub use self::model::raw_account::RawAccountStateReference;",
|
|
"pub use self::model::raw_outcome::RawAcquisitionWriteOutcome;",
|
|
"pub use self::model::raw_outcome::RawEntityWriteOutcome;",
|
|
"pub use self::model::raw_outcome::RawObservationWriteOutcome;",
|
|
"pub use self::model::raw_pagination::MAX_RAW_PAGE_CURSOR_BYTES;",
|
|
"pub use self::model::raw_pagination::RawAccountStateQuery;",
|
|
"pub use self::model::raw_pagination::RawPage;",
|
|
"pub use self::model::raw_pagination::RawPageCursor;",
|
|
"pub use self::model::raw_pagination::RawPageLimit;",
|
|
"pub use self::model::raw_pagination::RawPageRequest;",
|
|
"pub use self::model::raw_pagination::RawSlotRange;",
|
|
"pub use self::model::raw_pagination::RawSortDirection;",
|
|
"pub use self::model::raw_pagination::RawTransactionQuery;",
|
|
"pub use self::model::raw_primitives::MAX_RAW_ACCOUNT_DATA_BYTES;",
|
|
"pub use self::model::raw_primitives::MAX_RAW_CODE_BYTES;",
|
|
"pub use self::model::raw_primitives::MAX_RAW_PAYLOAD_BYTES;",
|
|
"pub use self::model::raw_primitives::MAX_RAW_SOURCE_PAYLOAD_BYTES;",
|
|
"pub use self::model::raw_primitives::MAX_RAW_UNIX_MILLIS;",
|
|
"pub use self::model::raw_primitives::RawAcquisitionOrigin;",
|
|
"pub use self::model::raw_primitives::RawAcquisitionProvenance;",
|
|
"pub use self::model::raw_primitives::RawContentHash;",
|
|
"pub use self::model::raw_primitives::RawFormatId;",
|
|
"pub use self::model::raw_primitives::RawNetworkId;",
|
|
"pub use self::model::raw_primitives::RawObservationKey;",
|
|
"pub use self::model::raw_primitives::RawPayload;",
|
|
"pub use self::model::raw_primitives::RawProvenanceCode;",
|
|
"pub use self::model::raw_primitives::RawTimestamp;",
|
|
"pub use self::model::raw_primitives::RawTransactionSignature;",
|
|
"pub use self::model::raw_retention::RawRetentionState;",
|
|
"pub use self::model::raw_retention::RawRetentionWriteOutcome;",
|
|
"pub use self::model::raw_retention::RawTransactionAcquisitionMode;",
|
|
"pub use self::model::raw_retention::RawTransactionRetentionTransition;",
|
|
"pub use self::model::raw_retention::RawTransactionTombstone;",
|
|
"pub use self::model::raw_transaction::RawTransaction;",
|
|
"pub use self::model::raw_transaction::RawTransactionObservation;",
|
|
"pub use self::model::raw_transaction::RawTransactionReference;",
|
|
];
|
|
expected.sort_unstable();
|
|
assert_eq!(actual, expected);
|
|
assert!(!crate_root.contains("pub mod "));
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_007_exact_production_module_inventory_is_raw_only() {
|
|
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
|
|
let root_names = rust_file_names(root.as_path());
|
|
assert!(root_names.is_ok());
|
|
let root_names = match root_names {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
assert_eq!(root_names, std::vec!["capability.rs", "error.rs", "lib.rs", "model.rs"]);
|
|
let model_names = rust_file_names(root.join("model").as_path());
|
|
assert!(model_names.is_ok());
|
|
let model_names = match model_names {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
assert_eq!(
|
|
model_names,
|
|
std::vec!["raw_account.rs", "raw_outcome.rs", "raw_pagination.rs", "raw_primitives.rs", "raw_retention.rs", "raw_transaction.rs"]
|
|
);
|
|
let capability_names = rust_file_names(root.join("capability").as_path());
|
|
assert!(capability_names.is_ok());
|
|
let capability_names = match capability_names {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
assert_eq!(capability_names, std::vec!["raw_account.rs", "raw_retention.rs", "raw_transaction.rs"]);
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_007_public_evolutive_enums_remain_non_exhaustive() {
|
|
let sources = [
|
|
include_str!("../src/model/raw_outcome.rs"),
|
|
include_str!("../src/model/raw_pagination.rs"),
|
|
include_str!("../src/model/raw_primitives.rs"),
|
|
include_str!("../src/model/raw_retention.rs"),
|
|
];
|
|
for enum_name in [
|
|
"RawAcquisitionOrigin",
|
|
"RawEntityWriteOutcome",
|
|
"RawObservationWriteOutcome",
|
|
"RawRetentionState",
|
|
"RawRetentionWriteOutcome",
|
|
"RawSortDirection",
|
|
"RawTransactionAcquisitionMode",
|
|
] {
|
|
assert_non_exhaustive(&sources, enum_name);
|
|
}
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_007_interface_store_ownership_and_negative_scope_remain_explicit() {
|
|
let interface_root = include_str!("../../ksp-interface-lib/src/lib.rs");
|
|
let store_sources = [
|
|
include_str!("../src/lib.rs"),
|
|
include_str!("../src/model.rs"),
|
|
include_str!("../src/model/raw_account.rs"),
|
|
include_str!("../src/model/raw_outcome.rs"),
|
|
include_str!("../src/model/raw_pagination.rs"),
|
|
include_str!("../src/model/raw_primitives.rs"),
|
|
include_str!("../src/model/raw_retention.rs"),
|
|
include_str!("../src/model/raw_transaction.rs"),
|
|
include_str!("../src/capability.rs"),
|
|
include_str!("../src/capability/raw_account.rs"),
|
|
include_str!("../src/capability/raw_retention.rs"),
|
|
include_str!("../src/capability/raw_transaction.rs"),
|
|
];
|
|
for persistent_name in ["RawTransaction", "RawAccountState", "RawRetentionState", "RawTransactionTombstone"] {
|
|
assert!(!interface_root.contains(persistent_name), "persistent Store model leaked into Interface crate root: {persistent_name}");
|
|
}
|
|
for source in store_sources {
|
|
for forbidden in [
|
|
"RawLogNotification",
|
|
"RawSlotEvent",
|
|
"RawVoteEvent",
|
|
"YellowstoneEntry",
|
|
"StructuralInstruction",
|
|
"StructuralTransaction",
|
|
"DecodedInstruction",
|
|
"DomainRecord",
|
|
"ProgramInstructionDecoder",
|
|
"PostgresStore",
|
|
"MySqlStore",
|
|
"StoreBackend",
|
|
] {
|
|
assert!(!source.contains(forbidden), "deferred or backend-specific Store surface detected: {forbidden}");
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
#[test]
|
|
fn pre_007_capability_inventory_stays_fine_grained_without_runtime_facade() {
|
|
let sources = [
|
|
include_str!("../src/capability/raw_account.rs"),
|
|
include_str!("../src/capability/raw_retention.rs"),
|
|
include_str!("../src/capability/raw_transaction.rs"),
|
|
];
|
|
let mut traits = std::vec::Vec::new();
|
|
for source in sources {
|
|
for line in source.lines() {
|
|
let trimmed = line.trim();
|
|
if trimmed.starts_with("pub trait ") {
|
|
traits.push(trimmed);
|
|
}
|
|
}
|
|
}
|
|
traits.sort_unstable();
|
|
let mut expected = std::vec![
|
|
"pub trait RawAccountObservationRead: std::marker::Send + std::marker::Sync {",
|
|
"pub trait RawAccountObservationWrite: std::marker::Send + std::marker::Sync {",
|
|
"pub trait RawAccountStateRead: std::marker::Send + std::marker::Sync {",
|
|
"pub trait RawAccountStateWrite: std::marker::Send + std::marker::Sync {",
|
|
"pub trait RawTransactionObservationRead: std::marker::Send + std::marker::Sync {",
|
|
"pub trait RawTransactionObservationWrite: std::marker::Send + std::marker::Sync {",
|
|
"pub trait RawTransactionRead: std::marker::Send + std::marker::Sync {",
|
|
"pub trait RawTransactionRetentionRead: std::marker::Send + std::marker::Sync {",
|
|
"pub trait RawTransactionRetentionWrite: std::marker::Send + std::marker::Sync {",
|
|
"pub trait RawTransactionWrite: std::marker::Send + std::marker::Sync {",
|
|
];
|
|
expected.sort_unstable();
|
|
assert_eq!(traits, expected);
|
|
return;
|
|
}
|
|
|
|
fn assert_non_exhaustive(sources: &[&str], enum_name: &str) {
|
|
let needle = "#[non_exhaustive]\n";
|
|
let declaration = std::format!("pub enum {enum_name}");
|
|
let mut found = false;
|
|
for source in sources {
|
|
if let std::option::Option::Some(position) = source.find(declaration.as_str()) {
|
|
let prefix = &source[..position];
|
|
let attribute_position = prefix.rfind(needle);
|
|
assert!(attribute_position.is_some(), "public evolutive enum is missing #[non_exhaustive]: {enum_name}");
|
|
found = true;
|
|
}
|
|
}
|
|
assert!(found, "public evolutive enum not found: {enum_name}");
|
|
return;
|
|
}
|
|
|
|
fn rust_file_names(directory: &std::path::Path) -> std::io::Result<std::vec::Vec<std::string::String>> {
|
|
let entries = match std::fs::read_dir(directory) {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let mut names = std::vec::Vec::new();
|
|
for entry in entries {
|
|
let entry = match entry {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let file_type = match entry.file_type() {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
if !file_type.is_file() {
|
|
continue;
|
|
}
|
|
let name = match entry.file_name().into_string() {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => continue,
|
|
};
|
|
if name.ends_with(".rs") {
|
|
names.push(name);
|
|
}
|
|
}
|
|
names.sort_unstable();
|
|
return std::result::Result::Ok(names);
|
|
}
|