v0.3.7-pre.006

This commit is contained in:
2026-09-02 15:05:18 +02:00
parent 57ff11b774
commit 3c4ae51ae2
18 changed files with 475 additions and 82 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-app-backfill-desk/frontend/main.html -->
<!-- version: 1 -->
<!-- version: 2 -->
<!DOCTYPE html>
<html lang="fr">
@@ -51,11 +51,34 @@
</div>
<span id="appVersionBadge" class="badge text-bg-secondary"></span>
</div>
<div class="app-placeholder">
<i class="fa-solid fa-clock-rotate-left text-body-secondary" aria-hidden="true"></i>
<div>
<h2 class="h5">Backfill runtime non ouvert dans pre.002</h2>
<p class="text-body-secondary mb-0">Aucun Transport, Store ou Job Backfill n'est encore créé par le frontend.</p>
<div class="row g-4">
<div class="col-xl-7">
<div class="card shadow-sm h-100">
<div class="card-header fw-semibold">Transport HTTP</div>
<div class="card-body">
<label for="backfillHttpRoute" class="form-label">Route de lecture</label>
<select id="backfillHttpRoute" class="form-select" disabled>
<option value="">Chargement des routes…</option>
</select>
<div id="backfillHttpRouteHelp" class="form-text">Le choix reste logique : aucune URL ni clé provider n'est exposée au frontend.</div>
<dl class="row mt-4 mb-0 app-runtime-list">
<dt class="col-sm-5">Réseau</dt><dd id="backfillNetwork" class="col-sm-7"></dd>
<dt class="col-sm-5">Transport</dt><dd id="backfillTransportReady" class="col-sm-7"></dd>
<dt class="col-sm-5">Store</dt><dd id="backfillStoreReady" class="col-sm-7"></dd>
<dt class="col-sm-5">Composition</dt><dd id="backfillCompositionReady" class="col-sm-7"></dd>
</dl>
<div id="backfillOptionsDiagnostic" class="alert alert-warning mt-3 mb-0" role="status" aria-live="polite" hidden></div>
</div>
</div>
</div>
<div class="col-xl-5">
<div class="app-placeholder h-100">
<i class="fa-solid fa-clock-rotate-left text-body-secondary" aria-hidden="true"></i>
<div>
<h2 class="h5">Campagne Backfill</h2>
<p class="text-body-secondary mb-0">Les scopes, commitments et le bouton Start seront branchés dans la tranche suivante. Le transport reste HTTP uniquement en v0.3.7.</p>
</div>
</div>
</div>
</div>
</section>

View File

