v0.3.8-pre.011
This commit is contained in:
369
crates/ksp-app-store-desk/tests/release_completeness.rs
Normal file
369
crates/ksp-app-store-desk/tests/release_completeness.rs
Normal file
@@ -0,0 +1,369 @@
|
||||
// file: crates/ksp-app-store-desk/tests/release_completeness.rs
|
||||
// version: 1
|
||||
|
||||
//! Release-completeness firewall for the Store Desk V1 RAW surface.
|
||||
|
||||
fn parse_json(source: &str) -> serde_json::Result<serde_json::Value> {
|
||||
return serde_json::from_str::<serde_json::Value>(source);
|
||||
}
|
||||
|
||||
fn toml_table_keys<'a>(source: &'a str, table: &str) -> std::vec::Vec<&'a str> {
|
||||
let header = format!("[{table}]");
|
||||
let mut in_table = false;
|
||||
let mut keys = std::vec::Vec::<&str>::new();
|
||||
for line in source.lines() {
|
||||
let trimmed = line.trim();
|
||||
if trimmed == header {
|
||||
in_table = true;
|
||||
continue;
|
||||
}
|
||||
if in_table && trimmed.starts_with('[') {
|
||||
break;
|
||||
}
|
||||
if !in_table || trimmed.is_empty() || trimmed.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
if let std::option::Option::Some((key, _)) = trimmed.split_once('=') {
|
||||
let key = key.trim();
|
||||
let normalized = match key.strip_suffix(".workspace") {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => key,
|
||||
};
|
||||
keys.push(normalized);
|
||||
}
|
||||
}
|
||||
keys.sort_unstable();
|
||||
return keys;
|
||||
}
|
||||
|
||||
fn json_object_keys(value: &serde_json::Value) -> std::vec::Vec<&str> {
|
||||
let mut keys = match value.as_object() {
|
||||
std::option::Option::Some(object) => object.keys().map(std::string::String::as_str).collect::<std::vec::Vec<&str>>(),
|
||||
std::option::Option::None => std::vec::Vec::<&str>::new(),
|
||||
};
|
||||
keys.sort_unstable();
|
||||
return keys;
|
||||
}
|
||||
|
||||
fn source_segment<'a>(source: &'a str, start: &str, end: &str) -> &'a str {
|
||||
let after_start = match source.split_once(start) {
|
||||
std::option::Option::Some((_, tail)) => tail,
|
||||
std::option::Option::None => return "",
|
||||
};
|
||||
return match after_start.split_once(end) {
|
||||
std::option::Option::Some((head, _)) => head,
|
||||
std::option::Option::None => after_start,
|
||||
};
|
||||
}
|
||||
|
||||
fn handler_commands(source: &str) -> std::vec::Vec<&str> {
|
||||
let block = source_segment(source, "tauri::generate_handler![", "]);\n}");
|
||||
let mut commands = block.lines().map(str::trim).filter(|line| !line.is_empty()).map(|line| line.trim_end_matches(',')).collect::<std::vec::Vec<&str>>();
|
||||
commands.sort_unstable();
|
||||
return commands;
|
||||
}
|
||||
|
||||
fn crate_modules(source: &str) -> std::vec::Vec<&str> {
|
||||
let mut modules = source
|
||||
.lines()
|
||||
.map(str::trim)
|
||||
.filter(|line| line.starts_with("mod ") && line.ends_with(';'))
|
||||
.map(|line| line.trim_start_matches("mod ").trim_end_matches(';'))
|
||||
.collect::<std::vec::Vec<&str>>();
|
||||
modules.sort_unstable();
|
||||
return modules;
|
||||
}
|
||||
|
||||
fn visible_struct_names(source: &str) -> std::vec::Vec<&str> {
|
||||
let mut names = source
|
||||
.lines()
|
||||
.map(str::trim)
|
||||
.filter(|line| line.starts_with("pub(crate) struct "))
|
||||
.filter_map(|line| line.strip_prefix("pub(crate) struct "))
|
||||
.filter_map(|line| line.split_whitespace().next())
|
||||
.map(|name| name.trim_end_matches('{'))
|
||||
.collect::<std::vec::Vec<&str>>();
|
||||
names.sort_unstable();
|
||||
return names;
|
||||
}
|
||||
|
||||
fn const_line<'a>(source: &'a str, name: &str) -> &'a str {
|
||||
let prefix = format!("const {name}:");
|
||||
for line in source.lines() {
|
||||
if line.starts_with(prefix.as_str()) {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_011_rust_and_frontend_dependency_inventories_are_exact() {
|
||||
let manifest = include_str!("../Cargo.toml");
|
||||
assert_eq!(toml_table_keys(manifest, "build-dependencies"), ["tauri-build"]);
|
||||
assert_eq!(
|
||||
toml_table_keys(manifest, "dependencies"),
|
||||
[
|
||||
"chrono",
|
||||
"fs2",
|
||||
"ksp-config-lib",
|
||||
"ksp-core-lib",
|
||||
"ksp-logging-lib",
|
||||
"ksp-store-lib",
|
||||
"serde",
|
||||
"tauri",
|
||||
"tauri-plugin-tracing",
|
||||
"tokio",
|
||||
"ts-rs",
|
||||
]
|
||||
);
|
||||
assert_eq!(toml_table_keys(manifest, "dev-dependencies"), ["serde_json"]);
|
||||
let package = parse_json(include_str!("../package.json"));
|
||||
assert!(package.is_ok(), "Store Desk package.json must stay valid JSON");
|
||||
if let std::result::Result::Ok(package) = package {
|
||||
assert_eq!(
|
||||
json_object_keys(&package["dependencies"]),
|
||||
[
|
||||
"@fltsci/tauri-plugin-tracing",
|
||||
"@fortawesome/fontawesome-free",
|
||||
"@tauri-apps/api",
|
||||
"bootstrap",
|
||||
"datatables.net-bs5",
|
||||
"resize-observer-polyfill",
|
||||
"simplebar",
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
json_object_keys(&package["devDependencies"]),
|
||||
["@tauri-apps/cli", "@types/bootstrap", "@types/node", "sass-embedded", "typescript", "vite"]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_011_module_command_and_tauri_capability_inventories_are_exact() {
|
||||
let lib = include_str!("../src/lib.rs");
|
||||
assert_eq!(
|
||||
crate_modules(lib),
|
||||
[
|
||||
"app_state",
|
||||
"bootstrap",
|
||||
"constants",
|
||||
"dto_account",
|
||||
"dto_common",
|
||||
"dto_observation",
|
||||
"dto_transaction",
|
||||
"errors",
|
||||
"frontend_logging",
|
||||
"logging_runtime",
|
||||
"splash",
|
||||
"store_runtime",
|
||||
"tauri",
|
||||
"tw_main",
|
||||
"tw_splash",
|
||||
]
|
||||
);
|
||||
let tauri = include_str!("../src/tauri.rs");
|
||||
assert_eq!(
|
||||
handler_commands(tauri),
|
||||
[
|
||||
"emit_frontend_log",
|
||||
"get_shell_status",
|
||||
"splash_frontend_ready",
|
||||
"store_get_account_detail",
|
||||
"store_get_transaction_detail",
|
||||
"store_query_account_observations",
|
||||
"store_query_accounts",
|
||||
"store_query_transaction_observations",
|
||||
"store_query_transactions",
|
||||
"store_runtime_status",
|
||||
]
|
||||
);
|
||||
let capability = parse_json(include_str!("../capabilities/default.json"));
|
||||
assert!(capability.is_ok(), "Store Desk capability JSON must stay valid");
|
||||
if let std::result::Result::Ok(capability) = capability {
|
||||
assert_eq!(capability["identifier"], "default");
|
||||
assert_eq!(capability["windows"], serde_json::json!(["splash", "main"]));
|
||||
assert_eq!(capability["permissions"], serde_json::json!(["core:default", "tracing:default"]));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_011_config_composition_is_exact_network_scoped_and_secret_free() {
|
||||
let source = include_str!("../../../config/composite.ksp-app-store-desk.json");
|
||||
let composite = parse_json(source);
|
||||
assert!(composite.is_ok(), "Store Desk composite must stay valid JSON");
|
||||
if let std::result::Result::Ok(composite) = composite {
|
||||
assert_eq!(composite["format_version"], 1);
|
||||
assert_eq!(composite["default_profile"], "mainnet");
|
||||
let profiles = composite["profiles"].as_array();
|
||||
assert!(profiles.is_some());
|
||||
if let std::option::Option::Some(profiles) = profiles {
|
||||
assert_eq!(profiles.len(), 3);
|
||||
let profile_ids = profiles
|
||||
.iter()
|
||||
.map(|profile| match profile["profile_id"].as_str() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => "",
|
||||
})
|
||||
.collect::<std::vec::Vec<&str>>();
|
||||
assert_eq!(profile_ids, ["devnet", "mainnet", "testnet"]);
|
||||
for profile in profiles {
|
||||
let profile_id = match profile["profile_id"].as_str() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => "",
|
||||
};
|
||||
let documents = profile["documents"].as_array();
|
||||
assert!(documents.is_some());
|
||||
if let std::option::Option::Some(documents) = documents {
|
||||
assert_eq!(documents.len(), 2);
|
||||
assert_eq!(documents[0]["component_id"], "logging");
|
||||
assert_eq!(documents[0]["file_id"], "cfg.std.logging");
|
||||
assert_eq!(documents[0]["profile_id"], "supertrace");
|
||||
assert_eq!(documents[1]["component_id"], "store");
|
||||
assert_eq!(documents[1]["file_id"], "cfg.std.store");
|
||||
assert_eq!(documents[1]["profile_id"], profile_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for forbidden in ["postgres://", "postgresql://", "password", "credential", "connection_uri", "connectionUri", "KSP_STORE_POSTGRES"] {
|
||||
assert!(!source.contains(forbidden), "Store Desk composite leaked physical/secret material: {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_011_frontend_security_scan_keeps_network_storage_native_dialogs_and_physical_store_out() {
|
||||
let frontend = [
|
||||
include_str!("../frontend/ts/frontend_log.ts"),
|
||||
include_str!("../frontend/ts/invoke.ts"),
|
||||
include_str!("../frontend/ts/main.ts"),
|
||||
include_str!("../frontend/ts/splash.ts"),
|
||||
include_str!("../frontend/main.html"),
|
||||
include_str!("../frontend/splash.html"),
|
||||
]
|
||||
.join("\n");
|
||||
for forbidden in [
|
||||
"fetch(",
|
||||
"XMLHttpRequest",
|
||||
"WebSocket(",
|
||||
"EventSource(",
|
||||
"localStorage",
|
||||
"sessionStorage",
|
||||
"indexedDB",
|
||||
"document.cookie",
|
||||
"window.open(",
|
||||
"alert(",
|
||||
"confirm(",
|
||||
"prompt(",
|
||||
"postgres://",
|
||||
"postgresql://",
|
||||
"connection_uri",
|
||||
"connectionUri",
|
||||
"ksp_store_postgres",
|
||||
"tokio_postgres",
|
||||
"deadpool_postgres",
|
||||
"SELECT ",
|
||||
"INSERT ",
|
||||
"UPDATE ",
|
||||
"DELETE ",
|
||||
] {
|
||||
assert!(!frontend.contains(forbidden), "Store Desk frontend crossed the release security firewall: {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_011_ipc_dto_inventory_is_exact_and_table_contracts_remain_raw_byte_free() {
|
||||
let dto_account = include_str!("../src/dto_account.rs");
|
||||
let dto_common = include_str!("../src/dto_common.rs");
|
||||
let dto_observation = include_str!("../src/dto_observation.rs");
|
||||
let dto_transaction = include_str!("../src/dto_transaction.rs");
|
||||
let dto_sources = [dto_account, dto_common, dto_observation, dto_transaction].join("\n");
|
||||
assert_eq!(
|
||||
visible_struct_names(dto_sources.as_str()),
|
||||
[
|
||||
"CommandErrorDto",
|
||||
"ShellStatusDto",
|
||||
"StoreAccountDetailDto",
|
||||
"StoreAccountDetailRequestDto",
|
||||
"StoreAccountObservationQueryRequestDto",
|
||||
"StoreAccountObservationQueryResponseDto",
|
||||
"StoreAccountObservationRowDto",
|
||||
"StoreAccountQueryRequestDto",
|
||||
"StoreAccountQueryResponseDto",
|
||||
"StoreAccountRowDto",
|
||||
"StoreObservationProvenanceDto",
|
||||
"StoreRuntimeStatusDto",
|
||||
"StoreTransactionDetailDto",
|
||||
"StoreTransactionDetailRequestDto",
|
||||
"StoreTransactionObservationQueryRequestDto",
|
||||
"StoreTransactionObservationQueryResponseDto",
|
||||
"StoreTransactionObservationRowDto",
|
||||
"StoreTransactionQueryRequestDto",
|
||||
"StoreTransactionQueryResponseDto",
|
||||
"StoreTransactionRowDto",
|
||||
]
|
||||
);
|
||||
for request_marker in [
|
||||
"pub(crate) struct StoreTransactionQueryRequestDto",
|
||||
"pub(crate) struct StoreTransactionDetailRequestDto",
|
||||
"pub(crate) struct StoreAccountQueryRequestDto",
|
||||
"pub(crate) struct StoreAccountDetailRequestDto",
|
||||
"pub(crate) struct StoreTransactionObservationQueryRequestDto",
|
||||
"pub(crate) struct StoreAccountObservationQueryRequestDto",
|
||||
] {
|
||||
assert!(dto_sources.contains(request_marker), "missing Store Desk request DTO: {request_marker}");
|
||||
}
|
||||
for forbidden in ["RawPageCursor", "tokio_postgres", "deadpool_postgres", "ksp_store_postgres", "source_payload_bytes", "raw_bytes"] {
|
||||
assert!(!dto_sources.contains(forbidden), "Store Desk DTO surface leaked forbidden contract material: {forbidden}");
|
||||
}
|
||||
assert!(!source_segment(dto_transaction, "pub(crate) struct StoreTransactionRowDto", "/// Bounded detail projection").contains("payload_preview"));
|
||||
assert!(!source_segment(dto_account, "pub(crate) struct StoreAccountRowDto", "/// Bounded detail projection").contains("data_preview"));
|
||||
assert!(!dto_observation.contains("payload_preview"));
|
||||
assert!(!dto_observation.contains("data_preview"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_011_cursor_keyset_navigation_and_random_access_inspection_coexist_without_contract_collision() {
|
||||
let api = include_str!("../../ksp-store-api/src/lib.rs");
|
||||
let facade = include_str!("../../ksp-store-lib/src/lib.rs");
|
||||
let transaction = include_str!("../../ksp-store-postgres-lib/src/raw_transaction.rs");
|
||||
let account = include_str!("../../ksp-store-postgres-lib/src/raw_account.rs");
|
||||
let app_runtime = include_str!("../src/store_runtime.rs");
|
||||
for expected in ["RawPageCursor", "RawPageRequest", "RawInspectionPageRequest", "RawTransactionInspectionQuery", "RawAccountStateInspectionQuery"] {
|
||||
assert!(api.contains(expected), "Store API lost pagination/inspection contract: {expected}");
|
||||
assert!(facade.contains(expected), "Store facade lost pagination/inspection re-export: {expected}");
|
||||
}
|
||||
for cursor_statement in ["LIST_TRANSACTIONS_ASC_SQL", "LIST_TRANSACTIONS_DESC_SQL"] {
|
||||
let statement = const_line(transaction, cursor_statement);
|
||||
assert!(!statement.is_empty(), "missing transaction cursor statement: {cursor_statement}");
|
||||
assert!(!statement.contains("OFFSET"), "transaction cursor statement became OFFSET based: {cursor_statement}");
|
||||
}
|
||||
for cursor_statement in [
|
||||
"LIST_ACCOUNT_STATES_ASC_SQL",
|
||||
"LIST_ACCOUNT_STATES_DESC_SQL",
|
||||
"LIST_ACCOUNT_STATES_BY_PUBKEY_ASC_SQL",
|
||||
"LIST_ACCOUNT_STATES_BY_PUBKEY_DESC_SQL",
|
||||
] {
|
||||
let statement = const_line(account, cursor_statement);
|
||||
assert!(!statement.is_empty(), "missing account cursor statement: {cursor_statement}");
|
||||
assert!(!statement.contains("OFFSET"), "account cursor statement became OFFSET based: {cursor_statement}");
|
||||
}
|
||||
for inspection_statement in
|
||||
["INSPECT_TRANSACTIONS_ASC_SQL", "INSPECT_TRANSACTIONS_DESC_SQL", "INSPECT_OBSERVATIONS_ASC_SQL", "INSPECT_OBSERVATIONS_DESC_SQL"]
|
||||
{
|
||||
let statement = const_line(transaction, inspection_statement);
|
||||
assert!(statement.contains("OFFSET"), "transaction inspection statement lost random access: {inspection_statement}");
|
||||
}
|
||||
for inspection_statement in [
|
||||
"INSPECT_ACCOUNT_STATES_ASC_SQL",
|
||||
"INSPECT_ACCOUNT_STATES_DESC_SQL",
|
||||
"INSPECT_ACCOUNT_OBSERVATIONS_ASC_SQL",
|
||||
"INSPECT_ACCOUNT_OBSERVATIONS_DESC_SQL",
|
||||
] {
|
||||
let statement = const_line(account, inspection_statement);
|
||||
assert!(statement.contains("OFFSET"), "account inspection statement lost random access: {inspection_statement}");
|
||||
}
|
||||
assert!(app_runtime.contains("RawInspectionPageRequest::new"));
|
||||
assert!(!app_runtime.contains("RawPageCursor"));
|
||||
}
|
||||
Reference in New Issue
Block a user