use jacs::simple::{CreateAgentParams, SimpleAgent};
use jacs_binding_core::{AgentWrapper, DocumentServiceWrapper};
use serde_json::Value;
use serial_test::serial;
use std::fs;
const TEST_PASSWORD: &str = "TestP@ss123!#";
fn agent_with_storage(storage: &str) -> (AgentWrapper, tempfile::TempDir) {
let tmp = tempfile::TempDir::new().expect("create tempdir");
let tmp_canonical = tmp
.path()
.canonicalize()
.unwrap_or_else(|_| tmp.path().to_path_buf());
let data_dir = tmp_canonical.join("jacs_data");
let key_dir = tmp_canonical.join("jacs_keys");
let config_path = tmp_canonical.join("jacs.config.json");
let params = CreateAgentParams::builder()
.name("binding-doc-wrapper-backend-test")
.password(TEST_PASSWORD)
.algorithm("ring-Ed25519")
.data_directory(data_dir.to_str().unwrap())
.key_directory(key_dir.to_str().unwrap())
.config_path(config_path.to_str().unwrap())
.default_storage("fs")
.build();
let (_agent, _info) =
SimpleAgent::create_with_params(params).expect("create_with_params should succeed");
let mut config_json: Value =
serde_json::from_str(&fs::read_to_string(&config_path).expect("read generated config"))
.expect("parse generated config");
config_json["jacs_default_storage"] = Value::String(storage.to_string());
config_json["jacs_dns_validate"] = Value::Bool(false);
fs::write(
&config_path,
serde_json::to_string_pretty(&config_json).expect("serialize config"),
)
.expect("write updated config");
unsafe {
std::env::set_var("JACS_PRIVATE_KEY_PASSWORD", TEST_PASSWORD);
std::env::set_var("JACS_KEY_RESOLUTION", "local");
}
let wrapper = AgentWrapper::new();
wrapper
.load(config_path.to_string_lossy().into_owned())
.expect("agent load should succeed");
(wrapper, tmp)
}
fn sqlite_ready_agent() -> (AgentWrapper, tempfile::TempDir) {
let tmp = tempfile::TempDir::new().expect("create tempdir");
let tmp_canonical = tmp
.path()
.canonicalize()
.unwrap_or_else(|_| tmp.path().to_path_buf());
let data_dir = tmp_canonical.join("jacs_data");
let key_dir = tmp_canonical.join("jacs_keys");
let config_path = tmp_canonical.join("jacs.config.json");
let params = CreateAgentParams::builder()
.name("binding-doc-wrapper-sqlite")
.password(TEST_PASSWORD)
.algorithm("ring-Ed25519")
.data_directory(data_dir.to_str().unwrap())
.key_directory(key_dir.to_str().unwrap())
.config_path(config_path.to_str().unwrap())
.default_storage("fs")
.build();
let (_agent, _info) =
SimpleAgent::create_with_params(params).expect("create_with_params should succeed");
let mut config_json: Value =
serde_json::from_str(&fs::read_to_string(&config_path).expect("read generated config"))
.expect("parse generated config");
config_json["jacs_default_storage"] = Value::String("rusqlite".to_string());
config_json["jacs_dns_validate"] = Value::Bool(false);
fs::write(
&config_path,
serde_json::to_string_pretty(&config_json).expect("serialize config"),
)
.expect("write updated config");
unsafe {
std::env::set_var("JACS_PRIVATE_KEY_PASSWORD", TEST_PASSWORD);
std::env::set_var("JACS_KEY_RESOLUTION", "local");
}
let wrapper = AgentWrapper::new();
wrapper
.load(config_path.to_string_lossy().into_owned())
.expect("agent load should succeed");
(wrapper, tmp)
}
#[test]
#[serial]
#[ignore = "pre-existing: publicKeyHash mismatch between create and reload paths"]
fn from_agent_wrapper_uses_sqlite_search_backend() {
let (agent, _tmp) = sqlite_ready_agent();
let docs = DocumentServiceWrapper::from_agent_wrapper(&agent)
.expect("document service wrapper should resolve sqlite backend");
docs.create_json(r#"{"content":"bindingsqliteprobe alpha"}"#, None)
.expect("create first doc");
docs.create_json(r#"{"content":"bindingsqliteprobe beta"}"#, None)
.expect("create second doc");
let result_json = docs
.search_json(r#"{"query":"bindingsqliteprobe","limit":10,"offset":0}"#)
.expect("search_json should succeed");
let result: Value = serde_json::from_str(&result_json).expect("search result should be JSON");
assert_eq!(result["method"], "FullText");
assert!(
result["results"]
.as_array()
.map(|items| !items.is_empty())
.unwrap_or(false),
"sqlite-backed search should return at least one hit: {}",
result
);
}
#[test]
#[serial]
fn from_agent_wrapper_uses_filesystem_by_default() {
let (agent, tmp) = agent_with_storage("fs");
let saved_cwd = std::env::current_dir().expect("get cwd");
std::env::set_current_dir(tmp.path()).expect("set cwd to temp dir");
let docs = DocumentServiceWrapper::from_agent_wrapper(&agent)
.expect("document service wrapper should resolve filesystem backend");
let created_json = docs
.create_json(r#"{"content":"fsprobe alpha"}"#, None)
.expect("create doc on filesystem");
let created: Value =
serde_json::from_str(&created_json).expect("created doc should be valid JSON");
assert!(
created.get("jacsId").is_some(),
"created doc should have jacsId"
);
let result_json = docs
.search_json(r#"{"query":"fsprobe","limit":10,"offset":0}"#)
.expect("search_json should succeed on filesystem");
let result: Value =
serde_json::from_str(&result_json).expect("search result should be valid JSON");
assert_eq!(
result["method"], "FieldMatch",
"filesystem search should use FieldMatch method, got: {}",
result
);
std::env::set_current_dir(saved_cwd).expect("restore cwd");
}
#[test]
#[serial]
#[cfg(all(not(target_arch = "wasm32"), feature = "attestation"))]
fn service_from_agent_with_sqlite_connection_string() {
let tmp = tempfile::TempDir::new().expect("create tempdir");
let db_path = tmp.path().join("custom.sqlite3");
let conn_string = format!("sqlite://{}", db_path.display());
let (agent, _agent_tmp) = agent_with_storage("fs");
let agent_arc = agent.inner_arc();
{
let mut agent_guard = agent_arc.lock().unwrap();
if let Some(ref mut config) = agent_guard.config {
let mut config_val = serde_json::to_value(&*config).unwrap();
config_val["jacs_default_storage"] = Value::String(conn_string.clone());
*config = serde_json::from_value(config_val).unwrap();
}
}
let service = jacs::document::service_from_agent(agent_arc)
.expect("service_from_agent should resolve sqlite connection string");
let doc = service
.create(
r#"{"data":"connection string test"}"#,
jacs::document::types::CreateOptions::default(),
)
.expect("create document via connection-string-resolved sqlite backend");
assert!(
!doc.id.is_empty(),
"created doc should have an assigned jacsId"
);
assert!(
db_path.exists(),
"SQLite database should exist at the connection-string path: {}",
db_path.display()
);
}
#[test]
#[serial]
fn service_from_agent_rejects_unsupported_backend() {
let (agent, _tmp) = agent_with_storage("fs");
let agent_arc = agent.inner_arc();
{
let mut agent_guard = agent_arc.lock().unwrap();
if let Some(ref mut config) = agent_guard.config {
let mut config_val = serde_json::to_value(&*config).unwrap();
config_val["jacs_default_storage"] = Value::String("memory".to_string());
*config = serde_json::from_value(config_val).unwrap();
}
}
let result = jacs::document::service_from_agent(agent_arc);
let err_msg = match result {
Err(e) => e.to_string(),
Ok(_) => panic!("service_from_agent should fail for unsupported backend 'memory'"),
};
assert!(
err_msg.contains("memory"),
"error message should mention the unsupported backend name 'memory': {}",
err_msg
);
}