@@ -1,10 +1,12 @@
// file: crates/ksp-app-backfill-desk/frontend/ts/main.ts
// version: 1
// version: 2
import "bootstrap";
import ResizeObserver from "resize-observer-polyfill";
import "simplebar";
import { getCurrentWindow } from "@tauri-apps/api/window";
import type { BackfillDeskOptionsDto } from "./bindings/ksp_app_backfill_desk/dto_common/BackfillDeskOptionsDto.ts";
import type { BackfillHttpRouteOptionDto } from "./bindings/ksp_app_backfill_desk/dto_common/BackfillHttpRouteOptionDto.ts";
import type { ShellStatusDto } from "./bindings/ksp_app_backfill_desk/dto_common/ShellStatusDto.ts";
import { frontendDebug, frontendInfo, frontendTrace, frontendWarn, installFrontendConsoleBridge } from "./frontend_log";
import { invokeKsp } from "./invoke";
@@ -77,6 +79,74 @@ function bindNavigation(): void {
frontendTrace("main", "Backfill Desk navigation handlers installed");
}
function routeLabel(route: BackfillHttpRouteOptionDto): string {
if (route.pooled) {
return `Pool multi-provider — ${route.providers.join(" + ")}`;
}
return route.providers.length === 1 ? `Provider — ${route.providers[0]}` : route.role;
}
function renderBackfillOptions(options: BackfillDeskOptionsDto): void {
const select = document.querySelector<HTMLSelectElement>("#backfillHttpRoute");
if (select) {
const previous = select.value;
select.replaceChildren();
for (const route of options.httpRoutes) {
const option = document.createElement("option");
option.value = route.role;
option.textContent = routeLabel(route);
select.append(option);
}
select.disabled = !options.transportReady || options.httpRoutes.length === 0;
const reusable = options.httpRoutes.some(route => route.role === previous);
if (reusable) {
select.value = previous;
}
}
const values: Record<string, string> = {
backfillNetwork: options.configuredNetworks.length === 1 ? options.configuredNetworks[0] : options.configuredNetworks.join(", ") || "indisponible",
backfillTransportReady: options.transportReady ? "ready" : "non ready",
backfillStoreReady: options.storeReady ? "ready" : "non ready",
backfillCompositionReady: options.compositionReady ? "ready" : "non ready",
};
for (const [id, value] of Object.entries(values)) {
const element = document.querySelector<HTMLElement>(`#${id}`);
if (element) {
element.textContent = value;
}
}
const diagnostic = document.querySelector<HTMLElement>("#backfillOptionsDiagnostic");
if (diagnostic) {
const error = options.transportDiagnostic ?? options.storeDiagnostic;
diagnostic.hidden = error === null;
diagnostic.textContent = error === null ? "" : `${error.domain}/${error.code}: ${error.message}`;
}
frontendTrace("main", "Backfill Desk HTTP route options rendered", {
compositionReady: options.compositionReady,
routeCount: options.httpRoutes.length,
transportReady: options.transportReady,
});
}
async function loadBackfillOptions(source: "startup" | "user"): Promise<void> {
frontendDebug("main", "Backfill Desk HTTP route options load started", { source });
const options = await invokeKsp<BackfillDeskOptionsDto>("main", "backfill_options");
renderBackfillOptions(options);
frontendDebug("main", "Backfill Desk HTTP route options load completed", { source, routeCount: options.httpRoutes.length });
}
function bindBackfillRouteSelection(): void {
const select = document.querySelector<HTMLSelectElement>("#backfillHttpRoute");
if (!select) {
return;
}
select.addEventListener("change", () => {
frontendDebug("main", "Backfill Desk HTTP route selection changed", { role: select.value || "none" });
});
frontendTrace("main", "Backfill Desk HTTP route selection handler installed");
}
function renderRuntimeStatus(status: ShellStatusDto): void {
const values: Record<string, string> = {
runtimeVersion: status.applicationVersion,
@@ -131,9 +201,10 @@ async function initializeMain(): Promise<void> {
bindFrontendInteractions();
bindNavigation();
bindRuntimeStatusRefresh();
bindBackfillRouteSelection();
activateView("backfill", "startup");
try {
await loadRuntimeStatus("startup");
await Promise.all([loadRuntimeStatus("startup"), loadBackfillOptions("startup")]);
} catch {
frontendWarn("main", "Backfill Desk startup runtime status load failed");
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-backfill-desk/src/app_state.rs
// version: 3
// version: 4
//! Shared backend state owned by the Backfill Desk Tauri application.
@@ -121,7 +121,7 @@ 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.005-store-readiness".to_owned(),
shell_phase: "pre.006-mainnet-http-routing".to_owned(),
startup_diagnostic: runtime.startup_diagnostic.clone(),
});
}
@@ -132,7 +132,7 @@ impl crate::AppState {
let options = 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(),
http_routes: std::vec::Vec::new(),
composition_ready: false,
configured_networks: std::vec::Vec::new(),
network_coherent: false,

View File

@@ -1,20 +1,35 @@
// file: crates/ksp-app-backfill-desk/src/dto_common.rs
// version: 3
// version: 4
//! 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.
/// Safe HTTP route exposed for operator selection before a Backfill run.
///
/// Campaign scopes, commitments and backend-owned bounds are added in the dedicated request/DTO slice. Endpoint URLs, provider identities and credentials
/// are intentionally absent.
/// The route is identified only by the logical Transport role. Provider labels are safe metadata used for operator choice; endpoint URLs and credentials never
/// cross IPC.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_backfill_desk/dto_common/BackfillHttpRouteOptionDto.ts")]
pub(crate) struct BackfillHttpRouteOptionDto {
/// Whether more than one configured provider participates in this logical route.
pub(crate) pooled: bool,
/// Safe provider labels participating in the route, sorted and deduplicated.
pub(crate) providers: std::vec::Vec<String>,
/// Logical Transport role later passed to the Backfill runtime.
pub(crate) role: String,
}
/// Safe Transport-and-Store 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 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>,
/// Selectable logical HTTP routes that support both RPC methods required by the Backfill runtime.
pub(crate) http_routes: std::vec::Vec<BackfillHttpRouteOptionDto>,
/// Whether the Transport and Store readiness gates jointly permit later Backfill composition.
pub(crate) composition_ready: bool,
/// Distinct enabled HTTP cluster labels selected by the active Transport profile.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-backfill-desk/src/lib.rs
// version: 4
// version: 5
//! Tauri desktop application scaffold for controlling and inspecting KSP RAW backfill jobs.
@@ -62,8 +62,10 @@ 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.
/// Safe Transport-and-Store readiness subset of the Backfill Desk options contract.
pub(crate) use self::dto_common::BackfillDeskOptionsDto;
/// Safe logical HTTP route exposed for operator selection.
pub(crate) use self::dto_common::BackfillHttpRouteOptionDto;
/// Safe command error projection exposed to Tauri commands.
pub(crate) use self::dto_common::CommandErrorDto;
/// Safe scaffold/runtime snapshot exposed to the shell.

View File

@@ -1,7 +1,7 @@
// file: crates/ksp-app-backfill-desk/src/transport_runtime.rs
// version: 2
// version: 3
//! Composite-selected HTTP Transport readiness owned by Backfill Desk.
//! Composite-selected HTTP Transport readiness and route inventory owned by Backfill Desk.
/// Safe and executable Transport runtime retained by the application state.
pub(crate) struct TransportRuntime {
@@ -30,16 +30,16 @@ impl TransportRuntime {
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 {
let http_routes = compatible_backfill_http_routes(&self.pool, &snapshot);
let http_routes = match http_routes {
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;
let transport_ready = configured_networks.len() == 1 && !http_routes.is_empty() && snapshot.available_endpoint_count() > 0;
return std::result::Result::Ok(crate::BackfillDeskOptionsDto {
compatible_roles,
composition_ready: false,
configured_networks,
http_routes,
network_coherent: false,
store_diagnostic: std::option::Option::None,
store_network: std::option::Option::None,
@@ -89,17 +89,17 @@ pub(crate) fn initialize_transport(management: &ksp_config_lib::ConfigManagement
domain = crate::TRACING_DOMAIN_TRANSPORT,
transport_profile = runtime.profile_id(),
network_count = options.configured_networks.len(),
compatible_role_count = options.compatible_roles.len(),
http_route_count = options.http_routes.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(
fn compatible_backfill_http_routes(
pool: &ksp_onchain_transport_lib::HttpTransportPool,
snapshot: &ksp_onchain_transport_lib::HttpTransportPoolSnapshot,
) -> ksp_core_lib::Result<std::vec::Vec<String>> {
) -> ksp_core_lib::Result<std::vec::Vec<crate::BackfillHttpRouteOptionDto>> {
let signatures = required_http_rpc_method("getSignaturesForAddress");
let signatures = match signatures {
std::result::Result::Ok(value) => value,
@@ -110,22 +110,23 @@ fn compatible_backfill_roles(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut candidates = std::collections::BTreeSet::new();
let mut candidates = std::collections::BTreeMap::<String, std::collections::BTreeSet<String>>::new();
for endpoint in snapshot.endpoints() {
if !endpoint.enabled() {
continue;
}
for role in endpoint.roles() {
if role.enabled() {
candidates.insert(role.role().to_owned());
candidates.entry(role.role().to_owned()).or_default().insert(endpoint.provider().to_owned());
}
}
}
let mut compatible = std::vec::Vec::new();
for candidate in candidates {
for (candidate, providers) 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);
let providers = providers.into_iter().collect::<std::vec::Vec<_>>();
compatible.push(crate::BackfillHttpRouteOptionDto { pooled: providers.len() > 1, providers, role: candidate });
}
}
return std::result::Result::Ok(compatible);

View File

@@ -1,7 +1,7 @@
// file: crates/ksp-app-backfill-desk/tests/config_composition.rs
// version: 4
// version: 5
//! Config composite, Transport readiness and Store-network composition contracts for Backfill Desk through pre.005.
//! Config composite, Transport readiness and Store-network composition contracts for Backfill Desk through pre.006.
#![forbid(unsafe_code)]
#![deny(unreachable_pub)]
@@ -32,7 +32,7 @@ fn assert_component(composite: &ksp_config_lib::ResolvedConfigComposite, compone
}
#[test]
fn pre_003_default_composite_selects_trace_devnet_logging_transport_and_store() {
fn pre_003_devnet_profile_selects_trace_logging_transport_and_store() {
let engine = workspace_engine();
assert!(engine.is_ok(), "workspace Config engine should be constructible: {engine:?}");
let engine = match engine {
@@ -45,8 +45,8 @@ fn pre_003_default_composite_selects_trace_devnet_logging_transport_and_store()
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 = engine.load_resolved_composite(&file_id, std::option::Option::Some("devnet"));
assert!(composite.is_ok(), "committed Backfill Desk devnet profile should resolve: {composite:?}");
if let std::result::Result::Ok(composite) = composite {
assert_eq!(composite.profile_id(), "devnet");
assert_component(&composite, "logging", ksp_config_lib::FILE_ID_STD_LOGGING, "supertrace");
@@ -74,7 +74,7 @@ fn pre_003_named_profiles_keep_transport_and_store_network_selection_paired() {
std::result::Result::Err(_) => return,
};
for (profile_id, logging_profile, transport_profile, store_profile) in
[("mainnet", "console_info", "mainnet_public", "mainnet"), ("testnet", "console_info", "publicnode_testnet", "testnet")]
[("mainnet", "supertrace", "mainnet_backfill_pool", "mainnet"), ("testnet", "console_info", "publicnode_testnet", "testnet")]
{
let composite = engine.load_resolved_composite(&file_id, std::option::Option::Some(profile_id));
assert!(composite.is_ok(), "Backfill Desk composite profile {profile_id} should resolve: {composite:?}");
@@ -100,8 +100,8 @@ fn pre_004_default_composite_builds_http_pool_for_both_backfill_rpc_methods() {
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 = engine.load_resolved_composite(&file_id, std::option::Option::Some("devnet"));
assert!(composite.is_ok(), "committed Backfill Desk devnet profile should resolve: {composite:?}");
let composite = match composite {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
@@ -215,3 +215,52 @@ fn pre_005_all_composite_profiles_keep_declared_http_and_store_networks_identica
}
}
}
#[test]
fn pre_006_default_mainnet_profile_exposes_pool_and_targeted_http_routes_without_secret_provider_data() {
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(), "default Backfill Desk composite should resolve: {composite:?}");
let composite = match composite {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(composite.profile_id(), "mainnet");
assert_component(&composite, "logging", ksp_config_lib::FILE_ID_STD_LOGGING, "supertrace");
assert_component(&composite, "transport", ksp_config_lib::FILE_ID_STD_TRANSPORT, "mainnet_backfill_pool");
assert_component(&composite, "store", ksp_config_lib::FILE_ID_STD_STORE, "mainnet");
let transport = match composite.component("transport") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let profile = transport.resolved().profile();
let endpoints = profile.get("endpoints").and_then(serde_json::Value::as_array);
assert!(endpoints.is_some(), "mainnet Backfill Transport profile should expose HTTP endpoints");
let endpoints = match endpoints {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let providers = endpoints
.iter()
.filter_map(|endpoint| endpoint.get("provider").and_then(serde_json::Value::as_str))
.collect::<std::collections::BTreeSet<_>>();
assert_eq!(providers, std::collections::BTreeSet::from(["publicnode", "solana-public"]));
let roles = endpoints
.iter()
.flat_map(|endpoint| endpoint.get("roles").and_then(serde_json::Value::as_array).into_iter().flatten())
.filter_map(|role| role.get("role").and_then(serde_json::Value::as_str))
.collect::<std::collections::BTreeSet<_>>();
assert!(roles.contains("backfill_pool"));
assert!(roles.contains("backfill_publicnode"));
assert!(roles.contains("backfill_solana_public"));
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-backfill-desk/tests/desktop_contract.rs
// version: 5
// version: 6
//! Structural desktop contract checks for the Backfill Desk scaffold.
@@ -193,8 +193,9 @@ fn pre_004_transport_runtime_builds_pool_and_validates_both_required_rpc_methods
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("compatible_backfill_http_routes"));
assert!(transport.contains("endpoint.provider()"));
for forbidden in ["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());
@@ -220,7 +221,6 @@ fn pre_005_store_readiness_proves_network_before_open_and_closes_on_main_window_
assert!(!store.contains("BackfillRequest"));
let app_state = read_text(root.join("src/app_state.rs").as_path());
assert!(app_state.contains("initialize_store"));
assert!(app_state.contains("pre.005-store-readiness"));
assert!(app_state.contains("composition_ready"));
assert!(app_state.contains("close_store"));
let tauri = read_text(root.join("src/tauri.rs").as_path());
@@ -228,3 +228,26 @@ fn pre_005_store_readiness_proves_network_before_open_and_closes_on_main_window_
assert!(tauri.contains(required), "missing bounded Store shutdown lifecycle marker {required}");
}
}
#[test]
fn pre_006_mainnet_default_and_http_route_selector_are_explicit_without_start_runtime() {
let root = app_root();
let workspace = root.join("../..");
let composite = read_text(workspace.join("config/composite.ksp-app-backfill-desk.json").as_path());
assert!(composite.contains("\"default_profile\": \"mainnet\""));
assert!(composite.contains("\"profile_id\": \"mainnet_backfill_pool\""));
let transport = read_text(workspace.join("config/std.transport.json").as_path());
for required in ["mainnet_backfill_pool", "backfill_pool", "backfill_publicnode", "backfill_solana_public", "solana-rpc.publicnode.com"] {
assert!(transport.contains(required), "missing Backfill HTTP route marker {required}");
}
let app_state = read_text(root.join("src/app_state.rs").as_path());
assert!(app_state.contains("pre.006-mainnet-http-routing"));
let frontend = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(frontend.contains("Backfill Desk HTTP route selection changed"));
assert!(frontend.contains("backfill_options"));
let html = read_text(root.join("frontend/main.html").as_path());
assert!(html.contains("backfillHttpRoute"));
for forbidden in ["backfill_start", "BackfillJobRuntime", "grpc", "websocket"] {
assert!(!frontend.contains(forbidden), "pre.006 frontend opens deferred runtime marker {forbidden}");
}
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-backfill-desk/tests/desktop_security.rs
// version: 5
// version: 6
//! Security and dependency-boundary checks for the Backfill Desk scaffold.
@@ -137,7 +137,23 @@ fn pre_005_opens_store_facade_without_backend_or_job_dependencies() {
}
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", "connection_uri"] {
for forbidden in ["endpoint_url", "pub(crate) url", "pub(crate) credential", "pub(crate) token", "connection_uri"] {
assert!(!dto.contains(forbidden), "readiness options DTO source contains forbidden field marker {forbidden}");
}
}
#[test]
fn pre_006_frontend_receives_only_safe_http_route_metadata_and_no_endpoint_material() {
let root = app_root();
let dto = read_text(root.join("src/dto_common.rs").as_path());
assert!(dto.contains("BackfillHttpRouteOptionDto"));
assert!(dto.contains("pub(crate) providers"));
assert!(dto.contains("pub(crate) role"));
for forbidden in ["endpoint_url", "pub(crate) url", "pub(crate) credential", "pub(crate) api_key", "pub(crate) authorization"] {
assert!(!dto.contains(forbidden), "route DTO leaks forbidden endpoint material marker {forbidden}");
}
let frontend = read_text(root.join("frontend/ts/main.ts").as_path());
for forbidden in ["http://", "https://", "apiKey", "authorization", "token"] {
assert!(!frontend.contains(forbidden), "frontend embeds forbidden provider material marker {forbidden}");
}
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-backfill-desk/unit_tests/dto_common.rs
// version: 3
// version: 4
#[test]
fn command_error_projection_excludes_arbitrary_context_and_source_values() {
@@ -16,7 +16,7 @@ fn command_error_projection_excludes_arbitrary_context_and_source_values() {
#[test]
fn transport_options_projection_contains_only_safe_transport_metadata() {
let dto = crate::BackfillDeskOptionsDto {
compatible_roles: vec!["default".to_owned()],
http_routes: vec![crate::BackfillHttpRouteOptionDto { pooled: false, providers: vec!["solana-public".to_owned()], role: "default".to_owned() }],
composition_ready: true,
configured_networks: vec!["devnet".to_owned()],
network_coherent: true,
@@ -32,7 +32,7 @@ fn transport_options_projection_contains_only_safe_transport_metadata() {
let serialized = value.to_string();
assert!(serialized.contains("default"));
assert!(serialized.contains("devnet"));
assert!(!serialized.contains("provider"));
assert!(serialized.contains("solana-public"));
assert!(!serialized.contains("url"));
assert!(!serialized.contains("endpoint"));
assert!(!serialized.contains("postgres"));

View File

@@ -46,7 +46,7 @@ fn pool(endpoints: std::vec::Vec<ksp_onchain_transport_lib::HttpEndpointSettings
}
#[test]
fn compatible_roles_require_both_backfill_rpc_methods() {
fn http_routes_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());
@@ -76,10 +76,15 @@ fn compatible_roles_require_both_backfill_rpc_methods() {
std::result::Result::Err(_) => return,
};
let snapshot = runtime_pool.snapshot();
let roles = super::compatible_backfill_roles(&runtime_pool, &snapshot);
let roles = super::compatible_backfill_http_routes(&runtime_pool, &snapshot);
assert!(roles.is_ok());
if let std::result::Result::Ok(roles) = roles {
assert_eq!(roles, vec!["complete".to_owned()]);
assert_eq!(roles.len(), 1);
if let std::option::Option::Some(route) = roles.first() {
assert_eq!(route.role, "complete");
assert_eq!(route.providers, vec!["fixture".to_owned()]);
assert!(!route.pooled);
}
}
}

View File

@@ -1,6 +1,6 @@
{
"format_version": 1,
"default_profile": "devnet",
"default_profile": "mainnet",
"profiles": [
{
"profile_id": "devnet",