use easy_nostr::EasyNostr; use nostr_sdk::prelude::*; use std::time::Duration; use tokio::time::sleep; #[tokio::main] async fn main() -> anyhow::Result<()> { println!("=== EasyNostr Production Test Suite ==="); // 1. Identity let keys = Keys::generate(); let nsec = keys.secret_key().to_bech32()?; let npub = keys.public_key().to_bech32()?; println!("[INFO] Identity: {}", npub); // 2. Init let ez = EasyNostr::new(&nsec).await?; // 3. Relays (More reliable set) println!("\n[STEP 1] Connecting to Relays..."); let relays = vec![ "wss://relay.damus.io", "wss://nos.lol", "wss://relay.primal.net", ]; ez.add_relays(relays).await?; // Give relays a moment to fully welcome us sleep(Duration::from_secs(2)).await; println!(" [OK] Connected."); // 4. Follow Self (Critical for Timeline Test reliability) println!("\n[STEP 2] Following Self (for robust timeline testing)..."); ez.create_contact(&npub, Some("Me Myself".to_string())) .await?; println!(" [OK] Followed self."); // 5. Publish Post println!("\n[STEP 3] Publishing Post..."); let secret_code = format!("Test Run {}", Timestamp::now().as_secs()); let event_id = ez .post_text(&format!("Hello Nostr! {}", secret_code)) .await?; println!(" [OK] Posted: {}", event_id.to_bech32()?); // 6. Verify Timeline (with Retry) println!("\n[STEP 4] verifying Timeline (expecting own post)..."); let mut found_post = false; for i in 1..=5 { sleep(Duration::from_secs(2)).await; // Wait for propagation let posts = ez.get_timeline(vec![npub.clone()]).await?; // Look for our specific post if posts.iter().any(|p| p.content.contains(&secret_code)) { println!(" [OK] Found our post in timeline after {}s!", i * 2); found_post = true; break; } else { println!(" ... attempt {}/5: Post not yet visible...", i); } } if !found_post { println!(" [ERR] Verified Failed: Post did not appear in timeline."); } // 7. DM Test (with Retry) println!("\n[STEP 5] Testing Direct Messages..."); let dm_msg = format!("Secret DM {}", secret_code); ez.send_private_message(&npub, &dm_msg).await?; let mut found_dm = false; for i in 1..=5 { sleep(Duration::from_secs(2)).await; let dms = ez.get_private_messages(&npub).await?; if dms.iter().any(|m| m.content == dm_msg) { println!(" [OK] Found DM after {}s!", i * 2); found_dm = true; break; } else { println!(" ... attempt {}/5: DM not yet visible...", i); } } if !found_dm { println!(" [ERR] DM propagation failed."); // Non-fatal, DMs are slower } // 8. Global feed (Discovery) println!("\n[STEP 6] testing Global Feed Discovery..."); let random_posts = ez.get_random_posts().await?; if !random_posts.is_empty() { println!(" [OK] Found {} random posts.", random_posts.len()); } else { println!(" [WARN] No random posts found (depends on relay traffic)."); } // 9. Hashtag search (NIP-12) println!("\n[STEP 7] Testing Hashtag Search (NIP-12)..."); // We search for something common or wait for propagation of a tagged post. // For a robust test, we could publish a tagged post, but for now we search for common tags. let tags = vec!["nostr".to_string(), "bitcoin".to_string()]; let filtered_posts = ez.get_posts_by_hashtags(tags).await?; if !filtered_posts.is_empty() { println!( " [OK] Found {} posts with hashtags.", filtered_posts.len() ); } else { println!(" [WARN] No posts found for tags (depends on relay traffic)."); } // 10. Metadata Update (Kind 0) println!("\n[STEP 8] Testing Profile Metadata Update..."); let metadata_id = ez .set_metadata( Some("EasyNostr Test Bot".to_string()), Some("EasyNostr".to_string()), Some("Automated test for metadata updates".to_string()), None, // NIP-05 None, // LUD-16 Some("https://github.com/Bytemalte/easy-nostr".to_string()), Some( "https://raw.githubusercontent.com/Bytemalte/easy-nostr/main/banner.png" .to_string(), ), Some(true), // bot Some("2024-01-31".to_string()), // birthday ) .await?; println!(" [OK] Metadata updated: {}", metadata_id.to_bech32()?); println!("\n=== Test Complete ==="); Ok(()) }