use assert_cmd::Command;
use predicates::prelude::*;
use serde_json::Value;
const ENGLISH_12: &str =
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
fn japanese_12_word() -> String {
bip39::Mnemonic::from_entropy_in(bip39::Language::Japanese, &[0xABu8; 16])
.expect("valid entropy length")
.to_string()
}
fn share_header(s: &str) -> (char, char, String) {
let sep = s.rfind('1').unwrap();
let b = s.as_bytes();
let threshold = b[sep + 1] as char;
let id: String = s[sep + 2..sep + 6].to_string();
let index = b[sep + 6] as char;
(threshold, index, id)
}
#[test]
fn split_english_phrase_emits_n_shares_text() {
let out = Command::cargo_bin("ms")
.unwrap()
.args([
"split",
"--phrase",
ENGLISH_12,
"-k",
"2",
"-n",
"3",
"--group-size",
"0",
])
.assert()
.success()
.get_output()
.stdout
.clone();
let s = String::from_utf8(out).unwrap();
let shares: Vec<&str> = s
.lines()
.filter(|l| l.starts_with("ms1") && !l.contains(' '))
.collect();
assert_eq!(shares.len(), 3, "expected 3 share strings; got:\n{s}");
let mut indices = Vec::new();
let mut ids = Vec::new();
for sh in &shares {
let (thr, idx, id) = share_header(sh);
assert_eq!(thr, '2', "threshold char");
assert_ne!(idx, 's', "distributed share must not be index s");
indices.push(idx);
ids.push(id);
}
let mut sorted = indices.clone();
sorted.sort_unstable();
sorted.dedup();
assert_eq!(sorted.len(), 3, "distinct indices");
assert!(ids.windows(2).all(|w| w[0] == w[1]), "shared id");
}
#[test]
fn split_emits_private_key_material_advisory() {
Command::cargo_bin("ms")
.unwrap()
.args(["split", "--phrase", ENGLISH_12, "-k", "2", "-n", "3"])
.assert()
.success()
.stderr(predicate::str::contains("private key material"));
}
#[test]
fn split_json_shape() {
let out = Command::cargo_bin("ms")
.unwrap()
.args([
"split",
"--hex",
&"ab".repeat(16),
"-k",
"3",
"-n",
"5",
"--json",
])
.assert()
.success()
.get_output()
.stdout
.clone();
let v: Value = serde_json::from_slice(&out).expect("valid json");
assert_eq!(v["schema_version"], "1");
assert_eq!(v["k"], 3);
assert_eq!(v["n"], 5);
assert_eq!(v["kind"], "entr");
assert_eq!(v["shares"].as_array().unwrap().len(), 5);
assert!(v["id"].is_string());
assert!(v.get("language").is_none() || v["language"].is_null());
}
#[test]
fn split_japanese_phrase_json_has_language_and_mnem_kind() {
let ja = japanese_12_word();
let out = Command::cargo_bin("ms")
.unwrap()
.args([
"split",
"--language",
"japanese",
"--phrase",
&ja,
"-k",
"2",
"-n",
"3",
"--json",
])
.assert()
.success()
.get_output()
.stdout
.clone();
let v: Value = serde_json::from_slice(&out).expect("valid json");
assert_eq!(v["kind"], "mnem");
assert_eq!(v["language"], "japanese");
assert_eq!(v["shares"].as_array().unwrap().len(), 3);
}
#[test]
fn split_k_below_2_rejected() {
Command::cargo_bin("ms")
.unwrap()
.args(["split", "--phrase", ENGLISH_12, "-k", "1", "-n", "3"])
.assert()
.failure()
.code(1); }
#[test]
fn split_n_below_k_rejected() {
Command::cargo_bin("ms")
.unwrap()
.args(["split", "--phrase", ENGLISH_12, "-k", "3", "-n", "2"])
.assert()
.failure()
.code(1); }
#[test]
fn split_n_above_31_rejected() {
Command::cargo_bin("ms")
.unwrap()
.args(["split", "--phrase", ENGLISH_12, "-k", "2", "-n", "32"])
.assert()
.failure()
.code(1); }