use jacs_binding_core::SimpleAgentWrapper;
use serde_json::Value;
use serial_test::serial;
use std::path::{Path, PathBuf};
struct CwdGuard {
original: PathBuf,
}
impl CwdGuard {
fn change_to(path: &Path) -> Self {
let original = std::env::current_dir().expect("current dir should be available");
std::env::set_current_dir(path).expect("should change current dir");
Self { original }
}
}
impl Drop for CwdGuard {
fn drop(&mut self) {
let _ = std::env::set_current_dir(&self.original);
}
}
fn canonical_display(path: &Path) -> String {
std::fs::canonicalize(path)
.unwrap_or_else(|_| path.to_path_buf())
.to_string_lossy()
.to_string()
}
fn assert_same_path(actual: &Value, expected: &Path) {
let actual_path = actual.as_str().expect("path value should be a string");
assert_eq!(
canonical_display(Path::new(actual_path)),
canonical_display(expected)
);
}
fn ephemeral_wrapper() -> SimpleAgentWrapper {
let (wrapper, _info) =
SimpleAgentWrapper::ephemeral(Some("ed25519")).expect("ephemeral should succeed");
wrapper
}
#[test]
#[serial]
fn test_create_returns_wrapper_and_info_json() {
let tmp = tempfile::TempDir::new().unwrap();
let _cwd_guard = CwdGuard::change_to(tmp.path());
unsafe {
std::env::set_var("JACS_PRIVATE_KEY_PASSWORD", "TestP@ss123!#");
}
let result = SimpleAgentWrapper::create("test-agent", None, Some("ed25519"));
let (wrapper, info_json) = result.expect("create should succeed");
let info: Value = serde_json::from_str(&info_json).expect("info should be valid JSON");
assert!(
info.get("agent_id").is_some(),
"info should contain agent_id"
);
assert!(
!info["agent_id"].as_str().unwrap_or("").is_empty(),
"agent_id should be non-empty"
);
let signed = wrapper.sign_message_json(r#"{"test": true}"#);
assert!(signed.is_ok(), "wrapper from create should be able to sign");
}
#[test]
#[serial]
fn test_load_roundtrips_with_create() {
let tmp = tempfile::TempDir::new().unwrap();
let _cwd_guard = CwdGuard::change_to(tmp.path());
let data_dir = tmp.path().join("jacs_data");
let key_dir = tmp.path().join("jacs_keys");
let config_path = tmp.path().join("jacs.config.json");
let params = jacs::simple::CreateAgentParams::builder()
.name("load-test")
.password("TestP@ss123!#")
.algorithm("ring-Ed25519")
.data_directory(data_dir.to_str().unwrap())
.key_directory(key_dir.to_str().unwrap())
.config_path(config_path.to_str().unwrap())
.build();
let (_agent, _info) =
jacs::simple::SimpleAgent::create_with_params(params).expect("create should succeed");
unsafe {
std::env::set_var("JACS_PRIVATE_KEY_PASSWORD", "TestP@ss123!#");
}
let wrapper = SimpleAgentWrapper::load(Some(config_path.to_str().unwrap()), None)
.expect("load should succeed");
unsafe {
std::env::remove_var("JACS_PRIVATE_KEY_PASSWORD");
}
let diag = wrapper.diagnostics();
let diag_value: Value = serde_json::from_str(&diag).expect("diagnostics should be JSON");
assert_eq!(diag_value["agent_loaded"], true);
}
#[test]
#[serial]
fn test_load_with_info_returns_resolved_metadata() {
let tmp = tempfile::TempDir::new().unwrap();
let config_dir = tmp.path().join("nested");
let data_dir = config_dir.join("jacs_data");
let key_dir = config_dir.join("jacs_keys");
let config_path = config_dir.join("jacs.config.json");
let params = jacs::simple::CreateAgentParams::builder()
.name("load-with-info-test")
.password("TestP@ss123!#")
.algorithm("ring-Ed25519")
.data_directory(data_dir.to_str().unwrap())
.key_directory(key_dir.to_str().unwrap())
.config_path(config_path.to_str().unwrap())
.domain("load-info.example.com")
.build();
let (_agent, created_info) =
jacs::simple::SimpleAgent::create_with_params(params).expect("create should succeed");
let _cwd_guard = CwdGuard::change_to(tmp.path());
unsafe {
std::env::set_var("JACS_PRIVATE_KEY_PASSWORD", "TestP@ss123!#");
}
let (_wrapper, info_json) =
SimpleAgentWrapper::load_with_info(Some("./nested/jacs.config.json"), None)
.expect("load_with_info should succeed");
let info: Value = serde_json::from_str(&info_json).expect("info should be valid JSON");
assert_eq!(info["agent_id"], created_info.agent_id);
assert_eq!(info["version"], created_info.version);
assert_eq!(info["algorithm"], created_info.algorithm);
assert_same_path(&info["config_path"], &config_path);
assert_same_path(&info["data_directory"], &data_dir);
assert_same_path(&info["key_directory"], &key_dir);
assert_same_path(
&info["public_key_path"],
&PathBuf::from(&key_dir).join("jacs.public.pem"),
);
assert_same_path(
&info["private_key_path"],
&PathBuf::from(&key_dir).join("jacs.private.pem.enc"),
);
unsafe {
std::env::remove_var("JACS_PRIVATE_KEY_PASSWORD");
}
}
#[test]
fn test_ephemeral_creates_wrapper() {
let (wrapper, info_json) =
SimpleAgentWrapper::ephemeral(Some("ed25519")).expect("ephemeral should succeed");
let info: Value = serde_json::from_str(&info_json).expect("info should be valid JSON");
assert!(!info["agent_id"].as_str().unwrap_or("").is_empty());
let signed = wrapper.sign_message_json(r#"{"ephemeral": true}"#);
assert!(signed.is_ok());
}
#[cfg(feature = "pq-tests")]
#[test]
fn test_ephemeral_default_algorithm() {
let (wrapper, info_json) =
SimpleAgentWrapper::ephemeral(None).expect("ephemeral(None) should succeed");
let info: Value = serde_json::from_str(&info_json).expect("info should be valid JSON");
assert!(
info["algorithm"].as_str().unwrap_or("").contains("pq2025"),
"default should be pq2025"
);
let signed = wrapper.sign_message_json(r#"{"pq": true}"#);
assert!(signed.is_ok());
}
#[test]
fn test_sign_message_json_and_verify_roundtrip() {
let wrapper = ephemeral_wrapper();
let data = r#"{"action": "test", "value": 42}"#;
let signed_json = wrapper
.sign_message_json(data)
.expect("sign_message_json should succeed");
let signed: Value =
serde_json::from_str(&signed_json).expect("signed output should be valid JSON");
assert!(signed.get("jacsSignature").is_some());
let verify_json = wrapper
.verify_json(&signed_json)
.expect("verify_json should succeed");
let result: Value =
serde_json::from_str(&verify_json).expect("verify result should be valid JSON");
assert_eq!(result["valid"], true, "verification should succeed");
}
#[test]
fn test_export_agent_returns_valid_json() {
let wrapper = ephemeral_wrapper();
let exported = wrapper.export_agent().expect("export_agent should succeed");
let parsed: Value =
serde_json::from_str(&exported).expect("exported agent should be valid JSON");
assert!(parsed.get("jacsId").is_some(), "should have jacsId");
}
#[test]
fn test_get_public_key_pem_returns_pem() {
let wrapper = ephemeral_wrapper();
let pem = wrapper
.get_public_key_pem()
.expect("get_public_key_pem should succeed");
assert!(!pem.is_empty());
assert!(
pem.contains("-----BEGIN") || pem.contains("PUBLIC KEY"),
"should be PEM format"
);
}
#[test]
fn test_diagnostics_returns_json() {
let wrapper = ephemeral_wrapper();
let diag = wrapper.diagnostics();
let parsed: Value = serde_json::from_str(&diag).expect("diagnostics should be valid JSON");
assert!(parsed.get("jacs_version").is_some());
assert_eq!(parsed["agent_loaded"], true);
}
#[test]
fn test_simple_agent_wrapper_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<SimpleAgentWrapper>();
}
#[test]
fn test_get_agent_id() {
let wrapper = ephemeral_wrapper();
let agent_id = wrapper.get_agent_id().expect("get_agent_id should succeed");
assert!(!agent_id.is_empty());
}
#[test]
fn test_get_public_key_returns_base64() {
let wrapper = ephemeral_wrapper();
let key_b64 = wrapper
.get_public_key_base64()
.expect("get_public_key_base64 should succeed");
assert!(!key_b64.is_empty());
use base64::Engine;
let decoded = base64::engine::general_purpose::STANDARD
.decode(&key_b64)
.expect("should be valid base64");
assert!(!decoded.is_empty(), "decoded key should be non-empty");
}
#[test]
fn test_verify_self() {
let wrapper = ephemeral_wrapper();
let result_json = wrapper.verify_self().expect("verify_self should succeed");
let result: Value =
serde_json::from_str(&result_json).expect("verify_self result should be JSON");
assert_eq!(result["valid"], true);
}
#[test]
fn test_key_id() {
let wrapper = ephemeral_wrapper();
let kid = wrapper.key_id().expect("key_id should succeed");
assert!(!kid.is_empty());
}
#[test]
fn test_is_strict_default() {
let wrapper = ephemeral_wrapper();
assert!(!wrapper.is_strict());
}
#[test]
fn test_config_path_ephemeral() {
let wrapper = ephemeral_wrapper();
assert!(wrapper.config_path().is_none());
}
#[test]
fn test_sign_raw_bytes() {
let wrapper = ephemeral_wrapper();
let sig_b64 = wrapper
.sign_raw_bytes_base64(b"hello world")
.expect("sign_raw_bytes_base64 should succeed");
assert!(!sig_b64.is_empty());
use base64::Engine;
base64::engine::general_purpose::STANDARD
.decode(&sig_b64)
.expect("signature should be valid base64");
}
#[test]
fn test_sign_message_json_ffi() {
let wrapper = ephemeral_wrapper();
let signed = jacs_binding_core::sign_message_json(&wrapper, r#"{"go_ffi": true}"#)
.expect("sign_message_json should succeed");
let parsed: Value = serde_json::from_str(&signed).expect("should be valid JSON");
assert!(parsed.get("jacsSignature").is_some());
}
#[test]
fn test_verify_json_ffi() {
let wrapper = ephemeral_wrapper();
let signed = wrapper
.sign_message_json(r#"{"ffi_verify": true}"#)
.expect("sign should succeed");
let result =
jacs_binding_core::verify_json(&wrapper, &signed).expect("verify_json should succeed");
let parsed: Value = serde_json::from_str(&result).expect("should be valid JSON");
assert_eq!(parsed["valid"], true);
}
#[test]
fn test_verify_with_key_json_roundtrip() {
let wrapper = ephemeral_wrapper();
let signed = wrapper
.sign_message_json(r#"{"key_test": true}"#)
.expect("sign should succeed");
let key_b64 = wrapper
.get_public_key_base64()
.expect("get_public_key_base64 should succeed");
let result = wrapper
.verify_with_key_json(&signed, &key_b64)
.expect("verify_with_key_json should succeed");
let parsed: Value = serde_json::from_str(&result).expect("should be valid JSON");
assert_eq!(
parsed["valid"], true,
"verification with explicit key should succeed"
);
}
#[test]
fn test_verify_with_key_json_invalid_base64() {
let wrapper = ephemeral_wrapper();
let signed = wrapper
.sign_message_json(r#"{"test": 1}"#)
.expect("sign should succeed");
let result = wrapper.verify_with_key_json(&signed, "not-valid-base64!!!");
assert!(result.is_err(), "invalid base64 should return error");
}
#[test]
fn test_verify_by_id_json_bad_format() {
let wrapper = ephemeral_wrapper();
let result = wrapper.verify_by_id_json("not-a-valid-id");
assert!(
result.is_err(),
"verify_by_id with bad format should return error"
);
}
#[test]
fn test_verify_by_id_json_missing_document() {
let wrapper = ephemeral_wrapper();
let result = wrapper.verify_by_id_json("00000000-0000-0000-0000-000000000000:1");
assert!(
result.is_err(),
"verify_by_id for missing doc should return error"
);
}
#[test]
fn test_sign_file_json() {
let tmp = tempfile::TempDir::new().unwrap();
let file_path = tmp.path().join("test_file.txt");
std::fs::write(&file_path, b"hello world").unwrap();
let wrapper = ephemeral_wrapper();
let signed = wrapper
.sign_file_json(file_path.to_str().unwrap(), true)
.expect("sign_file_json should succeed");
let parsed: Value = serde_json::from_str(&signed).expect("should be valid JSON");
assert!(
parsed.get("jacsSignature").is_some(),
"signed file should have jacsSignature"
);
}
#[test]
fn test_from_agent() {
let (agent, _info) =
jacs::simple::SimpleAgent::ephemeral(Some("ed25519")).expect("ephemeral should succeed");
let wrapper = SimpleAgentWrapper::from_agent(agent);
let agent_id = wrapper.get_agent_id().expect("get_agent_id should succeed");
assert!(
!agent_id.is_empty(),
"wrapper from from_agent should be usable"
);
}
#[test]
#[serial]
fn test_create_with_params_json() {
let tmp = tempfile::TempDir::new().unwrap();
let data_dir = tmp.path().join("data");
let key_dir = tmp.path().join("keys");
let config_path = tmp.path().join("config.json");
let params_json = serde_json::json!({
"name": "params-test",
"password": "TestP@ss123!#",
"algorithm": "ring-Ed25519",
"data_directory": data_dir.to_str().unwrap(),
"key_directory": key_dir.to_str().unwrap(),
"config_path": config_path.to_str().unwrap()
})
.to_string();
let (wrapper, info_json) = SimpleAgentWrapper::create_with_params(¶ms_json)
.expect("create_with_params should succeed");
let info: Value = serde_json::from_str(&info_json).expect("info should be valid JSON");
assert!(!info["agent_id"].as_str().unwrap_or("").is_empty());
unsafe {
std::env::set_var("JACS_PRIVATE_KEY_PASSWORD", "TestP@ss123!#");
}
let signed = wrapper
.sign_message_json(r#"{"params_test": true}"#)
.expect("signing should succeed after create_with_params");
unsafe {
std::env::remove_var("JACS_PRIVATE_KEY_PASSWORD");
}
assert!(!signed.is_empty());
}
#[test]
fn test_create_with_params_invalid_json() {
let result = SimpleAgentWrapper::create_with_params("not json");
assert!(result.is_err(), "invalid JSON should return error");
}