v0.3.15-pre.006-fix.001

This commit is contained in:
2026-09-13 15:47:32 +02:00
parent 0a8ce6e5f9
commit f3ed86e54c
19 changed files with 676 additions and 341 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-app-raw-transaction-ingest-desk/README.md -->
<!-- version: 2 -->
<!-- version: 3 -->
# `ksp-app-raw-transaction-ingest-desk`
@@ -27,6 +27,8 @@ Worker -> acquisition continue et lifecycle d'exécution
L'inventaire de routes est calculé côté Rust à partir du composite Config validé et des settings Transport/Store résolus. Il ne lance aucun probe réseau et n'ouvre ni Store ni Worker.
Les profils applicatifs sont réseau-centriques (`devnet`, `mainnet`, `testnet`). Un profil peut agréger plusieurs profils Transport du même réseau : le provider reste une source de capability et ne devient jamais une identité réseau ni un choix de profil applicatif.
Le frontend ne reçoit aucune URL, credential, URI Store, metadata secrète, source key, handle runtime ou payload `RawTransaction`.
## Routes V1

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-app-raw-transaction-ingest-desk/USAGE.md -->
<!-- version: 2 -->
<!-- version: 3 -->
# Utilisation de `ksp-app-raw-transaction-ingest-desk`
@@ -17,7 +17,7 @@ Le runtime prépare les ressources Config nécessaires, charge le composite déd
## 2. Vue Routes
La vue Routes charge un inventaire déterministe construit côté Rust depuis le composite Config validé. Le sélecteur présente les profils logiques du composite et la vue projette, pour chaque route V1 :
La vue Routes charge un inventaire déterministe construit côté Rust depuis le composite Config validé. Le sélecteur présente les réseaux logiques `devnet`, `mainnet` et `testnet`, jamais les providers. Chaque réseau agrège les profils Transport same-network configurés pour ses capabilities. La vue projette, pour chaque route V1 :
```text
route_id
@@ -31,7 +31,7 @@ génération d'inventaire
`Configured` signifie uniquement que toutes les requirements Config nécessaires à la route sont cohérentes. Aucun probe réseau, aucune ouverture de Store et aucun Worker ne sont déclenchés par l'inventaire.
Un refresh reconstruit l'inventaire depuis Config et publie une nouvelle génération. La sélection visuelle d'un profil ne choisit aucune URL ou ressource physique et ne démarre aucune acquisition.
Un refresh reconstruit l'inventaire depuis Config et publie une nouvelle génération. La sélection visuelle d'un réseau ne choisit aucune URL, provider ou ressource physique et ne démarre aucune acquisition. Une capability optionnelle dont le secret n'est pas résolu rend uniquement la route qui en dépend indisponible ; les routes satisfaites par le Transport de base du même réseau restent composables.
Les URLs, tokens, metadata secrètes, handles Transport/Store/Worker, URI Store, source keys et payloads RAW restent backend-only.

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-app-raw-transaction-ingest-desk/frontend/main.html -->
<!-- version: 3 -->
<!-- version: 4 -->
<!DOCTYPE html>
<html lang="fr">
@@ -59,8 +59,8 @@
</div>
<div class="row g-3 align-items-end mb-4">
<div class="col-12 col-md-6">
<label class="form-label" for="routeProfile">Profil applicatif</label>
<select id="routeProfile" class="form-select" aria-label="Profil Raw Transaction Ingest Desk"></select>
<label class="form-label" for="routeProfile">Réseau logique</label>
<select id="routeProfile" class="form-select" aria-label="Réseau Raw Transaction Ingest Desk"></select>
</div>
<div class="col-6 col-md-3">
<div class="small text-body-secondary">Réseau</div>
@@ -74,7 +74,7 @@
<div class="alert alert-info" role="status">
<strong>Configured</strong> signifie uniquement que Config prouve la composabilité. Les connexions, le Store et le Worker seront revalidés au Start dans les tranches suivantes.
</div>
<div class="small text-body-secondary mb-3">Profil affiché : <span id="routeSelectedProfile" class="app-route-code"></span></div>
<div class="small text-body-secondary mb-3">Réseau affiché : <span id="routeSelectedProfile" class="app-route-code"></span></div>
<div id="routeFoundation" class="row g-3 app-route-grid" aria-live="polite"></div>
</section>

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-raw-transaction-ingest-desk/frontend/ts/main.ts
// version: 3
// version: 4
import "bootstrap";
import ResizeObserver from "resize-observer-polyfill";
@@ -226,7 +226,7 @@ function bindRefreshControls(): void {
return;
}
selectedProfileId = selector.value;
frontendDebug("main", "Raw Transaction Ingest Desk logical profile selected", { profileId: selectedProfileId });
frontendDebug("main", "Raw Transaction Ingest Desk logical network selected", { profileId: selectedProfileId });
renderSelectedProfile();
});
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-raw-transaction-ingest-desk/src/constants.rs
// version: 2
// version: 3
//! Application-owned tracing targets and Config component identifiers.
@@ -9,6 +9,12 @@ pub(crate) const COMPOSITE_COMPONENT_ID_LOGGING: &str = "logging";
pub(crate) const COMPOSITE_COMPONENT_ID_STORE: &str = "store";
/// Composite component identifier for Transport.
pub(crate) const COMPOSITE_COMPONENT_ID_TRANSPORT: &str = "transport";
/// Composite-local optional Transport source for Helius WebSocket capabilities.
pub(crate) const COMPOSITE_COMPONENT_ID_TRANSPORT_HELIUS: &str = "transport.helius";
/// Prefix used by optional same-network Transport capability sources.
pub(crate) const COMPOSITE_COMPONENT_ID_TRANSPORT_PREFIX: &str = "transport.";
/// Composite-local optional Transport source for Yellowstone gRPC capabilities.
pub(crate) const COMPOSITE_COMPONENT_ID_TRANSPORT_YELLOWSTONE: &str = "transport.yellowstone";
/// Structured domain used while bootstrapping Config and Logging.
pub(crate) const TRACING_DOMAIN_BOOTSTRAP: &str = "raw_transaction_ingest.bootstrap";
/// Structured domain used by technical frontend events.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-raw-transaction-ingest-desk/src/lib.rs
// version: 2
// version: 3
//! Tauri desktop application scaffold for composing and supervising KSP live RAW transaction ingest routes.
@@ -44,6 +44,12 @@ pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_LOGGING;
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_STORE;
/// Composite-local identifier for the standard Transport component.
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT;
/// Composite-local optional Transport source for Helius WebSocket capabilities.
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT_HELIUS;
/// Prefix used by optional same-network Transport capability sources.
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT_PREFIX;
/// Composite-local optional Transport source for Yellowstone gRPC capabilities.
pub(crate) use self::constants::COMPOSITE_COMPONENT_ID_TRANSPORT_YELLOWSTONE;
/// Structured domain used while bootstrapping Config and Logging.
pub(crate) use self::constants::TRACING_DOMAIN_BOOTSTRAP;
/// Structured domain used by technical frontend events.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-raw-transaction-ingest-desk/src/route_inventory.rs
// version: 1
// version: 2
//! Config-only logical route inventory for Raw Transaction Ingest Desk.
@@ -8,28 +8,74 @@ struct CompositeProfileCatalog {
profile_ids: std::vec::Vec<String>,
}
#[derive(Default)]
struct RouteCapabilities {
has_get_transaction: bool,
has_http_block_scan: bool,
has_yellowstone: bool,
has_standard_logs: bool,
has_standard_block: bool,
has_helius_transaction: bool,
helius_source_missing_secret: bool,
yellowstone_source_missing_secret: bool,
}
impl RouteCapabilities {
fn absorb_transport(&mut self, transport: &ksp_config_lib::ResolvedTransportConfig, network: &str) {
self.has_get_transaction |= http_role_supports_methods(transport.http_settings(), &["getTransaction"]);
self.has_http_block_scan |= http_role_supports_methods(transport.http_settings(), &["getSlot", "getBlocksWithLimit", "getBlock"]);
self.has_yellowstone |= has_yellowstone_grpc(transport, network);
self.has_standard_logs |= has_ws_capability(
transport,
network,
ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard,
ksp_onchain_transport_lib::WsSubscriptionKind::Logs,
);
self.has_standard_block |= has_ws_capability(
transport,
network,
ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard,
ksp_onchain_transport_lib::WsSubscriptionKind::Block,
);
self.has_helius_transaction |= has_ws_capability(
transport,
network,
ksp_onchain_transport_lib::WsProtocolKind::HeliusLaserStream,
ksp_onchain_transport_lib::WsSubscriptionKind::HeliusTransaction,
);
}
}
/// Rebuilds the complete safe route inventory from validated Config without opening any network, Store or Worker resource.
pub(crate) fn build_route_inventory(management: &ksp_config_lib::ConfigManagement, generation: u32) -> ksp_core_lib::Result<crate::RawIngestRouteInventoryDto> {
let catalog = composite_profile_catalog(management);
let catalog = match catalog {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
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),
};
return build_route_inventory_with_environment(management, &environment, generation);
}
fn build_route_inventory_with_environment(
management: &ksp_config_lib::ConfigManagement,
environment: &ksp_config_lib::ConfigEnvironment,
generation: u32,
) -> ksp_core_lib::Result<crate::RawIngestRouteInventoryDto> {
let catalog = composite_profile_catalog(management);
let catalog = match catalog {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut profiles = std::vec::Vec::with_capacity(catalog.profile_ids.len());
for profile_id in &catalog.profile_ids {
profiles.push(build_profile_inventory(management, &environment, profile_id.as_str(), profile_id == &catalog.default_profile));
profiles.push(build_profile_inventory(management, environment, profile_id.as_str(), profile_id == &catalog.default_profile));
}
ksp_logging_lib::debug!(
target: crate::TRACING_TARGET,
domain = crate::TRACING_DOMAIN_INVENTORY,
generation,
profile_count = profiles.len(),
"rebuilt Raw Transaction Ingest Desk Config-only route inventory"
"rebuilt Raw Transaction Ingest Desk network-centric Config-only route inventory"
);
return std::result::Result::Ok(crate::RawIngestRouteInventoryDto { default_profile: catalog.default_profile, generation, profiles });
}
@@ -92,14 +138,6 @@ fn build_profile_inventory(
return unavailable_profile(profile_id, is_default, std::option::Option::None, crate::RawIngestRouteUnavailableReason::ProfileUnresolved);
},
};
let transport_profile =
crate::required_composite_component_profile(&composite, crate::COMPOSITE_COMPONENT_ID_TRANSPORT, ksp_config_lib::FILE_ID_STD_TRANSPORT);
let transport_profile = match transport_profile {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return unavailable_profile(profile_id, is_default, std::option::Option::None, crate::RawIngestRouteUnavailableReason::ProfileUnresolved);
},
};
let store_profile = crate::required_composite_component_profile(&composite, crate::COMPOSITE_COMPONENT_ID_STORE, ksp_config_lib::FILE_ID_STD_STORE);
let store_profile = match store_profile {
std::result::Result::Ok(value) => value,
@@ -107,91 +145,152 @@ fn build_profile_inventory(
return unavailable_profile(profile_id, is_default, std::option::Option::None, crate::RawIngestRouteUnavailableReason::ProfileUnresolved);
},
};
let transport = management.engine().resolve_transport_config_profile(&transport_profile, environment);
let store = management.engine().resolve_store_config_profile(&store_profile, environment);
let network_hint = resolved_network_hint(&transport, &store);
if transport.is_err() || store.is_err() {
let reason = resolution_failure_reason(&transport, &store);
return unavailable_profile(profile_id, is_default, network_hint.as_deref(), reason);
}
let transport = match transport {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return unavailable_profile(profile_id, is_default, network_hint.as_deref(), crate::RawIngestRouteUnavailableReason::ProfileUnresolved);
},
};
let store = match store {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return unavailable_profile(profile_id, is_default, network_hint.as_deref(), crate::RawIngestRouteUnavailableReason::ProfileUnresolved);
std::result::Result::Err(error) => {
let reason = resolution_error_reason(&error);
return unavailable_profile(profile_id, is_default, std::option::Option::None, reason);
},
};
let store_network = store.settings().network().as_str().to_owned();
if !transport_matches_network(&transport, store_network.as_str()) {
let network = store.settings().network().as_str().to_owned();
let base_transport_profile =
crate::required_composite_component_profile(&composite, crate::COMPOSITE_COMPONENT_ID_TRANSPORT, ksp_config_lib::FILE_ID_STD_TRANSPORT);
let base_transport_profile = match base_transport_profile {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return unavailable_profile(
profile_id,
is_default,
std::option::Option::Some(network.as_str()),
crate::RawIngestRouteUnavailableReason::ProfileUnresolved,
);
},
};
let base_transport = management.engine().resolve_transport_config_profile(&base_transport_profile, environment);
let base_transport = match base_transport {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return unavailable_profile(profile_id, is_default, std::option::Option::Some(network.as_str()), resolution_error_reason(&error));
},
};
if !transport_matches_network(&base_transport, network.as_str()) {
return unavailable_profile(
profile_id,
is_default,
std::option::Option::Some(store_network.as_str()),
std::option::Option::Some(network.as_str()),
crate::RawIngestRouteUnavailableReason::NetworkMismatch,
);
}
let routes = build_configured_profile_routes(&transport, store_network.as_str());
return crate::RawIngestProfileInventoryDto {
is_default,
network: std::option::Option::Some(store_network),
profile_id: profile_id.to_owned(),
routes,
};
let mut capabilities = RouteCapabilities::default();
capabilities.absorb_transport(&base_transport, network.as_str());
for (component_id, component) in composite.components() {
if component_id == crate::COMPOSITE_COMPONENT_ID_TRANSPORT {
continue;
}
if !component_id.starts_with(crate::COMPOSITE_COMPONENT_ID_TRANSPORT_PREFIX) {
continue;
}
if component_id != crate::COMPOSITE_COMPONENT_ID_TRANSPORT_HELIUS && component_id != crate::COMPOSITE_COMPONENT_ID_TRANSPORT_YELLOWSTONE {
return unavailable_profile(
profile_id,
is_default,
std::option::Option::Some(network.as_str()),
crate::RawIngestRouteUnavailableReason::ProfileUnresolved,
);
}
if component.resolved().file_id().as_str() != ksp_config_lib::FILE_ID_STD_TRANSPORT {
return unavailable_profile(
profile_id,
is_default,
std::option::Option::Some(network.as_str()),
crate::RawIngestRouteUnavailableReason::ProfileUnresolved,
);
}
let resolved = management.engine().resolve_transport_config_profile(component.resolved(), environment);
let resolved = match resolved {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) if error.code() == ksp_config_lib::ERROR_CODE_ENVIRONMENT_VARIABLE_MISSING => {
mark_optional_source_missing_secret(&mut capabilities, component_id.as_str());
continue;
},
std::result::Result::Err(_) => {
return unavailable_profile(
profile_id,
is_default,
std::option::Option::Some(network.as_str()),
crate::RawIngestRouteUnavailableReason::ProfileUnresolved,
);
},
};
if !transport_matches_network(&resolved, network.as_str()) {
return unavailable_profile(
profile_id,
is_default,
std::option::Option::Some(network.as_str()),
crate::RawIngestRouteUnavailableReason::NetworkMismatch,
);
}
capabilities.absorb_transport(&resolved, network.as_str());
}
let routes = build_configured_profile_routes(&capabilities, network.as_str());
return crate::RawIngestProfileInventoryDto { is_default, network: std::option::Option::Some(network), profile_id: profile_id.to_owned(), routes };
}
fn build_configured_profile_routes(transport: &ksp_config_lib::ResolvedTransportConfig, network: &str) -> std::vec::Vec<crate::RawIngestRouteDto> {
let has_get_transaction = http_role_supports_methods(transport.http_settings(), &["getTransaction"]);
let has_http_block_scan = http_role_supports_methods(transport.http_settings(), &["getSlot", "getBlocksWithLimit", "getBlock"]);
let has_yellowstone = has_yellowstone_grpc(transport, network);
let has_standard_logs =
has_ws_capability(transport, network, ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard, ksp_onchain_transport_lib::WsSubscriptionKind::Logs);
let has_standard_block =
has_ws_capability(transport, network, ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard, ksp_onchain_transport_lib::WsSubscriptionKind::Block);
let has_helius_transaction = has_ws_capability(
transport,
network,
ksp_onchain_transport_lib::WsProtocolKind::HeliusLaserStream,
ksp_onchain_transport_lib::WsSubscriptionKind::HeliusTransaction,
);
fn mark_optional_source_missing_secret(capabilities: &mut RouteCapabilities, component_id: &str) {
if component_id == crate::COMPOSITE_COMPONENT_ID_TRANSPORT_HELIUS {
capabilities.helius_source_missing_secret = true;
}
if component_id == crate::COMPOSITE_COMPONENT_ID_TRANSPORT_YELLOWSTONE {
capabilities.yellowstone_source_missing_secret = true;
}
}
fn build_configured_profile_routes(capabilities: &RouteCapabilities, network: &str) -> std::vec::Vec<crate::RawIngestRouteDto> {
let yellowstone_reason = if capabilities.yellowstone_source_missing_secret {
crate::RawIngestRouteUnavailableReason::MissingRequiredSecret
} else {
crate::RawIngestRouteUnavailableReason::MissingYellowstoneGrpc
};
let helius_reason = if capabilities.helius_source_missing_secret {
crate::RawIngestRouteUnavailableReason::MissingRequiredSecret
} else {
crate::RawIngestRouteUnavailableReason::MissingHeliusTransactionCapability
};
return vec![
route_with_requirements(
crate::RawIngestRouteId::YellowstoneHydrated,
network,
&[
(has_yellowstone, crate::RawIngestRouteUnavailableReason::MissingYellowstoneGrpc),
(has_get_transaction, crate::RawIngestRouteUnavailableReason::MissingHttpGetTransaction),
(capabilities.has_yellowstone, yellowstone_reason),
(capabilities.has_get_transaction, crate::RawIngestRouteUnavailableReason::MissingHttpGetTransaction),
],
),
route_with_requirements(
crate::RawIngestRouteId::StandardLogsHydrated,
network,
&[
(has_standard_logs, crate::RawIngestRouteUnavailableReason::MissingWsLogsCapability),
(has_get_transaction, crate::RawIngestRouteUnavailableReason::MissingHttpGetTransaction),
(capabilities.has_standard_logs, crate::RawIngestRouteUnavailableReason::MissingWsLogsCapability),
(capabilities.has_get_transaction, crate::RawIngestRouteUnavailableReason::MissingHttpGetTransaction),
],
),
route_with_requirements(
crate::RawIngestRouteId::StandardBlockDirect,
network,
&[(has_standard_block, crate::RawIngestRouteUnavailableReason::MissingWsBlockCapability)],
&[(capabilities.has_standard_block, crate::RawIngestRouteUnavailableReason::MissingWsBlockCapability)],
),
route_with_requirements(
crate::RawIngestRouteId::HeliusTransactionHydrated,
network,
&[
(has_helius_transaction, crate::RawIngestRouteUnavailableReason::MissingHeliusTransactionCapability),
(has_get_transaction, crate::RawIngestRouteUnavailableReason::MissingHttpGetTransaction),
(capabilities.has_helius_transaction, helius_reason),
(capabilities.has_get_transaction, crate::RawIngestRouteUnavailableReason::MissingHttpGetTransaction),
],
),
route_with_requirements(
crate::RawIngestRouteId::HttpBlockPolling,
network,
&[(has_http_block_scan, crate::RawIngestRouteUnavailableReason::MissingHttpBlockScan)],
&[(capabilities.has_http_block_scan, crate::RawIngestRouteUnavailableReason::MissingHttpBlockScan)],
),
];
}
@@ -222,40 +321,13 @@ fn unavailable_profile(
return crate::RawIngestProfileInventoryDto { is_default, network: network.map(str::to_owned), profile_id: profile_id.to_owned(), routes };
}
fn resolution_failure_reason(
transport: &ksp_core_lib::Result<ksp_config_lib::ResolvedTransportConfig>,
store: &ksp_core_lib::Result<ksp_config_lib::ResolvedStoreConfig>,
) -> crate::RawIngestRouteUnavailableReason {
let transport_missing_secret = match transport {
std::result::Result::Err(error) => error.code() == ksp_config_lib::ERROR_CODE_ENVIRONMENT_VARIABLE_MISSING,
std::result::Result::Ok(_) => false,
};
let store_missing_secret = match store {
std::result::Result::Err(error) => error.code() == ksp_config_lib::ERROR_CODE_ENVIRONMENT_VARIABLE_MISSING,
std::result::Result::Ok(_) => false,
};
if transport_missing_secret || store_missing_secret {
fn resolution_error_reason(error: &ksp_core_lib::Error) -> crate::RawIngestRouteUnavailableReason {
if error.code() == ksp_config_lib::ERROR_CODE_ENVIRONMENT_VARIABLE_MISSING {
return crate::RawIngestRouteUnavailableReason::MissingRequiredSecret;
}
return crate::RawIngestRouteUnavailableReason::ProfileUnresolved;
}
fn resolved_network_hint(
transport: &ksp_core_lib::Result<ksp_config_lib::ResolvedTransportConfig>,
store: &ksp_core_lib::Result<ksp_config_lib::ResolvedStoreConfig>,
) -> std::option::Option<String> {
if let std::result::Result::Ok(store) = store {
return std::option::Option::Some(store.settings().network().as_str().to_owned());
}
if let std::result::Result::Ok(transport) = transport {
let networks = enabled_transport_networks(transport);
if networks.len() == 1 {
return networks.into_iter().next();
}
}
return std::option::Option::None;
}
fn transport_matches_network(transport: &ksp_config_lib::ResolvedTransportConfig, network: &str) -> bool {
let networks = enabled_transport_networks(transport);
return networks.len() == 1 && networks.contains(network);

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-raw-transaction-ingest-desk/unit_tests/route_inventory.rs
// version: 1
// version: 2
#[test]
fn pre_006_route_requirement_projection_is_fail_closed_and_ordered() {
@@ -27,14 +27,10 @@ fn pre_006_route_requirement_projection_is_fail_closed_and_ordered() {
#[test]
fn pre_006_unavailable_profile_projects_all_five_routes_without_physical_material() {
let profile = super::unavailable_profile(
"provider_profile",
false,
std::option::Option::Some("testnet"),
crate::RawIngestRouteUnavailableReason::MissingRequiredSecret,
);
let profile =
super::unavailable_profile("mainnet", false, std::option::Option::Some("mainnet"), crate::RawIngestRouteUnavailableReason::MissingRequiredSecret);
assert_eq!(profile.routes.len(), 5);
assert_eq!(profile.network.as_deref(), std::option::Option::Some("testnet"));
assert_eq!(profile.network.as_deref(), std::option::Option::Some("mainnet"));
for route in profile.routes {
assert!(!route.selectable);
assert_eq!(route.reason, std::option::Option::Some(crate::RawIngestRouteUnavailableReason::MissingRequiredSecret));
@@ -42,60 +38,163 @@ fn pre_006_unavailable_profile_projects_all_five_routes_without_physical_materia
}
#[test]
fn pre_006_committed_devnet_public_inventory_proves_only_config_composability() {
let arguments = std::vec::Vec::<std::ffi::OsString>::new();
let management = crate::config_management(arguments.as_slice());
fn pre_006_fix_001_network_profiles_aggregate_same_network_transport_capabilities() {
let management = committed_management();
assert!(management.is_ok());
let management = match management {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let inventory = crate::build_route_inventory(&management, 7);
let environment = all_optional_source_secrets();
let inventory = super::build_route_inventory_with_environment(&management, &environment, 7);
assert!(inventory.is_ok());
let inventory = match inventory {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(inventory.generation, 7);
assert_eq!(inventory.default_profile, "devnet_public");
let profile = inventory.profiles.iter().find(|profile| return profile.profile_id == "devnet_public");
assert!(profile.is_some());
let profile = match profile {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert!(profile.is_default);
assert_eq!(profile.network.as_deref(), std::option::Option::Some("devnet"));
assert_eq!(profile.routes.len(), 5);
assert_route(
profile,
crate::RawIngestRouteId::YellowstoneHydrated,
false,
std::option::Option::Some(crate::RawIngestRouteUnavailableReason::MissingYellowstoneGrpc),
assert_eq!(inventory.default_profile, "devnet");
assert_eq!(
inventory.profiles.iter().map(|profile| return profile.profile_id.as_str()).collect::<std::vec::Vec<_>>(),
vec!["devnet", "mainnet", "testnet"]
);
assert_route(profile, crate::RawIngestRouteId::StandardLogsHydrated, true, std::option::Option::None);
assert_route(
profile,
crate::RawIngestRouteId::StandardBlockDirect,
false,
std::option::Option::Some(crate::RawIngestRouteUnavailableReason::MissingWsBlockCapability),
);
assert_route(
profile,
crate::RawIngestRouteId::HeliusTransactionHydrated,
false,
std::option::Option::Some(crate::RawIngestRouteUnavailableReason::MissingHeliusTransactionCapability),
);
assert_route(profile, crate::RawIngestRouteId::HttpBlockPolling, true, std::option::Option::None);
let devnet = profile(&inventory, "devnet");
assert!(devnet.is_some());
if let std::option::Option::Some(devnet) = devnet {
assert!(devnet.is_default);
assert_eq!(devnet.network.as_deref(), std::option::Option::Some("devnet"));
assert_route(devnet, crate::RawIngestRouteId::YellowstoneHydrated, true, std::option::Option::None);
assert_route(devnet, crate::RawIngestRouteId::StandardLogsHydrated, true, std::option::Option::None);
assert_route(
devnet,
crate::RawIngestRouteId::StandardBlockDirect,
false,
std::option::Option::Some(crate::RawIngestRouteUnavailableReason::MissingWsBlockCapability),
);
assert_route(devnet, crate::RawIngestRouteId::HeliusTransactionHydrated, true, std::option::Option::None);
assert_route(devnet, crate::RawIngestRouteId::HttpBlockPolling, true, std::option::Option::None);
}
let mainnet = profile(&inventory, "mainnet");
assert!(mainnet.is_some());
if let std::option::Option::Some(mainnet) = mainnet {
assert_eq!(mainnet.network.as_deref(), std::option::Option::Some("mainnet"));
assert_route(mainnet, crate::RawIngestRouteId::YellowstoneHydrated, true, std::option::Option::None);
assert_route(mainnet, crate::RawIngestRouteId::StandardLogsHydrated, true, std::option::Option::None);
assert_route(mainnet, crate::RawIngestRouteId::HeliusTransactionHydrated, true, std::option::Option::None);
assert_route(mainnet, crate::RawIngestRouteId::HttpBlockPolling, true, std::option::Option::None);
}
let testnet = profile(&inventory, "testnet");
assert!(testnet.is_some());
if let std::option::Option::Some(testnet) = testnet {
assert_eq!(testnet.network.as_deref(), std::option::Option::Some("testnet"));
assert_route(testnet, crate::RawIngestRouteId::YellowstoneHydrated, true, std::option::Option::None);
assert_route(testnet, crate::RawIngestRouteId::StandardLogsHydrated, true, std::option::Option::None);
assert_route(
testnet,
crate::RawIngestRouteId::HeliusTransactionHydrated,
false,
std::option::Option::Some(crate::RawIngestRouteUnavailableReason::MissingHeliusTransactionCapability),
);
assert_route(testnet, crate::RawIngestRouteId::HttpBlockPolling, true, std::option::Option::None);
}
let serialized = serde_json::to_string(&inventory);
assert!(serialized.is_ok());
if let std::result::Result::Ok(serialized) = serialized {
for forbidden in ["http://", "https://", "ws://", "wss://", "api-key", "x-token", "connection_uri", "source_key", "endpoint_name"] {
assert!(!serialized.contains(forbidden), "safe inventory leaked forbidden marker {forbidden}");
for forbidden in [
"http://",
"https://",
"ws://",
"wss://",
"api-key",
"x-token",
"connection_uri",
"source_key",
"endpoint_name",
"publicnode_mainnet",
"helius_mainnet",
] {
assert!(!serialized.contains(forbidden), "safe network inventory leaked forbidden source marker {forbidden}");
}
}
}
#[test]
fn pre_006_fix_001_missing_optional_source_secret_does_not_disable_base_network_routes() {
let management = committed_management();
assert!(management.is_ok());
let management = match management {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let environment = ksp_config_lib::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
let inventory = super::build_route_inventory_with_environment(&management, &environment, 11);
assert!(inventory.is_ok());
let inventory = match inventory {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let mainnet = profile(&inventory, "mainnet");
assert!(mainnet.is_some());
if let std::option::Option::Some(mainnet) = mainnet {
assert_route(mainnet, crate::RawIngestRouteId::StandardLogsHydrated, true, std::option::Option::None);
assert_route(mainnet, crate::RawIngestRouteId::HttpBlockPolling, true, std::option::Option::None);
assert_route(
mainnet,
crate::RawIngestRouteId::YellowstoneHydrated,
false,
std::option::Option::Some(crate::RawIngestRouteUnavailableReason::MissingRequiredSecret),
);
assert_route(
mainnet,
crate::RawIngestRouteId::HeliusTransactionHydrated,
false,
std::option::Option::Some(crate::RawIngestRouteUnavailableReason::MissingRequiredSecret),
);
}
}
fn committed_management() -> ksp_core_lib::Result<ksp_config_lib::ConfigManagement> {
let manifest = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let crates_root = manifest.parent();
let crates_root = match crates_root {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(ksp_core_lib::Error::new(crate::ERROR_CODE_ROUTE_INVENTORY_INVALID, "test manifest has no crates parent"));
},
};
let workspace = crates_root.parent();
let workspace = match workspace {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(ksp_core_lib::Error::new(crate::ERROR_CODE_ROUTE_INVENTORY_INVALID, "test crates root has no workspace parent"));
},
};
let bootstrap = ksp_config_lib::ConfigBootstrapOptions::from_paths(workspace.join("config"), workspace.join("config/schemas"));
let bootstrap = match bootstrap {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let registry = ksp_config_lib::ConfigFileRegistry::defaults();
let registry = match registry {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(ksp_config_lib::ConfigManagement::new(ksp_config_lib::ConfigDocumentEngine::new(bootstrap, registry)));
}
fn all_optional_source_secrets() -> ksp_config_lib::ConfigEnvironment {
let mut process = std::collections::BTreeMap::<String, String>::new();
process.insert("KSP_SECRET_HELIUS_API_KEY".to_owned(), "helius-route-inventory-canary".to_owned());
process.insert("KSP_SECRET_ORBITFLARE_DEVNET_GRPC_X_TOKEN".to_owned(), "orbitflare-route-inventory-canary".to_owned());
process.insert("KSP_SECRET_PUBLICNODE_MAINNET_GRPC_X_TOKEN".to_owned(), "publicnode-mainnet-route-inventory-canary".to_owned());
process.insert("KSP_SECRET_PUBLICNODE_TESTNET_GRPC_X_TOKEN".to_owned(), "publicnode-testnet-route-inventory-canary".to_owned());
return ksp_config_lib::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new());
}
fn profile<'a>(inventory: &'a crate::RawIngestRouteInventoryDto, profile_id: &str) -> std::option::Option<&'a crate::RawIngestProfileInventoryDto> {
return inventory.profiles.iter().find(|profile| return profile.profile_id == profile_id);
}
fn assert_route(
profile: &crate::RawIngestProfileInventoryDto,
route_id: crate::RawIngestRouteId,

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-config-lib/README.md -->
<!-- version: 13 -->
<!-- version: 14 -->
# ksp-config-lib
@@ -104,7 +104,7 @@ Le document Logging refuse les valeurs de sensibilité `Secret` dans sa configur
- [`../../config/std.store.json`](../../config/std.store.json) — targets Store PostgreSQL Devnet/Mainnet/Testnet et settings runtime bornés ;
- [`../../config/std.wallet.json`](../../config/std.wallet.json) — racine Wallet globale et sous-répertoire optionnel par profil ;
- [`../../config/composite.ksp-app-backfill-desk.json`](../../config/composite.ksp-app-backfill-desk.json) — composition Logging/Transport/Store de Backfill Desk ;
- [`../../config/composite.ksp-app-raw-transaction-ingest-desk.json`](../../config/composite.ksp-app-raw-transaction-ingest-desk.json) — profils cohérents Logging/Transport/Store du Raw Transaction Ingest Desk ;
- [`../../config/composite.ksp-app-raw-transaction-ingest-desk.json`](../../config/composite.ksp-app-raw-transaction-ingest-desk.json) — profils réseau-centriques Logging/Transport/Store du Raw Transaction Ingest Desk, capables dagréger plusieurs sources Transport same-network ;
- [`../../config/composite.ksp-app-solprices-desk.json`](../../config/composite.ksp-app-solprices-desk.json) — composition Logging/Off-chain Transport de SOL Prices Desk ;
- [`../../config/composite.ksp-app-store-desk.json`](../../config/composite.ksp-app-store-desk.json) — composition Logging/Store de Store Desk ;
- [`../../config/composite.ksp-app-wallet-desk.json`](../../config/composite.ksp-app-wallet-desk.json) — composition Logging/Off-chain Transport/On-chain Transport/Wallet de Wallet Desk ;

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-config-lib/USAGE.md -->
<!-- version: 16 -->
<!-- version: 17 -->
# Utilisation de ksp-config-lib
@@ -158,13 +158,13 @@ secret_metadata = metadata dont la valeur doit avoir une provenance KSP_SECRET_*
`protocol` n'est pas un nouveau `WsProtocolKind` et ne transforme pas PublicNode en protocole. Config résout les placeholders, vérifie la classe de sensibilité des metadata puis construit `YellowstoneGrpcTransportSettings`. Transport ne lit jamais l'environnement. Les valeurs de `secret_metadata` sont disponibles au runtime mais redacted dans `safe_value` et dans les représentations `Debug`.
Le provider affiche `solana-yellowstone-grpc.publicnode.com:443` ; le profil versionné `publicnode_mainnet` le représente sous la forme URL TLS requise par Transport, `https://solana-yellowstone-grpc.publicnode.com:443`, sans credential. Aucun hostname Testnet n'est versionné tant que sa valeur exacte n'est pas confirmée depuis une source opérateur/officielle.
Les profils versionnés `publicnode_mainnet` et `publicnode_testnet` représentent leurs endpoints Yellowstone sous la forme URL TLS requise par Transport et conservent le `x-token` dans `secret_metadata`. Ils restent des sources Transport network-scoped et ne définissent jamais lidentité du réseau applicatif.
L'accesseur historique `into_transport_settings()` conserve volontairement son tuple `(HTTP, Option<WS>)`. Un consumer ayant besoin des trois backends utilise `into_all_transport_settings()` ou les accesseurs séparés afin de ne pas casser silencieusement les consumers V2.
Les protocoles WebSocket restent `kind = "solana_standard"` et `kind = "helius_laserstream"`. Chaque endpoint peut en plus déclarer `capabilities`, liste de descriptors `WsSubscriptionKind` (`account`, `block`, `logs`, `program`, `root`, `signature`, `slot`, `slots_updates`, `vote`, `helius_transaction`). L'absence de cette liste reste backward-compatible mais ne constitue aucune preuve de support ; une liste présente est mappée explicitement vers Transport puis validée contre le protocole. Config ne déduit jamais une capability depuis `provider`.
Les profils Solana standard versionnés déclarent conservativement les six familles stables `account/logs/program/root/signature/slot` et ne déclarent donc pas `block`. Les profils `helius_devnet` et `helius_mainnet` déclarent les sept familles Helius déjà supportées par la façade (`account/logs/program/root/signature/slot/slots_updates`) plus `helius_transaction`, sans `block` ni `vote`. Helius LaserStream WebSocket conserve ses URLs Config-owned et sa clé `KSP_SECRET_HELIUS_API_KEY`; cette surface est indépendante de Yellowstone gRPC.
Les profils Solana standard versionnés déclarent conservativement les six familles stables `account/logs/program/root/signature/slot` et ne déclarent donc pas `block`. Le profil `testnet_public` fournit le socle HTTP + WS standard Testnet sans credential ; `publicnode_testnet` reste une source Yellowstone optionnelle séparée. Les profils `helius_devnet` et `helius_mainnet` déclarent les sept familles Helius déjà supportées par la façade (`account/logs/program/root/signature/slot/slots_updates`) plus `helius_transaction`, sans `block` ni `vote`. Helius LaserStream WebSocket conserve ses URLs Config-owned et sa clé `KSP_SECRET_HELIUS_API_KEY`; cette surface est indépendante de Yellowstone gRPC.
Les scalaires `*_ms` restent des valeurs Config et sont convertis en `std::time::Duration` par l'adapter. La dépendance reste unidirectionnelle : Config connaît les contrats Transport pour les construire ; Transport ne connaît ni Config, ni `.env`, ni les variables KSP.
@@ -347,6 +347,8 @@ Elles n'altèrent jamais l'environnement hérité du processus. Une valeur proce
Sur Unix, un nouveau `.env` est créé avec des permissions privées `0600`; les permissions existantes sont préservées lors des remplacements atomiques.
Le composite `cfg.composite.ksp-app-raw-transaction-ingest-desk` expose des profils applicatifs `devnet`, `mainnet` et `testnet`. Un même profil peut référencer `transport`, `transport.helius` et `transport.yellowstone` tant que tous les profils Transport résolus appartiennent au même réseau que Store. Le provider identifie une source de capability, jamais un réseau logique.
## 8. `.env.example`
`/.env.example` est l'inventaire versionné. `/.env` reste local et ignoré.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/unit_tests/composite.rs
// version: 5
// version: 6
const TEST_COMPOSITE_FILENAME: &str = "examples/composite.example.json";
const TEST_COMPOSITE_FILE_ID: &str = "cfg.composite.test";
@@ -129,7 +129,7 @@ fn duplicate_component_ids_are_rejected_inside_one_composite_profile() {
}
#[test]
fn v0_3_15_pre_003_raw_ingest_composite_keeps_transport_and_store_networks_coherent() {
fn v0_3_15_pre_006_fix_001_raw_ingest_composite_is_network_centric_and_keeps_all_transport_sources_coherent() {
let workspace = workspace_root();
let bootstrap = crate::ConfigBootstrapOptions::from_paths(workspace.join("config"), workspace.join("config/schemas"));
assert!(bootstrap.is_ok(), "committed Config roots should be valid: {bootstrap:?}");
@@ -148,47 +148,58 @@ fn v0_3_15_pre_003_raw_ingest_composite_keeps_transport_and_store_networks_coher
process.insert("KSP_SECRET_PUBLICNODE_MAINNET_GRPC_X_TOKEN".to_owned(), "publicnode-mainnet-composite-canary".to_owned());
process.insert("KSP_SECRET_PUBLICNODE_TESTNET_GRPC_X_TOKEN".to_owned(), "publicnode-testnet-composite-canary".to_owned());
let environment = crate::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new());
for (profile_id, expected_network) in [
("devnet_public", "devnet"),
("helius_devnet", "devnet"),
("orbitflare_devnet", "devnet"),
("mainnet_public", "mainnet"),
("helius_mainnet", "mainnet"),
("publicnode_mainnet", "mainnet"),
("publicnode_testnet", "testnet"),
for (profile_id, expected_network, expected_transport_profiles) in [
("devnet", "devnet", vec!["devnet_public", "helius_devnet", "orbitflare_devnet"]),
("mainnet", "mainnet", vec!["mainnet_public", "helius_mainnet", "publicnode_mainnet"]),
("testnet", "testnet", vec!["testnet_public", "publicnode_testnet"]),
] {
let composite = engine.load_resolved_composite(&file_id, std::option::Option::Some(profile_id));
assert!(composite.is_ok(), "Raw Ingest composite profile {profile_id} should resolve: {composite:?}");
assert!(composite.is_ok(), "Raw Ingest network profile {profile_id} should resolve: {composite:?}");
let composite = match composite {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => continue,
};
assert_eq!(composite.profile_id(), profile_id);
let transport_component = composite.component("transport");
let store_component = composite.component("store");
assert!(transport_component.is_some(), "profile {profile_id} should contain Transport");
assert!(store_component.is_some(), "profile {profile_id} should contain Store");
let (transport_component, store_component) = match (transport_component, store_component) {
(std::option::Option::Some(transport), std::option::Option::Some(store)) => (transport, store),
_ => continue,
let store_component = match store_component {
std::option::Option::Some(value) => value,
std::option::Option::None => continue,
};
assert_eq!(transport_component.resolved().profile_id(), profile_id);
assert_eq!(store_component.resolved().profile_id(), expected_network);
let transport = engine.resolve_transport_config_profile(transport_component.resolved(), &environment);
let store = engine.resolve_store_config_profile(store_component.resolved(), &environment);
assert!(transport.is_ok(), "Transport profile {profile_id} should map: {transport:?}");
assert!(store.is_ok(), "Store profile {expected_network} should map: {store:?}");
let (transport, store) = match (transport, store) {
(std::result::Result::Ok(transport), std::result::Result::Ok(store)) => (transport, store),
_ => continue,
};
assert_eq!(store.settings().network().as_str(), expected_network);
assert!(transport.http_settings().endpoints().iter().all(|endpoint| return endpoint.cluster().as_str() == expected_network));
if let std::option::Option::Some(ws) = transport.ws_settings() {
assert!(ws.endpoints().iter().all(|endpoint| return endpoint.cluster().as_str() == expected_network));
if let std::result::Result::Ok(store) = store {
assert_eq!(store.settings().network().as_str(), expected_network);
}
if let std::option::Option::Some(grpc) = transport.grpc_settings() {
assert!(grpc.endpoints().iter().all(|endpoint| return endpoint.cluster().as_str() == expected_network));
let transport_components = composite
.components()
.iter()
.filter(|(component_id, component)| {
return component_id.starts_with("transport") && component.resolved().file_id().as_str() == crate::FILE_ID_STD_TRANSPORT;
})
.map(|(_, component)| return component)
.collect::<std::vec::Vec<_>>();
assert_eq!(transport_components.len(), expected_transport_profiles.len());
let actual_profiles = transport_components.iter().map(|component| return component.resolved().profile_id()).collect::<std::vec::Vec<_>>();
for expected_transport_profile in expected_transport_profiles {
assert!(
actual_profiles.contains(&expected_transport_profile),
"network profile {profile_id} should include Transport source {expected_transport_profile}"
);
}
for component in transport_components {
let transport = engine.resolve_transport_config_profile(component.resolved(), &environment);
assert!(transport.is_ok(), "Transport source {} should map: {transport:?}", component.resolved().profile_id());
if let std::result::Result::Ok(transport) = transport {
assert!(transport.http_settings().endpoints().iter().all(|endpoint| return endpoint.cluster().as_str() == expected_network));
if let std::option::Option::Some(ws) = transport.ws_settings() {
assert!(ws.endpoints().iter().all(|endpoint| return endpoint.cluster().as_str() == expected_network));
}
if let std::option::Option::Some(grpc) = transport.grpc_settings() {
assert!(grpc.endpoints().iter().all(|endpoint| return endpoint.cluster().as_str() == expected_network));
}
}
}
}
}

View File

@@ -1,9 +1,9 @@
{
"format_version": 1,
"default_profile": "devnet_public",
"default_profile": "devnet",
"profiles": [
{
"profile_id": "devnet_public",
"profile_id": "devnet",
"documents": [
{
"component_id": "logging",
@@ -16,42 +16,12 @@
"profile_id": "secret_test"
},
{
"component_id": "store",
"file_id": "cfg.std.store",
"profile_id": "devnet"
}
]
},
{
"profile_id": "helius_devnet",
"documents": [
{
"component_id": "logging",
"file_id": "cfg.std.logging",
"profile_id": "local_dev"
},
{
"component_id": "transport",
"component_id": "transport.helius",
"file_id": "cfg.std.transport",
"profile_id": "helius_devnet"
},
{
"component_id": "store",
"file_id": "cfg.std.store",
"profile_id": "devnet"
}
]
},
{
"profile_id": "orbitflare_devnet",
"documents": [
{
"component_id": "logging",
"file_id": "cfg.std.logging",
"profile_id": "local_dev"
},
{
"component_id": "transport",
"component_id": "transport.yellowstone",
"file_id": "cfg.std.transport",
"profile_id": "secret_test"
},
@@ -63,7 +33,7 @@
]
},
{
"profile_id": "mainnet_public",
"profile_id": "mainnet",
"documents": [
{
"component_id": "logging",
@@ -75,6 +45,16 @@
"file_id": "cfg.std.transport",
"profile_id": "secret_test"
},
{
"component_id": "transport.helius",
"file_id": "cfg.std.transport",
"profile_id": "helius_devnet"
},
{
"component_id": "transport.yellowstone",
"file_id": "cfg.std.transport",
"profile_id": "secret_test"
},
{
"component_id": "store",
"file_id": "cfg.std.store",
@@ -83,7 +63,7 @@
]
},
{
"profile_id": "helius_mainnet",
"profile_id": "testnet",
"documents": [
{
"component_id": "logging",
@@ -96,42 +76,7 @@
"profile_id": "secret_test"
},
{
"component_id": "store",
"file_id": "cfg.std.store",
"profile_id": "mainnet"
}
]
},
{
"profile_id": "publicnode_mainnet",
"documents": [
{
"component_id": "logging",
"file_id": "cfg.std.logging",
"profile_id": "local_dev"
},
{
"component_id": "transport",
"file_id": "cfg.std.transport",
"profile_id": "secret_test"
},
{
"component_id": "store",
"file_id": "cfg.std.store",
"profile_id": "mainnet"
}
]
},
{
"profile_id": "publicnode_testnet",
"documents": [
{
"component_id": "logging",
"file_id": "cfg.std.logging",
"profile_id": "local_dev"
},
{
"component_id": "transport",
"component_id": "transport.yellowstone",
"file_id": "cfg.std.transport",
"profile_id": "secret_test"
},

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/unit_tests/transport.rs
// version: 14
// version: 15
#[test]
fn fixture_transport_profile_maps_complete_runtime_contract() {
@@ -211,6 +211,33 @@ fn committed_v3_orbitflare_devnet_profile_maps_provider_neutral_yellowstone_grpc
}
}
#[test]
fn v0_3_15_pre_006_fix_001_testnet_public_is_keyless_standard_transport_without_yellowstone() {
let engine = committed_engine();
let engine = match engine {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
let resolved = engine.load_resolved_transport_config(std::option::Option::Some("testnet_public"), &environment);
assert!(resolved.is_ok(), "committed keyless Testnet Transport profile should map without provider credentials: {resolved:?}");
if let std::result::Result::Ok(resolved) = resolved {
assert_eq!(resolved.profile_id(), "testnet_public");
assert_eq!(resolved.settings().endpoints().len(), 1);
assert_eq!(resolved.settings().endpoints()[0].cluster().as_str(), "testnet");
let ws = resolved.ws_settings();
assert!(ws.is_some());
if let std::option::Option::Some(ws) = ws {
assert_eq!(ws.endpoints().len(), 1);
assert_eq!(ws.endpoints()[0].cluster().as_str(), "testnet");
assert_eq!(ws.endpoints()[0].protocol(), ksp_onchain_transport_lib::WsProtocolKind::SolanaStandard);
assert!(ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Logs));
assert!(!ws.endpoints()[0].supports_subscription(ksp_onchain_transport_lib::WsSubscriptionKind::Block));
}
assert!(resolved.grpc_settings().is_none(), "keyless Testnet base profile must not conflate PublicNode Yellowstone with the network itself");
}
}
#[test]
fn committed_v3_publicnode_profiles_map_provider_neutral_yellowstone_grpc_with_network_scoped_secret_x_tokens() {
let engine = committed_engine();