use std::process::Command;
use std::sync::OnceLock;
use tempfile::TempDir;
#[allow(unused_mut)]
fn expected_ndr_binary_path() -> std::path::PathBuf {
let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.pop(); path.pop(); path.push("target");
path.push("debug");
path.push("ndr");
#[cfg(windows)]
path.set_extension("exe");
path
}
fn ndr_binary() -> &'static std::path::PathBuf {
static BIN: OnceLock<std::path::PathBuf> = OnceLock::new();
BIN.get_or_init(|| {
if let Some(bin) = option_env!("CARGO_BIN_EXE_ndr") {
let path = std::path::PathBuf::from(bin);
if path.exists() {
return path;
}
}
let fallback = expected_ndr_binary_path();
if fallback.exists() {
return fallback;
}
let status = Command::new("cargo")
.args(["build", "-p", "ndr"])
.status()
.expect("failed to build ndr binary");
assert!(status.success(), "cargo build -p ndr failed");
fallback
})
}
fn run_ndr(data_dir: &std::path::Path, args: &[&str]) -> serde_json::Value {
let output = Command::new(ndr_binary())
.env("NDR_IGNORE_PUBLISH_ERRORS", "1")
.env("NOSTR_PREFER_LOCAL", "0")
.arg("--json")
.arg("--data-dir")
.arg(data_dir)
.args(args)
.output()
.expect("Failed to run ndr");
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
if !output.status.success() {
panic!("ndr failed: {} {}", stdout, stderr);
}
serde_json::from_str(&stdout)
.unwrap_or_else(|e| panic!("Failed to parse ndr output: {}\nOutput: {}", e, stdout))
}
#[test]
fn test_cli_encrypt_decrypt_roundtrip() {
let alice_dir = TempDir::new().unwrap();
let bob_dir = TempDir::new().unwrap();
let alice_sk = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
let bob_sk = "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210";
let result = run_ndr(alice_dir.path(), &["login", alice_sk]);
assert_eq!(result["status"], "ok");
let result = run_ndr(bob_dir.path(), &["login", bob_sk]);
assert_eq!(result["status"], "ok");
let result = run_ndr(alice_dir.path(), &["invite", "create", "--label", "Test"]);
assert_eq!(result["status"], "ok");
let invite_url = result["data"]["url"].as_str().unwrap();
let invite_id = result["data"]["id"].as_str().unwrap();
let result = run_ndr(bob_dir.path(), &["chat", "join", invite_url]);
assert_eq!(result["status"], "ok");
let bob_chat_id = result["data"]["id"].as_str().unwrap();
let response_event = result["data"]["response_event"].as_str().unwrap();
let result = run_ndr(
alice_dir.path(),
&["invite", "accept", invite_id, response_event],
);
assert_eq!(result["status"], "ok");
let alice_chat_id = result["data"]["chat_id"].as_str().unwrap();
let result = run_ndr(bob_dir.path(), &["send", bob_chat_id, "Hello from Bob!"]);
assert_eq!(result["status"], "ok");
let encrypted_event = result["data"]["event"].as_str().unwrap();
let result = run_ndr(alice_dir.path(), &["receive", encrypted_event]);
assert_eq!(result["status"], "ok");
assert_eq!(result["data"]["content"], "Hello from Bob!");
let result = run_ndr(alice_dir.path(), &["read", alice_chat_id]);
assert_eq!(result["status"], "ok");
let messages = result["data"]["messages"].as_array().unwrap();
assert_eq!(messages.len(), 1);
assert_eq!(messages[0]["content"], "Hello from Bob!");
assert_eq!(messages[0]["is_outgoing"], false);
println!("CLI e2e test passed!");
}
#[test]
fn test_bidirectional_conversation() {
let alice_dir = TempDir::new().unwrap();
let bob_dir = TempDir::new().unwrap();
let alice_sk = "1111111111111111111111111111111111111111111111111111111111111111";
let bob_sk = "2222222222222222222222222222222222222222222222222222222222222222";
run_ndr(alice_dir.path(), &["login", alice_sk]);
run_ndr(bob_dir.path(), &["login", bob_sk]);
let result = run_ndr(alice_dir.path(), &["invite", "create"]);
let invite_url = result["data"]["url"].as_str().unwrap();
let invite_id = result["data"]["id"].as_str().unwrap();
let result = run_ndr(bob_dir.path(), &["chat", "join", invite_url]);
let bob_chat_id = result["data"]["id"].as_str().unwrap();
let response_event = result["data"]["response_event"].as_str().unwrap();
let result = run_ndr(
alice_dir.path(),
&["invite", "accept", invite_id, response_event],
);
let alice_chat_id = result["data"]["chat_id"].as_str().unwrap();
let result = run_ndr(bob_dir.path(), &["send", bob_chat_id, "Message 1 from Bob"]);
let event1 = result["data"]["event"].as_str().unwrap();
let result = run_ndr(alice_dir.path(), &["receive", event1]);
assert_eq!(result["data"]["content"], "Message 1 from Bob");
let result = run_ndr(
alice_dir.path(),
&["send", alice_chat_id, "Reply from Alice"],
);
let event2 = result["data"]["event"].as_str().unwrap();
let result = run_ndr(bob_dir.path(), &["receive", event2]);
assert_eq!(result["data"]["content"], "Reply from Alice");
let result = run_ndr(bob_dir.path(), &["send", bob_chat_id, "Message 2 from Bob"]);
let event3 = result["data"]["event"].as_str().unwrap();
let result = run_ndr(alice_dir.path(), &["receive", event3]);
assert_eq!(result["data"]["content"], "Message 2 from Bob");
println!("Bidirectional conversation test passed!");
}