v0.3.7-pre.004
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# file: crates/ksp-app-backfill-desk/Cargo.toml
|
||||
# version: 1
|
||||
# version: 2
|
||||
|
||||
[package]
|
||||
name = "ksp-app-backfill-desk"
|
||||
@@ -27,6 +27,7 @@ fs2.workspace = true
|
||||
ksp-config-lib = { path = "../ksp-config-lib" }
|
||||
ksp-core-lib = { path = "../ksp-core-lib" }
|
||||
ksp-logging-lib = { path = "../ksp-logging-lib" }
|
||||
ksp-onchain-transport-lib = { path = "../ksp-onchain-transport-lib" }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
tauri.workspace = true
|
||||
tauri-plugin-tracing.workspace = true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/app_state.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Shared backend state owned by the Backfill Desk Tauri application.
|
||||
|
||||
@@ -9,6 +9,8 @@ pub(crate) struct AppState {
|
||||
logging_runtime: std::sync::Mutex<LoggingRuntimeState>,
|
||||
splash_settings: crate::SplashSettings,
|
||||
splash_sequence_started: std::sync::atomic::AtomicBool,
|
||||
transport_runtime: std::option::Option<crate::TransportRuntime>,
|
||||
transport_startup_diagnostic: std::option::Option<crate::CommandErrorDto>,
|
||||
}
|
||||
|
||||
impl crate::AppState {
|
||||
@@ -29,6 +31,21 @@ impl crate::AppState {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let transport_startup = crate::initialize_transport(&config_management);
|
||||
let (transport_runtime, transport_startup_diagnostic) = match transport_startup {
|
||||
std::result::Result::Ok(value) => (std::option::Option::Some(value), std::option::Option::None),
|
||||
std::result::Result::Err(error) => {
|
||||
let diagnostic = crate::CommandErrorDto::from_error(&error);
|
||||
ksp_logging_lib::warn!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_TRANSPORT,
|
||||
error_domain = diagnostic.domain.as_str(),
|
||||
error_code = diagnostic.code.as_str(),
|
||||
"Backfill Desk Transport readiness is unavailable; keeping desktop shell available"
|
||||
);
|
||||
(std::option::Option::None, std::option::Option::Some(diagnostic))
|
||||
},
|
||||
};
|
||||
let splash_settings = crate::SplashSettings::load();
|
||||
let splash_settings = match splash_settings {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -65,6 +82,8 @@ impl crate::AppState {
|
||||
}),
|
||||
splash_settings,
|
||||
splash_sequence_started: std::sync::atomic::AtomicBool::new(false),
|
||||
transport_runtime,
|
||||
transport_startup_diagnostic,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -97,11 +116,25 @@ impl crate::AppState {
|
||||
application_version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||
config_document_count: document_count,
|
||||
fallback_logging_active: runtime.fallback_active,
|
||||
shell_phase: "pre.002-desktop-scaffold".to_owned(),
|
||||
shell_phase: "pre.004-transport-readiness".to_owned(),
|
||||
startup_diagnostic: runtime.startup_diagnostic.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
/// Builds the safe Transport-readiness subset of Backfill Desk options.
|
||||
pub(crate) fn backfill_options(&self) -> ksp_core_lib::Result<crate::BackfillDeskOptionsDto> {
|
||||
let runtime = self.transport_runtime.as_ref();
|
||||
return match runtime {
|
||||
std::option::Option::Some(value) => value.options(),
|
||||
std::option::Option::None => std::result::Result::Ok(crate::BackfillDeskOptionsDto {
|
||||
compatible_roles: std::vec::Vec::new(),
|
||||
configured_networks: std::vec::Vec::new(),
|
||||
transport_diagnostic: self.transport_startup_diagnostic.clone(),
|
||||
transport_ready: false,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the resolved splash timings captured during application bootstrap.
|
||||
#[must_use]
|
||||
pub(crate) const fn splash_settings(&self) -> crate::SplashSettings {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/constants.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
//! Application-owned tracing targets and domains.
|
||||
|
||||
@@ -15,6 +15,8 @@ pub(crate) const TRACING_DOMAIN_BOOTSTRAP: &str = "backfill.bootstrap";
|
||||
pub(crate) const TRACING_DOMAIN_FRONTEND: &str = "frontend";
|
||||
/// Structured domain used by the Backfill Desk shell.
|
||||
pub(crate) const TRACING_DOMAIN_SHELL: &str = "backfill.shell";
|
||||
/// Structured domain used by Transport readiness and role inventory operations.
|
||||
pub(crate) const TRACING_DOMAIN_TRANSPORT: &str = "backfill.transport";
|
||||
/// Structured domain used by Tauri window lifecycle operations.
|
||||
pub(crate) const TRACING_DOMAIN_WINDOWS: &str = "desktop.window";
|
||||
/// Owning target for backend events emitted by Backfill Desk.
|
||||
|
||||
@@ -1,10 +1,28 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/dto_common.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Common Tauri DTOs shared by the Backfill Desk shell.
|
||||
|
||||
use ts_rs::TS; // rust-rules: trait-import
|
||||
|
||||
/// Safe Transport-readiness subset of the Backfill Desk options contract.
|
||||
///
|
||||
/// Campaign scopes, commitments and backend-owned bounds are added in the dedicated request/DTO slice. Endpoint URLs, provider identities and credentials
|
||||
/// are intentionally absent.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_backfill_desk/dto_common/BackfillDeskOptionsDto.ts")]
|
||||
pub(crate) struct BackfillDeskOptionsDto {
|
||||
/// Logical roles that can route both RPC methods required by the Backfill runtime.
|
||||
pub(crate) compatible_roles: std::vec::Vec<String>,
|
||||
/// Distinct enabled HTTP cluster labels selected by the active Transport profile.
|
||||
pub(crate) configured_networks: std::vec::Vec<String>,
|
||||
/// Safe startup diagnostic when Transport configuration could not be resolved or constructed.
|
||||
pub(crate) transport_diagnostic: std::option::Option<CommandErrorDto>,
|
||||
/// Whether Transport currently has one coherent network and at least one compatible available role.
|
||||
pub(crate) transport_ready: bool,
|
||||
}
|
||||
|
||||
/// Safe command error projection that never serializes arbitrary KSP error context or source values.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/errors.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Application-local error codes for Backfill Desk composition and desktop runtime surfaces.
|
||||
|
||||
@@ -8,17 +8,13 @@ pub(crate) const ERROR_CODE_APP_STATE_INVALID: ksp_core_lib::ErrorCode = ksp_cor
|
||||
/// Shared Backfill Desk runtime state cannot be locked safely.
|
||||
pub(crate) const ERROR_CODE_APP_STATE_LOCK_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "app_state_lock_failed");
|
||||
/// Backfill Desk composite configuration is missing or references an unexpected document.
|
||||
pub(crate) const ERROR_CODE_CONFIG_COMPOSITE_INVALID: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("backfill_desk", "config_composite_invalid");
|
||||
pub(crate) const ERROR_CODE_CONFIG_COMPOSITE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "config_composite_invalid");
|
||||
/// Frontend logging requested an unsupported level.
|
||||
pub(crate) const ERROR_CODE_FRONTEND_LOG_LEVEL_INVALID: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("backfill_desk", "frontend_log_level_invalid");
|
||||
pub(crate) const ERROR_CODE_FRONTEND_LOG_LEVEL_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "frontend_log_level_invalid");
|
||||
/// Frontend logging requested a target outside the application whitelist.
|
||||
pub(crate) const ERROR_CODE_FRONTEND_LOG_TARGET_INVALID: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("backfill_desk", "frontend_log_target_invalid");
|
||||
pub(crate) const ERROR_CODE_FRONTEND_LOG_TARGET_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "frontend_log_target_invalid");
|
||||
/// Backfill Desk could not install the managed Logging runtime or its safe fallback.
|
||||
pub(crate) const ERROR_CODE_LOGGING_BOOTSTRAP_FAILED: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("backfill_desk", "logging_bootstrap_failed");
|
||||
pub(crate) const ERROR_CODE_LOGGING_BOOTSTRAP_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "logging_bootstrap_failed");
|
||||
/// Splash readiness was invoked from a window other than the splash window.
|
||||
pub(crate) const ERROR_CODE_SPLASH_ORIGIN_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "splash_origin_invalid");
|
||||
/// A KSP desk splash environment duration is malformed or exceeds its safety bound.
|
||||
@@ -30,3 +26,5 @@ pub(crate) const ERROR_CODE_TAURI_WINDOW_MISSING: ksp_core_lib::ErrorCode = ksp_
|
||||
/// A Tauri window show/focus/destroy/event operation failed.
|
||||
pub(crate) const ERROR_CODE_TAURI_WINDOW_OPERATION_FAILED: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("backfill_desk", "tauri_window_operation_failed");
|
||||
/// Backfill Desk cannot derive a safe Transport readiness contract from the Transport registry.
|
||||
pub(crate) const ERROR_CODE_TRANSPORT_READINESS_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "transport_readiness_invalid");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/lib.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
//! Tauri desktop application scaffold for controlling and inspecting KSP RAW backfill jobs.
|
||||
|
||||
@@ -16,6 +16,7 @@ mod frontend_logging;
|
||||
mod logging_runtime;
|
||||
mod splash;
|
||||
mod tauri;
|
||||
mod transport_runtime;
|
||||
mod tw_main;
|
||||
mod tw_splash;
|
||||
|
||||
@@ -46,6 +47,8 @@ pub(crate) use self::constants::TRACING_DOMAIN_BOOTSTRAP;
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_FRONTEND;
|
||||
/// Structured domain used by the Backfill Desk shell.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_SHELL;
|
||||
/// Structured domain used by Transport readiness and role inventory operations.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_TRANSPORT;
|
||||
/// Structured domain used by Tauri window lifecycle operations.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_WINDOWS;
|
||||
/// Owning target for backend events emitted by Backfill Desk.
|
||||
@@ -56,6 +59,8 @@ pub(crate) use self::constants::TRACING_TARGET_FRONTEND;
|
||||
pub(crate) use self::constants::TRACING_TARGET_FRONTEND_MAIN;
|
||||
/// Owning target for splash-window frontend events.
|
||||
pub(crate) use self::constants::TRACING_TARGET_FRONTEND_SPLASH;
|
||||
/// Safe Transport-readiness subset of the Backfill Desk options contract.
|
||||
pub(crate) use self::dto_common::BackfillDeskOptionsDto;
|
||||
/// Safe command error projection exposed to Tauri commands.
|
||||
pub(crate) use self::dto_common::CommandErrorDto;
|
||||
/// Safe scaffold/runtime snapshot exposed to the shell.
|
||||
@@ -82,6 +87,8 @@ pub(crate) use self::errors::ERROR_CODE_TAURI_RUNTIME_FAILED;
|
||||
pub(crate) use self::errors::ERROR_CODE_TAURI_WINDOW_MISSING;
|
||||
/// A Tauri window show/focus/destroy/event operation failed.
|
||||
pub(crate) use self::errors::ERROR_CODE_TAURI_WINDOW_OPERATION_FAILED;
|
||||
/// Backfill Desk cannot derive a safe Transport readiness contract.
|
||||
pub(crate) use self::errors::ERROR_CODE_TRANSPORT_READINESS_INVALID;
|
||||
/// Log payload sent by Backfill Desk frontend scripts.
|
||||
pub(crate) use self::frontend_logging::FrontendLogPayloadDto;
|
||||
/// Emits one validated frontend event through the KSP Logging facade.
|
||||
@@ -92,6 +99,10 @@ pub(crate) use self::logging_runtime::launch_identity;
|
||||
pub(crate) use self::splash::SplashOrderDto;
|
||||
/// Runtime timings used by the common desk splash lifecycle.
|
||||
pub(crate) use self::splash::SplashSettings;
|
||||
/// Safe and executable Transport runtime retained by application state.
|
||||
pub(crate) use self::transport_runtime::TransportRuntime;
|
||||
/// Initializes the composite-selected HTTP Transport runtime.
|
||||
pub(crate) use self::transport_runtime::initialize_transport;
|
||||
/// Resolves the required main window or returns a typed error.
|
||||
pub(crate) use self::tw_main::require_main_window;
|
||||
/// Shows and focuses the main Backfill Desk window.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/tauri.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
//! Tauri runtime assembly for the KSP Backfill desktop application.
|
||||
|
||||
@@ -72,7 +72,7 @@ fn configure_plugins(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<taur
|
||||
|
||||
#[allow(clippy::question_mark_used)] // Tauri generates the question-mark operator internally for async command dispatch.
|
||||
fn configure_commands(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
|
||||
return builder.invoke_handler(tauri::generate_handler![emit_frontend_log, get_runtime_status, splash_frontend_ready]);
|
||||
return builder.invoke_handler(tauri::generate_handler![backfill_options, emit_frontend_log, get_runtime_status, splash_frontend_ready]);
|
||||
}
|
||||
|
||||
fn configure_setup(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
|
||||
@@ -102,6 +102,15 @@ fn project_command_error(command: &'static str, domain: &'static str, error: &ks
|
||||
return crate::CommandErrorDto::from_error(error);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn backfill_options(state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::BackfillDeskOptionsDto, crate::CommandErrorDto> {
|
||||
let result = state.backfill_options();
|
||||
return match result {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(project_command_error("backfill_options", crate::TRACING_DOMAIN_TRANSPORT, &error)),
|
||||
};
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn emit_frontend_log(payload: crate::FrontendLogPayloadDto) -> std::result::Result<(), crate::CommandErrorDto> {
|
||||
let result = crate::emit_frontend_log_event(payload);
|
||||
|
||||
148
crates/ksp-app-backfill-desk/src/transport_runtime.rs
Normal file
148
crates/ksp-app-backfill-desk/src/transport_runtime.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/transport_runtime.rs
|
||||
// version: 1
|
||||
|
||||
//! Composite-selected HTTP Transport readiness owned by Backfill Desk.
|
||||
|
||||
/// Safe and executable Transport runtime retained by the application state.
|
||||
pub(crate) struct TransportRuntime {
|
||||
pool: ksp_onchain_transport_lib::HttpTransportPool,
|
||||
profile_id: String,
|
||||
}
|
||||
|
||||
impl TransportRuntime {
|
||||
/// Returns the composite-selected Transport profile identifier.
|
||||
#[must_use]
|
||||
pub(crate) fn profile_id(&self) -> &str {
|
||||
return self.profile_id.as_str();
|
||||
}
|
||||
|
||||
/// Builds the current safe Transport-only projection for the Backfill options surface.
|
||||
pub(crate) fn options(&self) -> ksp_core_lib::Result<crate::BackfillDeskOptionsDto> {
|
||||
let snapshot = self.pool.snapshot();
|
||||
let configured_networks = configured_networks(&snapshot);
|
||||
let compatible_roles = compatible_backfill_roles(&self.pool, &snapshot);
|
||||
let compatible_roles = match compatible_roles {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let transport_ready = configured_networks.len() == 1 && !compatible_roles.is_empty() && snapshot.available_endpoint_count() > 0;
|
||||
return std::result::Result::Ok(crate::BackfillDeskOptionsDto {
|
||||
compatible_roles,
|
||||
configured_networks,
|
||||
transport_diagnostic: std::option::Option::None,
|
||||
transport_ready,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves the composite-selected Transport profile and builds the executable HTTP pool.
|
||||
pub(crate) fn initialize_transport(management: &ksp_config_lib::ConfigManagement) -> ksp_core_lib::Result<TransportRuntime> {
|
||||
let environment = ksp_config_lib::ConfigEnvironment::load();
|
||||
let environment = match environment {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let composite = crate::load_backfill_desk_composite(management);
|
||||
let composite = match composite {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let profile = crate::required_composite_component_profile(&composite, crate::COMPOSITE_COMPONENT_ID_TRANSPORT, ksp_config_lib::FILE_ID_STD_TRANSPORT);
|
||||
let profile = match profile {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let resolved = management.engine().resolve_transport_config_profile(&profile, &environment);
|
||||
let resolved = match resolved {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let profile_id = resolved.profile_id().to_owned();
|
||||
let pool = ksp_onchain_transport_lib::HttpTransportPool::new(resolved.into_settings());
|
||||
let pool = match pool {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let runtime = TransportRuntime { pool, profile_id };
|
||||
let options = runtime.options();
|
||||
let options = match options {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
ksp_logging_lib::debug!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_TRANSPORT,
|
||||
transport_profile = runtime.profile_id(),
|
||||
network_count = options.configured_networks.len(),
|
||||
compatible_role_count = options.compatible_roles.len(),
|
||||
transport_ready = options.transport_ready,
|
||||
"initialized Backfill Desk HTTP Transport readiness from composite-managed configuration"
|
||||
);
|
||||
return std::result::Result::Ok(runtime);
|
||||
}
|
||||
|
||||
fn compatible_backfill_roles(
|
||||
pool: &ksp_onchain_transport_lib::HttpTransportPool,
|
||||
snapshot: &ksp_onchain_transport_lib::HttpTransportPoolSnapshot,
|
||||
) -> ksp_core_lib::Result<std::vec::Vec<String>> {
|
||||
let signatures = required_http_rpc_method("getSignaturesForAddress");
|
||||
let signatures = match signatures {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let transaction = required_http_rpc_method("getTransaction");
|
||||
let transaction = match transaction {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut candidates = std::collections::BTreeSet::new();
|
||||
for endpoint in snapshot.endpoints() {
|
||||
if !endpoint.enabled() {
|
||||
continue;
|
||||
}
|
||||
for role in endpoint.roles() {
|
||||
if role.enabled() {
|
||||
candidates.insert(role.role().to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut compatible = std::vec::Vec::new();
|
||||
for candidate in candidates {
|
||||
let role = ksp_onchain_transport_lib::HttpRoleName::new(candidate.clone());
|
||||
if pool.select_for_method(&role, signatures).is_ok() && pool.select_for_method(&role, transaction).is_ok() {
|
||||
compatible.push(candidate);
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(compatible);
|
||||
}
|
||||
|
||||
fn configured_networks(snapshot: &ksp_onchain_transport_lib::HttpTransportPoolSnapshot) -> std::vec::Vec<String> {
|
||||
let mut values = snapshot
|
||||
.endpoints()
|
||||
.iter()
|
||||
.filter_map(|endpoint| {
|
||||
if endpoint.enabled() {
|
||||
return std::option::Option::Some(endpoint.cluster().to_owned());
|
||||
}
|
||||
return std::option::Option::None;
|
||||
})
|
||||
.collect::<std::vec::Vec<_>>();
|
||||
values.sort();
|
||||
values.dedup();
|
||||
return values;
|
||||
}
|
||||
|
||||
fn required_http_rpc_method(method: &'static str) -> ksp_core_lib::Result<&'static ksp_onchain_transport_lib::HttpRpcMethodDescriptor> {
|
||||
let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method);
|
||||
return match descriptor {
|
||||
std::option::Option::Some(value) => std::result::Result::Ok(value),
|
||||
std::option::Option::None => std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_TRANSPORT_READINESS_INVALID, "Backfill Desk Transport registry is missing a required HTTP RPC method")
|
||||
.with_context("rpc_method", method),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/transport_runtime.rs"]
|
||||
mod tests;
|
||||
@@ -1,7 +1,7 @@
|
||||
// file: crates/ksp-app-backfill-desk/tests/config_composition.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Config composite contracts for Backfill Desk pre.003.
|
||||
//! Config composite and Transport-readiness contracts for Backfill Desk through pre.004.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -86,3 +86,73 @@ fn pre_003_named_profiles_keep_transport_and_store_network_selection_paired() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_004_default_composite_builds_http_pool_for_both_backfill_rpc_methods() {
|
||||
let engine = workspace_engine();
|
||||
assert!(engine.is_ok(), "workspace Config engine should be constructible: {engine:?}");
|
||||
let engine = match engine {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let file_id = ksp_config_lib::ConfigFileId::new(ksp_config_lib::FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK);
|
||||
let file_id = match file_id {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let composite = engine.load_resolved_composite(&file_id, std::option::Option::None);
|
||||
assert!(composite.is_ok(), "committed Backfill Desk composite should resolve: {composite:?}");
|
||||
let composite = match composite {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let transport = composite.component("transport");
|
||||
assert!(transport.is_some(), "Backfill Desk composite should expose Transport");
|
||||
let transport = match transport {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let environment = ksp_config_lib::ConfigEnvironment::load();
|
||||
assert!(environment.is_ok(), "Config environment should load for committed public devnet profile");
|
||||
let environment = match environment {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let resolved = engine.resolve_transport_config_profile(transport.resolved(), &environment);
|
||||
assert!(resolved.is_ok(), "composite-selected Transport should map: {resolved:?}");
|
||||
let resolved = match resolved {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert_eq!(resolved.profile_id(), "devnet_public");
|
||||
assert_eq!(resolved.selection_source(), ksp_config_lib::ConfigProfileSelectionSource::Composite);
|
||||
let pool = ksp_onchain_transport_lib::HttpTransportPool::new(resolved.into_settings());
|
||||
assert!(pool.is_ok(), "composite-selected Transport settings should construct an HTTP pool");
|
||||
let pool = match pool {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let snapshot = pool.snapshot();
|
||||
let mut clusters = snapshot
|
||||
.endpoints()
|
||||
.iter()
|
||||
.filter_map(|endpoint| {
|
||||
if endpoint.enabled() {
|
||||
return std::option::Option::Some(endpoint.cluster().to_owned());
|
||||
}
|
||||
return std::option::Option::None;
|
||||
})
|
||||
.collect::<std::vec::Vec<_>>();
|
||||
clusters.sort();
|
||||
clusters.dedup();
|
||||
assert_eq!(clusters, vec!["devnet".to_owned()]);
|
||||
let role = ksp_onchain_transport_lib::HttpRoleName::new("default");
|
||||
for method in ["getSignaturesForAddress", "getTransaction"] {
|
||||
let descriptor = ksp_onchain_transport_lib::find_http_rpc_method(method);
|
||||
assert!(descriptor.is_some(), "required Transport method {method} should exist");
|
||||
if let std::option::Option::Some(descriptor) = descriptor {
|
||||
let selection = pool.select_for_method(&role, descriptor);
|
||||
assert!(selection.is_ok(), "role default should route {method}: {selection:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/tests/desktop_contract.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Structural desktop contract checks for the Backfill Desk scaffold.
|
||||
|
||||
@@ -99,13 +99,7 @@ fn pre_002_package_dependencies_match_desk_baseline() {
|
||||
fn pre_002_splash_assets_and_styles_match_existing_desk_template() {
|
||||
let root = app_root();
|
||||
let reference = workspace_root().join("crates/ksp-app-solprices-desk");
|
||||
for relative in [
|
||||
"frontend/fonts/DOS_Amazigh.ttf",
|
||||
"frontend/imgs/logo.png",
|
||||
"frontend/imgs/splash.png",
|
||||
"icons/favicon.png",
|
||||
"icons/favicon.ico",
|
||||
] {
|
||||
for relative in ["frontend/fonts/DOS_Amazigh.ttf", "frontend/imgs/logo.png", "frontend/imgs/splash.png", "icons/favicon.png", "icons/favicon.ico"] {
|
||||
assert_eq!(read_bytes(root.join(relative).as_path()), read_bytes(reference.join(relative).as_path()), "Desk asset differs: {relative}");
|
||||
}
|
||||
let splash = read_text(root.join("frontend/splash.html").as_path());
|
||||
@@ -113,9 +107,7 @@ fn pre_002_splash_assets_and_styles_match_existing_desk_template() {
|
||||
assert!(splash.contains("Backfill Desk"));
|
||||
let splash_style = read_text(root.join("frontend/sass/splash.scss").as_path());
|
||||
let reference_style = read_text(reference.join("frontend/sass/splash.scss").as_path());
|
||||
let normalized = splash_style
|
||||
.replace("crates/ksp-app-backfill-desk", "crates/ksp-app-solprices-desk")
|
||||
.replace("// version: 1", "// version: 2");
|
||||
let normalized = splash_style.replace("crates/ksp-app-backfill-desk", "crates/ksp-app-solprices-desk").replace("// version: 1", "// version: 2");
|
||||
assert_eq!(normalized, reference_style);
|
||||
}
|
||||
|
||||
@@ -179,7 +171,7 @@ fn pre_003_composite_packaging_follows_atomic_config_registry_contract() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_003_bootstrap_uses_composite_logging_without_opening_transport_or_store() {
|
||||
fn pre_003_bootstrap_keeps_logging_composition_separate_from_transport_runtime() {
|
||||
let root = app_root();
|
||||
let bootstrap = read_text(root.join("src/bootstrap.rs").as_path());
|
||||
assert!(bootstrap.contains("FILE_ID_COMPOSITE_KSP_APP_BACKFILL_DESK"));
|
||||
@@ -188,6 +180,23 @@ fn pre_003_bootstrap_uses_composite_logging_without_opening_transport_or_store()
|
||||
assert!(bootstrap.contains("COMPOSITE_COMPONENT_ID_STORE"));
|
||||
assert!(bootstrap.contains("LogFilterLevel::Trace"));
|
||||
for forbidden in ["HttpTransportPool", "Store::open", "BackfillJobRuntime", "BackfillRequest"] {
|
||||
assert!(!bootstrap.contains(forbidden), "pre.003 opens later runtime surface {forbidden}");
|
||||
assert!(!bootstrap.contains(forbidden), "bootstrap.rs absorbs another runtime responsibility: {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_004_transport_runtime_builds_pool_and_validates_both_required_rpc_methods() {
|
||||
let root = app_root();
|
||||
let transport = read_text(root.join("src/transport_runtime.rs").as_path());
|
||||
assert!(transport.contains("HttpTransportPool::new"));
|
||||
assert!(transport.contains("getSignaturesForAddress"));
|
||||
assert!(transport.contains("getTransaction"));
|
||||
assert!(transport.contains("select_for_method"));
|
||||
assert!(transport.contains("configured_networks"));
|
||||
assert!(transport.contains("compatible_backfill_roles"));
|
||||
for forbidden in ["provider()", "url()", "Store::open", "BackfillJobRuntime", "BackfillRequest"] {
|
||||
assert!(!transport.contains(forbidden), "pre.004 opens or projects forbidden surface {forbidden}");
|
||||
}
|
||||
let tauri = read_text(root.join("src/tauri.rs").as_path());
|
||||
assert!(tauri.contains("backfill_options"));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/tests/desktop_security.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Security and dependency-boundary checks for the Backfill Desk scaffold.
|
||||
|
||||
@@ -43,7 +43,7 @@ fn read_text(path: &std::path::Path) -> String {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_002_capability_surface_is_core_plus_tracing_only() {
|
||||
fn capability_surface_remains_core_plus_tracing_while_transport_is_backend_only() {
|
||||
let root = app_root();
|
||||
let capability = read_text(root.join("capabilities/default.json").as_path());
|
||||
assert!(capability.contains("\"core:default\""));
|
||||
@@ -53,16 +53,9 @@ fn pre_002_capability_surface_is_core_plus_tracing_only() {
|
||||
}
|
||||
let manifest = read_text(root.join("Cargo.toml").as_path());
|
||||
assert!(manifest.contains("tauri-plugin-tracing.workspace = true"));
|
||||
for forbidden in [
|
||||
"ksp-job-backfill-lib",
|
||||
"ksp-onchain-transport-lib",
|
||||
"ksp-store-lib",
|
||||
"ksp-store-api",
|
||||
"ksp-store-postgres-lib",
|
||||
"reqwest",
|
||||
"tokio-postgres",
|
||||
] {
|
||||
assert!(!manifest.contains(forbidden), "pre.002 opens a later-layer dependency: {forbidden}");
|
||||
assert!(manifest.contains("ksp-onchain-transport-lib = { path = \"../ksp-onchain-transport-lib\" }"));
|
||||
for forbidden in ["ksp-job-backfill-lib", "ksp-store-lib", "ksp-store-api", "ksp-store-postgres-lib", "reqwest", "tokio-postgres"] {
|
||||
assert!(!manifest.contains(forbidden), "current Backfill Desk opens a forbidden dependency: {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +88,7 @@ fn pre_002_tauri_commands_remain_centralized() {
|
||||
assert_eq!(count, 0, "{} declares a Tauri command outside tauri.rs", path.display());
|
||||
}
|
||||
}
|
||||
assert_eq!(command_count, 3);
|
||||
assert_eq!(command_count, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -121,15 +114,21 @@ fn pre_002_frontend_instrumentation_avoids_business_or_secret_payloads() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_003_keeps_config_only_composition_boundary() {
|
||||
fn pre_004_opens_only_config_logging_and_onchain_transport_dependencies() {
|
||||
let root = app_root();
|
||||
let manifest = read_text(root.join("Cargo.toml").as_path());
|
||||
assert!(manifest.contains("ksp-config-lib = { path = \"../ksp-config-lib\" }"));
|
||||
for forbidden in ["ksp-job-backfill-lib", "ksp-onchain-transport-lib", "ksp-store-lib", "ksp-store-api", "ksp-store-postgres-lib"] {
|
||||
assert!(!manifest.contains(forbidden), "pre.003 opens a later-layer dependency: {forbidden}");
|
||||
assert!(manifest.contains("ksp-onchain-transport-lib = { path = \"../ksp-onchain-transport-lib\" }"));
|
||||
for forbidden in ["ksp-job-backfill-lib", "ksp-store-lib", "ksp-store-api", "ksp-store-postgres-lib"] {
|
||||
assert!(!manifest.contains(forbidden), "pre.004 opens a later-layer dependency: {forbidden}");
|
||||
}
|
||||
let bootstrap = read_text(root.join("src/bootstrap.rs").as_path());
|
||||
assert!(bootstrap.contains("LogFilterLevel::Trace"));
|
||||
assert!(!bootstrap.contains("HttpTransportPool"));
|
||||
assert!(!bootstrap.contains("Store::open"));
|
||||
let dto = read_text(root.join("src/dto_common.rs").as_path());
|
||||
assert!(dto.contains("BackfillDeskOptionsDto"));
|
||||
for forbidden in ["pub(crate) provider", "endpoint_url", "pub(crate) url", "pub(crate) credential", "pub(crate) token"] {
|
||||
assert!(!dto.contains(forbidden), "Transport options DTO source contains forbidden field marker {forbidden}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/unit_tests/dto_common.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
#[test]
|
||||
fn command_error_projection_excludes_arbitrary_context_and_source_values() {
|
||||
@@ -12,3 +12,23 @@ fn command_error_projection_excludes_arbitrary_context_and_source_values() {
|
||||
assert_eq!(dto.code, "failed");
|
||||
assert_eq!(dto.message, "safe message");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transport_options_projection_contains_only_safe_transport_metadata() {
|
||||
let dto = crate::BackfillDeskOptionsDto {
|
||||
compatible_roles: vec!["default".to_owned()],
|
||||
configured_networks: vec!["devnet".to_owned()],
|
||||
transport_diagnostic: std::option::Option::None,
|
||||
transport_ready: true,
|
||||
};
|
||||
let value = serde_json::to_value(dto);
|
||||
assert!(value.is_ok());
|
||||
if let std::result::Result::Ok(value) = value {
|
||||
let serialized = value.to_string();
|
||||
assert!(serialized.contains("default"));
|
||||
assert!(serialized.contains("devnet"));
|
||||
assert!(!serialized.contains("provider"));
|
||||
assert!(!serialized.contains("url"));
|
||||
assert!(!serialized.contains("endpoint"));
|
||||
}
|
||||
}
|
||||
|
||||
101
crates/ksp-app-backfill-desk/unit_tests/transport_runtime.rs
Normal file
101
crates/ksp-app-backfill-desk/unit_tests/transport_runtime.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
// file: crates/ksp-app-backfill-desk/unit_tests/transport_runtime.rs
|
||||
// version: 1
|
||||
|
||||
fn endpoint(
|
||||
name: &str,
|
||||
cluster: &str,
|
||||
role_name: &str,
|
||||
request_kinds: std::vec::Vec<ksp_onchain_transport_lib::HttpRequestKind>,
|
||||
) -> ksp_core_lib::Result<ksp_onchain_transport_lib::HttpEndpointSettings> {
|
||||
let url = ksp_onchain_transport_lib::HttpEndpointUrl::parse(format!("https://{name}.example.invalid"));
|
||||
let url = match url {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let role = ksp_onchain_transport_lib::HttpEndpointRoleSettings::new(
|
||||
ksp_onchain_transport_lib::HttpRoleName::new(role_name),
|
||||
true,
|
||||
request_kinds,
|
||||
0,
|
||||
ksp_onchain_transport_lib::HttpRoleLimits::new(
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
),
|
||||
);
|
||||
return std::result::Result::Ok(ksp_onchain_transport_lib::HttpEndpointSettings::new(
|
||||
name,
|
||||
true,
|
||||
ksp_onchain_transport_lib::HttpProviderName::new("fixture"),
|
||||
ksp_onchain_transport_lib::HttpClusterName::new(cluster),
|
||||
url,
|
||||
std::time::Duration::from_secs(1),
|
||||
std::time::Duration::from_secs(2),
|
||||
std::option::Option::Some(1),
|
||||
vec![role],
|
||||
));
|
||||
}
|
||||
|
||||
fn pool(endpoints: std::vec::Vec<ksp_onchain_transport_lib::HttpEndpointSettings>) -> ksp_core_lib::Result<ksp_onchain_transport_lib::HttpTransportPool> {
|
||||
let settings = ksp_onchain_transport_lib::HttpTransportSettings::new(
|
||||
endpoints,
|
||||
ksp_onchain_transport_lib::HttpRetrySettings::new(0, std::time::Duration::from_millis(10), std::time::Duration::from_millis(10)),
|
||||
);
|
||||
return ksp_onchain_transport_lib::HttpTransportPool::new(settings);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compatible_roles_require_both_backfill_rpc_methods() {
|
||||
let signatures = ksp_onchain_transport_lib::find_http_rpc_method("getSignaturesForAddress");
|
||||
let transaction = ksp_onchain_transport_lib::find_http_rpc_method("getTransaction");
|
||||
assert!(signatures.is_some());
|
||||
assert!(transaction.is_some());
|
||||
let signatures = match signatures {
|
||||
std::option::Option::Some(value) => ksp_onchain_transport_lib::HttpRequestKind::new(value.request_kind()),
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let transaction = match transaction {
|
||||
std::option::Option::Some(value) => ksp_onchain_transport_lib::HttpRequestKind::new(value.request_kind()),
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let complete = endpoint("complete", "devnet", "complete", vec![signatures.clone(), transaction]);
|
||||
let complete = match complete {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let partial = endpoint("partial", "devnet", "partial", vec![signatures]);
|
||||
let partial = match partial {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let runtime_pool = pool(vec![complete, partial]);
|
||||
assert!(runtime_pool.is_ok());
|
||||
let runtime_pool = match runtime_pool {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let snapshot = runtime_pool.snapshot();
|
||||
let roles = super::compatible_backfill_roles(&runtime_pool, &snapshot);
|
||||
assert!(roles.is_ok());
|
||||
if let std::result::Result::Ok(roles) = roles {
|
||||
assert_eq!(roles, vec!["complete".to_owned()]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configured_networks_are_safe_sorted_and_deduplicated() {
|
||||
let one = endpoint("one", "testnet", "default", vec![ksp_onchain_transport_lib::HttpRequestKind::wildcard()]);
|
||||
let two = endpoint("two", "devnet", "default", vec![ksp_onchain_transport_lib::HttpRequestKind::wildcard()]);
|
||||
let three = endpoint("three", "devnet", "default", vec![ksp_onchain_transport_lib::HttpRequestKind::wildcard()]);
|
||||
let (one, two, three) = match (one, two, three) {
|
||||
(std::result::Result::Ok(one), std::result::Result::Ok(two), std::result::Result::Ok(three)) => (one, two, three),
|
||||
_ => return,
|
||||
};
|
||||
let runtime_pool = pool(vec![one, two, three]);
|
||||
assert!(runtime_pool.is_ok());
|
||||
if let std::result::Result::Ok(runtime_pool) = runtime_pool {
|
||||
let values = super::configured_networks(&runtime_pool.snapshot());
|
||||
assert_eq!(values, vec!["devnet".to_owned(), "testnet".to_owned()]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user