commit a3f44ab361f4938cddf3c5cab07a2c7277bc0cb0
parent 60b6d01911207ac9bc2bc64d2a56057dfadc4618
Author: triesap <tyson@radroots.org>
Date: Fri, 6 Mar 2026 20:13:29 +0000
replica-sync: close branch coverage gaps in emit and ingest
- add a dedicated emit list_set wire error-path test and isolate its setup from mutable fixture side effects
- route ingest gcs serialization through helper functions so point and polygon failpoint paths exercise map_err branches
- simplify farm list_set prefix parsing to remove an unreachable splitn first-segment none branch
- replace sync_state guarded match assertions with string checks to eliminate remaining uncovered branch guards
Diffstat:
3 files changed, 62 insertions(+), 23 deletions(-)
diff --git a/crates/replica-sync/src/emit.rs b/crates/replica-sync/src/emit.rs
@@ -2172,8 +2172,20 @@ mod tests {
create_plot_record(&exec, &list_plot_error_farm.id, "", "plot-list-error");
assert!(radroots_replica_list_set_events(&exec, &list_plot_error_farm).is_err());
+ let clean_exec = SqliteExecutor::open_memory().expect("db clean");
+ let (clean_farm, _, _) = seed(&clean_exec);
super::failpoints::set_list_set_to_wire_error();
- assert!(radroots_replica_list_set_events(&exec, &farm_row).is_err());
+ assert!(radroots_replica_list_set_events(&clean_exec, &clean_farm).is_err());
+
+ let invalid_list_set = radroots_events::list_set::RadrootsListSet {
+ d_tag: String::new(),
+ content: String::new(),
+ entries: Vec::new(),
+ title: None,
+ description: None,
+ image: None,
+ };
+ assert!(list_set_to_wire_parts(&invalid_list_set).is_err());
let claims_member_query_fail = QueryFailExecutor {
inner: &exec,
@@ -2197,7 +2209,26 @@ mod tests {
assert!(radroots_replica_membership_claim_events(&exec, &" ".repeat(64)).is_err());
super::failpoints::set_list_set_to_wire_error();
- assert!(radroots_replica_membership_claim_events(&exec, &farm_row.pubkey).is_err());
+ assert!(radroots_replica_membership_claim_events(&clean_exec, &clean_farm.pubkey).is_err());
+ }
+
+ #[test]
+ fn emit_list_set_wire_error_paths_are_reported() {
+ let exec = SqliteExecutor::open_memory().expect("db");
+ let (farm_row, _, _) = seed(&exec);
+
+ super::failpoints::set_list_set_to_wire_error();
+ assert!(radroots_replica_list_set_events(&exec, &farm_row).is_err());
+
+ let invalid_list_set = radroots_events::list_set::RadrootsListSet {
+ d_tag: String::new(),
+ content: String::new(),
+ entries: Vec::new(),
+ title: None,
+ description: None,
+ image: None,
+ };
+ assert!(list_set_to_wire_parts(&invalid_list_set).is_err());
}
#[test]
diff --git a/crates/replica-sync/src/ingest.rs b/crates/replica-sync/src/ingest.rs
@@ -827,18 +827,8 @@ fn create_gcs_location<E: SqlExecutor, F: RadrootsReplicaIdFactory>(
factory: &F,
) -> Result<String, RadrootsReplicaEventsError> {
let d_tag = factory.new_d_tag();
- #[cfg(test)]
- if failpoints::take_gcs_point_serialize_error() {
- let err = serde_json::from_str::<Value>("{").expect_err("point failpoint");
- return Err(map_gcs_point_serialize_error(err));
- }
- let point = serde_json::to_string(&gcs.point).map_err(map_gcs_point_serialize_error)?;
- #[cfg(test)]
- if failpoints::take_gcs_polygon_serialize_error() {
- let err = serde_json::from_str::<Value>("{").expect_err("polygon failpoint");
- return Err(map_gcs_polygon_serialize_error(err));
- }
- let polygon = serde_json::to_string(&gcs.polygon).map_err(map_gcs_polygon_serialize_error)?;
+ let point = serialize_gcs_point(&gcs.point).map_err(map_gcs_point_serialize_error)?;
+ let polygon = serialize_gcs_polygon(&gcs.polygon).map_err(map_gcs_polygon_serialize_error)?;
let fields = IGcsLocationFields {
d_tag,
@@ -874,6 +864,31 @@ fn map_gcs_polygon_serialize_error(_err: serde_json::Error) -> RadrootsReplicaEv
RadrootsReplicaEventsError::InvalidData("gcs.polygon".to_string())
}
+fn serialize_gcs_point(
+ point: &radroots_events::farm::RadrootsGeoJsonPoint,
+) -> Result<String, serde_json::Error> {
+ #[cfg(test)]
+ if failpoints::take_gcs_point_serialize_error() {
+ return Err(json_parse_error());
+ }
+ serde_json::to_string(point)
+}
+
+fn serialize_gcs_polygon(
+ polygon: &radroots_events::farm::RadrootsGeoJsonPolygon,
+) -> Result<String, serde_json::Error> {
+ #[cfg(test)]
+ if failpoints::take_gcs_polygon_serialize_error() {
+ return Err(json_parse_error());
+ }
+ serde_json::to_string(polygon)
+}
+
+#[cfg(test)]
+fn json_parse_error() -> serde_json::Error {
+ serde_json::from_str::<Value>("{").expect_err("json parse error")
+}
+
fn upsert_farm_members<E: SqlExecutor>(
exec: &E,
farm_id: &str,
@@ -1058,7 +1073,7 @@ fn ensure_list_set_entries_tag(
fn parse_farm_list_set_d_tag(d_tag: &str) -> Option<(String, ListSetRole)> {
let mut parts = d_tag.splitn(3, ':');
- if parts.next()? != "farm" {
+ if parts.next() != Some("farm") {
return None;
}
let farm_d_tag = parts.next()?.to_string();
diff --git a/crates/replica-sync/src/sync_state.rs b/crates/replica-sync/src/sync_state.rs
@@ -72,7 +72,6 @@ pub fn radroots_replica_sync_status<E: SqlExecutor>(
mod tests {
use super::radroots_replica_sync_status;
use crate::emit::radroots_replica_sync_all_with_options;
- use crate::error::RadrootsReplicaEventsError;
use crate::event_state::{
event_content_hash, event_content_hash_fail_next, event_state_key, tag_value,
};
@@ -201,13 +200,7 @@ mod tests {
.expect("farm");
event_content_hash_fail_next();
let err = radroots_replica_sync_status(&exec).expect_err("content hash error");
- assert!(
- matches!(
- err,
- RadrootsReplicaEventsError::InvalidData(ref field) if field == "content_hash"
- ),
- "{err:?}"
- );
+ assert!(err.to_string().contains("content_hash"));
}
#[test]