commit 7f26ce966c39fbd816ed59dbb0b762b7e15c62bf
parent 91dffaf359ab207df6344a6655636d40b24c0420
Author: triesap <tyson@radroots.org>
Date: Thu, 5 Mar 2026 22:22:11 +0000
nostr-ndb: cover ndb error paths
- add test hooks to exercise ndb operation failures without altering runtime behavior
- expand ndb tests for open, ingest, subscribe, query, and profile error flows
- replace enum matches with string-based error assertions
- run cargo check -p radroots-nostr-ndb, cargo test -p radroots-nostr-ndb, cargo run -q -p xtask -- sdk coverage run-crate --crate radroots-nostr-ndb --out target/coverage/radroots_nostr_ndb --test-threads 1, cargo run -q -p xtask -- sdk coverage report --scope radroots-nostr-ndb --summary target/coverage/radroots_nostr_ndb/coverage-summary.json --lcov target/coverage/radroots_nostr_ndb/coverage-lcov.info --out target/coverage/radroots_nostr_ndb/gate-report.json --fail-under-exec-lines 100 --fail-under-functions 100 --fail-under-regions 100 --fail-under-branches 100 --require-branches
Diffstat:
2 files changed, 354 insertions(+), 46 deletions(-)
diff --git a/crates/nostr-ndb/src/error.rs b/crates/nostr-ndb/src/error.rs
@@ -42,16 +42,15 @@ mod tests {
#[test]
fn converts_nostrdb_error() {
let converted: RadrootsNostrNdbError = nostrdb::Error::NotFound.into();
- assert!(matches!(converted, RadrootsNostrNdbError::Ndb(_)));
+ assert!(converted.to_string().starts_with("nostrdb error:"));
}
#[test]
fn converts_serde_json_error() {
let source = serde_json::from_str::<serde_json::Value>("not json").expect_err("json error");
let converted: RadrootsNostrNdbError = source.into();
- assert!(matches!(
- converted,
- RadrootsNostrNdbError::EventJsonEncode(_)
- ));
+ assert!(converted
+ .to_string()
+ .starts_with("event json encode failed:"));
}
}
diff --git a/crates/nostr-ndb/src/ndb.rs b/crates/nostr-ndb/src/ndb.rs
@@ -16,6 +16,25 @@ pub struct RadrootsNostrNdb {
pub(crate) inner: nostrdb::Ndb,
}
+#[cfg(test)]
+mod test_hooks {
+ use std::sync::atomic::{AtomicBool, Ordering};
+
+ pub static FORCE_EVENT_JSON_ERROR: AtomicBool = AtomicBool::new(false);
+ pub static FORCE_PROCESS_EVENT_ERROR: AtomicBool = AtomicBool::new(false);
+ pub static FORCE_SUBSCRIBE_ERROR: AtomicBool = AtomicBool::new(false);
+ pub static FORCE_UNSUBSCRIBE_ERROR: AtomicBool = AtomicBool::new(false);
+ pub static FORCE_WAIT_ERROR: AtomicBool = AtomicBool::new(false);
+ pub static FORCE_TRANSACTION_ERROR: AtomicBool = AtomicBool::new(false);
+ pub static FORCE_QUERY_ERROR: AtomicBool = AtomicBool::new(false);
+ pub static FORCE_NOTE_JSON_ERROR: AtomicBool = AtomicBool::new(false);
+ pub static FORCE_PROFILE_QUERY_ERROR: AtomicBool = AtomicBool::new(false);
+
+ pub fn take(flag: &AtomicBool) -> bool {
+ flag.swap(false, Ordering::SeqCst)
+ }
+}
+
fn map_profile_lookup_result<T>(
result: Result<T, nostrdb::Error>,
) -> Result<Option<T>, RadrootsNostrNdbError> {
@@ -27,6 +46,114 @@ fn map_profile_lookup_result<T>(
}
impl RadrootsNostrNdb {
+ fn serialize_event(event: &RadrootsNostrEvent) -> Result<String, RadrootsNostrNdbError> {
+ #[cfg(test)]
+ if test_hooks::take(&test_hooks::FORCE_EVENT_JSON_ERROR) {
+ return Err(RadrootsNostrNdbError::EventJsonEncode(
+ "forced event json error".into(),
+ ));
+ }
+ serde_json::to_string(event).map_err(Into::into)
+ }
+
+ fn process_event_with_inner(
+ &self,
+ json: &str,
+ metadata: nostrdb::IngestMetadata,
+ ) -> Result<(), RadrootsNostrNdbError> {
+ #[cfg(test)]
+ if test_hooks::take(&test_hooks::FORCE_PROCESS_EVENT_ERROR) {
+ return Err(RadrootsNostrNdbError::Ndb(
+ "forced process event error".into(),
+ ));
+ }
+ self.inner
+ .process_event_with(json, metadata)
+ .map_err(Into::into)
+ }
+
+ fn subscribe_inner(
+ &self,
+ filters: &[nostrdb::Filter],
+ ) -> Result<nostrdb::Subscription, RadrootsNostrNdbError> {
+ #[cfg(test)]
+ if test_hooks::take(&test_hooks::FORCE_SUBSCRIBE_ERROR) {
+ return Err(RadrootsNostrNdbError::Ndb("forced subscribe error".into()));
+ }
+ self.inner.subscribe(filters).map_err(Into::into)
+ }
+
+ fn unsubscribe_inner(
+ &self,
+ subscription: nostrdb::Subscription,
+ ) -> Result<(), RadrootsNostrNdbError> {
+ #[cfg(test)]
+ if test_hooks::take(&test_hooks::FORCE_UNSUBSCRIBE_ERROR) {
+ return Err(RadrootsNostrNdbError::Ndb("forced unsubscribe error".into()));
+ }
+ let mut inner = self.inner.clone();
+ inner.unsubscribe(subscription).map_err(Into::into)
+ }
+
+ #[cfg(feature = "rt")]
+ async fn wait_for_notes_inner(
+ &self,
+ subscription: nostrdb::Subscription,
+ max_notes: u32,
+ ) -> Result<Vec<nostrdb::NoteKey>, RadrootsNostrNdbError> {
+ #[cfg(test)]
+ if test_hooks::take(&test_hooks::FORCE_WAIT_ERROR) {
+ return Err(RadrootsNostrNdbError::Ndb("forced wait error".into()));
+ }
+ self.inner
+ .wait_for_notes(subscription, max_notes)
+ .await
+ .map_err(Into::into)
+ }
+
+ fn open_txn(&self) -> Result<nostrdb::Transaction, RadrootsNostrNdbError> {
+ #[cfg(test)]
+ if test_hooks::take(&test_hooks::FORCE_TRANSACTION_ERROR) {
+ return Err(RadrootsNostrNdbError::Ndb("forced transaction error".into()));
+ }
+ nostrdb::Transaction::new(&self.inner).map_err(Into::into)
+ }
+
+ fn query_inner<'a>(
+ &self,
+ txn: &'a nostrdb::Transaction,
+ filters: &[nostrdb::Filter],
+ max_results: i32,
+ ) -> Result<Vec<nostrdb::QueryResult<'a>>, RadrootsNostrNdbError> {
+ #[cfg(test)]
+ if test_hooks::take(&test_hooks::FORCE_QUERY_ERROR) {
+ return Err(RadrootsNostrNdbError::Ndb("forced query error".into()));
+ }
+ self.inner
+ .query(txn, filters, max_results)
+ .map_err(Into::into)
+ }
+
+ fn note_json_value(note: &nostrdb::Note) -> Result<String, RadrootsNostrNdbError> {
+ #[cfg(test)]
+ if test_hooks::take(&test_hooks::FORCE_NOTE_JSON_ERROR) {
+ return Err(RadrootsNostrNdbError::Ndb("forced note json error".into()));
+ }
+ note.json().map_err(Into::into)
+ }
+
+ fn get_profile_record<'a>(
+ &self,
+ txn: &'a nostrdb::Transaction,
+ pubkey: &[u8; 32],
+ ) -> Result<Option<nostrdb::ProfileRecord<'a>>, RadrootsNostrNdbError> {
+ #[cfg(test)]
+ if test_hooks::take(&test_hooks::FORCE_PROFILE_QUERY_ERROR) {
+ return map_profile_lookup_result(Err(nostrdb::Error::QueryError));
+ }
+ map_profile_lookup_result(self.inner.get_profile_by_pubkey(txn, pubkey))
+ }
+
pub fn open(config: RadrootsNostrNdbConfig) -> Result<Self, RadrootsNostrNdbError> {
let mut inner_config = nostrdb::Config::new().skip_validation(config.skip_validation());
if let Some(mapsize_bytes) = config.mapsize_bytes() {
@@ -53,7 +180,7 @@ impl RadrootsNostrNdb {
source: RadrootsNostrNdbIngestSource,
) -> Result<(), RadrootsNostrNdbError> {
let metadata = source.to_ndb_metadata();
- self.inner.process_event_with(json, metadata)?;
+ self.process_event_with_inner(json, metadata)?;
Ok(())
}
@@ -66,7 +193,7 @@ impl RadrootsNostrNdb {
event: &RadrootsNostrEvent,
source: RadrootsNostrNdbIngestSource,
) -> Result<(), RadrootsNostrNdbError> {
- let json = serde_json::to_string(event)?;
+ let json = Self::serialize_event(event)?;
self.ingest_event_json_with_source(json.as_str(), source)
}
@@ -100,7 +227,7 @@ impl RadrootsNostrNdb {
.iter()
.map(|filter_spec| filter_spec.to_ndb_filter())
.collect::<Result<Vec<_>, _>>()?;
- let subscription = self.inner.subscribe(filters.as_slice())?;
+ let subscription = self.subscribe_inner(filters.as_slice())?;
Ok(RadrootsNostrNdbSubscriptionHandle::new(subscription.id()))
}
@@ -108,8 +235,8 @@ impl RadrootsNostrNdb {
&self,
handle: RadrootsNostrNdbSubscriptionHandle,
) -> Result<(), RadrootsNostrNdbError> {
- let mut inner = self.inner.clone();
- inner.unsubscribe(nostrdb::Subscription::new(handle.id()))?;
+ let subscription = nostrdb::Subscription::new(handle.id());
+ self.unsubscribe_inner(subscription)?;
Ok(())
}
@@ -132,8 +259,7 @@ impl RadrootsNostrNdb {
max_notes: u32,
) -> Result<Vec<RadrootsNostrNdbNoteKey>, RadrootsNostrNdbError> {
let note_keys = self
- .inner
- .wait_for_notes(nostrdb::Subscription::new(handle.id()), max_notes)
+ .wait_for_notes_inner(nostrdb::Subscription::new(handle.id()), max_notes)
.await?;
Ok(note_keys
.into_iter()
@@ -166,16 +292,14 @@ impl RadrootsNostrNdb {
.iter()
.map(|filter_spec| filter_spec.to_ndb_filter())
.collect::<Result<Vec<_>, _>>()?;
- let txn = nostrdb::Transaction::new(&self.inner)?;
- let query_results =
- self.inner
- .query(&txn, filters.as_slice(), spec.max_results() as i32)?;
+ let txn = self.open_txn()?;
+ let query_results = self.query_inner(&txn, filters.as_slice(), spec.max_results() as i32)?;
query_results
.into_iter()
.map(|query_result| {
let note = query_result.note;
- let json = note.json()?;
+ let json = Self::note_json_value(¬e)?;
Ok(RadrootsNostrNdbNote {
note_key: query_result.note_key.as_u64(),
id_hex: hex::encode(note.id()),
@@ -186,8 +310,7 @@ impl RadrootsNostrNdb {
json,
})
})
- .collect::<Result<Vec<_>, nostrdb::Error>>()
- .map_err(Into::into)
+ .collect::<Result<Vec<_>, RadrootsNostrNdbError>>()
}
pub fn get_profile_by_pubkey_hex(
@@ -195,10 +318,8 @@ impl RadrootsNostrNdb {
pubkey_hex: &str,
) -> Result<Option<RadrootsNostrNdbProfile>, RadrootsNostrNdbError> {
let pubkey = parse_hex_32(pubkey_hex, "pubkey")?;
- let txn = nostrdb::Transaction::new(&self.inner)?;
- let Some(profile_record) =
- map_profile_lookup_result(self.inner.get_profile_by_pubkey(&txn, &pubkey))?
- else {
+ let txn = self.open_txn()?;
+ let Some(profile_record) = self.get_profile_record(&txn, &pubkey)? else {
return Ok(None);
};
@@ -228,6 +349,7 @@ mod tests {
use futures::StreamExt;
use radroots_nostr::prelude::{RadrootsNostrEventBuilder, RadrootsNostrKeys};
use radroots_nostr::prelude::{RadrootsNostrMetadata, radroots_nostr_build_metadata_event};
+ use std::sync::atomic::Ordering;
use std::time::Duration;
use tempfile::TempDir;
@@ -252,8 +374,9 @@ mod tests {
map_profile_lookup_result::<u64>(Err(nostrdb::Error::NotFound)).expect("none");
assert!(not_found.is_none());
- let query_error = map_profile_lookup_result::<u64>(Err(nostrdb::Error::QueryError));
- assert!(matches!(query_error, Err(RadrootsNostrNdbError::Ndb(_))));
+ let query_error = map_profile_lookup_result::<u64>(Err(nostrdb::Error::QueryError))
+ .expect_err("query error");
+ assert!(query_error.to_string().starts_with("nostrdb error:"));
}
#[test]
@@ -269,6 +392,27 @@ mod tests {
assert!(db_dir.exists());
}
+ #[cfg(unix)]
+ #[test]
+ fn open_rejects_non_utf8_path() {
+ use std::os::unix::ffi::OsStrExt;
+
+ let path = std::path::PathBuf::from(std::ffi::OsStr::from_bytes(b"ndb-\xFF"));
+ let config = RadrootsNostrNdbConfig::new(&path);
+ let err = RadrootsNostrNdb::open(config).expect_err("non utf8 path");
+ assert!(err.to_string().contains("utf-8"));
+ }
+
+ #[test]
+ fn open_reports_ndb_error_for_file_path() {
+ let tmp_dir = TempDir::new().expect("tempdir should open");
+ let db_dir = tmp_dir.path().join("ndb");
+ std::fs::write(&db_dir, "not a directory").expect("write db file");
+ let config = RadrootsNostrNdbConfig::new(&db_dir);
+ let err = RadrootsNostrNdb::open(config).expect_err("file path should fail");
+ assert!(err.to_string().starts_with("nostrdb error:"));
+ }
+
#[test]
fn ingest_source_builders_track_origin() {
assert_eq!(
@@ -321,6 +465,41 @@ mod tests {
}
#[test]
+ fn ingest_event_json_rejects_invalid_json() {
+ let tmp_dir = TempDir::new().expect("tempdir should open");
+ let db_dir = tmp_dir.path().join("ndb");
+ let config = RadrootsNostrNdbConfig::new(&db_dir);
+ let ndb = RadrootsNostrNdb::open(config).expect("database should open");
+
+ test_hooks::FORCE_PROCESS_EVENT_ERROR.store(true, Ordering::SeqCst);
+ let err = ndb
+ .ingest_event_json_with_source("not json", RadrootsNostrNdbIngestSource::client())
+ .expect_err("process event error");
+ assert!(err.to_string().starts_with("nostrdb error:"));
+ }
+
+ #[test]
+ fn ingest_event_reports_event_json_error() {
+ let tmp_dir = TempDir::new().expect("tempdir should open");
+ let db_dir = tmp_dir.path().join("ndb");
+ let config = RadrootsNostrNdbConfig::new(&db_dir);
+ let ndb = RadrootsNostrNdb::open(config).expect("database should open");
+
+ let keys = RadrootsNostrKeys::generate();
+ let event = RadrootsNostrEventBuilder::text_note("forced json error")
+ .sign_with_keys(&keys)
+ .expect("event should sign");
+ test_hooks::FORCE_EVENT_JSON_ERROR.store(true, Ordering::SeqCst);
+
+ let err = ndb
+ .ingest_event(&event, RadrootsNostrNdbIngestSource::client())
+ .expect_err("forced json error");
+ assert!(err
+ .to_string()
+ .starts_with("event json encode failed:"));
+ }
+
+ #[test]
fn subscribe_poll_and_unsubscribe_round_trip() {
let tmp_dir = TempDir::new().expect("tempdir should open");
let db_dir = tmp_dir.path().join("ndb");
@@ -353,6 +532,33 @@ mod tests {
ndb.unsubscribe(handle).expect("unsubscribe should succeed");
}
+ #[test]
+ fn subscribe_reports_ndb_error() {
+ let tmp_dir = TempDir::new().expect("tempdir should open");
+ let db_dir = tmp_dir.path().join("ndb");
+ let config = RadrootsNostrNdbConfig::new(&db_dir);
+ let ndb = RadrootsNostrNdb::open(config).expect("database should open");
+ let spec = RadrootsNostrNdbSubscriptionSpec::text_notes(Some(10), None);
+ test_hooks::FORCE_SUBSCRIBE_ERROR.store(true, Ordering::SeqCst);
+
+ let err = ndb.subscribe(&spec).expect_err("forced subscribe error");
+ assert!(err.to_string().starts_with("nostrdb error:"));
+ }
+
+ #[test]
+ fn unsubscribe_reports_ndb_error() {
+ let tmp_dir = TempDir::new().expect("tempdir should open");
+ let db_dir = tmp_dir.path().join("ndb");
+ let config = RadrootsNostrNdbConfig::new(&db_dir);
+ let ndb = RadrootsNostrNdb::open(config).expect("database should open");
+ let spec = RadrootsNostrNdbSubscriptionSpec::text_notes(Some(10), None);
+ let handle = ndb.subscribe(&spec).expect("subscribe should succeed");
+ test_hooks::FORCE_UNSUBSCRIBE_ERROR.store(true, Ordering::SeqCst);
+
+ let err = ndb.unsubscribe(handle).expect_err("forced unsubscribe error");
+ assert!(err.to_string().starts_with("nostrdb error:"));
+ }
+
#[tokio::test]
async fn wait_for_note_keys_yields_results() {
let tmp_dir = TempDir::new().expect("tempdir should open");
@@ -376,6 +582,23 @@ mod tests {
assert!(!notes.is_empty());
}
+ #[tokio::test]
+ async fn wait_for_note_keys_reports_ndb_error() {
+ let tmp_dir = TempDir::new().expect("tempdir should open");
+ let db_dir = tmp_dir.path().join("ndb");
+ let config = RadrootsNostrNdbConfig::new(&db_dir);
+ let ndb = RadrootsNostrNdb::open(config).expect("database should open");
+ let spec = RadrootsNostrNdbSubscriptionSpec::text_notes(Some(10), None);
+ let handle = ndb.subscribe(&spec).expect("subscribe should succeed");
+ test_hooks::FORCE_WAIT_ERROR.store(true, Ordering::SeqCst);
+
+ let err = ndb
+ .wait_for_note_keys(handle, 1)
+ .await
+ .expect_err("forced wait error");
+ assert!(err.to_string().starts_with("nostrdb error:"));
+ }
+
#[test]
fn query_notes_returns_ingested_results() {
let tmp_dir = TempDir::new().expect("tempdir should open");
@@ -420,6 +643,75 @@ mod tests {
}
#[test]
+ fn query_notes_rejects_invalid_filters() {
+ let tmp_dir = TempDir::new().expect("tempdir should open");
+ let db_dir = tmp_dir.path().join("ndb");
+ let config = RadrootsNostrNdbConfig::new(&db_dir);
+ let ndb = RadrootsNostrNdb::open(config).expect("database should open");
+
+ let spec = RadrootsNostrNdbQuerySpec::single(
+ RadrootsNostrNdbFilterSpec::new().with_author_hex("not-hex"),
+ 10,
+ );
+ let err = ndb.query_notes(&spec).expect_err("invalid filter");
+ assert!(err.to_string().contains("invalid hex"));
+ }
+
+ #[test]
+ fn query_notes_reports_transaction_error() {
+ let tmp_dir = TempDir::new().expect("tempdir should open");
+ let db_dir = tmp_dir.path().join("ndb");
+ let config = RadrootsNostrNdbConfig::new(&db_dir);
+ let ndb = RadrootsNostrNdb::open(config).expect("database should open");
+ let spec = RadrootsNostrNdbQuerySpec::text_notes(Some(10), None, 10);
+ test_hooks::FORCE_TRANSACTION_ERROR.store(true, Ordering::SeqCst);
+
+ let err = ndb.query_notes(&spec).expect_err("forced transaction error");
+ assert!(err.to_string().starts_with("nostrdb error:"));
+ }
+
+ #[test]
+ fn query_notes_reports_query_error() {
+ let tmp_dir = TempDir::new().expect("tempdir should open");
+ let db_dir = tmp_dir.path().join("ndb");
+ let config = RadrootsNostrNdbConfig::new(&db_dir);
+ let ndb = RadrootsNostrNdb::open(config).expect("database should open");
+ let spec = RadrootsNostrNdbQuerySpec::text_notes(Some(10), None, 10);
+ test_hooks::FORCE_QUERY_ERROR.store(true, Ordering::SeqCst);
+
+ let err = ndb.query_notes(&spec).expect_err("forced query error");
+ assert!(err.to_string().starts_with("nostrdb error:"));
+ }
+
+ #[test]
+ fn query_notes_reports_note_json_error() {
+ let tmp_dir = TempDir::new().expect("tempdir should open");
+ let db_dir = tmp_dir.path().join("ndb");
+ let config = RadrootsNostrNdbConfig::new(&db_dir);
+ let ndb = RadrootsNostrNdb::open(config).expect("database should open");
+
+ let keys = RadrootsNostrKeys::generate();
+ let event = RadrootsNostrEventBuilder::text_note("note json error")
+ .sign_with_keys(&keys)
+ .expect("event should sign");
+ ndb.ingest_event(&event, RadrootsNostrNdbIngestSource::client())
+ .expect("ingest should succeed");
+
+ let query_spec = RadrootsNostrNdbQuerySpec::text_notes(Some(50), None, 50);
+ for _ in 0..40 {
+ let notes = ndb.query_notes(&query_spec).expect("query should succeed");
+ if !notes.is_empty() {
+ break;
+ }
+ std::thread::sleep(Duration::from_millis(25));
+ }
+ test_hooks::FORCE_NOTE_JSON_ERROR.store(true, Ordering::SeqCst);
+
+ let err = ndb.query_notes(&query_spec).expect_err("forced note json error");
+ assert!(err.to_string().starts_with("nostrdb error:"));
+ }
+
+ #[test]
fn profile_lookup_returns_metadata_fields() {
let tmp_dir = TempDir::new().expect("tempdir should open");
let db_dir = tmp_dir.path().join("ndb");
@@ -471,6 +763,36 @@ mod tests {
}
#[test]
+ fn profile_lookup_reports_query_error() {
+ let tmp_dir = TempDir::new().expect("tempdir should open");
+ let db_dir = tmp_dir.path().join("ndb");
+ let config = RadrootsNostrNdbConfig::new(&db_dir);
+ let ndb = RadrootsNostrNdb::open(config).expect("database should open");
+ let pubkey_hex = RadrootsNostrKeys::generate().public_key().to_hex();
+ test_hooks::FORCE_PROFILE_QUERY_ERROR.store(true, Ordering::SeqCst);
+
+ let err = ndb
+ .get_profile_by_pubkey_hex(pubkey_hex.as_str())
+ .expect_err("forced profile query error");
+ assert!(err.to_string().starts_with("nostrdb error:"));
+ }
+
+ #[test]
+ fn profile_lookup_reports_transaction_error() {
+ let tmp_dir = TempDir::new().expect("tempdir should open");
+ let db_dir = tmp_dir.path().join("ndb");
+ let config = RadrootsNostrNdbConfig::new(&db_dir);
+ let ndb = RadrootsNostrNdb::open(config).expect("database should open");
+ let pubkey_hex = RadrootsNostrKeys::generate().public_key().to_hex();
+ test_hooks::FORCE_TRANSACTION_ERROR.store(true, Ordering::SeqCst);
+
+ let err = ndb
+ .get_profile_by_pubkey_hex(pubkey_hex.as_str())
+ .expect_err("forced transaction error");
+ assert!(err.to_string().starts_with("nostrdb error:"));
+ }
+
+ #[test]
fn profile_lookup_returns_none_without_metadata_record() {
let tmp_dir = TempDir::new().expect("tempdir should open");
let db_dir = tmp_dir.path().join("ndb");
@@ -524,13 +846,7 @@ mod tests {
RadrootsNostrNdbFilterSpec::new().with_author_hex("not-hex"),
);
let err = ndb.subscribe(&spec).expect_err("subscribe should fail");
- assert!(matches!(
- err,
- RadrootsNostrNdbError::InvalidHex {
- field: "author",
- ..
- }
- ));
+ assert!(err.to_string().contains("invalid hex for author"));
}
#[test]
@@ -543,13 +859,9 @@ mod tests {
let err = ndb
.get_profile_by_pubkey_hex("abcd")
.expect_err("lookup should fail");
- assert!(matches!(
- err,
- RadrootsNostrNdbError::InvalidHexLength {
- field: "pubkey",
- ..
- }
- ));
+ assert!(err
+ .to_string()
+ .contains("invalid hex length for pubkey"));
}
#[tokio::test]
@@ -643,13 +955,10 @@ mod tests {
let ndb = RadrootsNostrNdb::open(config).expect("database should open");
let result = ndb.add_giftwrap_secret_key_hex("abcd");
- assert!(matches!(
- result,
- Err(RadrootsNostrNdbError::InvalidHexLength {
- field: "secret_key",
- ..
- })
- ));
+ let err = result.expect_err("invalid giftwrap key");
+ assert!(err
+ .to_string()
+ .contains("invalid hex length for secret_key"));
}
#[cfg(feature = "giftwrap")]