mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
test(serverless): live agent encrypted-receive test + RELAY_URL override
Add a live, #[ignore]d end-to-end test proving a HarnessRelay in serverless mode (as a managed agent runs) receives + decrypts a NIP-17 gift-wrapped message over a real public relay and surfaces it via next_event() with the verified sender preserved. Matches the repo's e2e convention (RELAY_URL env, default damus). Also wire RELAY_URL into the existing serverless live tests so they aren't hardcoded to damus.
This commit is contained in:
@@ -3796,4 +3796,99 @@ mod tests {
|
||||
);
|
||||
assert_eq!(extract_h_tag_uuid(&event), Some(channel));
|
||||
}
|
||||
|
||||
/// Live end-to-end test of the agent's serverless encrypted-receive path
|
||||
/// against a real public relay (default `wss://relay.damus.io`, override
|
||||
/// with `RELAY_URL`). Proves that a `HarnessRelay` running in serverless
|
||||
/// mode — exactly as a managed agent does — receives a NIP-17 gift-wrapped
|
||||
/// message addressed to it, unwraps it, and surfaces the inner channel
|
||||
/// message via `next_event()`.
|
||||
///
|
||||
/// `#[ignore]` because it hits the network. Run with:
|
||||
/// cargo test -p sprout-acp --lib agent_serverless_encrypted_receive -- --ignored --nocapture
|
||||
#[tokio::test]
|
||||
#[ignore = "network: hits a live public relay (RELAY_URL, default damus)"]
|
||||
async fn agent_serverless_encrypted_receive_live() {
|
||||
use nostr::{EventBuilder, Keys, Kind, Tag};
|
||||
|
||||
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||
let relay_url =
|
||||
std::env::var("RELAY_URL").unwrap_or_else(|_| "wss://relay.damus.io".to_string());
|
||||
|
||||
// The "agent" identity — connects in serverless mode like a managed agent.
|
||||
let agent = Keys::generate();
|
||||
let agent_pubkey_hex = agent.public_key().to_hex();
|
||||
// The "sender" — a member who DMs / posts in a private channel.
|
||||
let sender = Keys::generate();
|
||||
let channel = uuid::Uuid::new_v4();
|
||||
let secret = format!("live-encrypted-{}", &channel.to_string()[..8]);
|
||||
|
||||
// 1. Agent connects serverless (no AUTH) and brings up its inbox.
|
||||
// subscribe_membership_notifications() also subscribes the gift-wrap
|
||||
// inbox (kind 1059, #p = agent) — see RelayCommand::SubscribeMembership.
|
||||
let mut relay = HarnessRelay::connect_with_mode(
|
||||
&relay_url,
|
||||
&agent,
|
||||
&agent_pubkey_hex,
|
||||
None,
|
||||
true, // serverless
|
||||
)
|
||||
.await
|
||||
.expect("agent connect (serverless)");
|
||||
relay
|
||||
.subscribe_membership_notifications()
|
||||
.await
|
||||
.expect("subscribe membership + gift-wrap inbox");
|
||||
|
||||
// Give the REQ time to register on the relay before publishing.
|
||||
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||
|
||||
// 2. Sender gift-wraps a kind-9 channel message addressed to the agent
|
||||
// and publishes it over a plain WS (independent of the harness).
|
||||
let rumor = EventBuilder::new(Kind::Custom(9), secret.clone())
|
||||
.tags([Tag::parse(vec!["h", &channel.to_string()]).unwrap()])
|
||||
.build(sender.public_key());
|
||||
let wrap = EventBuilder::gift_wrap(&sender, &agent.public_key(), rumor, [])
|
||||
.await
|
||||
.expect("gift wrap");
|
||||
|
||||
{
|
||||
use futures_util::SinkExt;
|
||||
use tokio_tungstenite::{connect_async, tungstenite::Message};
|
||||
let (ws, _) = connect_async(&relay_url).await.expect("sender connect");
|
||||
let (mut write, _read) = ws.split();
|
||||
let ev = serde_json::json!(["EVENT", wrap]).to_string();
|
||||
write
|
||||
.send(Message::Text(ev.into()))
|
||||
.await
|
||||
.expect("publish gift wrap");
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
let _ = write.close().await;
|
||||
}
|
||||
|
||||
// 3. The agent's harness must surface the DECRYPTED inner message as a
|
||||
// normal SproutEvent on the right channel.
|
||||
let deadline = std::time::Duration::from_secs(20);
|
||||
let got = tokio::time::timeout(deadline, async {
|
||||
loop {
|
||||
match relay.next_event().await {
|
||||
Some(ev) if ev.event.content == secret => return Some(ev),
|
||||
Some(_) => continue, // unrelated event — keep waiting
|
||||
None => return None, // connection lost
|
||||
}
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("timed out waiting for decrypted message");
|
||||
|
||||
let got = got.expect("connection lost before message arrived");
|
||||
assert_eq!(got.channel_id, channel, "routed to the right channel");
|
||||
assert_eq!(got.event.content, secret);
|
||||
assert_eq!(
|
||||
got.event.pubkey,
|
||||
sender.public_key(),
|
||||
"verified sender preserved through unwrap"
|
||||
);
|
||||
eprintln!("✅ agent received + decrypted encrypted message over {relay_url}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,8 @@ async fn serverless_create_join_roundtrip() {
|
||||
let joiner_pk = joiner_keys.public_key().to_hex();
|
||||
eprintln!("creator={creator_pk}\njoiner={joiner_pk}");
|
||||
|
||||
let relay = "wss://relay.damus.io";
|
||||
let relay = std::env::var("RELAY_URL").unwrap_or_else(|_| "wss://relay.damus.io".to_string());
|
||||
let relay = relay.as_str();
|
||||
|
||||
// Build a serverless AppState for the CREATOR.
|
||||
let creator_state = build_app_state();
|
||||
@@ -283,7 +284,9 @@ async fn serverless_multi_relay_fanout() {
|
||||
let pk = keys.public_key().to_hex();
|
||||
|
||||
// Two relays. Publish fans out to both; reads merge+dedup from both.
|
||||
let relays = "wss://relay.damus.io,wss://nos.lol";
|
||||
let relays = std::env::var("RELAY_URL")
|
||||
.unwrap_or_else(|_| "wss://relay.damus.io,wss://nos.lol".to_string());
|
||||
let relays = relays.as_str();
|
||||
|
||||
let state = build_app_state();
|
||||
*state.keys.lock().unwrap() = keys.clone();
|
||||
|
||||
@@ -188,7 +188,9 @@ mod tests {
|
||||
let c = Keys::generate();
|
||||
let channel = uuid::Uuid::new_v4().to_string();
|
||||
let secret = format!("encrypted-{}", &channel[..8]);
|
||||
let relay = "wss://relay.damus.io";
|
||||
let relay =
|
||||
std::env::var("RELAY_URL").unwrap_or_else(|_| "wss://relay.damus.io".to_string());
|
||||
let relay = relay.as_str();
|
||||
|
||||
// A builds + publishes one gift wrap per member.
|
||||
let wraps = build_gift_wraps(
|
||||
|
||||
Reference in New Issue
Block a user