From 0aa895070b0e87f35e63cd997bfe179603486f11 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Fri, 13 Feb 2026 15:29:17 -0800 Subject: [PATCH] tests: Mock arweave resolver to fix flaky arweave_file_data_sources test The test relied on real HTTP calls to arweave.net and a 1500ms sleep hack to work around the resulting race condition. Replace with a StaticArweaveResolver that returns synthetic content from memory, making the test deterministic and faster. Also generalize arweave_service() to accept Arc instead of Arc, and ensure setup_inner uses a single resolver instance for both the polling service and TestContext. --- core/src/polling_monitor/arweave_service.rs | 6 +- tests/src/fixture/mod.rs | 64 +++++++++++++++++++-- tests/tests/runner_tests.rs | 32 +++++------ 3 files changed, 78 insertions(+), 24 deletions(-) diff --git a/core/src/polling_monitor/arweave_service.rs b/core/src/polling_monitor/arweave_service.rs index 51249324df7..d0e557905f6 100644 --- a/core/src/polling_monitor/arweave_service.rs +++ b/core/src/polling_monitor/arweave_service.rs @@ -2,7 +2,7 @@ use anyhow::Error; use bytes::Bytes; use graph::futures03::future::BoxFuture; use graph::{ - components::link_resolver::{ArweaveClient, ArweaveResolver, FileSizeLimit}, + components::link_resolver::{ArweaveResolver, FileSizeLimit}, data_source::offchain::Base64, derive::CheapClone, prelude::CheapClone, @@ -13,7 +13,7 @@ use tower::{buffer::Buffer, ServiceBuilder, ServiceExt}; pub type ArweaveService = Buffer, Error>>>; pub fn arweave_service( - client: Arc, + client: Arc, rate_limit: u16, max_file_size: FileSizeLimit, ) -> ArweaveService { @@ -34,7 +34,7 @@ pub fn arweave_service( #[derive(Clone, CheapClone)] struct ArweaveServiceInner { - client: Arc, + client: Arc, max_file_size: FileSizeLimit, } diff --git a/tests/src/fixture/mod.rs b/tests/src/fixture/mod.rs index 62b8eb1f7f4..931a6ef80f8 100644 --- a/tests/src/fixture/mod.rs +++ b/tests/src/fixture/mod.rs @@ -20,7 +20,8 @@ use graph::blockchain::{ }; use graph::cheap_clone::CheapClone; use graph::components::link_resolver::{ - ArweaveClient, ArweaveResolver, FileLinkResolver, FileSizeLimit, LinkResolverContext, + ArweaveClient, ArweaveClientError, ArweaveResolver, FileLinkResolver, FileSizeLimit, + LinkResolverContext, }; use graph::components::metrics::MetricsRegistry; use graph::components::network_provider::ChainName; @@ -422,6 +423,38 @@ pub struct TestInfo { pub hash: DeploymentHash, } +#[derive(Debug)] +pub struct StaticArweaveResolver { + content: HashMap>, +} + +impl StaticArweaveResolver { + pub fn new(content: HashMap>) -> Self { + Self { content } + } +} + +#[async_trait] +impl ArweaveResolver for StaticArweaveResolver { + async fn get( + &self, + file: &graph::data_source::offchain::Base64, + ) -> Result, ArweaveClientError> { + self.get_with_limit(file, &FileSizeLimit::Unlimited).await + } + + async fn get_with_limit( + &self, + file: &graph::data_source::offchain::Base64, + _limit: &FileSizeLimit, + ) -> Result, ArweaveClientError> { + self.content + .get(file.as_str()) + .cloned() + .ok_or(ArweaveClientError::UnableToCheckFileSize) + } +} + pub async fn setup( test_info: &TestInfo, stores: &Stores, @@ -429,7 +462,7 @@ pub async fn setup( graft_block: Option, env_vars: Option, ) -> TestContext { - setup_inner(test_info, stores, chain, graft_block, env_vars, None).await + setup_inner(test_info, stores, chain, graft_block, env_vars, None, None).await } pub async fn setup_with_file_link_resolver( @@ -449,6 +482,27 @@ pub async fn setup_with_file_link_resolver( graft_block, env_vars, Some(link_resolver), + None, + ) + .await +} + +pub async fn setup_with_arweave_resolver( + test_info: &TestInfo, + stores: &Stores, + chain: &impl TestChainTrait, + graft_block: Option, + env_vars: Option, + arweave_resolver: Arc, +) -> TestContext { + setup_inner( + test_info, + stores, + chain, + graft_block, + env_vars, + None, + Some(arweave_resolver), ) .await } @@ -460,6 +514,7 @@ pub async fn setup_inner( graft_block: Option, env_vars: Option, link_resolver: Option>, + arweave_resolver: Option>, ) -> TestContext { let env_vars = Arc::new(match env_vars { Some(ev) => ev, @@ -506,7 +561,8 @@ pub async fn setup_inner( env_vars.mappings.ipfs_request_limit, ); - let arweave_resolver = Arc::new(ArweaveClient::default()); + let arweave_resolver: Arc = + arweave_resolver.unwrap_or_else(|| Arc::new(ArweaveClient::default())); let arweave_service = arweave_service( arweave_resolver.cheap_clone(), env_vars.mappings.ipfs_request_limit, @@ -603,8 +659,6 @@ pub async fn setup_inner( .await .expect("failed to create subgraph version"); - let arweave_resolver = Arc::new(ArweaveClient::default()); - TestContext { logger: logger_factory.subgraph_logger(&deployment), provider: subgraph_provider, diff --git a/tests/tests/runner_tests.rs b/tests/tests/runner_tests.rs index 0b846b7c49a..6a9102fd283 100644 --- a/tests/tests/runner_tests.rs +++ b/tests/tests/runner_tests.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::marker::PhantomData; use std::str::FromStr; use std::sync::atomic::{self, AtomicBool}; @@ -23,8 +24,8 @@ use graph_tests::fixture::ethereum::{ }; use graph_tests::fixture::{ - self, test_ptr, test_ptr_reorged, MockAdapterSelector, NoopAdapterSelector, TestChainTrait, - TestContext, TestInfo, + self, test_ptr, test_ptr_reorged, MockAdapterSelector, NoopAdapterSelector, + StaticArweaveResolver, TestChainTrait, TestContext, TestInfo, }; use graph_tests::recipe::{build_subgraph_with_pnpm_cmd_and_arg, RunnerTestRecipe}; use slog::{o, Discard, Logger}; @@ -1141,23 +1142,22 @@ async fn arweave_file_data_sources() { // HASH used in the mappings. let id = "8APeQ5lW0-csTcBaGdPBDLAL2ci2AT9pTn2tppGPU_8"; - // This test assumes the file data sources will be processed in the same block in which they are - // created. But the test might fail due to a race condition if for some reason it takes longer - // than expected to fetch the file from arweave. The sleep here will conveniently happen after the - // data source is added to the offchain monitor but before the monitor is checked, in an an - // attempt to ensure the monitor has enough time to fetch the file. - let adapter_selector = NoopAdapterSelector { - x: PhantomData, - triggers_in_block_sleep: Duration::from_millis(1500), - }; - let chain = chain( - &test_info.test_name, - blocks.clone(), + // Use a mock arweave resolver to avoid real network calls and eliminate the + // race condition that caused this test to be flaky. + let arweave_content: HashMap> = + HashMap::from([(id.to_string(), b"test arweave content".to_vec())]); + let arweave_resolver = Arc::new(StaticArweaveResolver::new(arweave_content)); + + let chain = chain(&test_info.test_name, blocks.clone(), &stores, None).await; + let ctx = fixture::setup_with_arweave_resolver( + &test_info, &stores, - Some(Arc::new(adapter_selector)), + &chain, + None, + None, + arweave_resolver, ) .await; - let ctx = fixture::setup(&test_info, &stores, &chain, None, None).await; ctx.start_and_sync_to(test_ptr(2)).await; let store = ctx.store.cheap_clone();