Files
khadhroony-solana-project/crates/ksp-app-wallet-desk/tests/desktop_contract.rs
2026-08-26 11:23:01 +02:00

500 lines
25 KiB
Rust

// file: crates/ksp-app-wallet-desk/tests/desktop_contract.rs
// version: 23
//! Desktop build, shell and Config-status contract audits for Wallet Desk.
fn app_root() -> std::path::PathBuf {
return std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
}
fn read_text(path: &std::path::Path) -> String {
let source = std::fs::read_to_string(path);
assert!(source.is_ok(), "unable to read {}", path.display());
return match source {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => String::new(),
};
}
fn read_source(relative_path: &str) -> String {
let root = app_root();
return read_text(root.join(relative_path).as_path());
}
fn read_json(path: &std::path::Path) -> serde_json::Value {
let source = read_text(path);
let parsed = serde_json::from_str::<serde_json::Value>(source.as_str());
assert!(parsed.is_ok(), "unable to parse {}", path.display());
return match parsed {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => serde_json::Value::Null,
};
}
fn parse_semver_core(value: &str) -> std::option::Option<(u64, u64, u64)> {
let core = match value.split_once('-') {
std::option::Option::Some((core, _)) => core,
std::option::Option::None => value,
};
let mut parts = core.split('.');
let major = match parts.next().and_then(|part| return part.parse::<u64>().ok()) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::option::Option::None,
};
let minor = match parts.next().and_then(|part| return part.parse::<u64>().ok()) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::option::Option::None,
};
let patch = match parts.next().and_then(|part| return part.parse::<u64>().ok()) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::option::Option::None,
};
if parts.next().is_some() {
return std::option::Option::None;
}
return std::option::Option::Some((major, minor, patch));
}
fn assert_packaged_version_floor(value: std::option::Option<&str>, field: &str) {
assert!(value.is_some(), "{field} packaged version must exist");
let value = match value {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let parsed = parse_semver_core(value);
assert!(parsed.is_some(), "{field} packaged version must be SemVer-like");
if let std::option::Option::Some(parsed) = parsed {
assert!(parsed >= (0, 2, 6), "{field} packaged version must be >= 0.2.6");
}
}
#[test]
fn tauri_shell_uses_reserved_wallet_desk_ports_and_template_windows() {
let root = app_root();
let tauri = read_json(root.join("tauri.conf.json").as_path());
assert_eq!(tauri.pointer("/build/devUrl").and_then(serde_json::Value::as_str), std::option::Option::Some("http://localhost:1432"));
assert_eq!(tauri.pointer("/build/beforeDevCommand/script").and_then(serde_json::Value::as_str), std::option::Option::Some("npm run dev"));
assert_eq!(tauri.pointer("/build/beforeDevCommand/cwd").and_then(serde_json::Value::as_str), std::option::Option::Some("."));
assert_eq!(tauri.pointer("/build/beforeBuildCommand/script").and_then(serde_json::Value::as_str), std::option::Option::Some("npm run build"));
assert_eq!(tauri.pointer("/build/beforeBuildCommand/cwd").and_then(serde_json::Value::as_str), std::option::Option::Some("."));
assert_eq!(tauri.pointer("/app/security/csp"), std::option::Option::Some(&serde_json::Value::Null));
let manifest = read_text(root.join("Cargo.toml").as_path());
assert!(manifest.contains("name = \"ksp-app-wallet-desk\""));
let windows = tauri.pointer("/app/windows").and_then(serde_json::Value::as_array);
assert!(windows.is_some());
let windows = match windows {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let labels = windows.iter().filter_map(|window| return window.get("label").and_then(serde_json::Value::as_str)).collect::<std::vec::Vec<_>>();
assert_eq!(labels, ["splash", "main"]);
}
#[test]
fn package_reuses_config_desk_frontend_baseline_without_standalone_check_script() {
let root = app_root();
let package = read_json(root.join("package.json").as_path());
assert!(package.pointer("/scripts/check").is_none());
let dependencies = package.get("dependencies").and_then(serde_json::Value::as_object);
assert!(dependencies.is_some());
let dependencies = match dependencies {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
for dependency in [
"@fltsci/tauri-plugin-tracing",
"@fortawesome/fontawesome-free",
"@tauri-apps/api",
"bootstrap",
"datatables.net-bs5",
"datatables.net-select-bs5",
"resize-observer-polyfill",
"simplebar",
] {
assert!(dependencies.contains_key(dependency), "missing dependency {dependency}");
}
}
#[test]
fn shell_contains_wallet_navigation_datatable_and_lock_state_icons() {
let root = app_root();
let html = read_text(root.join("frontend/main.html").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(html.contains("id=\"walletInventoryTable\""));
assert!(html.contains("fa-lock"));
assert!(html.contains("fa-lock-open"));
assert!(html.contains("data-view=\"wallets\""));
assert!(html.contains("id=\"runtimeCompositeProfile\""));
assert!(html.contains("id=\"runtimeEffectiveWalletsDirectory\""));
assert!(main.contains("new DataTable"));
assert!(main.contains("datatables.net-select-bs5"));
assert!(main.contains("ResizeObserver"));
assert!(main.contains("simplebar"));
}
#[test]
fn pre_004_inventory_uses_wallet_library_and_exposes_only_locked_safe_fields() {
let root = app_root();
let manifest = read_text(root.join("Cargo.toml").as_path());
let tauri = read_text(root.join("src/tauri.rs").as_path());
let inventory = read_text(root.join("src/wallet_inventory.rs").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(manifest.contains("ksp-wallet-lib = { path = \"../ksp-wallet-lib\" }"));
assert!(inventory.contains("ksp_wallet_lib::inspect_locked_wallet_file"));
assert!(inventory.contains("symlink_metadata"));
assert!(inventory.contains("WALLET_FILE_SUFFIX"));
assert!(tauri.contains("list_wallets"));
assert!(tauri.contains("refresh_wallets"));
assert!(tauri.contains("select_wallet"));
assert!(main.contains("WalletInventoryEntryDto"));
assert!(main.contains("LockedWalletDto"));
assert!(main.contains("refresh_wallets"));
assert!(!inventory.contains("pubkey"));
assert!(!inventory.contains("alias"));
assert!(!inventory.contains("notes"));
}
#[test]
fn frontend_control_interactions_are_trace_logged_without_control_values() {
let root = app_root();
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(main.contains("bindFrontendInteractions"));
assert!(main.contains("Frontend control clicked"));
assert!(main.contains("[role=\"tab\"]"));
assert!(main.contains("shellAction: control.dataset.shellAction"));
assert!(main.contains("viewId: control.dataset.view"));
assert!(!main.contains("control.value"));
}
#[test]
fn pre_005_create_and_session_lifecycle_are_backend_owned() {
let root = app_root();
let state = read_text(root.join("src/app_state.rs").as_path());
let session = read_text(root.join("src/wallet_session.rs").as_path());
let tauri = read_text(root.join("src/tauri.rs").as_path());
let html = read_text(root.join("frontend/main.html").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(state.contains("ksp_wallet_lib::create_wallet_file"));
assert!(state.contains("WalletSession::Owner"));
assert!(state.contains("WalletSession::Locked"));
assert!(state.contains("std::mem::replace"));
assert!(session.contains("WalletCreateRequestDto"));
assert!(session.contains("WalletAuthorizedDto"));
assert!(tauri.contains("create_wallet"));
assert!(tauri.contains("deselect_wallet"));
assert!(tauri.contains("lock_wallet"));
assert!(html.contains("id=\"createWalletForm\""));
assert!(html.contains("id=\"createWalletOwnerPassword\""));
assert!(html.contains("id=\"createWalletViewPassword\""));
assert!(main.contains("clearCreateFormSensitiveInputs"));
assert!(main.contains("Authorized Wallet session rendered"));
}
#[test]
fn pre_006_unlock_surface_is_explicit_backend_owned_and_supports_view_owner() {
let root = app_root();
let state = read_text(root.join("src/app_state.rs").as_path());
let secrets = read_text(root.join("src/wallet_secrets.rs").as_path());
let session = read_text(root.join("src/wallet_session.rs").as_path());
let tauri = read_text(root.join("src/tauri.rs").as_path());
let html = read_text(root.join("frontend/main.html").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(state.contains("open_wallet_view_file"));
assert!(state.contains("open_wallet_owner_file"));
assert!(state.contains("WalletSession::PrivilegedOperation"));
assert!(state.contains("WalletSession::View"));
assert!(secrets.contains("environment_report"));
assert!(secrets.contains("reveal_effective_environment_value"));
assert!(secrets.contains("normalize_wallet_secret_label"));
assert!(session.contains("WalletUnlockRequestDto"));
assert!(tauri.contains("unlock_wallet_view_manual"));
assert!(tauri.contains("unlock_wallet_owner_manual"));
assert!(tauri.contains("unlock_wallet_view_configured"));
assert!(tauri.contains("unlock_wallet_owner_configured"));
assert!(html.contains("id=\"unlockViewManual\""));
assert!(html.contains("id=\"unlockOwnerManual\""));
assert!(html.contains("id=\"unlockViewConfigured\""));
assert!(html.contains("id=\"unlockOwnerConfigured\""));
assert!(main.contains("beginFrontendUnlock"));
assert!(main.contains("configuredSecretCandidateCount"));
}
#[test]
fn pre_007_balance_uses_authorized_pubkey_and_composite_transport() {
let root = app_root();
let manifest = read_text(root.join("Cargo.toml").as_path());
let state = read_text(root.join("src/app_state.rs").as_path());
let transport = read_text(root.join("src/transport_runtime.rs").as_path());
let balance = read_text(root.join("src/wallet_balance.rs").as_path());
let tauri = read_text(root.join("src/tauri.rs").as_path());
let html = read_text(root.join("frontend/main.html").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(manifest.contains("ksp-onchain-transport-lib = { path = \"../ksp-onchain-transport-lib\" }"));
assert!(transport.contains("resolve_transport_config_profile"));
assert!(transport.contains("HttpTransportPool::new"));
assert!(transport.contains("getBalance"));
assert!(state.contains("get_balance"));
assert!(state.contains("SolanaCommitment::Confirmed"));
assert!(state.contains("wallet.pubkey()"));
assert!(balance.contains("WalletBalanceDto"));
assert!(balance.contains("format_lamports_as_sol"));
assert!(tauri.contains("refresh_wallet_balance"));
assert!(html.contains("id=\"refreshWalletBalance\""));
assert!(html.contains("id=\"detailsBalanceLamports\""));
assert!(main.contains("renderWalletBalance"));
assert!(main.contains("refresh_wallet_balance"));
}
#[test]
fn pre_008_import_uses_rust_native_picker_and_wallet_transfer_adapters() {
let root = app_root();
let manifest = read_text(root.join("Cargo.toml").as_path());
let state = read_text(root.join("src/app_state.rs").as_path());
let import = read_text(root.join("src/wallet_import.rs").as_path());
let tauri = read_text(root.join("src/tauri.rs").as_path());
let html = read_text(root.join("frontend/main.html").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(manifest.contains("tauri-plugin-dialog.workspace = true"));
assert!(manifest.contains("zeroize.workspace = true"));
assert!(tauri.contains("tauri_plugin_dialog::init()"));
assert!(tauri.contains("blocking_pick_file"));
assert!(tauri.contains("inspect_import_source"));
assert!(tauri.contains("import_wallet"));
assert!(import.contains("ksp_wallet_lib::inspect_wallet_transfer"));
assert!(import.contains("zeroize::Zeroizing"));
assert!(state.contains("ksp_wallet_lib::import_wallet_transfer"));
assert!(html.contains("id=\"inspectImportSource\""));
assert!(html.contains("id=\"importWalletForm\""));
assert!(html.contains("id=\"importSourcePubkey\""));
assert!(main.contains("WalletTransferInspectionDto"));
assert!(main.contains("bindImportWalletForm"));
}
#[test]
fn pre_009_owner_metadata_administration_uses_wallet_owner_and_refreshes_projection() {
let root = app_root();
let state = read_text(root.join("src/app_state.rs").as_path());
let metadata = read_text(root.join("src/wallet_metadata.rs").as_path());
let session = read_text(root.join("src/wallet_session.rs").as_path());
let tauri = read_text(root.join("src/tauri.rs").as_path());
let html = read_text(root.join("frontend/main.html").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(state.contains("owner.update_alias"));
assert!(state.contains("owner.add_note"));
assert!(state.contains("owner.update_note"));
assert!(state.contains("owner.delete_note"));
assert!(state.contains("ERROR_CODE_STATE_CONFLICT"));
assert!(session.contains("OwnerOperation"));
assert!(metadata.contains("WalletAliasUpdateRequestDto"));
assert!(metadata.contains("WalletNoteAddRequestDto"));
assert!(metadata.contains("WalletNoteUpdateRequestDto"));
assert!(metadata.contains("WalletNoteDeleteRequestDto"));
assert!(tauri.contains("update_wallet_alias"));
assert!(tauri.contains("add_wallet_note"));
assert!(tauri.contains("update_wallet_note"));
assert!(tauri.contains("delete_wallet_note"));
assert!(html.contains("id=\"ownerWalletAlias\""));
assert!(html.contains("id=\"ownerNotesList\""));
assert!(html.contains("id=\"deleteWalletNoteModal\""));
assert!(main.contains("runOwnerMetadataMutation"));
assert!(main.contains("recoverOwnerStateConflict"));
}
#[test]
fn pre_010_owner_and_view_authorized_credential_rotations_preserve_session_projection() {
let root = app_root();
let security = read_text(root.join("src/wallet_security.rs").as_path());
let state = read_text(root.join("src/app_state.rs").as_path());
let tauri = read_text(root.join("src/tauri.rs").as_path());
let html = read_text(root.join("frontend/main.html").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(security.contains("WalletPasswordRotationRequestDto"));
assert!(state.contains("owner.rotate_owner_password"));
assert!(state.contains("owner.rotate_view_password"));
assert!(state.contains("view.rotate_view_password"));
assert!(state.contains("begin_view_operation"));
assert!(state.contains("finish_view_operation"));
assert!(state.contains("begin_owner_operation"));
assert!(state.contains("finish_owner_operation"));
assert!(tauri.contains("rotate_owner_password"));
assert!(tauri.contains("rotate_view_password"));
assert!(html.contains("id=\"rotateOwnerPasswordSubmit\""));
assert!(html.contains("id=\"rotateViewPasswordSubmit\""));
assert!(main.contains("rotateWalletPassword"));
assert!(main.contains("bindRotationActions"));
assert!(main.contains("viewAuthorized"));
assert!(main.contains("Les secrets Config"));
}
#[test]
fn pre_011_strong_view_administration_is_owner_only_and_refreshes_safe_status() {
let root = app_root();
let security = read_text(root.join("src/wallet_security.rs").as_path());
let state = read_text(root.join("src/app_state.rs").as_path());
let tauri = read_text(root.join("src/tauri.rs").as_path());
let html = read_text(root.join("frontend/main.html").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(security.contains("WalletViewSecurityStatusDto"));
assert!(state.contains("owner.disable_view"));
assert!(state.contains("owner.recreate_view"));
assert!(state.contains("result_view_enabled = false"));
assert!(state.contains("result_view_enabled = true"));
assert!(tauri.contains("disable_wallet_view"));
assert!(tauri.contains("recreate_wallet_view"));
assert!(html.contains("id=\"disableWalletViewModal\""));
assert!(html.contains("id=\"recreateWalletViewModal\""));
assert!(html.contains("id=\"disableWalletView\""));
assert!(html.contains("id=\"recreateWalletView\""));
assert!(main.contains("runStrongViewDisable"));
assert!(main.contains("runStrongViewRecreate"));
assert!(main.contains("applyViewSecurityStatus"));
assert!(main.contains("bindStrongViewSecurityActions"));
}
#[test]
fn pre_012_owner_export_uses_rust_native_save_picker_and_wallet_file_adapter() {
let root = app_root();
let state = read_text(root.join("src/app_state.rs").as_path());
let export = read_text(root.join("src/wallet_export.rs").as_path());
let transfer = read_text(root.join("src/wallet_transfer.rs").as_path());
let tauri = read_text(root.join("src/tauri.rs").as_path());
let html = read_text(root.join("frontend/main.html").as_path());
let main = read_text(root.join("frontend/ts/main.ts").as_path());
assert!(export.contains("WalletExportRequestDto"));
assert!(export.contains("WalletExportResultDto"));
assert!(transfer.contains("WalletTransferFormatDto"));
assert!(transfer.contains("solana_cli_json"));
assert!(transfer.contains("solana_keypair_base58"));
assert!(state.contains("wallet_export_picker_filename"));
assert!(state.contains("begin_owner_operation(\"keypair_export\")"));
assert!(state.contains("owner.export_transfer_file"));
assert!(tauri.contains("blocking_save_file"));
assert!(tauri.contains("export_wallet_transfer"));
assert!(html.contains("id=\"walletExportFormat\""));
assert!(html.contains("id=\"exportWalletKeypair\""));
assert!(html.contains("id=\"exportWalletKeypairModal\""));
assert!(main.contains("bindWalletExportActions"));
assert!(main.contains("export_wallet_transfer"));
}
#[test]
fn pre_013_integration_compliance_and_devnet_smoke_are_durable_release_gates() {
let root = app_root();
let compliance = read_text(root.join("tests/release_compliance.rs").as_path());
let smoke = read_text(root.join("tests/wallet_desk_devnet_smoke.rs").as_path());
let validation = read_text(root.join("../../docs/validation/009-V0_2_6_WALLET_DESK_COMPLIANCE.md").as_path());
assert!(compliance.contains("wallet_desk_dependency_firewall_and_tauri_capabilities_are_minimal"));
assert!(compliance.contains("frontend_has_no_direct_filesystem_network_or_secret_persistence_surface"));
assert!(smoke.contains("committed_wallet_desk_composite_creates_wallet_and_reaches_devnet_balance"));
assert!(smoke.contains("#[ignore"));
assert!(validation.contains("Config -> Wallet -> Transport -> Devnet getBalance"));
}
#[test]
fn pre_014_template_has_normalized_html_headers_light_header_and_kbot_style_splash_contract() {
let root = app_root();
let main = read_text(root.join("frontend/main.html").as_path());
let splash = read_text(root.join("frontend/splash.html").as_path());
let app_style = read_text(root.join("frontend/sass/_app.scss").as_path());
let main_ts = read_text(root.join("frontend/ts/main.ts").as_path());
let state = read_text(root.join("src/app_state.rs").as_path());
let splash_style = read_text(root.join("frontend/sass/splash.scss").as_path());
let splash_ts = read_text(root.join("frontend/ts/splash.ts").as_path());
assert!(main.starts_with("<!-- file: crates/ksp-app-wallet-desk/frontend/main.html -->"));
assert!(main.contains("navbar h-100 py-0 bg-light text-dark"));
assert!(main.contains("ps-2 fs-4 fw-semibold text-primary text-nowrap\">Wallet Desk"));
assert!(main.contains("row g-0 h-100 flex-nowrap"));
assert!(main.contains("col app-content app-scrollable h-100"));
assert!(main.contains("card app-shell-card mx-auto shadow-sm border-0"));
assert!(main.contains("card-body p-4"));
assert!(main.contains("&copy; 2026 SASEDEV"));
assert!(!main.contains("id=\"shellStatus\""));
assert!(app_style.contains("flex-wrap: nowrap"));
assert!(app_style.contains("min-width: 0"));
assert!(state.contains("pre.014-desktop-polish"));
assert!(main_ts.contains("pre.014-desktop-polish"));
assert!(!main_ts.contains("#shellStatus"));
assert!(main.contains("app-sidebar"));
assert!(splash.starts_with("<!-- file: crates/ksp-app-wallet-desk/frontend/splash.html -->"));
assert!(splash.contains("id=\"debug-info\""));
assert!(splash.contains("id=\"messages-container\""));
assert!(splash.contains("sass/splash.scss"));
assert!(splash_style.contains("overflow: hidden"));
assert!(splash_style.contains("width: 100%"));
assert!(splash_style.contains("user-select: none"));
assert!(splash_ts.contains("add_debug"));
assert!(splash_ts.contains("add_message"));
assert!(!splash_ts.contains("../sass/splash.scss"));
}
#[test]
fn pre_016_wallet_desk_uses_version_neutral_wallet_dispatch() {
let state = read_source("src/app_state.rs");
let inventory = read_source("src/wallet_inventory.rs");
assert!(state.contains("ksp_wallet_lib::create_wallet_file"));
assert!(state.contains("ksp_wallet_lib::import_wallet_transfer"));
assert!(state.contains("ksp_wallet_lib::open_wallet_view_file"));
assert!(state.contains("ksp_wallet_lib::open_wallet_owner_file"));
assert!(inventory.contains("ksp_wallet_lib::inspect_locked_wallet_file"));
assert!(!state.contains("create_wallet_file_v1"));
assert!(!state.contains("import_wallet_transfer_v1"));
assert!(!state.contains("open_wallet_view_file_v1"));
assert!(!state.contains("open_wallet_owner_file_v1"));
assert!(!inventory.contains("inspect_locked_wallet_file_v1"));
}
#[test]
fn pre_017_wallet_desk_open_paths_remain_non_migrating() {
let state = read_source("src/app_state.rs");
let inventory = read_source("src/wallet_inventory.rs");
assert!(!state.contains("migrate_wallet_v1_to_v2"));
assert!(!state.contains("migrate_wallet_file_v1_to_v2"));
assert!(!inventory.contains("migrate_wallet_v1_to_v2"));
assert!(state.contains("ksp_wallet_lib::open_wallet_view_file"));
assert!(state.contains("ksp_wallet_lib::open_wallet_owner_file"));
assert!(inventory.contains("ksp_wallet_lib::inspect_locked_wallet_file"));
}
#[test]
fn pre_018_packaged_runtime_bundles_config_resources_and_keeps_wallet_desk_version_coherent() {
let root = app_root();
let tauri = read_json(root.join("tauri.conf.json").as_path());
let tauri_version = tauri.pointer("/version").and_then(serde_json::Value::as_str);
assert_packaged_version_floor(tauri_version, "tauri.conf.json");
let package = read_json(root.join("package.json").as_path());
let package_version = package.pointer("/version").and_then(serde_json::Value::as_str);
assert_packaged_version_floor(package_version, "package.json");
assert_eq!(tauri_version, package_version, "desktop package metadata versions must remain synchronized");
let packaged_version = match package_version {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let resources = tauri.pointer("/bundle/resources").and_then(serde_json::Value::as_object);
assert!(resources.is_some(), "packaged Wallet Desk Config resources map must exist");
if let std::option::Option::Some(resources) = resources {
assert_eq!(resources.len(), 10);
assert_eq!(
resources.get("../../config/composite.ksp-app-wallet-desk.json").and_then(serde_json::Value::as_str),
std::option::Option::Some("config/composite.ksp-app-wallet-desk.json"),
);
assert_eq!(
resources.get("../../config/schemas/composite.schema.json").and_then(serde_json::Value::as_str),
std::option::Option::Some("config/schemas/composite.schema.json"),
);
assert_eq!(
resources.get("../../config/std.offchain_transport.json").and_then(serde_json::Value::as_str),
std::option::Option::Some("config/std.offchain_transport.json"),
);
assert_eq!(
resources.get("../../config/schemas/std.offchain_transport.schema.json").and_then(serde_json::Value::as_str),
std::option::Option::Some("config/schemas/std.offchain_transport.schema.json"),
);
}
let main = read_text(root.join("frontend/main.html").as_path());
assert!(main.contains(packaged_version));
let tauri_source = read_text(root.join("src/tauri.rs").as_path());
assert!(tauri_source.contains("ksp_config_lib::prepare_packaged_runtime"));
assert!(tauri_source.contains("tauri::utils::platform::resource_dir"));
assert!(tauri_source.contains("std::env::set_current_dir(layout.runtime_root())"));
}