storebound/apps/desktop/src-tauri/tests/accounts.rs

99 lines
4.8 KiB
Rust

//! Account handling: what is stored where, what is shown, and the key checks. No real keys anywhere.
use serde_json::{json, Map};
use storebound_desktop::accounts::*;
use storebound_desktop::commands::{dialog_excerpt, dialog_summary, has_config, tilde};
use storebound_desktop::sidecar::managed_tool_env;
use storebound_desktop::store::{self, Project, State};
const FAKE_PEM: &str = "-----BEGIN PRIVATE KEY-----\nnot-a-real-key\n-----END PRIVATE KEY-----";
#[test]
fn shown_metadata_carries_no_secret() {
let a = Account { id: "abc".into(), label: "Studio Play".into(), kind: AccountKind::Play, created: "2026-10-10T00:00:00Z".into(), studio: Some("s1".into()) };
let v = serde_json::to_value(&a).unwrap();
assert_eq!(v.as_object().unwrap().keys().cloned().collect::<Vec<_>>(), vec!["created", "id", "kind", "label", "studio"]);
assert_eq!(v["kind"], "play");
}
#[test]
fn secrets_map_to_the_cli_env_names() {
let mut m = Map::new();
Secret::Apple { key_id: "ABCDEFGH12".into(), issuer_id: "00000000-0000-0000-0000-000000000000".into(), p8: FAKE_PEM.into() }.to_credentials(&mut m);
Secret::Play { service_account: "{}".into() }.to_credentials(&mut m);
assert_eq!(m.keys().cloned().collect::<Vec<_>>(), vec!["ASC_ISSUER_ID", "ASC_KEY_ID", "ASC_P8", "PLAY_SERVICE_ACCOUNT"]);
let s = Secret::Play { service_account: "{}".into() };
let back: Secret = serde_json::from_str(&serde_json::to_string(&s).unwrap()).unwrap();
assert_eq!(back, s);
}
#[test]
fn key_checks() {
let sa = json!({ "type": "service_account", "client_email": "bot@example.com", "private_key": FAKE_PEM }).to_string();
assert!(check_service_account(&sa).is_ok());
assert!(check_service_account("{\"client_email\":\"x@example.com\"}").is_err());
assert!(check_service_account("not json").is_err());
assert_eq!(check_key_id(" ABCDEFGH12 ").unwrap(), "ABCDEFGH12");
assert!(check_key_id("abc").is_err());
assert!(check_key_id("abcdefgh12").is_err());
let upper = "00000000-0000-0000-0000-00000000000A";
assert_eq!(check_issuer_id(upper).unwrap(), upper.to_ascii_lowercase());
assert!(check_issuer_id("0000-0000").is_err());
assert!(check_p8(FAKE_PEM).is_ok());
assert!(check_p8("hello").is_err());
assert!(check_label("").is_err());
assert!(check_label("a\u{7}").is_err());
assert!(check_label(&"x".repeat(61)).is_err());
assert_eq!(check_label(" Studio ").unwrap(), "Studio");
}
#[test]
fn state_file_round_trip_is_atomic_and_tolerant() {
let dir = tempfile::tempdir().unwrap();
assert_eq!(store::load(dir.path()), State::default());
let st = State {
projects: vec![Project { id: "1".into(), path: "/games/a".into(), name: "A".into(), play_account: Some("p".into()), apple_account: None, added: "2026-10-10T00:00:00Z".into() }],
accounts: vec![],
..State::default()
};
store::save(dir.path(), &st).unwrap();
assert_eq!(store::load(dir.path()), st);
assert!(!dir.path().join("state.json.tmp").exists());
let text = std::fs::read_to_string(dir.path().join("state.json")).unwrap();
assert!(text.contains("\"playAccount\": \"p\""));
std::fs::write(dir.path().join("state.json"), "{broken").unwrap();
assert_eq!(store::load(dir.path()), State::default());
assert_eq!(store::iso_from_unix(0), "1970-01-01T00:00:00Z");
assert_eq!(store::iso_from_unix(1_700_000_000), "2023-11-14T22:13:20Z");
assert_eq!(store::new_id().unwrap().len(), 16);
}
#[test]
fn managed_tools_are_passed_only_when_present() {
let dir = tempfile::tempdir().unwrap();
assert!(managed_tool_env(dir.path()).is_empty());
std::fs::create_dir_all(dir.path().join("tools/bin")).unwrap();
let env = managed_tool_env(dir.path());
assert_eq!(env.len(), 1);
assert_eq!(env[0].0, "STOREBOUND_TOOL_PATH");
assert!(env[0].1.ends_with("bin"));
}
#[test]
fn confirm_dialog_text() {
let s = dialog_summary("Push Play listing text", "Title and descriptions.", "Prism Puddle", true, true);
assert!(s.starts_with("Push Play listing text · Prism Puddle"));
assert!(s.contains("not public until you submit"));
assert!(s.contains("replaces"));
let out: String = (1..=40).map(|i| format!("line {i}\n")).collect();
let ex = dialog_excerpt(&out);
assert!(ex.starts_with("(12 earlier lines"));
assert!(ex.ends_with("line 40"));
assert!(dialog_excerpt(&"x".repeat(5000)).chars().count() < 2_500);
assert_eq!(tilde("would write /home/you/a/index.html", Some("/home/you/")), "would write ~/a/index.html");
assert_eq!(tilde("/x", None), "/x");
assert_eq!(tilde("/x", Some("/")), "/x");
let dir = tempfile::tempdir().unwrap();
assert!(!has_config(dir.path()));
std::fs::write(dir.path().join("storebound.config.ts"), "export default {}").unwrap();
assert!(has_config(dir.path()));
}