use totalreclaw_memory::billing;
use totalreclaw_memory::crypto;
use totalreclaw_memory::relay::{RelayClient, RelayConfig};
use totalreclaw_memory::setup;
use totalreclaw_memory::wallet;
const RELAY_URL: &str = "https://api-staging.totalreclaw.xyz";
#[tokio::test]
#[ignore] async fn test_e2e_register_and_billing() {
println!("=== E2E: Register + Billing Status ===\n");
let mnemonic = setup::generate_mnemonic();
println!("1. Generated test mnemonic: {}...", &mnemonic[..25]);
let keys = crypto::derive_keys_from_mnemonic(&mnemonic).unwrap();
let auth_key_hex = hex::encode(keys.auth_key);
let auth_key_hash = crypto::compute_auth_key_hash(&keys.auth_key);
let salt_hex = hex::encode(keys.salt);
let eth_wallet = wallet::derive_eoa(&mnemonic).unwrap();
let smart_account =
wallet::resolve_smart_account_address(ð_wallet.address, "https://sepolia.base.org")
.await
.unwrap();
println!(" Smart Account: {}", smart_account);
let relay = RelayClient::new(RelayConfig {
relay_url: RELAY_URL.to_string(),
auth_key_hex: auth_key_hex.clone(),
wallet_address: smart_account.clone(),
is_test: true,
chain_id: 84532,
});
println!("\n2. Registering with relay (proves rust-client:zeroclaw header works)...");
match relay.register(&auth_key_hash, &salt_hex).await {
Ok(uid) => println!(" Registered: {} -- PASS", uid),
Err(e) => println!(" Warning: {} (may already exist)", e),
}
println!("\n3. Fetching billing status...");
match billing::fetch_billing_status(&relay).await {
Ok(cache) => {
println!(" Tier: {}", cache.tier);
println!(" Facts used: {}/{}", cache.facts_used, cache.facts_limit);
println!(" Extraction interval: {}", billing::get_extraction_interval(Some(&cache)));
println!(" Max facts/extraction: {}", billing::get_max_facts_per_extraction(Some(&cache)));
println!(" Max candidate pool: {}", billing::get_max_candidate_pool(Some(&cache)));
println!(" LLM dedup enabled: {}", billing::is_llm_dedup_enabled(Some(&cache)));
println!(" Quota warning: {:?}", cache.quota_warning_message());
println!(" PASS");
assert_eq!(cache.tier, "free", "New user should be free tier");
}
Err(e) => {
println!(" Warning: {}", e);
}
}
println!("\n4. Checking billing cache persistence...");
let cached = billing::read_cache();
if let Some(c) = cached {
println!(" Cached tier: {} -- PASS", c.tier);
} else {
println!(" No cache found (may be in temp dir) -- SKIP");
}
println!("\nE2E Register + Billing PASSED!");
}
#[tokio::test]
#[ignore] async fn test_e2e_store_recall_with_dedup() {
println!("=== E2E: Store + Recall + Dedup ===\n");
let mnemonic = setup::generate_mnemonic();
let keys = crypto::derive_keys_from_mnemonic(&mnemonic).unwrap();
let auth_key_hex = hex::encode(keys.auth_key);
let auth_key_hash = crypto::compute_auth_key_hash(&keys.auth_key);
let salt_hex = hex::encode(keys.salt);
let eth_wallet = wallet::derive_eoa(&mnemonic).unwrap();
let smart_account =
wallet::resolve_smart_account_address(ð_wallet.address, "https://sepolia.base.org")
.await
.unwrap();
println!("1. Smart Account: {}", smart_account);
let relay = RelayClient::new(RelayConfig {
relay_url: RELAY_URL.to_string(),
auth_key_hex: auth_key_hex.clone(),
wallet_address: smart_account.clone(),
is_test: true,
chain_id: 84532,
});
relay.register(&auth_key_hash, &salt_hex).await.ok();
let ts = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let fact_text = format!("E2E spec validation fact at {}", ts);
println!("\n2. Storing fact with importance=8 (decayScore should be 0.8): '{}'", fact_text);
let encrypted_b64 = crypto::encrypt(&fact_text, &keys.encryption_key).unwrap();
let encrypted_bytes = base64::Engine::decode(
&base64::engine::general_purpose::STANDARD,
&encrypted_b64,
)
.unwrap();
let encrypted_hex = hex::encode(&encrypted_bytes);
let blind_indices = totalreclaw_memory::blind::generate_blind_indices(&fact_text);
let content_fp =
totalreclaw_memory::fingerprint::generate_content_fingerprint(&fact_text, &keys.dedup_key);
let fact_id = uuid::Uuid::now_v7().to_string();
let timestamp = chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true);
let payload = totalreclaw_memory::protobuf::FactPayload {
id: fact_id.clone(),
timestamp,
owner: smart_account.clone(),
encrypted_blob_hex: encrypted_hex,
blind_indices,
decay_score: 0.8, source: "zeroclaw:e2e-spec-validation".to_string(),
content_fp,
agent_id: "e2e-spec-validation".to_string(),
encrypted_embedding: None,
version: totalreclaw_memory::protobuf::DEFAULT_PROTOBUF_VERSION,
};
let protobuf = totalreclaw_memory::protobuf::encode_fact_protobuf(&payload);
let result = relay
.submit_fact_native(&protobuf, ð_wallet.private_key)
.await
.expect("Store should succeed");
println!(
" Stored: txHash={} success={} -- {}",
result.tx_hash,
result.success,
if result.success { "PASS" } else { "FAIL" }
);
assert!(result.success);
println!("\n3. Waiting 45s for subgraph indexing...");
tokio::time::sleep(std::time::Duration::from_secs(45)).await;
println!("\n4. Recalling via blind index search...");
let trapdoors =
totalreclaw_memory::blind::generate_blind_indices("e2e spec validation fact");
let billing_cache = billing::read_cache();
let max_candidates = billing::get_max_candidate_pool(billing_cache.as_ref());
println!(" Using candidate pool: {}", max_candidates);
let candidates = totalreclaw_memory::search::search_candidates(
&relay,
&smart_account,
&trapdoors,
max_candidates,
)
.await
.unwrap_or_default();
println!(" Found {} candidates", candidates.len());
let mut found = false;
for fact in &candidates {
if let Some(b64) = totalreclaw_memory::search::hex_blob_to_base64(&fact.encrypted_blob) {
if let Ok(text) = crypto::decrypt(&b64, &keys.encryption_key) {
println!(" Decrypted: {}", text);
if text.contains(&ts.to_string()) {
found = true;
if let Some(ds) = &fact.decay_score {
let ds_val: f64 = ds.parse().unwrap_or(0.0);
println!(" DecayScore: {} (expected ~0.8)", ds_val);
}
}
}
}
}
assert!(found, "Should find the stored fact -- FAIL");
println!(" Found stored fact -- PASS");
println!("\nE2E Store + Recall + Dedup PASSED!");
}