@@ -355,4 +355,147 @@ defmodule AshPostgres.BulkCreateTest do
355355 |> Ash . read! ( )
356356 end
357357 end
358+
359+ describe "nested bulk operations" do
360+ test "supports bulk_create in after_action callbacks" do
361+ result =
362+ Ash . bulk_create! (
363+ [ % { title: "trigger_nested" } ] ,
364+ Post ,
365+ :create_with_nested_bulk_create ,
366+ return_records?: true ,
367+ authorize?: false
368+ )
369+
370+ # Assert the bulk result contains the expected data
371+ assert % Ash.BulkResult { records: [ original_post ] } = result
372+ assert original_post . title == "trigger_nested"
373+
374+ # Verify all posts that should exist after the nested operation
375+ all_posts =
376+ Post
377+ |> Ash.Query . sort ( :title )
378+ |> Ash . read! ( )
379+
380+ # Should have: 1 original + 2 nested = 3 total posts
381+ assert length ( all_posts ) == 3
382+
383+ # Verify we have the expected posts with correct titles
384+ post_titles = Enum . map ( all_posts , & & 1 . title ) |> Enum . sort ( )
385+ assert post_titles == [ "nested_post_1" , "nested_post_2" , "trigger_nested" ]
386+
387+ # Verify the specific nested posts were created by the after_action callback
388+ nested_posts =
389+ Post
390+ |> Ash.Query . filter ( expr ( title in [ "nested_post_1" , "nested_post_2" ] ) )
391+ |> Ash.Query . sort ( :title )
392+ |> Ash . read! ( )
393+
394+ assert length ( nested_posts ) == 2
395+ assert [ % { title: "nested_post_1" } , % { title: "nested_post_2" } ] = nested_posts
396+
397+ # Verify that each nested post has proper metadata
398+ Enum . each ( nested_posts , fn post ->
399+ assert is_binary ( post . id )
400+ assert post . title in [ "nested_post_1" , "nested_post_2" ]
401+ end )
402+ end
403+
404+ test "supports bulk_update in after_action callbacks" do
405+ result =
406+ Ash . bulk_create! (
407+ [ % { title: "trigger_nested_update" } ] ,
408+ Post ,
409+ :create_with_nested_bulk_update ,
410+ return_records?: true ,
411+ authorize?: false
412+ )
413+
414+ # Assert the bulk result contains the expected data
415+ assert % Ash.BulkResult { records: [ original_post ] } = result
416+ assert original_post . title == "trigger_nested_update"
417+
418+ # Verify all posts that should exist after the nested operations
419+ all_posts =
420+ Post
421+ |> Ash.Query . sort ( :title )
422+ |> Ash . read! ( )
423+
424+ # Should have: 1 original + 2 created and updated = 3 total posts
425+ assert length ( all_posts ) == 3
426+
427+ # Verify the original post still exists
428+ original_posts =
429+ Post
430+ |> Ash.Query . filter ( expr ( title == "trigger_nested_update" ) )
431+ |> Ash . read! ( )
432+
433+ assert length ( original_posts ) == 1
434+ assert hd ( original_posts ) . title == "trigger_nested_update"
435+
436+ # Verify the nested posts were created and then updated by the after_action callback
437+ updated_posts =
438+ Post
439+ |> Ash.Query . filter ( expr ( title == "updated_via_nested_bulk" ) )
440+ |> Ash . read! ( )
441+
442+ assert length ( updated_posts ) == 2
443+
444+ # Verify that the updated posts have proper metadata and were actually updated
445+ Enum . each ( updated_posts , fn post ->
446+ assert is_binary ( post . id )
447+ assert post . title == "updated_via_nested_bulk"
448+ end )
449+
450+ # Verify no posts remain with the intermediate titles
451+ intermediate_posts =
452+ Post
453+ |> Ash.Query . filter ( expr ( title in [ "post_to_update_1" , "post_to_update_2" ] ) )
454+ |> Ash . read! ( )
455+
456+ assert length ( intermediate_posts ) == 0 ,
457+ "Posts should have been updated, not left with intermediate titles"
458+ end
459+
460+ test "nested bulk operations handle metadata indexing correctly" do
461+ # Create multiple posts in the parent bulk operation to test indexing
462+ result =
463+ Ash . bulk_create! (
464+ [
465+ % { title: "trigger_nested" } ,
466+ % { title: "trigger_nested_2" }
467+ ] ,
468+ Post ,
469+ :create_with_nested_bulk_create ,
470+ return_records?: true ,
471+ authorize?: false
472+ )
473+
474+ # Assert both parent posts were created
475+ assert % Ash.BulkResult { records: parent_posts } = result
476+ assert length ( parent_posts ) == 2
477+
478+ parent_titles = Enum . map ( parent_posts , & & 1 . title ) |> Enum . sort ( )
479+ assert parent_titles == [ "trigger_nested" , "trigger_nested_2" ]
480+
481+ # Verify total posts: 2 parent + (2 nested per parent) = 6 total
482+ all_posts = Post |> Ash.Query . sort ( :title ) |> Ash . read! ( )
483+ assert length ( all_posts ) == 6
484+
485+ # Count posts by type
486+ nested_posts =
487+ Post
488+ |> Ash.Query . filter ( expr ( title in [ "nested_post_1" , "nested_post_2" ] ) )
489+ |> Ash . read! ( )
490+
491+ # Should have 4 nested posts (2 for each parent operation)
492+ assert length ( nested_posts ) == 4
493+
494+ # Verify each nested post has proper structure
495+ Enum . each ( nested_posts , fn post ->
496+ assert is_binary ( post . id )
497+ assert post . title in [ "nested_post_1" , "nested_post_2" ]
498+ end )
499+ end
500+ end
358501end
0 commit comments