@@ -390,6 +390,64 @@ defmodule Ash.Test.Actions.BulkDestroyTest do
390390 end
391391 end
392392
393+ defmodule StrictPost do
394+ @ moduledoc false
395+ use Ash.Resource ,
396+ domain: Domain ,
397+ data_layer: Ash.DataLayer.Ets ,
398+ authorizers: [ Ash.Policy.Authorizer ]
399+
400+ ets do
401+ private? true
402+ end
403+
404+ actions do
405+ default_accept :*
406+ defaults [ :create ]
407+
408+ read :read do
409+ primary? true
410+ pagination keyset?: true , required?: false
411+ end
412+
413+ read :read_for_destroy do
414+ pagination keyset?: true , required?: false
415+ end
416+
417+ destroy :destroy do
418+ primary? true
419+ end
420+
421+ destroy :destroy_with_read_action do
422+ end
423+ end
424+
425+ policies do
426+ default_access_type :strict
427+
428+ policy action ( :create ) do
429+ authorize_if always ( )
430+ end
431+
432+ policy action ( :read_for_destroy ) do
433+ authorize_if always ( )
434+ end
435+
436+ policy action ( :destroy_with_read_action ) do
437+ authorize_if always ( )
438+ end
439+
440+ policy action ( :destroy ) do
441+ authorize_if always ( )
442+ end
443+ end
444+
445+ attributes do
446+ uuid_primary_key :id
447+ attribute :title , :string , allow_nil?: false , public?: true
448+ end
449+ end
450+
393451 setup do
394452 capture_log ( fn ->
395453 Ash.DataLayer.Mnesia . start ( Domain , [ MnesiaPost ] )
@@ -1441,4 +1499,40 @@ defmodule Ash.Test.Actions.BulkDestroyTest do
14411499 assert Exception . message ( error ) =~ "status cannot be bad"
14421500 end
14431501 end
1502+
1503+ describe "read_action option" do
1504+ test "bulk_destroy respects read_action option when query is not pre-validated" do
1505+ StrictPost
1506+ |> Ash.Changeset . for_create ( :create , % { title: "test" } )
1507+ |> Ash . create! ( authorize?: false )
1508+
1509+ # Build a query without __validated_for_action__ (as AshGraphql does via do_filter)
1510+ query = Ash.Query . do_filter ( StrictPost , % { title: "test" } )
1511+
1512+ # Primary :read is forbidden under strict access type,
1513+ # but :read_for_destroy is authorized
1514+ assert % Ash.BulkResult { status: :success } =
1515+ Ash . bulk_destroy ( query , :destroy_with_read_action , % { } ,
1516+ read_action: :read_for_destroy ,
1517+ authorize?: true ,
1518+ return_errors?: true
1519+ )
1520+ end
1521+
1522+ test "bulk_destroy fails when read_action is not specified and primary read is forbidden" do
1523+ StrictPost
1524+ |> Ash.Changeset . for_create ( :create , % { title: "test" } )
1525+ |> Ash . create! ( authorize?: false )
1526+
1527+ query = Ash.Query . do_filter ( StrictPost , % { title: "test" } )
1528+
1529+ # Without read_action, bulk_destroy should use the primary :read action
1530+ # which is forbidden under strict access type
1531+ assert % Ash.BulkResult { status: :error } =
1532+ Ash . bulk_destroy ( query , :destroy_with_read_action , % { } ,
1533+ authorize?: true ,
1534+ return_errors?: true
1535+ )
1536+ end
1537+ end
14441538end
0 commit comments