//! D5 release pipeline: one version everywhere, the updater's key and endpoints, the `storebound://open?path=` deep //! link and the self-test the release runs on the exact artifact. use std::fs; use std::path::PathBuf; use base64::Engine; use serde_json::Value; use storebound_desktop::policy::deep_link_open_path; use storebound_desktop::{selftest, updates}; use url::Url; fn root() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) } fn conf() -> Value { serde_json::from_str(&fs::read_to_string(root().join("tauri.conf.json")).unwrap()).unwrap() } #[test] fn one_version_with_the_cli() { let pkg: Value = serde_json::from_str(&fs::read_to_string(root().join("../../../package.json")).unwrap()).unwrap(); // one product, one number: the desktop app has the npm CLI's version (its bundled sidecar is that CLI) let want = pkg["version"].as_str().expect("package.json version"); assert_eq!(env!("CARGO_PKG_VERSION"), want, "Cargo.toml (bun tools/desktop-version.ts --set {want})"); assert_eq!(conf()["version"], want, "tauri.conf.json"); let app: Value = serde_json::from_str(&fs::read_to_string(root().join("../package.json")).unwrap()).unwrap(); assert_eq!(app["version"], want, "apps/desktop/package.json"); } #[test] fn updater_has_a_real_minisign_key_and_only_storebound_dev() { let c = conf(); let key = c["plugins"]["updater"]["pubkey"].as_str().unwrap(); let text = String::from_utf8(base64::engine::general_purpose::STANDARD.decode(key).expect("base64 pubkey")).unwrap(); let mut lines = text.lines(); assert!(lines.next().unwrap().starts_with("untrusted comment: minisign public key")); let raw = base64::engine::general_purpose::STANDARD.decode(lines.next().unwrap()).unwrap(); assert_eq!(raw.len(), 42, "Ed + key id + 32-byte key"); assert_eq!(&raw[..2], b"Ed"); assert_eq!(c["plugins"]["updater"]["endpoints"], serde_json::json!([updates::STABLE])); // updater bundles are signed by tools/desktop-release.ts on the release Mac, never by a CI runner assert_eq!(c["bundle"]["createUpdaterArtifacts"], false); for ch in ["stable", "beta", "nightly", ""] { assert!(updates::endpoint_allowed(&Url::parse(updates::endpoint(ch)).unwrap()), "{ch}"); } assert_eq!(updates::endpoint("beta"), updates::BETA); assert_eq!(updates::endpoint("anything"), updates::STABLE); for bad in ["http://storebound.dev/desktop/latest.json", "https://storebound.dev:8443/desktop/latest.json", "https://evil.test/desktop/latest.json", "https://storebound.dev/latest.json"] { assert!(!updates::endpoint_allowed(&Url::parse(bad).unwrap()), "{bad}"); } assert!(updates::error_text("signature verification failed").contains("not installed")); } #[test] fn open_deep_link_takes_one_plain_absolute_path() { let p = |s: &str| deep_link_open_path(&Url::parse(s).unwrap()); let abs = if cfg!(windows) { "C:%5Cgames%5Cdemo-app" } else { "%2FUsers%2Fyou%2Fgames%2Fdemo-app" }; let want = if cfg!(windows) { "C:\\games\\demo-app" } else { "/Users/you/games/demo-app" }; assert_eq!(p(&format!("storebound://open?path={abs}")), Some(PathBuf::from(want))); assert_eq!(p(&format!("storebound://open/?path={abs}")), Some(PathBuf::from(want))); for bad in [ "storebound://open?path=games%2Fdemo-app".to_string(), "storebound://open?path=".to_string(), "storebound://open".to_string(), format!("storebound://open?path={abs}&path={abs}"), format!("storebound://open?path={abs}&run=1"), format!("storebound://open?path={abs}%2F..%2F..%2Fetc"), format!("storebound://open?path={abs}%0A"), format!("storebound://open/x?path={abs}"), format!("storebound://open?path={abs}#f"), format!("storebound://user@open?path={abs}"), format!("storebound://apps?path={abs}"), format!("https://open?path={abs}"), format!("storebound://open?path={}", "a".repeat(5000)), ] { assert_eq!(p(&bad), None, "{bad}"); } } #[test] fn self_test_looks_for_the_sidecar_next_to_the_executable() { let p = selftest::sidecar_path().unwrap(); let exe = std::env::current_exe().unwrap(); assert_eq!(p.parent(), exe.parent()); assert_eq!(p.file_name().unwrap().to_string_lossy(), format!("storebound{}", std::env::consts::EXE_SUFFIX)); assert_eq!(selftest::FLAG, "--self-test"); // the flag is handled in main before Tauri starts, so no window opens let main = fs::read_to_string(root().join("src/main.rs")).unwrap(); let flag = main.find("selftest::FLAG").unwrap(); assert!(flag < main.find("storebound_desktop::run()").unwrap()); } #[test] fn deep_links_to_folders_ask_before_adding() { let cmds = fs::read_to_string(root().join("src/commands.rs")).unwrap(); let at = cmds.find("async fn open_folder(").unwrap(); let body = &cmds[at..]; let confirm = body.find("confirm_dialog_with(").unwrap(); let add = body.find("add_folder(").unwrap(); assert!(confirm < add, "the native dialog comes before add_folder"); let at = cmds.find("pub async fn update_install(").unwrap(); let body = &cmds[at..]; assert!(body.find("confirm_dialog_with(").unwrap() < body.find("download_and_install(").unwrap()); }