From 0498331e91535b015c67ed07322f4143b75dfe77 Mon Sep 17 00:00:00 2001 From: PepeSilvia <0xpepesilvia@gmail.com> Date: Fri, 10 Apr 2026 12:14:26 +1000 Subject: [PATCH 1/2] fix(tests): retry event topic assertion until indexer has scanned The indexer_scans_network_events step queried getEvents once with no retry. If the indexer hadn't finished scanning/indexing events yet, the query returned an incomplete set and the assertion failed immediately. Now retries for up to 30 seconds, checking that all expected topics are present before asserting. On timeout, still reports the specific missing topic for debugging. Closes #1975 Co-Authored-By: Claude Opus 4.6 --- integration_tests/tests/steps/indexer.rs | 62 +++++++++++++++--------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/integration_tests/tests/steps/indexer.rs b/integration_tests/tests/steps/indexer.rs index 779d2bb35..2cdbfb715 100644 --- a/integration_tests/tests/steps/indexer.rs +++ b/integration_tests/tests/steps/indexer.rs @@ -109,30 +109,46 @@ async fn indexer_scans_network_events( let account = world.wallet_accounts.get(&account_name).unwrap_or_else(|| { panic!("No wallet account found with name {}", account_name); }); - let mut graphql_client = indexer.get_graphql_indexer_client().await; - let query = r#"{ getEvents { substateId, templateAddress, txHash, topic, payload } }"#.to_string(); - let res = graphql_client - .send_request::>>(&query, None, None) - .await - .expect("Failed to obtain getEvents query result"); + let component_address = account.component_address().to_string(); + let expected_topics: Vec<&str> = topics_str.split(',').collect(); - let events = res.get("getEvents").unwrap(); - let topics_for_component = events - .iter() - .filter(|e| e.substate_id == Some(account.component_address().to_string())) - .map(|e| e.topic.as_str()) - .collect::>(); - - let expected_topics = topics_str.split(','); - for (ind, topic) in expected_topics.enumerate() { - assert!( - topics_for_component.contains(topic), - "Unexpected topic at index {}. Events emitted were {}. Expected topic {} (ALL {:?})", - ind, - topics_for_component.display(), - topic, - events - ); + let mut remaining = 30; + loop { + let mut graphql_client = indexer.get_graphql_indexer_client().await; + let query = r#"{ getEvents { substateId, templateAddress, txHash, topic, payload } }"#.to_string(); + let res = graphql_client + .send_request::>>(&query, None, None) + .await + .expect("Failed to obtain getEvents query result"); + + let events = res.get("getEvents").unwrap(); + let topics_for_component = events + .iter() + .filter(|e| e.substate_id == Some(component_address.clone())) + .map(|e| e.topic.as_str()) + .collect::>(); + + let all_found = expected_topics.iter().all(|topic| topics_for_component.contains(topic)); + if all_found { + return; + } + + if remaining == 0 { + // Report which topics were missing + for (ind, topic) in expected_topics.iter().enumerate() { + assert!( + topics_for_component.contains(topic), + "Unexpected topic at index {}. Events emitted were {}. Expected topic {} (ALL {:?})", + ind, + topics_for_component.display(), + topic, + events + ); + } + unreachable!(); + } + remaining -= 1; + tokio::time::sleep(std::time::Duration::from_secs(1)).await; } } From b1799fb3e490effb224cccb921d1f25cab63fddd Mon Sep 17 00:00:00 2001 From: PepeSilvia <0xpepesilvia@gmail.com> Date: Fri, 10 Apr 2026 16:22:35 +1000 Subject: [PATCH 2/2] fix(tests): use correct topic names for account pay_fee events The previous attempt wrapped the event assertion in a 30-second retry loop, but the underlying bug was that the feature file expected `std.vault.pay_fee` events while the step definition filters events by `substate_id == component_address`. `std.vault.pay_fee` is emitted by the runtime with `substate_id = vault_id` (see crates/engine/src/runtime/impl.rs:1784-1790), so the filter can never match it. The retry would always time out. The `Account.pay_fee` custom event (emitted via `emit_event` in templates/account/src/lib.rs) carries the component substate id and does match the filter. Revert the topic names so the assertion is looking for events that can actually be found. The retry loop is kept as defence-in-depth against indexer catch-up timing. --- integration_tests/tests/features/indexer.feature | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integration_tests/tests/features/indexer.feature b/integration_tests/tests/features/indexer.feature index 949c41c86..551b6a312 100644 --- a/integration_tests/tests/features/indexer.feature +++ b/integration_tests/tests/features/indexer.feature @@ -54,7 +54,7 @@ Feature: Indexer node Then I wait for the indexer INDEXER to sync with the network # Scan the network for the event emitted on ACC creation - When indexer INDEXER scans the network events for account ACC with topics std.component.created,std.vault.pay_fee,std.component.updated + When indexer INDEXER scans the network events for account ACC with topics std.component.created,Account.pay_fee,std.component.updated Scenario: Indexer GraphQL requests work # Initialize a base node, wallet, miner and VN @@ -75,10 +75,10 @@ Feature: Indexer node Then I wait for the indexer INDEXER to sync with the network ##### Scenario # Scan the network for the event emitted on ACC_1 creation - When indexer INDEXER scans the network events for account ACC_1 with topics std.component.created,std.vault.pay_fee + When indexer INDEXER scans the network events for account ACC_1 with topics std.component.created,Account.pay_fee # Scan the network for the event emitted on ACC_2 creation - When indexer INDEXER scans the network events for account ACC_2 with topics std.component.created,std.vault.pay_fee + When indexer INDEXER scans the network events for account ACC_2 with topics std.component.created,Account.pay_fee Scenario: Indexer GraphQL filtering and pagination of events Given a network with registered validator VN and wallet daemon WALLET_D