tangle


git clone https://radroots.dev/git/tangle.git
Log | Files | Refs | README | LICENSE

commit c862449bc1a18f7586b12f36430e69466c9478d4
parent 93a61fe2ed14f7a63192a6515bf54daa82196e3e
Author: triesap <tyson@radroots.org>
Date:   Sat,  6 Jun 2026 13:28:58 -0700

tests: add nip50 conformance suite

- add a relay-backed NIP-50 search conformance test
- reuse a generic WebSocket REQ helper for search filters
- verify matching and missing search results through live relay frames
- assert persisted SurrealDB search document state after relay shutdown

Diffstat:
Acrates/tangle/tests/nip50_conformance.rs | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/tangle/tests/support/mod.rs | 11++++++++++-
2 files changed, 87 insertions(+), 1 deletion(-)

diff --git a/crates/tangle/tests/nip50_conformance.rs b/crates/tangle/tests/nip50_conformance.rs @@ -0,0 +1,77 @@ +#![forbid(unsafe_code)] + +mod support; + +use std::fs; +use support::{ + RelayHarness, assert_ok, connect_client, next_label, reopen_store, send_auth, send_event, + send_req, +}; +use tangle_test_support::{ + FixtureKey, auth_event_spec, build_fixture_event, valid_public_listing_spec, +}; + +#[tokio::test] +async fn nip50_conformance_searches_persisted_listing_documents() { + let seller = FixtureKey::Seller.public_key(); + let harness = RelayHarness::start( + "nip50_conformance", + serde_json::json!({ + "approved_sellers": [seller.as_str()] + }), + ); + let listing = build_fixture_event(&valid_public_listing_spec()).expect("listing"); + let auth = build_fixture_event(&auth_event_spec()).expect("auth"); + + let mut client = connect_client(harness.port).await; + assert_ok(&send_auth(&mut client, &auth).await, true); + assert_ok(&send_event(&mut client, &listing).await, true); + + let found = send_req( + &mut client, + "nip50-search", + serde_json::json!({ + "search": "carrot", + "kinds": [30402], + "authors": [seller.as_str()], + "limit": 5 + }), + ) + .await; + assert_eq!(found[0], "EVENT"); + assert_eq!(found[1], "nip50-search"); + assert_eq!(found[2]["id"], listing.id().as_str()); + assert_eq!(next_label(&mut client).await, "EOSE"); + + let miss = send_req( + &mut client, + "nip50-miss", + serde_json::json!({ + "search": "rutabaga", + "kinds": [30402], + "authors": [seller.as_str()], + "limit": 5 + }), + ) + .await; + assert_eq!(miss[0], "EOSE"); + assert_eq!(miss[1], "nip50-miss"); + + let store_config = harness.store_config(); + let root = harness.root.clone(); + drop(client); + harness.stop(); + let store = reopen_store(&store_config).await; + let listing_key = format!("30402:{}:listing-a", seller.as_str()); + let search_row = store + .search_document_row(&listing_key) + .await + .expect("search row") + .expect("search row exists"); + assert_eq!(search_row["event_id"], listing.id().as_str()); + assert_eq!(search_row["doc_type"], "listing"); + assert_eq!(search_row["title"], "Carrot bunches"); + assert_eq!(search_row["visible"], true); + drop(store); + fs::remove_dir_all(root).expect("remove runtime root"); +} diff --git a/crates/tangle/tests/support/mod.rs b/crates/tangle/tests/support/mod.rs @@ -108,9 +108,18 @@ pub async fn request_event_by_id( subscription: &str, event: &Event, ) -> Value { + send_req( + client, + subscription, + serde_json::json!({ "ids": [event.id().as_str()] }), + ) + .await +} + +pub async fn send_req(client: &mut RelayClient, subscription: &str, filter: Value) -> Value { client .send(Message::Text( - serde_json::json!(["REQ", subscription, { "ids": [event.id().as_str()] }]) + serde_json::json!(["REQ", subscription, filter]) .to_string() .into(), ))