lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

commit 29a52a6c8b2a1aa1c096294de65d48176153dbfc
parent 2eac6175e6622501d26d75ee494be4a1038d1de2
Author: triesap <tyson@radroots.org>
Date:   Fri,  6 Mar 2026 16:55:16 +0000

replica-sync: expand ingest error-path coverage

- add queryfail executor coverage helpers in ingest tests
- exercise profile farm plot and list-set failure branches with targeted sql needles
- add parser and state-write error assertions for ingest edge paths
- preserve runtime behavior while increasing replica-sync test path coverage

Diffstat:
Mcrates/replica-sync/src/ingest.rs | 642+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 626 insertions(+), 16 deletions(-)

diff --git a/crates/replica-sync/src/ingest.rs b/crates/replica-sync/src/ingest.rs @@ -1186,6 +1186,42 @@ mod tests { } } + struct QueryFailExecutor<'a> { + inner: &'a SqliteExecutor, + needle: &'static str, + err: SqlError, + } + + impl SqlExecutor for QueryFailExecutor<'_> { + fn exec(&self, sql: &str, params_json: &str) -> Result<ExecOutcome, SqlError> { + let normalized = sql.to_ascii_lowercase(); + if normalized.contains(self.needle) { + return Err(self.err.clone()); + } + self.inner.exec(sql, params_json) + } + + fn query_raw(&self, sql: &str, params_json: &str) -> Result<String, SqlError> { + let normalized = sql.to_ascii_lowercase(); + if normalized.contains(self.needle) { + return Err(self.err.clone()); + } + self.inner.query_raw(sql, params_json) + } + + fn begin(&self) -> Result<(), SqlError> { + self.inner.begin() + } + + fn commit(&self) -> Result<(), SqlError> { + self.inner.commit() + } + + fn rollback(&self) -> Result<(), SqlError> { + self.inner.rollback() + } + } + fn sample_gcs(lat: f64, lng: f64, geohash: &str) -> RadrootsGcsLocation { RadrootsGcsLocation { lat, @@ -1484,7 +1520,10 @@ mod tests { assert!(begin_err.to_string().contains("replica_sync.sql")); assert!(begin_executor.commit().is_ok()); assert_eq!( - begin_executor.exec("select 1", "[]").expect_err("exec").code(), + begin_executor + .exec("select 1", "[]") + .expect_err("exec") + .code(), "ERR_UNSUPPORTED_PLATFORM" ); assert_eq!( @@ -2163,8 +2202,7 @@ mod tests { let claims = farm_list_sets::member_of_farms_list_set(vec![farm_pubkey.clone()]) .expect("claims list set"); - let claims_event = - list_set_event(504, &profile_pubkey, 54, KIND_LIST_SET_GENERIC, &claims); + let claims_event = list_set_event(504, &profile_pubkey, 54, KIND_LIST_SET_GENERIC, &claims); assert_eq!( ingest_list_set_event(&pass, &claims_event).expect("claims list set"), RadrootsReplicaIngestOutcome::Applied @@ -2193,8 +2231,13 @@ mod tests { image: None, }; assert!( - upsert_farm_members(&pass, &farm_row.id, ListSetRole::Members, &mixed_member_entries) - .is_ok() + upsert_farm_members( + &pass, + &farm_row.id, + ListSetRole::Members, + &mixed_member_entries + ) + .is_ok() ); let mixed_claim_entries = RadrootsListSet { d_tag: "member_of.farms".to_string(), @@ -2344,8 +2387,8 @@ mod tests { ingest_profile_event(&pass_txn, &profile_event_row).expect("txn profile"), RadrootsReplicaIngestOutcome::Applied ); - let profile_decision = event_state_decision(&pass_txn, &profile_event_row, "") - .expect("profile decision"); + let profile_decision = + event_state_decision(&pass_txn, &profile_event_row, "").expect("profile decision"); assert!(!profile_decision.apply); assert!( radroots_replica_ingest_event_state( @@ -2357,8 +2400,12 @@ mod tests { .is_ok() ); assert_eq!( - radroots_replica_ingest_event_with_factory(&pass_txn, &profile_event_row, &FixedFactory) - .expect("txn wrapper"), + radroots_replica_ingest_event_with_factory( + &pass_txn, + &profile_event_row, + &FixedFactory + ) + .expect("txn wrapper"), RadrootsReplicaIngestOutcome::Skipped ); @@ -2418,7 +2465,9 @@ mod tests { assert!(upsert_plot_tags(&pass_txn, &plot_id, Some(vec!["y".to_string()])).is_ok()); assert!(clear_farm_locations(&pass_txn, &farm_row.id).is_ok()); assert!(clear_plot_locations(&pass_txn, &plot_id).is_ok()); - assert!(create_gcs_location(&pass_txn, sample_gcs(14.0, 24.0, "s4"), &FixedFactory).is_ok()); + assert!( + create_gcs_location(&pass_txn, sample_gcs(14.0, 24.0, "s4"), &FixedFactory).is_ok() + ); assert!( upsert_farm_location( &pass_txn, @@ -2455,8 +2504,8 @@ mod tests { upsert_farm_members(&pass_txn, &farm_row.id, ListSetRole::Members, &members_list) .is_ok() ); - let member_of_list = farm_list_sets::member_of_farms_list_set(vec![farm_pubkey.clone()]) - .expect("member_of"); + let member_of_list = + farm_list_sets::member_of_farms_list_set(vec![farm_pubkey.clone()]).expect("member_of"); assert!(upsert_member_claims(&pass_txn, &"m".repeat(64), &member_of_list).is_ok()); let rollback_count = Arc::new(AtomicUsize::new(0)); @@ -2470,8 +2519,10 @@ mod tests { assert!(ingest_profile_event(&txn, &profile_event_row).is_err()); assert!(event_state_decision(&txn, &profile_event_row, "").is_err()); assert!(radroots_replica_ingest_event_state(&txn, &profile_event_row, "", "hash").is_err()); - assert!(radroots_replica_ingest_event_with_factory(&txn, &profile_event_row, &FixedFactory) - .is_err()); + assert!( + radroots_replica_ingest_event_with_factory(&txn, &profile_event_row, &FixedFactory) + .is_err() + ); assert!(ingest_farm_event(&txn, &farm_event_row, &FixedFactory).is_err()); @@ -2513,9 +2564,568 @@ mod tests { ) .is_err() ); + assert!(upsert_farm_members(&txn, "farm-id", ListSetRole::Members, &members_list).is_err()); + assert!(upsert_member_claims(&txn, &"m".repeat(64), &member_of_list).is_err()); + } + + #[test] + fn ingest_sqlite_queryfail_and_parser_edges_are_covered() { + let exec = SqliteExecutor::open_memory().expect("db"); + migrations::run_all_up(&exec).expect("migrations"); + let pass_through = QueryFailExecutor { + inner: &exec, + needle: "__missing__", + err: SqlError::Internal, + }; + assert!(pass_through.query_raw("select 1", "[]").is_ok()); assert!( - upsert_farm_members(&txn, "farm-id", ListSetRole::Members, &members_list).is_err() + pass_through + .exec( + "create table if not exists coverage_probe (id integer)", + "[]" + ) + .is_ok() + ); + let _ = pass_through.begin(); + let _ = pass_through.rollback(); + let _ = pass_through.commit(); + + let farm_pubkey = "f".repeat(64); + let farm_d_tag = "AAAAAAAAAAAAAAAAAAAAAA"; + let plot_d_tag = "AAAAAAAAAAAAAAAAAAAAAQ"; + let profile_pubkey = "p".repeat(64); + + let profile = profile_event( + 800, + &profile_pubkey, + 80, + Some(RadrootsProfileType::Individual), + "profile-base", + ); + let mut profile_bad_content = profile.clone(); + profile_bad_content.content = "{".to_string(); + assert!(ingest_profile_event(&exec, &profile_bad_content).is_err()); + + let profile_query_fail = QueryFailExecutor { + inner: &exec, + needle: "nostr_profile", + err: SqlError::Internal, + }; + assert!(ingest_profile_event(&profile_query_fail, &profile).is_err()); + + assert_eq!( + ingest_profile_event(&exec, &profile).expect("profile seed"), + RadrootsReplicaIngestOutcome::Applied + ); + let profile_update = profile_event( + 801, + &profile_pubkey, + 81, + Some(RadrootsProfileType::Individual), + "profile-update", + ); + let profile_update_fail = QueryFailExecutor { + inner: &exec, + needle: "update nostr_profile", + err: SqlError::Internal, + }; + assert!(ingest_profile_event(&profile_update_fail, &profile_update).is_err()); + + let profile_create_fail = QueryFailExecutor { + inner: &exec, + needle: "insert into nostr_profile", + err: SqlError::Internal, + }; + let profile_new = profile_event( + 802, + &"n".repeat(64), + 82, + Some(RadrootsProfileType::Individual), + "profile-new", + ); + assert!(ingest_profile_event(&profile_create_fail, &profile_new).is_err()); + + let profile_state_fail = QueryFailExecutor { + inner: &exec, + needle: "nostr_event_state", + err: SqlError::Internal, + }; + let profile_state_event = profile_event( + 803, + &"s".repeat(64), + 83, + Some(RadrootsProfileType::Individual), + "profile-state", + ); + assert!(ingest_profile_event(&profile_state_fail, &profile_state_event).is_err()); + + let farm_seed = farm_event( + 810, + &farm_pubkey, + 90, + farm_d_tag, + "farm-seed", + Some(RadrootsFarmLocation { + primary: Some("primary".to_string()), + city: Some("city".to_string()), + region: Some("region".to_string()), + country: Some("country".to_string()), + gcs: sample_gcs(10.0, 20.0, "s0"), + }), + Some(vec!["seed".to_string()]), + ); + assert_eq!( + ingest_farm_event(&exec, &farm_seed, &FixedFactory).expect("farm seed"), + RadrootsReplicaIngestOutcome::Applied + ); + + let mut farm_bad_content = farm_seed.clone(); + farm_bad_content.content = "{".to_string(); + assert!(ingest_farm_event(&exec, &farm_bad_content, &FixedFactory).is_err()); + + let farm_query_fail = QueryFailExecutor { + inner: &exec, + needle: "from farm", + err: SqlError::Internal, + }; + let farm_query_event = farm_event( + 811, + &"q".repeat(64), + 91, + farm_d_tag, + "farm-query", + None, + None, + ); + assert!(ingest_farm_event(&farm_query_fail, &farm_query_event, &FixedFactory).is_err()); + + let farm_update_fail = QueryFailExecutor { + inner: &exec, + needle: "update farm", + err: SqlError::Internal, + }; + let farm_update = farm_event( + 812, + &farm_pubkey, + 92, + farm_d_tag, + "farm-update", + None, + Some(vec!["u".to_string()]), + ); + assert!(ingest_farm_event(&farm_update_fail, &farm_update, &FixedFactory).is_err()); + + let farm_create_fail = QueryFailExecutor { + inner: &exec, + needle: "insert into farm", + err: SqlError::Internal, + }; + let farm_create = farm_event( + 813, + &"c".repeat(64), + 93, + farm_d_tag, + "farm-create", + None, + None, + ); + assert!(ingest_farm_event(&farm_create_fail, &farm_create, &FixedFactory).is_err()); + + let farm_tag_fail = QueryFailExecutor { + inner: &exec, + needle: "farm_tag", + err: SqlError::Internal, + }; + let farm_tag_event = farm_event( + 814, + &"t".repeat(64), + 94, + farm_d_tag, + "farm-tag", + None, + Some(vec!["coffee".to_string()]), + ); + assert!(ingest_farm_event(&farm_tag_fail, &farm_tag_event, &FixedFactory).is_err()); + + let farm_gcs_fail = QueryFailExecutor { + inner: &exec, + needle: "gcs_location", + err: SqlError::Internal, + }; + let farm_gcs_event = farm_event( + 815, + &"g".repeat(64), + 95, + farm_d_tag, + "farm-gcs", + Some(RadrootsFarmLocation { + primary: Some("primary".to_string()), + city: None, + region: None, + country: None, + gcs: sample_gcs(11.0, 21.0, "s1"), + }), + None, + ); + assert!(ingest_farm_event(&farm_gcs_fail, &farm_gcs_event, &FixedFactory).is_err()); + + let farm_rel_fail = QueryFailExecutor { + inner: &exec, + needle: "farm_gcs_location", + err: SqlError::Internal, + }; + let farm_rel_event = farm_event( + 816, + &"r".repeat(64), + 96, + farm_d_tag, + "farm-rel", + Some(RadrootsFarmLocation { + primary: Some("primary".to_string()), + city: None, + region: None, + country: None, + gcs: sample_gcs(12.0, 22.0, "s2"), + }), + None, + ); + assert!(ingest_farm_event(&farm_rel_fail, &farm_rel_event, &FixedFactory).is_err()); + + let farm_state_fail = QueryFailExecutor { + inner: &exec, + needle: "nostr_event_state", + err: SqlError::Internal, + }; + let farm_state_event = farm_event( + 817, + &"w".repeat(64), + 97, + farm_d_tag, + "farm-state", + None, + None, + ); + assert!(ingest_farm_event(&farm_state_fail, &farm_state_event, &FixedFactory).is_err()); + + let mut bad_point = sample_gcs(13.0, 23.0, "s3"); + bad_point.point.coordinates = [f64::NAN, 13.0]; + let farm_bad_point = farm_event( + 818, + &"x".repeat(64), + 98, + farm_d_tag, + "farm-bad-point", + Some(RadrootsFarmLocation { + primary: Some("primary".to_string()), + city: None, + region: None, + country: None, + gcs: bad_point, + }), + None, + ); + assert!(ingest_farm_event(&exec, &farm_bad_point, &FixedFactory).is_err()); + + let mut bad_polygon = sample_gcs(14.0, 24.0, "s4"); + bad_polygon.polygon.coordinates[0][1][0] = f64::NAN; + let farm_bad_polygon = farm_event( + 819, + &"y".repeat(64), + 99, + farm_d_tag, + "farm-bad-polygon", + Some(RadrootsFarmLocation { + primary: Some("primary".to_string()), + city: None, + region: None, + country: None, + gcs: bad_polygon, + }), + None, + ); + assert!(ingest_farm_event(&exec, &farm_bad_polygon, &FixedFactory).is_err()); + + let plot_seed = plot_event( + 820, + &farm_pubkey, + 100, + plot_d_tag, + RadrootsFarmRef { + pubkey: farm_pubkey.clone(), + d_tag: farm_d_tag.to_string(), + }, + "plot-seed", + Some(RadrootsPlotLocation { + primary: Some("primary".to_string()), + city: None, + region: None, + country: None, + gcs: sample_gcs(15.0, 25.0, "s5"), + }), + Some(vec!["orchard".to_string()]), + ); + assert_eq!( + ingest_plot_event(&exec, &plot_seed, &FixedFactory).expect("plot seed"), + RadrootsReplicaIngestOutcome::Applied + ); + + let mut plot_bad_content = plot_seed.clone(); + plot_bad_content.content = "{".to_string(); + assert!(ingest_plot_event(&exec, &plot_bad_content, &FixedFactory).is_err()); + + let plot_query_fail = QueryFailExecutor { + inner: &exec, + needle: "from plot", + err: SqlError::Internal, + }; + let plot_query = plot_event( + 821, + &farm_pubkey, + 101, + plot_d_tag, + RadrootsFarmRef { + pubkey: farm_pubkey.clone(), + d_tag: farm_d_tag.to_string(), + }, + "plot-query", + None, + None, + ); + assert!(ingest_plot_event(&plot_query_fail, &plot_query, &FixedFactory).is_err()); + + let plot_update_fail = QueryFailExecutor { + inner: &exec, + needle: "update plot", + err: SqlError::Internal, + }; + let plot_update = plot_event( + 822, + &farm_pubkey, + 102, + plot_d_tag, + RadrootsFarmRef { + pubkey: farm_pubkey.clone(), + d_tag: farm_d_tag.to_string(), + }, + "plot-update", + None, + Some(vec!["u".to_string()]), + ); + assert!(ingest_plot_event(&plot_update_fail, &plot_update, &FixedFactory).is_err()); + + let plot_create_fail = QueryFailExecutor { + inner: &exec, + needle: "insert into plot", + err: SqlError::Internal, + }; + let plot_create = plot_event( + 823, + &farm_pubkey, + 103, + "AAAAAAAAAAAAAAAAAAAAAg", + RadrootsFarmRef { + pubkey: farm_pubkey.clone(), + d_tag: farm_d_tag.to_string(), + }, + "plot-create", + None, + None, + ); + assert!(ingest_plot_event(&plot_create_fail, &plot_create, &FixedFactory).is_err()); + + let plot_tag_fail = QueryFailExecutor { + inner: &exec, + needle: "plot_tag", + err: SqlError::Internal, + }; + let plot_tag_event = plot_event( + 824, + &farm_pubkey, + 104, + "AAAAAAAAAAAAAAAAAAAAAw", + RadrootsFarmRef { + pubkey: farm_pubkey.clone(), + d_tag: farm_d_tag.to_string(), + }, + "plot-tag", + None, + Some(vec!["tag".to_string()]), + ); + assert!(ingest_plot_event(&plot_tag_fail, &plot_tag_event, &FixedFactory).is_err()); + + let plot_gcs_fail = QueryFailExecutor { + inner: &exec, + needle: "gcs_location", + err: SqlError::Internal, + }; + let plot_gcs_event = plot_event( + 825, + &farm_pubkey, + 105, + "AAAAAAAAAAAAAAAAAAAAAw", + RadrootsFarmRef { + pubkey: farm_pubkey.clone(), + d_tag: farm_d_tag.to_string(), + }, + "plot-gcs", + Some(RadrootsPlotLocation { + primary: Some("primary".to_string()), + city: None, + region: None, + country: None, + gcs: sample_gcs(16.0, 26.0, "s6"), + }), + None, + ); + assert!(ingest_plot_event(&plot_gcs_fail, &plot_gcs_event, &FixedFactory).is_err()); + + let plot_rel_fail = QueryFailExecutor { + inner: &exec, + needle: "plot_gcs_location", + err: SqlError::Internal, + }; + let plot_rel_event = plot_event( + 826, + &farm_pubkey, + 106, + "AAAAAAAAAAAAAAAAAAAAAw", + RadrootsFarmRef { + pubkey: farm_pubkey.clone(), + d_tag: farm_d_tag.to_string(), + }, + "plot-rel", + Some(RadrootsPlotLocation { + primary: Some("primary".to_string()), + city: None, + region: None, + country: None, + gcs: sample_gcs(17.0, 27.0, "s7"), + }), + None, + ); + assert!(ingest_plot_event(&plot_rel_fail, &plot_rel_event, &FixedFactory).is_err()); + + let plot_state_fail = QueryFailExecutor { + inner: &exec, + needle: "nostr_event_state", + err: SqlError::Internal, + }; + let plot_state_event = plot_event( + 827, + &farm_pubkey, + 107, + "AAAAAAAAAAAAAAAAAAAAAw", + RadrootsFarmRef { + pubkey: farm_pubkey.clone(), + d_tag: farm_d_tag.to_string(), + }, + "plot-state", + None, + None, + ); + assert!(ingest_plot_event(&plot_state_fail, &plot_state_event, &FixedFactory).is_err()); + + let mut list_decode_fail = profile_event( + 830, + &farm_pubkey, + 108, + Some(RadrootsProfileType::Farm), + "unused", + ); + list_decode_fail.kind = KIND_LIST_SET_GENERIC; + list_decode_fail.content = "{".to_string(); + list_decode_fail.tags = Vec::new(); + assert!(ingest_list_set_event(&exec, &list_decode_fail).is_err()); + + let members_list = farm_list_sets::farm_members_list_set(farm_d_tag, vec!["m".repeat(64)]) + .expect("members list"); + let member_event = + list_set_event(831, &farm_pubkey, 109, KIND_LIST_SET_GENERIC, &members_list); + let list_decision_fail = QueryFailExecutor { + inner: &exec, + needle: "nostr_event_state", + err: SqlError::Internal, + }; + assert!(ingest_list_set_event(&list_decision_fail, &member_event).is_err()); + + let member_of = + farm_list_sets::member_of_farms_list_set(vec![farm_pubkey.clone()]).expect("member-of"); + let member_of_event = + list_set_event(832, &"m".repeat(64), 110, KIND_LIST_SET_GENERIC, &member_of); + let claims_fail = QueryFailExecutor { + inner: &exec, + needle: "farm_member_claim", + err: SqlError::Internal, + }; + assert!(ingest_list_set_event(&claims_fail, &member_of_event).is_err()); + + let claims_state_fail = QueryFailExecutor { + inner: &exec, + needle: "nostr_event_state", + err: SqlError::Internal, + }; + assert!(ingest_list_set_event(&claims_state_fail, &member_of_event).is_err()); + + let plots_list = farm_list_sets::farm_plots_list_set( + farm_d_tag, + &farm_pubkey, + vec![plot_d_tag.to_string()], + ) + .expect("plots list"); + let plots_event = + list_set_event(833, &farm_pubkey, 111, KIND_LIST_SET_GENERIC, &plots_list); + let plots_state_fail = QueryFailExecutor { + inner: &exec, + needle: "nostr_event_state", + err: SqlError::Internal, + }; + assert!(ingest_list_set_event(&plots_state_fail, &plots_event).is_err()); + + let missing_farm_members = + farm_list_sets::farm_members_list_set(farm_d_tag, vec!["n".repeat(64)]).expect("list"); + let missing_farm_event = list_set_event( + 834, + &"z".repeat(64), + 112, + KIND_LIST_SET_GENERIC, + &missing_farm_members, + ); + assert!(ingest_list_set_event(&exec, &missing_farm_event).is_err()); + + let members_create_fail = QueryFailExecutor { + inner: &exec, + needle: "farm_member", + err: SqlError::Internal, + }; + assert!(ingest_list_set_event(&members_create_fail, &member_event).is_err()); + + let members_state_fail = QueryFailExecutor { + inner: &exec, + needle: "nostr_event_state", + err: SqlError::Internal, + }; + assert!(ingest_list_set_event(&members_state_fail, &member_event).is_err()); + + assert!(parse_farm_list_set_d_tag("").is_none()); + assert!(parse_farm_list_set_d_tag("farm").is_none()); + + let state_create_fail = QueryFailExecutor { + inner: &exec, + needle: "nostr_event_state", + err: SqlError::Internal, + }; + assert!( + radroots_replica_ingest_event_state(&state_create_fail, &profile, "", "hash").is_err() + ); + + radroots_replica_ingest_event_state(&exec, &profile, "", "hash").expect("seed state"); + let state_update_fail = QueryFailExecutor { + inner: &exec, + needle: "update nostr_event_state", + err: SqlError::Internal, + }; + assert!( + radroots_replica_ingest_event_state(&state_update_fail, &profile, "", "hash2").is_err() ); - assert!(upsert_member_claims(&txn, &"m".repeat(64), &member_of_list).is_err()); } }