@@ -874,3 +874,74 @@ async fn network_gateway_binds_to_configured_interface() {
874874 resp. status( )
875875 ) ;
876876}
877+
878+ #[ tokio:: test]
879+ async fn network_recovers_from_stale_descriptor ( ) {
880+ let ctx = TestContext :: new ( ) ;
881+ let project_dir = ctx. create_project_dir ( "stale-descriptor" ) ;
882+
883+ // Project manifest
884+ write_string ( & project_dir. join ( "icp.yaml" ) , NETWORK_RANDOM_PORT )
885+ . expect ( "failed to write project manifest" ) ;
886+
887+ // Ensure the network descriptor directory exists
888+ let network_dir = project_dir. join ( ".icp/cache/networks/random-network" ) ;
889+ std:: fs:: create_dir_all ( & network_dir) . expect ( "failed to create network directory" ) ;
890+
891+ // Create a stale descriptor with a PID that cannot exist
892+ let stale_descriptor = serde_json:: json!( {
893+ "v" : "1" ,
894+ "id" : "11111111-1111-1111-1111-111111111111" ,
895+ "project-dir" : project_dir. to_string( ) ,
896+ "network" : "random-network" ,
897+ "network-dir" : network_dir. to_string( ) ,
898+ "gateway" : {
899+ "fixed" : false ,
900+ "port" : 9999 ,
901+ "host" : "localhost" ,
902+ "ip" : "127.0.0.1"
903+ } ,
904+ "child-locator" : {
905+ "type" : "pid" ,
906+ "pid" : u32 :: MAX , // Non-existent PID
907+ "start-time" : 0
908+ } ,
909+ "root-key" : "308182301c300d06092a864886f70d0101010500030b008081007f" ,
910+ "pocketic-config-port" : null,
911+ "pocketic-instance-id" : null,
912+ "candid-ui-canister-id" : null,
913+ "proxy-canister-id" : null,
914+ "status-dir" : null,
915+ "use-friendly-domains" : false
916+ } ) ;
917+
918+ // Write the stale descriptor
919+ let descriptor_bytes =
920+ serde_json:: to_vec ( & stale_descriptor) . expect ( "failed to serialize descriptor" ) ;
921+ ctx. write_network_descriptor ( & project_dir, "random-network" , & descriptor_bytes) ;
922+
923+ // Start network - should succeed and clean up the stale descriptor
924+ ctx. icp ( )
925+ . current_dir ( & project_dir)
926+ . args ( [ "network" , "start" , "random-network" , "--background" ] )
927+ . assert ( )
928+ . success ( )
929+ . stderr ( contains ( "Found stale network descriptor" ) ) ;
930+
931+ // Verify the network actually started (descriptor should be updated with real process)
932+ let network = ctx. wait_for_network_descriptor ( & project_dir, "random-network" ) ;
933+
934+ ctx. ping_until_healthy ( & project_dir, "random-network" ) ;
935+
936+ // Verify we can query the network
937+ let agent = ic_agent:: Agent :: builder ( )
938+ . with_url ( format ! ( "http://127.0.0.1:{}" , network. gateway_port) )
939+ . build ( )
940+ . expect ( "Failed to build agent" ) ;
941+
942+ let status = agent. status ( ) . await . expect ( "Failed to get network status" ) ;
943+ assert ! (
944+ matches!( & status. replica_health_status, Some ( health) if health == "healthy" ) ,
945+ "Network should be healthy"
946+ ) ;
947+ }
0 commit comments