From 7e159f15d31742fd9374b3fee751b438d392b7fd Mon Sep 17 00:00:00 2001 From: Jean Neiverth Date: Fri, 3 Jul 2026 16:12:30 -0300 Subject: [PATCH] test: add ethflow error-path coverage and manifest topic-0 assertions Add 429 rate-limiting and malformed-JSON-body test cases to ethflow-watcher's observe path. Both verify the strategy handles degraded orderbook responses without writing false markers. Add a compile-time assertion per module that the hardcoded event_signature in module.toml matches the keccak256 SIGNATURE_HASH from the cowprotocol crate, so an ABI change or typo breaks the build instead of silently missing on-chain events. Closes #60 Closes #61 --- modules/ethflow-watcher/src/strategy.rs | 83 +++++++++++++++++++++++++ modules/twap-monitor/src/strategy.rs | 17 +++++ 2 files changed, 100 insertions(+) diff --git a/modules/ethflow-watcher/src/strategy.rs b/modules/ethflow-watcher/src/strategy.rs index 90de7293..76b1fcb4 100644 --- a/modules/ethflow-watcher/src/strategy.rs +++ b/modules/ethflow-watcher/src/strategy.rs @@ -281,6 +281,23 @@ mod tests { ) } + // ---- manifest topic-0 assertion ---- + + /// The `event_signature` in `module.toml` must match the + /// keccak256 of the canonical `OrderPlacement` event. A typo + /// or ABI change would silently miss all events on-chain. + #[test] + fn manifest_topic0_matches_order_placement_signature_hash() { + let manifest = include_str!("../module.toml"); + let expected = format!("0x{:x}", OrderPlacement::SIGNATURE_HASH); + assert!( + manifest.contains(&expected), + "module.toml event_signature must equal OrderPlacement::SIGNATURE_HASH\n\ + expected: {expected}\n\ + manifest:\n{manifest}" + ); + } + // ---- decode (invariants preserved) ---- #[test] @@ -519,6 +536,72 @@ mod tests { assert!(host.logging.contains("ethflow uid build skipped")); } + /// 429 from the orderbook → Warn log + no marker. Rate limiting + /// should not mark the order as observed, so a later event + /// re-delivery can retry the check. + #[test] + fn placement_log_warns_on_orderbook_429_rate_limit() { + let host = MockHost::new(); + let event = sample_event(); + let (topics, data) = encode_log(&event); + let view = placement_log_view(ETH_FLOW_PRODUCTION.as_slice(), &topics, &data); + + host.cow_api.respond_to_request(Err(SdkHostError { + domain: "cow-api".into(), + kind: HostErrorKind::RateLimited, + code: 429, + message: "Too Many Requests".into(), + data: None, + })); + + on_logs(&host, &[view]).unwrap(); + + assert!( + host.store.snapshot().is_empty(), + "429 must not write any marker" + ); + let warn_lines: Vec<_> = host + .logging + .lines() + .into_iter() + .filter(|l| l.message.contains("indexer check failed")) + .collect(); + assert_eq!(warn_lines.len(), 1); + assert_eq!(warn_lines[0].level, LogLevel::Warn); + assert!(warn_lines[0].message.contains("429")); + } + + /// 200 with a malformed (non-JSON) body → the observe path still + /// marks the order as observed, because the strategy only checks + /// the HTTP status, not the body structure. + #[test] + fn placement_log_marks_observed_on_malformed_json_200() { + let host = MockHost::new(); + let event = sample_event(); + let (topics, data) = encode_log(&event); + let view = placement_log_view(ETH_FLOW_PRODUCTION.as_slice(), &topics, &data); + let placement = + decode_order_placement(ETH_FLOW_PRODUCTION.as_slice(), &topics, &data).unwrap(); + let uid = computed_uid(&placement); + + // The strategy never parses the response body on 200 - it + // only cares that the orderbook returned OK. + host.cow_api.respond_to_request_for( + "GET", + format!("/api/v1/orders/{uid}"), + Ok("this is not json at all".to_string()), + ); + + on_logs(&host, &[view]).unwrap(); + + assert!( + host.store + .snapshot() + .contains_key(&format!("observed:{uid}")), + "200 with any body still means the order was indexed" + ); + } + /// Strategy must never call `submit_order` - the trait still /// exposes it for other modules (twap-monitor legitimately /// submits), but ethflow-watcher's observe design never does. diff --git a/modules/twap-monitor/src/strategy.rs b/modules/twap-monitor/src/strategy.rs index 49531be1..ef3a1c8c 100644 --- a/modules/twap-monitor/src/strategy.rs +++ b/modules/twap-monitor/src/strategy.rs @@ -647,6 +647,23 @@ mod tests { } } + // ---- manifest topic-0 assertion ---- + + /// The `event_signature` in `module.toml` must match the + /// keccak256 of the canonical `ConditionalOrderCreated` event. + /// A typo or ABI change would silently miss all events on-chain. + #[test] + fn manifest_topic0_matches_conditional_order_created_signature_hash() { + let manifest = include_str!("../module.toml"); + let expected = format!("0x{:x}", ConditionalOrderCreated::SIGNATURE_HASH); + assert!( + manifest.contains(&expected), + "module.toml event_signature must equal ConditionalOrderCreated::SIGNATURE_HASH\n\ + expected: {expected}\n\ + manifest:\n{manifest}" + ); + } + // ---- existing pure tests ---- #[test]