@@ -355,4 +355,147 @@ defmodule AshPostgres.BulkCreateTest do
355
355
|> Ash . read! ( )
356
356
end
357
357
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
358
501
end
0 commit comments