@@ -2376,6 +2376,245 @@ async def some_field(_info):
23762376 },
23772377 ]
23782378
2379+ @pytest .mark .xfail (strict = True , reason = "unannounced error completion" )
2380+ async def announces_a_nested_fragment_before_completing_its_shared_failure ():
2381+ """Announces a nested fragment before completing its shared failure"""
2382+ document = parse (
2383+ """
2384+ {
2385+ hero {
2386+ ... @defer(label: "R") {
2387+ nonNullName
2388+ }
2389+ ... @defer(label: "P") {
2390+ name
2391+ ... @defer(label: "C") {
2392+ nonNullName
2393+ }
2394+ }
2395+ }
2396+ }
2397+ """
2398+ )
2399+ result = await complete (
2400+ document , {"hero" : {"name" : "Luke" , "nonNullName" : None }}
2401+ )
2402+
2403+ assert result == [
2404+ {
2405+ "data" : {"hero" : {}},
2406+ "pending" : [
2407+ {"id" : "0" , "path" : ["hero" ], "label" : "R" },
2408+ {"id" : "1" , "path" : ["hero" ], "label" : "P" },
2409+ ],
2410+ "hasNext" : True ,
2411+ },
2412+ {
2413+ "pending" : [{"id" : "2" , "path" : ["hero" ], "label" : "C" }],
2414+ "incremental" : [{"data" : {"name" : "Luke" }, "id" : "1" }],
2415+ "completed" : [
2416+ {
2417+ "id" : "0" ,
2418+ "errors" : [
2419+ {
2420+ "message" : "Cannot return null"
2421+ " for non-nullable field Hero.nonNullName." ,
2422+ "locations" : [
2423+ {"line" : 5 , "column" : 19 },
2424+ {"line" : 10 , "column" : 21 },
2425+ ],
2426+ "path" : ["hero" , "nonNullName" ],
2427+ },
2428+ ],
2429+ },
2430+ {"id" : "1" },
2431+ {
2432+ "id" : "2" ,
2433+ "errors" : [
2434+ {
2435+ "message" : "Cannot return null"
2436+ " for non-nullable field Hero.nonNullName." ,
2437+ "locations" : [
2438+ {"line" : 5 , "column" : 19 },
2439+ {"line" : 10 , "column" : 21 },
2440+ ],
2441+ "path" : ["hero" , "nonNullName" ],
2442+ },
2443+ ],
2444+ },
2445+ ],
2446+ "hasNext" : False ,
2447+ },
2448+ ]
2449+
2450+ @pytest .mark .xfail (strict = True , reason = "unannounced error completion" )
2451+ async def reports_one_cached_shared_failure_when_a_nested_fragment_is_announced ():
2452+ """Reports one cached shared failure when a nested fragment is announced"""
2453+ document = parse (
2454+ """
2455+ {
2456+ hero {
2457+ ... @defer(label: "R1") {
2458+ first: nonNullName
2459+ }
2460+ ... @defer(label: "R2") {
2461+ second: nonNullName
2462+ }
2463+ ... @defer(label: "P") {
2464+ name
2465+ ... @defer(label: "C") {
2466+ first: nonNullName
2467+ second: nonNullName
2468+ }
2469+ }
2470+ }
2471+ }
2472+ """
2473+ )
2474+ result = await complete (
2475+ document , {"hero" : {"name" : "Luke" , "nonNullName" : None }}
2476+ )
2477+
2478+ assert result == [
2479+ {
2480+ "data" : {"hero" : {}},
2481+ "pending" : [
2482+ {"id" : "0" , "path" : ["hero" ], "label" : "R1" },
2483+ {"id" : "1" , "path" : ["hero" ], "label" : "R2" },
2484+ {"id" : "2" , "path" : ["hero" ], "label" : "P" },
2485+ ],
2486+ "hasNext" : True ,
2487+ },
2488+ {
2489+ "pending" : [{"id" : "3" , "path" : ["hero" ], "label" : "C" }],
2490+ "incremental" : [{"data" : {"name" : "Luke" }, "id" : "2" }],
2491+ "completed" : [
2492+ {
2493+ "id" : "0" ,
2494+ "errors" : [
2495+ {
2496+ "message" : "Cannot return null"
2497+ " for non-nullable field Hero.nonNullName." ,
2498+ "locations" : [
2499+ {"line" : 5 , "column" : 19 },
2500+ {"line" : 13 , "column" : 21 },
2501+ ],
2502+ "path" : ["hero" , "first" ],
2503+ },
2504+ ],
2505+ },
2506+ {
2507+ "id" : "1" ,
2508+ "errors" : [
2509+ {
2510+ "message" : "Cannot return null"
2511+ " for non-nullable field Hero.nonNullName." ,
2512+ "locations" : [
2513+ {"line" : 8 , "column" : 19 },
2514+ {"line" : 14 , "column" : 21 },
2515+ ],
2516+ "path" : ["hero" , "second" ],
2517+ },
2518+ ],
2519+ },
2520+ {"id" : "2" },
2521+ {
2522+ "id" : "3" ,
2523+ "errors" : [
2524+ {
2525+ "message" : "Cannot return null"
2526+ " for non-nullable field Hero.nonNullName." ,
2527+ "locations" : [
2528+ {"line" : 5 , "column" : 19 },
2529+ {"line" : 13 , "column" : 21 },
2530+ ],
2531+ "path" : ["hero" , "first" ],
2532+ },
2533+ ],
2534+ },
2535+ ],
2536+ "hasNext" : False ,
2537+ },
2538+ ]
2539+
2540+ @pytest .mark .xfail (strict = True , reason = "lost shared result" )
2541+ @pytest .mark .parametrize ("early_execution" , [False , True ])
2542+ async def delivers_a_buffered_shared_value_after_its_other_fragment_fails (
2543+ early_execution ,
2544+ ):
2545+ """Delivers a buffered shared value after its other fragment fails"""
2546+ document = parse (
2547+ """
2548+ {
2549+ hero {
2550+ ... @defer(label: "R") {
2551+ nonNullName
2552+ id
2553+ }
2554+ ... @defer(label: "P") {
2555+ name
2556+ ... @defer(label: "C") {
2557+ id
2558+ }
2559+ }
2560+ }
2561+ }
2562+ """
2563+ )
2564+ name_future : Future [str ] = Future ()
2565+
2566+ result = experimental_execute_incrementally (
2567+ schema ,
2568+ document ,
2569+ {"hero" : {"id" : 1 , "name" : name_future , "nonNullName" : None }},
2570+ enable_early_execution = early_execution ,
2571+ )
2572+ assert isinstance (result , ExperimentalIncrementalExecutionResults )
2573+
2574+ assert result .initial_result .formatted == {
2575+ "data" : {"hero" : {}},
2576+ "pending" : [
2577+ {"id" : "0" , "path" : ["hero" ], "label" : "R" },
2578+ {"id" : "1" , "path" : ["hero" ], "label" : "P" },
2579+ ],
2580+ "hasNext" : True ,
2581+ }
2582+
2583+ iterator = result .subsequent_results
2584+
2585+ assert (await anext (iterator )).formatted == {
2586+ "completed" : [
2587+ {
2588+ "id" : "0" ,
2589+ "errors" : [
2590+ {
2591+ "message" : "Cannot return null"
2592+ " for non-nullable field Hero.nonNullName." ,
2593+ "locations" : [{"line" : 5 , "column" : 19 }],
2594+ "path" : ["hero" , "nonNullName" ],
2595+ },
2596+ ],
2597+ },
2598+ ],
2599+ "hasNext" : True ,
2600+ }
2601+
2602+ # The shared id has settled, but C still waits for P's name.
2603+ name_future .set_result ("Luke" )
2604+
2605+ assert (await anext (iterator )).formatted == {
2606+ "pending" : [{"id" : "2" , "path" : ["hero" ], "label" : "C" }],
2607+ "incremental" : [
2608+ {"data" : {"name" : "Luke" }, "id" : "1" },
2609+ {"data" : {"id" : "1" }, "id" : "2" },
2610+ ],
2611+ "completed" : [{"id" : "1" }, {"id" : "2" }],
2612+ "hasNext" : False ,
2613+ }
2614+
2615+ with pytest .raises (StopAsyncIteration ): # pragma: no cover
2616+ await anext (iterator )
2617+
23792618 async def handles_cancelling_child_deferred_fragments_if_parent_fragment_fails ():
23802619 """Handles cancelling child deferred fragments if parent fragment fails"""
23812620 document = parse (
0 commit comments