@@ -156,6 +156,11 @@ pub(super) struct ManifestInfo {
156156 pub start_block_override : Option < BlockPtr > ,
157157 /// Deployment hash derived from the built manifest path.
158158 pub hash : DeploymentHash ,
159+ /// Subgraph name derived from the manifest's root directory (e.g., "test/my-subgraph").
160+ /// Fixed across all tests so that `cleanup` can always find and remove the
161+ /// previous test's entry — per-test names left dangling FK references that
162+ /// prevented `drop_chain` from clearing the chain head.
163+ pub subgraph_name : SubgraphName ,
159164}
160165
161166/// Load and pre-compute manifest data for the test run.
@@ -208,12 +213,27 @@ pub(super) fn load_manifest_info(opt: &TestOpt) -> Result<ManifestInfo> {
208213 )
209214 } ) ?;
210215
216+ // Derive subgraph name from the root directory (e.g., "my-subgraph" → "test/my-subgraph").
217+ // Sanitize to alphanumeric + hyphens + underscores for SubgraphName compatibility.
218+ let root_dir_name = manifest_dir
219+ . canonicalize ( )
220+ . unwrap_or ( manifest_dir. clone ( ) )
221+ . file_name ( )
222+ . and_then ( |s| s. to_str ( ) )
223+ . unwrap_or ( "gnd-test" )
224+ . chars ( )
225+ . filter ( |c| c. is_alphanumeric ( ) || * c == '-' || * c == '_' )
226+ . collect :: < String > ( ) ;
227+ let subgraph_name =
228+ SubgraphName :: new ( format ! ( "test/{}" , root_dir_name) ) . map_err ( |e| anyhow ! ( "{}" , e) ) ?;
229+
211230 Ok ( ManifestInfo {
212231 build_dir,
213232 network_name,
214233 min_start_block,
215234 start_block_override,
216235 hash,
236+ subgraph_name,
217237 } )
218238}
219239
@@ -301,20 +321,18 @@ pub async fn run_single_test(
301321 let logger = make_test_logger ( opt. verbose ) . new ( o ! ( "test" => test_file. name. clone( ) ) ) ;
302322
303323 // Initialize stores with the network name from the manifest.
304- let stores = setup_stores ( & logger, & db_url, & manifest_info. network_name ) . await ?;
324+ let stores = setup_stores (
325+ & logger,
326+ & db_url,
327+ & manifest_info. network_name ,
328+ & manifest_info. subgraph_name ,
329+ & manifest_info. hash ,
330+ )
331+ . await ?;
305332
306333 // Create the mock Ethereum chain that will feed our pre-built blocks.
307334 let chain = setup_chain ( & logger, blocks. clone ( ) , & stores) . await ?;
308335
309- // Sanitize test name for use as a subgraph name (alphanumeric + hyphens + underscores).
310- let test_name_sanitized = test_file
311- . name
312- . chars ( )
313- . filter ( |c| c. is_alphanumeric ( ) || * c == '-' || * c == '_' )
314- . collect :: < String > ( ) ;
315- let subgraph_name =
316- SubgraphName :: new ( format ! ( "test/{}" , test_name_sanitized) ) . map_err ( |e| anyhow ! ( "{}" , e) ) ?;
317-
318336 // Wire up all graph-node components (instance manager, provider, registrar, etc.)
319337 // and deploy the subgraph.
320338 let ctx = setup_context (
@@ -323,7 +341,7 @@ pub async fn run_single_test(
323341 & chain,
324342 & manifest_info. build_dir ,
325343 manifest_info. hash . clone ( ) ,
326- subgraph_name. clone ( ) ,
344+ manifest_info . subgraph_name . clone ( ) ,
327345 manifest_info. start_block_override . clone ( ) ,
328346 )
329347 . await ?;
@@ -456,6 +474,8 @@ async fn setup_stores(
456474 logger : & Logger ,
457475 db_url : & str ,
458476 network_name : & ChainName ,
477+ subgraph_name : & SubgraphName ,
478+ hash : & DeploymentHash ,
459479) -> Result < TestStores > {
460480 // Minimal graph-node config: one primary shard, no chain providers.
461481 // The chain→shard mapping defaults to "primary" in StoreBuilder::make_store,
@@ -492,14 +512,22 @@ ingestor = "default"
492512 let network_identifiers: Vec < ChainName > = vec ! [ network_name. clone( ) ] ;
493513 let network_store = store_builder. network_store ( network_identifiers) . await ;
494514
515+ // Clean up any leftover state from a previous run on this persistent database.
516+ // Order matters: deployments must be removed before the chain can be dropped,
517+ // because deployment_schemas has a FK constraint on the chains table.
518+ let subgraph_store = network_store. subgraph_store ( ) ;
519+ cleanup ( & subgraph_store, subgraph_name, hash) . await . ok ( ) ;
520+
521+ let block_store = network_store. block_store ( ) ;
522+ let _ = block_store. drop_chain ( network_name) . await ;
523+
495524 // Synthetic chain identifier — net_version "1" with zero genesis hash.
496525 let ident = ChainIdentifier {
497526 net_version : "1" . into ( ) ,
498527 genesis_block_hash : graph:: prelude:: alloy:: primitives:: B256 :: ZERO . into ( ) ,
499528 } ;
500529
501- let chain_store = network_store
502- . block_store ( )
530+ let chain_store = block_store
503531 . create_chain_store ( network_name, ident)
504532 . await
505533 . context ( "Failed to create chain store" ) ?;
@@ -643,9 +671,6 @@ async fn setup_context(
643671
644672 let subgraph_store = stores. network_store . subgraph_store ( ) ;
645673
646- // Remove any leftover deployment from a previous test run (idempotent).
647- cleanup ( & subgraph_store, & subgraph_name, & hash) . await . ok ( ) ;
648-
649674 // Map the network name to our mock chain so graph-node routes triggers correctly.
650675 let mut blockchain_map = BlockchainMap :: new ( ) ;
651676 blockchain_map. insert ( stores. network_name . clone ( ) , chain. clone ( ) ) ;
@@ -784,7 +809,10 @@ async fn cleanup(
784809 // Ignore errors - the subgraph might not exist on first run
785810 let _ = subgraph_store. remove_subgraph ( name. clone ( ) ) . await ;
786811
787- for locator in locators {
812+ for locator in & locators {
813+ // Unassign the deployment from its node first — remove_deployment
814+ // silently skips deletion if the deployment is still assigned.
815+ let _ = SubgraphStoreTrait :: unassign_subgraph ( subgraph_store, locator) . await ;
788816 subgraph_store. remove_deployment ( locator. id . into ( ) ) . await ?;
789817 }
790818
0 commit comments