From 8f9a560e04b29bddafc4a8e1941963453f4aa74b Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Sat, 24 Oct 2026 16:04:14 -0600 Subject: [PATCH 01/47] test: Add test template to migration_generator_test.exs for moving polymorphic delete, update, and name configurations. --- test/migration_generator_test.exs | 84 +++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index e24edbfc..e3f07d6d 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -5576,6 +5576,90 @@ defmodule AshPostgres.MigrationGeneratorTest do assert File.read!(file) =~ ~S[references(:post_comments, column: :id, name: "posts_best_comment_id_fkey", type: :uuid, prefix: "public")] end + + test "Legacy polymorphic_on_delete generates the expected foreign-key delete behavior." do + assert(true) + end + + test "Legacy polymorphic_on_delete emits a deprecation warning." do + assert(true) + end + + test "Multiple legacy polymorphic_on_delete configurations result in rejection." do + assert(true) + end + + test "on_delete within relationship_context generates the expected foreign-key delete behavior." do + assert(true) + end + + test "Using both legacy and new configuration for configuring polymorphic_on_delete results in rejection." do + assert(true) + end + + test "Multiple on_delete configurations within relationship_context result in rejection." do + assert(true) + end + + test "Omitting both polymorphic_on_delete and on_delete in relationship_context retains default behavior." do + assert(true) + end + + test "Legacy polymorphic_on_update generates the expected foreign-key update behavior." do + assert(true) + end + + test "Legacy polymorphic_on_update emits a deprecation warning." do + assert(true) + end + + test "Multiple legacy polymorphic_on_update configurations result in rejection." do + assert(true) + end + + test "on_update within relationship_context generates the expected foreign-key update behavior." do + assert(true) + end + + test "Using both legacy and new configuration for configuring polymorphic_on_update results in rejection." do + assert(true) + end + + test "Multiple on_update configurations within relationship_context result in rejection." do + assert(true) + end + + test "Omitting both polymorphic_on_update and on_update in relationship_context retains default behavior." do + assert(true) + end + + test "Legacy polymorphic_name uses the configured name for generated polymorphic foreign-key constraints." do + assert(true) + end + + test "Legacy polymorphic_name emits a deprecation warning." do + assert(true) + end + + test "Multiple legacy polymorphic_name configurations result in rejection." do + assert(true) + end + + test "name within relationship_context uses the configured name for generated polymorphic foreign-key constraints." do + assert(true) + end + + test "Using both legacy and new configuration for configuring polymorphic_name results in rejection." do + assert(true) + end + + test "Multiple name configurations within relationship_context result in rejection." do + assert(true) + end + + test "Omitting both polymorphic_name and name in relationship_context retains default behavior." do + assert(true) + end end describe "default values" do From 38922399e96f6ddb514f3b4528fc061da7a5e32d Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Sat, 24 Oct 2026 20:08:56 -0600 Subject: [PATCH 02/47] test: create the test, "Legacy polymorphic_on_delete generates the expected foreign-key delete behavior." --- test/migration_generator_test.exs | 80 ++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index e3f07d6d..32da6ce4 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -5577,8 +5577,84 @@ defmodule AshPostgres.MigrationGeneratorTest do ~S[references(:post_comments, column: :id, name: "posts_best_comment_id_fkey", type: :uuid, prefix: "public")] end - test "Legacy polymorphic_on_delete generates the expected foreign-key delete behavior." do - assert(true) + test "Legacy polymorphic_on_delete generates the expected foreign-key delete behavior.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + + defresource LegacyPolymorphicComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource LegacyPolymorphicPost do + postgres do + table "legacy_polymorphic_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_on_delete(:delete) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, LegacyPolymorphicComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{data_layer: %{table: "legacy_post_comments"}} + ) + end + end + + Code.compiler_options(ignore_module_conflict: true) + + defmodule LegacyPolymorphicDomain do + use Ash.Domain + + resources do + resource(LegacyPolymorphicPost) + resource(LegacyPolymorphicComment) + end + end + + Code.compiler_options(ignore_module_conflict: false) + + legacy_snapshot_path = Path.join(snapshot_path, "legacy_polymorphic") + legacy_migration_path = Path.join(migration_path, "legacy_polymorphic") + + AshPostgres.MigrationGenerator.generate(LegacyPolymorphicDomain, + snapshot_path: legacy_snapshot_path, + migration_path: legacy_migration_path, + quiet: true, + format: false, + auto_name: true + ) + + assert [file] = + Path.wildcard("#{legacy_migration_path}/**/*_migrate_resources*.exs") + |> Enum.reject(&String.contains?(&1, "extensions")) + + assert File.read!(file) =~ + ~S[references(:legacy_polymorphic_posts, column: :id, name: "legacy_post_comments_resource_id_fkey", type: :uuid, on_delete: :delete_all)] end test "Legacy polymorphic_on_delete emits a deprecation warning." do From 5dad7b60a5825359726435c94b30022c0452c67c Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:00:44 -0600 Subject: [PATCH 03/47] test: Add legacy polymorphic_on_update test --- test/migration_generator_test.exs | 93 ++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index eac90b59..0eed9823 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -5629,6 +5629,10 @@ defmodule AshPostgres.MigrationGeneratorTest do ~S[references(:post_comments, column: :id, name: "posts_best_comment_id_fkey", type: :uuid, prefix: "public")] end + ############################### + # polymorphic_on_delete tests # + ############################### + test "Legacy polymorphic_on_delete generates the expected foreign-key delete behavior.", %{ snapshot_path: snapshot_path, migration_path: migration_path @@ -5706,7 +5710,7 @@ defmodule AshPostgres.MigrationGeneratorTest do |> Enum.reject(&String.contains?(&1, "extensions")) assert File.read!(file) =~ - ~S[references(:legacy_polymorphic_posts, column: :id, name: "legacy_post_comments_resource_id_fkey", type: :uuid, on_delete: :delete_all)] + ~r/references\(:legacy_polymorphic_posts\b[^)]*on_delete: :delete_all/ end test "Legacy polymorphic_on_delete emits a deprecation warning." do @@ -5733,8 +5737,87 @@ defmodule AshPostgres.MigrationGeneratorTest do assert(true) end - test "Legacy polymorphic_on_update generates the expected foreign-key update behavior." do - assert(true) + ############################### + # polymorphic_on_update tests # + ############################### + + test "Legacy polymorphic_on_update generates the expected foreign-key update behavior.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource LegacyPolymorphicUpdateComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource LegacyPolymorphicUpdatePost do + postgres do + table "legacy_polymorphic_update_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_on_update(:update) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, LegacyPolymorphicUpdateComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{data_layer: %{table: "legacy_update_post_comments"}} + ) + end + end + + Code.compiler_options(ignore_module_conflict: true) + + defmodule LegacyPolymorphicUpdateDomain do + use Ash.Domain + + resources do + resource(LegacyPolymorphicUpdatePost) + resource(LegacyPolymorphicUpdateComment) + end + end + + Code.compiler_options(ignore_module_conflict: false) + + legacy_snapshot_path = Path.join(snapshot_path, "legacy_polymorphic_update") + legacy_migration_path = Path.join(migration_path, "legacy_polymorphic_update") + + AshPostgres.MigrationGenerator.generate(LegacyPolymorphicUpdateDomain, + snapshot_path: legacy_snapshot_path, + migration_path: legacy_migration_path, + quiet: true, + format: false, + auto_name: true + ) + + assert [file] = + Path.wildcard("#{legacy_migration_path}/**/*_migrate_resources*.exs") + |> Enum.reject(&String.contains?(&1, "extensions")) + + assert File.read!(file) =~ + ~r/references\(:legacy_polymorphic_update_posts\b[^)]*on_update: :update_all/ end test "Legacy polymorphic_on_update emits a deprecation warning." do @@ -5761,6 +5844,10 @@ defmodule AshPostgres.MigrationGeneratorTest do assert(true) end + ########################## + # polymorphic_name tests # + ########################## + test "Legacy polymorphic_name uses the configured name for generated polymorphic foreign-key constraints." do assert(true) end From c8aa1a5538c6df7962358fdb4824be2751267aa3 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:34:28 -0600 Subject: [PATCH 04/47] test: add legacy polymorphic_name test. --- test/migration_generator_test.exs | 79 ++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 0eed9823..be267688 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -5848,8 +5848,83 @@ defmodule AshPostgres.MigrationGeneratorTest do # polymorphic_name tests # ########################## - test "Legacy polymorphic_name uses the configured name for generated polymorphic foreign-key constraints." do - assert(true) + test "Legacy polymorphic_name uses the configured name for generated polymorphic foreign-key constraints.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource LegacyPolymorphicNameComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource LegacyPolymorphicNamePost do + postgres do + table "legacy_polymorphic_name_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_name("custom_polymorphic_name_fkey") + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, LegacyPolymorphicNameComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{data_layer: %{table: "legacy_polymorphic_name_comments"}} + ) + end + end + + Code.compiler_options(ignore_module_conflict: true) + + defmodule LegacyPolymorphicNameDomain do + use Ash.Domain + + resources do + resource(LegacyPolymorphicNamePost) + resource(LegacyPolymorphicNameComment) + end + end + + Code.compiler_options(ignore_module_conflict: false) + + legacy_snapshot_path = Path.join(snapshot_path, "legacy_polymorphic_name") + legacy_migration_path = Path.join(migration_path, "legacy_polymorphic_name") + + AshPostgres.MigrationGenerator.generate(LegacyPolymorphicNameDomain, + snapshot_path: legacy_snapshot_path, + migration_path: legacy_migration_path, + quiet: true, + format: false, + auto_name: true + ) + + assert [file] = + Path.wildcard("#{legacy_migration_path}/**/*_migrate_resources*.exs") + |> Enum.reject(&String.contains?(&1, "extensions")) + + assert File.read!(file) =~ + ~r/references\(:legacy_polymorphic_name_posts\b[^)]*name: "custom_polymorphic_name_fkey"/ end test "Legacy polymorphic_name emits a deprecation warning." do From a4248a012d5519925db0f8b1469c80cae2e53401 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:50:16 -0600 Subject: [PATCH 05/47] fix: Make polymorphic_name usable in the references macro. --- lib/data_layer.ex | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/data_layer.ex b/lib/data_layer.ex index 0c49b85c..32048d21 100644 --- a/lib/data_layer.ex +++ b/lib/data_layer.ex @@ -267,6 +267,11 @@ defmodule AshPostgres.DataLayer do type: {:one_of, [:update, :nilify, :nothing, :restrict]}, doc: "For polymorphic resources, configures the on_update behavior of the automatically generated foreign keys to source tables." + ], + polymorphic_name: [ + type: :string, + doc: + "For polymorphic resources, configures the name of the generated foreign-key constraint to the source table." ] ] } From 1f3e53b946cfb0fdf32c3b85349d780a2cf702ff Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Tue, 27 Oct 2026 16:45:17 -0600 Subject: [PATCH 06/47] test: Add polymorphic depreciation warning tests --- test/migration_generator_test.exs | 60 +++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index be267688..05757648 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -5714,7 +5714,25 @@ defmodule AshPostgres.MigrationGeneratorTest do end test "Legacy polymorphic_on_delete emits a deprecation warning." do - assert(true) + warning = + ExUnit.CaptureIO.capture_io(:stderr, fn -> + defresource DeprecatedPolymorphicDeletePost do + postgres do + table "deprecated_polymorphic_delete_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_on_delete(:delete) + end + end + + attributes do + uuid_primary_key(:id) + end + end + end) + + assert warning =~ ~r/polymorphic_on_delete[^\n]*deprecated/i end test "Multiple legacy polymorphic_on_delete configurations result in rejection." do @@ -5821,7 +5839,25 @@ defmodule AshPostgres.MigrationGeneratorTest do end test "Legacy polymorphic_on_update emits a deprecation warning." do - assert(true) + warning = + ExUnit.CaptureIO.capture_io(:stderr, fn -> + defresource DeprecatedPolymorphicUpdatePost do + postgres do + table "deprecated_polymorphic_update_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_on_update(:update) + end + end + + attributes do + uuid_primary_key(:id) + end + end + end) + + assert warning =~ ~r/polymorphic_on_update[^\n]*deprecated/i end test "Multiple legacy polymorphic_on_update configurations result in rejection." do @@ -5928,7 +5964,25 @@ defmodule AshPostgres.MigrationGeneratorTest do end test "Legacy polymorphic_name emits a deprecation warning." do - assert(true) + warning = + ExUnit.CaptureIO.capture_io(:stderr, fn -> + defresource DeprecatedPolymorphicNamePost do + postgres do + table "deprecated_polymorphic_name_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_name("deprecated_polymorphic_name_fkey") + end + end + + attributes do + uuid_primary_key(:id) + end + end + end) + + assert warning =~ ~r/polymorphic_name[^\n]*deprecated/i end test "Multiple legacy polymorphic_name configurations result in rejection." do From 3a86214835446ce77f42eb3aa17b820a79ab25bd Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Tue, 27 Oct 2026 17:15:34 -0600 Subject: [PATCH 07/47] enhansment: Add depreciation warnings to polumorphic commands in references. --- lib/data_layer.ex | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/data_layer.ex b/lib/data_layer.ex index 32048d21..796c09bd 100644 --- a/lib/data_layer.ex +++ b/lib/data_layer.ex @@ -238,6 +238,14 @@ defmodule AshPostgres.DataLayer do @references %Spark.Dsl.Section{ name: :references, + deprecations: [ + polymorphic_on_delete: + "Use `relationship_context: %{data_layer: %{references: %{on_delete: ...}}}` on the relationship instead.", + polymorphic_on_update: + "Use `relationship_context: %{data_layer: %{references: %{on_update: ...}}}` on the relationship instead.", + polymorphic_name: + "Use `relationship_context: %{data_layer: %{references: %{name: ...}}}` on the relationship instead." + ], describe: """ A section for configuring the references (foreign keys) in resource migrations. From 8e9fc86a26162b3ce081659b40581b52b773a025 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Tue, 27 Oct 2026 17:29:26 -0600 Subject: [PATCH 08/47] test: Update depreciation tests to check for replacement description. --- test/migration_generator_test.exs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 05757648..0a1fd5f2 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -5733,6 +5733,8 @@ defmodule AshPostgres.MigrationGeneratorTest do end) assert warning =~ ~r/polymorphic_on_delete[^\n]*deprecated/i + assert warning =~ + "Use `relationship_context: %{data_layer: %{references: %{on_delete: ...}}}` on the relationship instead." end test "Multiple legacy polymorphic_on_delete configurations result in rejection." do @@ -5858,6 +5860,8 @@ defmodule AshPostgres.MigrationGeneratorTest do end) assert warning =~ ~r/polymorphic_on_update[^\n]*deprecated/i + assert warning =~ + "Use `relationship_context: %{data_layer: %{references: %{on_update: ...}}}` on the relationship instead." end test "Multiple legacy polymorphic_on_update configurations result in rejection." do @@ -5983,6 +5987,8 @@ defmodule AshPostgres.MigrationGeneratorTest do end) assert warning =~ ~r/polymorphic_name[^\n]*deprecated/i + assert warning =~ + "Use `relationship_context: %{data_layer: %{references: %{name: ...}}}` on the relationship instead." end test "Multiple legacy polymorphic_name configurations result in rejection." do From 2d08e420c4cbe53b4946a123081f3c45e8dd30ee Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:08:39 -0600 Subject: [PATCH 09/47] test: Add tests for rejecting multiple legacy configurations to the same table. --- test/migration_generator_test.exs | 297 +++++++++++++++++++++++++++++- 1 file changed, 291 insertions(+), 6 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 0a1fd5f2..43c81904 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -5737,8 +5737,103 @@ defmodule AshPostgres.MigrationGeneratorTest do "Use `relationship_context: %{data_layer: %{references: %{on_delete: ...}}}` on the relationship instead." end - test "Multiple legacy polymorphic_on_delete configurations result in rejection." do - assert(true) + test "Multiple legacy polymorphic_on_delete configurations result in rejection.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource MultipleLegacyPolymorphicDeleteComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource MultipleLegacyPolymorphicDeletePost do + postgres do + table "multiple_legacy_polymorphic_delete_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_on_delete(:delete) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, MultipleLegacyPolymorphicDeleteComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: "multiple_legacy_polymorphic_delete_comments"} + } + ) + end + end + + defresource MultipleLegacyPolymorphicNilifyPost do + postgres do + table "multiple_legacy_polymorphic_delete_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_on_delete(:nilify) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, MultipleLegacyPolymorphicDeleteComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: "multiple_legacy_polymorphic_delete_comments"} + } + ) + end + end + + defmodule MultipleLegacyPolymorphicDeleteDomain do + use Ash.Domain + + resources do + resource(MultipleLegacyPolymorphicDeletePost) + resource(MultipleLegacyPolymorphicNilifyPost) + resource(MultipleLegacyPolymorphicDeleteComment) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + AshPostgres.MigrationGenerator.generate(MultipleLegacyPolymorphicDeleteDomain, + snapshot_path: Path.join(snapshot_path, "multiple_legacy_polymorphic_delete"), + migration_path: Path.join(migration_path, "multiple_legacy_polymorphic_delete"), + quiet: true, + format: false, + auto_name: true + ) + end end test "on_delete within relationship_context generates the expected foreign-key delete behavior." do @@ -5864,8 +5959,103 @@ defmodule AshPostgres.MigrationGeneratorTest do "Use `relationship_context: %{data_layer: %{references: %{on_update: ...}}}` on the relationship instead." end - test "Multiple legacy polymorphic_on_update configurations result in rejection." do - assert(true) + test "Multiple legacy polymorphic_on_update configurations result in rejection.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource MultipleLegacyPolymorphicUpdateComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource MultipleLegacyPolymorphicUpdatePost do + postgres do + table "multiple_legacy_polymorphic_update_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_on_update(:update) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, MultipleLegacyPolymorphicUpdateComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: "multiple_legacy_polymorphic_update_comments"} + } + ) + end + end + + defresource MultipleLegacyPolymorphicRestrictUpdatePost do + postgres do + table "multiple_legacy_polymorphic_update_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_on_update(:restrict) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, MultipleLegacyPolymorphicUpdateComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: "multiple_legacy_polymorphic_update_comments"} + } + ) + end + end + + defmodule MultipleLegacyPolymorphicUpdateDomain do + use Ash.Domain + + resources do + resource(MultipleLegacyPolymorphicUpdatePost) + resource(MultipleLegacyPolymorphicRestrictUpdatePost) + resource(MultipleLegacyPolymorphicUpdateComment) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + AshPostgres.MigrationGenerator.generate(MultipleLegacyPolymorphicUpdateDomain, + snapshot_path: Path.join(snapshot_path, "multiple_legacy_polymorphic_update"), + migration_path: Path.join(migration_path, "multiple_legacy_polymorphic_update"), + quiet: true, + format: false, + auto_name: true + ) + end end test "on_update within relationship_context generates the expected foreign-key update behavior." do @@ -5991,8 +6181,103 @@ defmodule AshPostgres.MigrationGeneratorTest do "Use `relationship_context: %{data_layer: %{references: %{name: ...}}}` on the relationship instead." end - test "Multiple legacy polymorphic_name configurations result in rejection." do - assert(true) + test "Multiple legacy polymorphic_name configurations result in rejection.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource MultipleLegacyPolymorphicNameComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource MultipleLegacyPolymorphicNamePost do + postgres do + table "multiple_legacy_polymorphic_name_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_name("multiple_legacy_polymorphic_name_delete_fkey") + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, MultipleLegacyPolymorphicNameComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: "multiple_legacy_polymorphic_name_comments"} + } + ) + end + end + + defresource MultipleLegacyPolymorphicCustomNamePost do + postgres do + table "multiple_legacy_polymorphic_name_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_name("multiple_legacy_polymorphic_name_update_fkey") + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, MultipleLegacyPolymorphicNameComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: "multiple_legacy_polymorphic_name_comments"} + } + ) + end + end + + defmodule MultipleLegacyPolymorphicNameDomain do + use Ash.Domain + + resources do + resource(MultipleLegacyPolymorphicNamePost) + resource(MultipleLegacyPolymorphicCustomNamePost) + resource(MultipleLegacyPolymorphicNameComment) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + AshPostgres.MigrationGenerator.generate(MultipleLegacyPolymorphicNameDomain, + snapshot_path: Path.join(snapshot_path, "multiple_legacy_polymorphic_name"), + migration_path: Path.join(migration_path, "multiple_legacy_polymorphic_name"), + quiet: true, + format: false, + auto_name: true + ) + end end test "name within relationship_context uses the configured name for generated polymorphic foreign-key constraints." do From 35af5eeb7a6c9163eedc6763e041fa88316465e3 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:25:33 -0600 Subject: [PATCH 10/47] test: on_delete within relationship_context generates the expected foreign-key delete behavior. --- test/migration_generator_test.exs | 75 ++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 43c81904..2ee638bd 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -5836,8 +5836,79 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - test "on_delete within relationship_context generates the expected foreign-key delete behavior." do - assert(true) + test "on_delete within relationship_context generates the expected foreign-key delete behavior.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource RelationshipContextPolymorphicDeleteComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource RelationshipContextPolymorphicDeletePost do + postgres do + table "relationship_context_polymorphic_delete_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, RelationshipContextPolymorphicDeleteComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "relationship_context_polymorphic_delete_comments", + references: %{on_delete: :delete} + } + } + ) + end + end + + defmodule RelationshipContextPolymorphicDeleteDomain do + use Ash.Domain + + resources do + resource(RelationshipContextPolymorphicDeletePost) + resource(RelationshipContextPolymorphicDeleteComment) + end + end + + AshPostgres.MigrationGenerator.generate(RelationshipContextPolymorphicDeleteDomain, + snapshot_path: Path.join(snapshot_path, "relationship_context_polymorphic_delete"), + migration_path: Path.join(migration_path, "relationship_context_polymorphic_delete"), + quiet: true, + format: false, + auto_name: true + ) + + assert [file] = + Path.wildcard( + "#{migration_path}/relationship_context_polymorphic_delete/**/*_migrate_resources*.exs" + ) + |> Enum.reject(&String.contains?(&1, "extensions")) + + assert File.read!(file) =~ + ~r/references\(:relationship_context_polymorphic_delete_posts\b[^)]*on_delete: :delete_all/ end test "Using both legacy and new configuration for configuring polymorphic_on_delete results in rejection." do From b5c47d034774dd14df4df2e135f042885f8201cf Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:43:05 -0600 Subject: [PATCH 11/47] Enhancement: Update polymorpic_on_delete to accomodate both old and new methods. --- lib/migration_generator/migration_generator.ex | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/migration_generator/migration_generator.ex b/lib/migration_generator/migration_generator.ex index 861727fd..0437b24f 100644 --- a/lib/migration_generator/migration_generator.ex +++ b/lib/migration_generator/migration_generator.ex @@ -3837,7 +3837,7 @@ defmodule AshPostgres.MigrationGenerator do multitenancy: multitenancy(relationship.source), table: AshPostgres.DataLayer.Info.table(relationship.source), schema: AshPostgres.DataLayer.Info.schema(relationship.source), - on_delete: AshPostgres.DataLayer.Info.polymorphic_on_delete(relationship.source), + on_delete: polymorphic_on_delete(relationship), on_update: AshPostgres.DataLayer.Info.polymorphic_on_update(relationship.source), primary_key?: source_attribute.primary_key?, name: @@ -3855,6 +3855,11 @@ defmodule AshPostgres.MigrationGenerator do end end + defp polymorphic_on_delete(relationship) do + get_in(relationship.context, [:data_layer, :references, :on_delete]) || + AshPostgres.DataLayer.Info.polymorphic_on_delete(relationship.source) + end + defp do_snapshot(resource, table, schema \\ nil) do %{ attributes: attributes(resource, table), From 781a5dd8499bbc60bfb49e6a0beba9256e2aa406 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:51:17 -0600 Subject: [PATCH 12/47] Enhancement: Implement polymorphic_on_delete and polymorphic_name for both old and new versions. --- lib/migration_generator/migration_generator.ex | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/migration_generator/migration_generator.ex b/lib/migration_generator/migration_generator.ex index 0437b24f..e9e79379 100644 --- a/lib/migration_generator/migration_generator.ex +++ b/lib/migration_generator/migration_generator.ex @@ -3838,11 +3838,9 @@ defmodule AshPostgres.MigrationGenerator do table: AshPostgres.DataLayer.Info.table(relationship.source), schema: AshPostgres.DataLayer.Info.schema(relationship.source), on_delete: polymorphic_on_delete(relationship), - on_update: AshPostgres.DataLayer.Info.polymorphic_on_update(relationship.source), + on_update: polymorphic_on_update(relationship), primary_key?: source_attribute.primary_key?, - name: - AshPostgres.DataLayer.Info.polymorphic_name(relationship.source) || - "#{relationship.context[:data_layer][:table]}_#{destination_attribute_source}_fkey" + name: polymorphic_name(relationship, destination_attribute_source) }) else attribute @@ -3860,6 +3858,17 @@ defmodule AshPostgres.MigrationGenerator do AshPostgres.DataLayer.Info.polymorphic_on_delete(relationship.source) end + defp polymorphic_on_update(relationship) do + get_in(relationship.context, [:data_layer, :references, :on_update]) || + AshPostgres.DataLayer.Info.polymorphic_on_update(relationship.source) + end + + defp polymorphic_name(relationship, destination_attribute_source) do + get_in(relationship.context, [:data_layer, :references, :name]) || + AshPostgres.DataLayer.Info.polymorphic_name(relationship.source) || + "#{relationship.context[:data_layer][:table]}_#{destination_attribute_source}_fkey" + end + defp do_snapshot(resource, table, schema \\ nil) do %{ attributes: attributes(resource, table), From f2f8fe6a9f2fab28ba0510bff059fcb126bbb6fe Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:55:39 -0600 Subject: [PATCH 13/47] test: Tests for new 'on_update' and 'name' options in the relationship context. --- test/migration_generator_test.exs | 152 +++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 4 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 2ee638bd..3cc68814 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -6129,8 +6129,80 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - test "on_update within relationship_context generates the expected foreign-key update behavior." do - assert(true) + test "on_update within relationship_context generates the expected foreign-key update behavior.", + %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource RelationshipContextPolymorphicUpdateComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource RelationshipContextPolymorphicUpdatePost do + postgres do + table "relationship_context_polymorphic_update_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, RelationshipContextPolymorphicUpdateComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "relationship_context_polymorphic_update_comments", + references: %{on_update: :update} + } + } + ) + end + end + + defmodule RelationshipContextPolymorphicUpdateDomain do + use Ash.Domain + + resources do + resource(RelationshipContextPolymorphicUpdatePost) + resource(RelationshipContextPolymorphicUpdateComment) + end + end + + AshPostgres.MigrationGenerator.generate(RelationshipContextPolymorphicUpdateDomain, + snapshot_path: Path.join(snapshot_path, "relationship_context_polymorphic_update"), + migration_path: Path.join(migration_path, "relationship_context_polymorphic_update"), + quiet: true, + format: false, + auto_name: true + ) + + assert [file] = + Path.wildcard( + "#{migration_path}/relationship_context_polymorphic_update/**/*_migrate_resources*.exs" + ) + |> Enum.reject(&String.contains?(&1, "extensions")) + + assert File.read!(file) =~ + ~r/references\(:relationship_context_polymorphic_update_posts\b[^)]*on_update: :update_all/ end test "Using both legacy and new configuration for configuring polymorphic_on_update results in rejection." do @@ -6351,8 +6423,80 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - test "name within relationship_context uses the configured name for generated polymorphic foreign-key constraints." do - assert(true) + test "name within relationship_context uses the configured name for generated polymorphic foreign-key constraints.", + %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource RelationshipContextPolymorphicNameComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource RelationshipContextPolymorphicNamePost do + postgres do + table "relationship_context_polymorphic_name_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, RelationshipContextPolymorphicNameComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "relationship_context_polymorphic_name_comments", + references: %{name: "relationship_context_polymorphic_name_fkey"} + } + } + ) + end + end + + defmodule RelationshipContextPolymorphicNameDomain do + use Ash.Domain + + resources do + resource(RelationshipContextPolymorphicNamePost) + resource(RelationshipContextPolymorphicNameComment) + end + end + + AshPostgres.MigrationGenerator.generate(RelationshipContextPolymorphicNameDomain, + snapshot_path: Path.join(snapshot_path, "relationship_context_polymorphic_name"), + migration_path: Path.join(migration_path, "relationship_context_polymorphic_name"), + quiet: true, + format: false, + auto_name: true + ) + + assert [file] = + Path.wildcard( + "#{migration_path}/relationship_context_polymorphic_name/**/*_migrate_resources*.exs" + ) + |> Enum.reject(&String.contains?(&1, "extensions")) + + assert File.read!(file) =~ + ~r/references\(:relationship_context_polymorphic_name_posts\b[^)]*name: "relationship_context_polymorphic_name_fkey"/ end test "Using both legacy and new configuration for configuring polymorphic_name results in rejection." do From 61cb541426e752aab047f754781895bf9b1a2b5b Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 29 Oct 2026 15:12:17 -0600 Subject: [PATCH 14/47] test: "Multiple on_delete configurations within relationship_context result in rejection." --- test/migration_generator_test.exs | 97 ++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 3cc68814..44ca01be 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -5915,8 +5915,101 @@ defmodule AshPostgres.MigrationGeneratorTest do assert(true) end - test "Multiple on_delete configurations within relationship_context result in rejection." do - assert(true) + test "Multiple on_delete configurations within relationship_context result in rejection.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource MultipleRelationshipContextPolymorphicDeleteComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource DeleteRelationshipContextPolymorphicDeletePost do + postgres do + table "multiple_relationship_context_polymorphic_delete_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, MultipleRelationshipContextPolymorphicDeleteComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "multiple_relationship_context_polymorphic_delete_comments", + references: %{on_delete: :delete} + } + } + ) + end + end + + defresource NilifyRelationshipContextPolymorphicDeletePost do + postgres do + table "multiple_relationship_context_polymorphic_delete_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, MultipleRelationshipContextPolymorphicDeleteComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "multiple_relationship_context_polymorphic_delete_comments", + references: %{on_delete: :nilify} + } + } + ) + end + end + + defmodule MultipleRelationshipContextPolymorphicDeleteDomain do + use Ash.Domain + + resources do + resource(DeleteRelationshipContextPolymorphicDeletePost) + resource(NilifyRelationshipContextPolymorphicDeletePost) + resource(MultipleRelationshipContextPolymorphicDeleteComment) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + AshPostgres.MigrationGenerator.generate(MultipleRelationshipContextPolymorphicDeleteDomain, + snapshot_path: Path.join(snapshot_path, "multiple_relationship_context_polymorphic_delete"), + migration_path: Path.join(migration_path, "multiple_relationship_context_polymorphic_delete"), + quiet: true, + format: false, + auto_name: true + ) + end end test "Omitting both polymorphic_on_delete and on_delete in relationship_context retains default behavior." do From 8cf4c8a5ad9cda66907f2edd92ea2c5ff3a4d5cb Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 29 Oct 2026 15:52:06 -0600 Subject: [PATCH 15/47] test: "Multiple on_update and multiple name configurations within relationship_context result in rejection." --- test/migration_generator_test.exs | 194 +++++++++++++++++++++++++++++- 1 file changed, 190 insertions(+), 4 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 44ca01be..0134ea1c 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -6302,8 +6302,101 @@ defmodule AshPostgres.MigrationGeneratorTest do assert(true) end - test "Multiple on_update configurations within relationship_context result in rejection." do - assert(true) + test "Multiple on_update configurations within relationship_context result in rejection.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource MultipleRelationshipContextPolymorphicUpdateComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource UpdateRelationshipContextPolymorphicUpdatePost do + postgres do + table "multiple_relationship_context_polymorphic_update_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, MultipleRelationshipContextPolymorphicUpdateComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "multiple_relationship_context_polymorphic_update_comments", + references: %{on_update: :update} + } + } + ) + end + end + + defresource RestrictRelationshipContextPolymorphicUpdatePost do + postgres do + table "multiple_relationship_context_polymorphic_update_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, MultipleRelationshipContextPolymorphicUpdateComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "multiple_relationship_context_polymorphic_update_comments", + references: %{on_update: :restrict} + } + } + ) + end + end + + defmodule MultipleRelationshipContextPolymorphicUpdateDomain do + use Ash.Domain + + resources do + resource(UpdateRelationshipContextPolymorphicUpdatePost) + resource(RestrictRelationshipContextPolymorphicUpdatePost) + resource(MultipleRelationshipContextPolymorphicUpdateComment) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + AshPostgres.MigrationGenerator.generate(MultipleRelationshipContextPolymorphicUpdateDomain, + snapshot_path: Path.join(snapshot_path, "multiple_relationship_context_polymorphic_update"), + migration_path: Path.join(migration_path, "multiple_relationship_context_polymorphic_update"), + quiet: true, + format: false, + auto_name: true + ) + end end test "Omitting both polymorphic_on_update and on_update in relationship_context retains default behavior." do @@ -6596,8 +6689,101 @@ defmodule AshPostgres.MigrationGeneratorTest do assert(true) end - test "Multiple name configurations within relationship_context result in rejection." do - assert(true) + test "Multiple name configurations within relationship_context result in rejection.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource MultipleRelationshipContextPolymorphicNameComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource FirstRelationshipContextPolymorphicNamePost do + postgres do + table "multiple_relationship_context_polymorphic_name_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, MultipleRelationshipContextPolymorphicNameComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "multiple_relationship_context_polymorphic_name_comments", + references: %{name: "first_polymorphic_name_fkey"} + } + } + ) + end + end + + defresource SecondRelationshipContextPolymorphicNamePost do + postgres do + table "multiple_relationship_context_polymorphic_name_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, MultipleRelationshipContextPolymorphicNameComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "multiple_relationship_context_polymorphic_name_comments", + references: %{name: "second_polymorphic_name_fkey"} + } + } + ) + end + end + + defmodule MultipleRelationshipContextPolymorphicNameDomain do + use Ash.Domain + + resources do + resource(FirstRelationshipContextPolymorphicNamePost) + resource(SecondRelationshipContextPolymorphicNamePost) + resource(MultipleRelationshipContextPolymorphicNameComment) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + AshPostgres.MigrationGenerator.generate(MultipleRelationshipContextPolymorphicNameDomain, + snapshot_path: Path.join(snapshot_path, "multiple_relationship_context_polymorphic_name"), + migration_path: Path.join(migration_path, "multiple_relationship_context_polymorphic_name"), + quiet: true, + format: false, + auto_name: true + ) + end end test "Omitting both polymorphic_name and name in relationship_context retains default behavior." do From 8f4e2bf283205b6514da64141fb17b865de5eb7a Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 29 Oct 2026 17:44:08 -0600 Subject: [PATCH 16/47] test: Using both legacy and new configuration for configuring polymorphic_on_delete results in rejection. --- .../migration_generator.ex | 12 ++- test/migration_generator_test.exs | 73 ++++++++++++++++++- 2 files changed, 81 insertions(+), 4 deletions(-) diff --git a/lib/migration_generator/migration_generator.ex b/lib/migration_generator/migration_generator.ex index e9e79379..ae257da4 100644 --- a/lib/migration_generator/migration_generator.ex +++ b/lib/migration_generator/migration_generator.ex @@ -3854,8 +3854,16 @@ defmodule AshPostgres.MigrationGenerator do end defp polymorphic_on_delete(relationship) do - get_in(relationship.context, [:data_layer, :references, :on_delete]) || - AshPostgres.DataLayer.Info.polymorphic_on_delete(relationship.source) + relationship_context_on_delete = + get_in(relationship.context, [:data_layer, :references, :on_delete]) + + legacy_on_delete = AshPostgres.DataLayer.Info.polymorphic_on_delete(relationship.source) + + if relationship_context_on_delete && legacy_on_delete do + raise "Conflicting configurations for references: both relationship_context references on_delete and polymorphic_on_delete are configured" + end + + relationship_context_on_delete || legacy_on_delete end defp polymorphic_on_update(relationship) do diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 0134ea1c..b054a7af 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -5911,8 +5911,77 @@ defmodule AshPostgres.MigrationGeneratorTest do ~r/references\(:relationship_context_polymorphic_delete_posts\b[^)]*on_delete: :delete_all/ end - test "Using both legacy and new configuration for configuring polymorphic_on_delete results in rejection." do - assert(true) + test "Using both legacy and new configuration for configuring polymorphic_on_delete results in rejection.", + %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource LegacyAndNewPolymorphicDeleteComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource LegacyAndNewPolymorphicDeletePost do + postgres do + table "legacy_and_new_polymorphic_delete_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_on_delete(:delete) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, LegacyAndNewPolymorphicDeleteComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "legacy_and_new_polymorphic_delete_comments", + references: %{on_delete: :nilify} + } + } + ) + end + end + + defmodule LegacyAndNewPolymorphicDeleteDomain do + use Ash.Domain + + resources do + resource(LegacyAndNewPolymorphicDeletePost) + resource(LegacyAndNewPolymorphicDeleteComment) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + AshPostgres.MigrationGenerator.generate(LegacyAndNewPolymorphicDeleteDomain, + snapshot_path: Path.join(snapshot_path, "legacy_and_new_polymorphic_delete"), + migration_path: Path.join(migration_path, "legacy_and_new_polymorphic_delete"), + quiet: true, + format: false, + auto_name: true + ) + end end test "Multiple on_delete configurations within relationship_context result in rejection.", %{ From 87525e985cd12ac251bd371dc8c1380baac761e4 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 29 Oct 2026 17:58:12 -0600 Subject: [PATCH 17/47] test: Conflicting polymorphic_on_delete configurations across resources result in rejection. --- test/migration_generator_test.exs | 100 +++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index b054a7af..8fbfc5ba 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -5911,7 +5911,7 @@ defmodule AshPostgres.MigrationGeneratorTest do ~r/references\(:relationship_context_polymorphic_delete_posts\b[^)]*on_delete: :delete_all/ end - test "Using both legacy and new configuration for configuring polymorphic_on_delete results in rejection.", + test "Configuring polymorphic_on_delete through both legacy and relationship-context options on one resource results in rejection.", %{ snapshot_path: snapshot_path, migration_path: migration_path @@ -5984,6 +5984,104 @@ defmodule AshPostgres.MigrationGeneratorTest do end end + test "Conflicting polymorphic_on_delete configurations across resources result in rejection.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource CrossResourcePolymorphicDeleteComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource CrossResourceLegacyPolymorphicDeletePost do + postgres do + table "cross_resource_polymorphic_delete_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_on_delete(:delete) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, CrossResourcePolymorphicDeleteComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: "cross_resource_polymorphic_delete_comments"} + } + ) + end + end + + defresource CrossResourceRelationshipContextPolymorphicDeletePost do + postgres do + table "cross_resource_polymorphic_delete_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, CrossResourcePolymorphicDeleteComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "cross_resource_polymorphic_delete_comments", + references: %{on_delete: :nilify} + } + } + ) + end + end + + defmodule CrossResourcePolymorphicDeleteDomain do + use Ash.Domain + + resources do + resource(CrossResourceLegacyPolymorphicDeletePost) + resource(CrossResourceRelationshipContextPolymorphicDeletePost) + resource(CrossResourcePolymorphicDeleteComment) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + AshPostgres.MigrationGenerator.generate(CrossResourcePolymorphicDeleteDomain, + snapshot_path: Path.join(snapshot_path, "cross_resource_polymorphic_delete"), + migration_path: Path.join(migration_path, "cross_resource_polymorphic_delete"), + quiet: true, + format: false, + auto_name: true + ) + end + end + test "Multiple on_delete configurations within relationship_context result in rejection.", %{ snapshot_path: snapshot_path, migration_path: migration_path From efadc1cecf1a0ed8254486e153f030cc85afb87a Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 29 Oct 2026 18:04:19 -0600 Subject: [PATCH 18/47] test: Refine polymorphic_on_update and polymorphic_name handling to raise errors for conflicting configurations across relationship context and legacy options. --- .../migration_generator.ex | 23 +- test/migration_generator_test.exs | 342 +++++++++++++++++- 2 files changed, 357 insertions(+), 8 deletions(-) diff --git a/lib/migration_generator/migration_generator.ex b/lib/migration_generator/migration_generator.ex index ae257da4..39e8785f 100644 --- a/lib/migration_generator/migration_generator.ex +++ b/lib/migration_generator/migration_generator.ex @@ -3867,13 +3867,28 @@ defmodule AshPostgres.MigrationGenerator do end defp polymorphic_on_update(relationship) do - get_in(relationship.context, [:data_layer, :references, :on_update]) || - AshPostgres.DataLayer.Info.polymorphic_on_update(relationship.source) + relationship_context_on_update = + get_in(relationship.context, [:data_layer, :references, :on_update]) + + legacy_on_update = AshPostgres.DataLayer.Info.polymorphic_on_update(relationship.source) + + if relationship_context_on_update && legacy_on_update do + raise "Conflicting configurations for references: both relationship_context references on_update and polymorphic_on_update are configured" + end + + relationship_context_on_update || legacy_on_update end defp polymorphic_name(relationship, destination_attribute_source) do - get_in(relationship.context, [:data_layer, :references, :name]) || - AshPostgres.DataLayer.Info.polymorphic_name(relationship.source) || + relationship_context_name = get_in(relationship.context, [:data_layer, :references, :name]) + + legacy_name = AshPostgres.DataLayer.Info.polymorphic_name(relationship.source) + + if relationship_context_name && legacy_name do + raise "Conflicting configurations for references: both relationship_context references name and polymorphic_name are configured" + end + + relationship_context_name || legacy_name || "#{relationship.context[:data_layer][:table]}_#{destination_attribute_source}_fkey" end diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 8fbfc5ba..13eb4106 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -6465,8 +6465,175 @@ defmodule AshPostgres.MigrationGeneratorTest do ~r/references\(:relationship_context_polymorphic_update_posts\b[^)]*on_update: :update_all/ end - test "Using both legacy and new configuration for configuring polymorphic_on_update results in rejection." do - assert(true) + test "Configuring polymorphic_on_update through both legacy and relationship-context options on one resource results in rejection.", + %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource LegacyAndNewPolymorphicUpdateComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource LegacyAndNewPolymorphicUpdatePost do + postgres do + table "legacy_and_new_polymorphic_update_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_on_update(:update) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, LegacyAndNewPolymorphicUpdateComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "legacy_and_new_polymorphic_update_comments", + references: %{on_update: :restrict} + } + } + ) + end + end + + defmodule LegacyAndNewPolymorphicUpdateDomain do + use Ash.Domain + + resources do + resource(LegacyAndNewPolymorphicUpdatePost) + resource(LegacyAndNewPolymorphicUpdateComment) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + AshPostgres.MigrationGenerator.generate(LegacyAndNewPolymorphicUpdateDomain, + snapshot_path: Path.join(snapshot_path, "legacy_and_new_polymorphic_update"), + migration_path: Path.join(migration_path, "legacy_and_new_polymorphic_update"), + quiet: true, + format: false, + auto_name: true + ) + end + end + + test "Conflicting polymorphic_on_update configurations across resources result in rejection.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource CrossResourcePolymorphicUpdateComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource CrossResourceLegacyPolymorphicUpdatePost do + postgres do + table "cross_resource_polymorphic_update_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_on_update(:update) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, CrossResourcePolymorphicUpdateComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: "cross_resource_polymorphic_update_comments"} + } + ) + end + end + + defresource CrossResourceRelationshipContextPolymorphicUpdatePost do + postgres do + table "cross_resource_polymorphic_update_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, CrossResourcePolymorphicUpdateComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "cross_resource_polymorphic_update_comments", + references: %{on_update: :restrict} + } + } + ) + end + end + + defmodule CrossResourcePolymorphicUpdateDomain do + use Ash.Domain + + resources do + resource(CrossResourceLegacyPolymorphicUpdatePost) + resource(CrossResourceRelationshipContextPolymorphicUpdatePost) + resource(CrossResourcePolymorphicUpdateComment) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + AshPostgres.MigrationGenerator.generate(CrossResourcePolymorphicUpdateDomain, + snapshot_path: Path.join(snapshot_path, "cross_resource_polymorphic_update"), + migration_path: Path.join(migration_path, "cross_resource_polymorphic_update"), + quiet: true, + format: false, + auto_name: true + ) + end end test "Multiple on_update configurations within relationship_context result in rejection.", %{ @@ -6852,8 +7019,175 @@ defmodule AshPostgres.MigrationGeneratorTest do ~r/references\(:relationship_context_polymorphic_name_posts\b[^)]*name: "relationship_context_polymorphic_name_fkey"/ end - test "Using both legacy and new configuration for configuring polymorphic_name results in rejection." do - assert(true) + test "Configuring polymorphic_name through both legacy and relationship-context options on one resource results in rejection.", + %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource LegacyAndNewPolymorphicNameComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource LegacyAndNewPolymorphicNamePost do + postgres do + table "legacy_and_new_polymorphic_name_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_name("legacy_polymorphic_name_fkey") + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, LegacyAndNewPolymorphicNameComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "legacy_and_new_polymorphic_name_comments", + references: %{name: "relationship_context_polymorphic_name_fkey"} + } + } + ) + end + end + + defmodule LegacyAndNewPolymorphicNameDomain do + use Ash.Domain + + resources do + resource(LegacyAndNewPolymorphicNamePost) + resource(LegacyAndNewPolymorphicNameComment) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + AshPostgres.MigrationGenerator.generate(LegacyAndNewPolymorphicNameDomain, + snapshot_path: Path.join(snapshot_path, "legacy_and_new_polymorphic_name"), + migration_path: Path.join(migration_path, "legacy_and_new_polymorphic_name"), + quiet: true, + format: false, + auto_name: true + ) + end + end + + test "Conflicting polymorphic_name configurations across resources result in rejection.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource CrossResourcePolymorphicNameComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource CrossResourceLegacyPolymorphicNamePost do + postgres do + table "cross_resource_polymorphic_name_posts" + repo(AshPostgres.TestRepo) + + references do + polymorphic_name("legacy_polymorphic_name_fkey") + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, CrossResourcePolymorphicNameComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: "cross_resource_polymorphic_name_comments"} + } + ) + end + end + + defresource CrossResourceRelationshipContextPolymorphicNamePost do + postgres do + table "cross_resource_polymorphic_name_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, CrossResourcePolymorphicNameComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: "cross_resource_polymorphic_name_comments", + references: %{name: "relationship_context_polymorphic_name_fkey"} + } + } + ) + end + end + + defmodule CrossResourcePolymorphicNameDomain do + use Ash.Domain + + resources do + resource(CrossResourceLegacyPolymorphicNamePost) + resource(CrossResourceRelationshipContextPolymorphicNamePost) + resource(CrossResourcePolymorphicNameComment) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + AshPostgres.MigrationGenerator.generate(CrossResourcePolymorphicNameDomain, + snapshot_path: Path.join(snapshot_path, "cross_resource_polymorphic_name"), + migration_path: Path.join(migration_path, "cross_resource_polymorphic_name"), + quiet: true, + format: false, + auto_name: true + ) + end end test "Multiple name configurations within relationship_context result in rejection.", %{ From 80ff16c96f0e99de61626b9097975506d66667cf Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 29 Oct 2026 18:14:24 -0600 Subject: [PATCH 19/47] test: Omitting both polymorphic_on_delete and on_delete in relationship_context retains default behavior. --- test/migration_generator_test.exs | 77 ++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 13eb4106..5242da6a 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -6179,8 +6179,81 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - test "Omitting both polymorphic_on_delete and on_delete in relationship_context retains default behavior." do - assert(true) + test "Omitting both polymorphic_on_delete and on_delete in relationship_context retains default behavior.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource DefaultPolymorphicDeleteComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource DefaultPolymorphicDeletePost do + postgres do + table "default_polymorphic_delete_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, DefaultPolymorphicDeleteComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: "default_polymorphic_delete_comments"} + } + ) + end + end + + defmodule DefaultPolymorphicDeleteDomain do + use Ash.Domain + + resources do + resource(DefaultPolymorphicDeletePost) + resource(DefaultPolymorphicDeleteComment) + end + end + + AshPostgres.MigrationGenerator.generate(DefaultPolymorphicDeleteDomain, + snapshot_path: Path.join(snapshot_path, "default_polymorphic_delete"), + migration_path: Path.join(migration_path, "default_polymorphic_delete"), + quiet: true, + format: false, + auto_name: true + ) + + assert [file] = + Path.wildcard( + "#{migration_path}/default_polymorphic_delete/**/*_migrate_resources*.exs" + ) + |> Enum.reject(&String.contains?(&1, "extensions")) + + migration = File.read!(file) + + assert migration =~ + ~r/references\(:default_polymorphic_delete_posts\b[^)]*name: "default_polymorphic_delete_comments_resource_id_fkey"/ + + refute migration =~ + ~r/references\(:default_polymorphic_delete_posts\b[^)]*on_delete:/ end ############################### From bf304708b0fd7c758caf72e2f670599e9c42d138 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 29 Oct 2026 18:20:10 -0600 Subject: [PATCH 20/47] test: Omitting resource settings retains default behavior. --- test/migration_generator_test.exs | 151 +++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 4 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 5242da6a..46ff8134 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -6806,8 +6806,81 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - test "Omitting both polymorphic_on_update and on_update in relationship_context retains default behavior." do - assert(true) + test "Omitting both polymorphic_on_update and on_update in relationship_context retains default behavior.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource DefaultPolymorphicUpdateComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource DefaultPolymorphicUpdatePost do + postgres do + table "default_polymorphic_update_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, DefaultPolymorphicUpdateComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: "default_polymorphic_update_comments"} + } + ) + end + end + + defmodule DefaultPolymorphicUpdateDomain do + use Ash.Domain + + resources do + resource(DefaultPolymorphicUpdatePost) + resource(DefaultPolymorphicUpdateComment) + end + end + + AshPostgres.MigrationGenerator.generate(DefaultPolymorphicUpdateDomain, + snapshot_path: Path.join(snapshot_path, "default_polymorphic_update"), + migration_path: Path.join(migration_path, "default_polymorphic_update"), + quiet: true, + format: false, + auto_name: true + ) + + assert [file] = + Path.wildcard( + "#{migration_path}/default_polymorphic_update/**/*_migrate_resources*.exs" + ) + |> Enum.reject(&String.contains?(&1, "extensions")) + + migration = File.read!(file) + + assert migration =~ + ~r/references\(:default_polymorphic_update_posts\b[^)]*name: "default_polymorphic_update_comments_resource_id_fkey"/ + + refute migration =~ + ~r/references\(:default_polymorphic_update_posts\b[^)]*on_update:/ end ########################## @@ -7360,8 +7433,78 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - test "Omitting both polymorphic_name and name in relationship_context retains default behavior." do - assert(true) + test "Omitting both polymorphic_name and name in relationship_context retains default behavior.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource DefaultPolymorphicNameComment do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource DefaultPolymorphicNamePost do + postgres do + table "default_polymorphic_name_posts" + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, DefaultPolymorphicNameComment, + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: "default_polymorphic_name_comments"} + } + ) + end + end + + defmodule DefaultPolymorphicNameDomain do + use Ash.Domain + + resources do + resource(DefaultPolymorphicNamePost) + resource(DefaultPolymorphicNameComment) + end + end + + AshPostgres.MigrationGenerator.generate(DefaultPolymorphicNameDomain, + snapshot_path: Path.join(snapshot_path, "default_polymorphic_name"), + migration_path: Path.join(migration_path, "default_polymorphic_name"), + quiet: true, + format: false, + auto_name: true + ) + + assert [file] = + Path.wildcard( + "#{migration_path}/default_polymorphic_name/**/*_migrate_resources*.exs" + ) + |> Enum.reject(&String.contains?(&1, "extensions")) + + migration = File.read!(file) + + assert migration =~ + ~r/references\(:default_polymorphic_name_posts\b[^)]*name: "default_polymorphic_name_comments_resource_id_fkey"/ end end From 63dfb7911d8a4a75bc8abcaecba67278441ca91c Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 29 Oct 2026 18:27:32 -0600 Subject: [PATCH 21/47] test: Update comments for separating test sections. --- test/migration_generator_test.exs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 46ff8134..63c30fc3 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -5629,9 +5629,7 @@ defmodule AshPostgres.MigrationGeneratorTest do ~S[references(:post_comments, column: :id, name: "posts_best_comment_id_fkey", type: :uuid, prefix: "public")] end - ############################### - # polymorphic_on_delete tests # - ############################### + # -- polymorphic_on_delete -- test "Legacy polymorphic_on_delete generates the expected foreign-key delete behavior.", %{ snapshot_path: snapshot_path, @@ -6256,9 +6254,7 @@ defmodule AshPostgres.MigrationGeneratorTest do ~r/references\(:default_polymorphic_delete_posts\b[^)]*on_delete:/ end - ############################### - # polymorphic_on_update tests # - ############################### + # -- polymorphic_on_update -- test "Legacy polymorphic_on_update generates the expected foreign-key update behavior.", %{ snapshot_path: snapshot_path, @@ -6883,9 +6879,7 @@ defmodule AshPostgres.MigrationGeneratorTest do ~r/references\(:default_polymorphic_update_posts\b[^)]*on_update:/ end - ########################## - # polymorphic_name tests # - ########################## + # -- polymorphic_name -- test "Legacy polymorphic_name uses the configured name for generated polymorphic foreign-key constraints.", %{ snapshot_path: snapshot_path, From b7d5c80c8619491a07fea7c77ed383fd7309b94a Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Mon, 2 Nov 2026 19:33:21 -0700 Subject: [PATCH 22/47] test: Extract helper function from new test. --- test/migration_generator_test.exs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 63c30fc3..7a779b65 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -149,6 +149,14 @@ defmodule AshPostgres.MigrationGeneratorTest do ) end + defp read_resource_migration!(migration_path) do + assert [file] = + Path.wildcard("#{migration_path}/**/*_migrate_resources*.exs") + |> Enum.reject(&String.contains?(&1, "extensions")) + + File.read!(file) + end + defp flush_mix_shell do receive do {:mix_shell, _, _} -> flush_mix_shell() @@ -5899,13 +5907,10 @@ defmodule AshPostgres.MigrationGeneratorTest do auto_name: true ) - assert [file] = - Path.wildcard( - "#{migration_path}/relationship_context_polymorphic_delete/**/*_migrate_resources*.exs" - ) - |> Enum.reject(&String.contains?(&1, "extensions")) + current_migration_path = Path.join(migration_path, "relationship_context_polymorphic_delete") + migration = read_resource_migration!(current_migration_path) - assert File.read!(file) =~ + assert migration =~ ~r/references\(:relationship_context_polymorphic_delete_posts\b[^)]*on_delete: :delete_all/ end From 8b29375870296bff2680af932f84e1bf312231d0 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Mon, 2 Nov 2026 19:45:24 -0700 Subject: [PATCH 23/47] test: Create generate_resource_migration helper function. --- test/migration_generator_test.exs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 7a779b65..0594ba08 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -149,6 +149,14 @@ defmodule AshPostgres.MigrationGeneratorTest do ) end + defp generate_resource_migration(domain, snapshot_path, migration_path, name) do + generate_post_migration( + domain, + Path.join(snapshot_path, name), + Path.join(migration_path, name) + ) + end + defp read_resource_migration!(migration_path) do assert [file] = Path.wildcard("#{migration_path}/**/*_migrate_resources*.exs") @@ -5899,12 +5907,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - AshPostgres.MigrationGenerator.generate(RelationshipContextPolymorphicDeleteDomain, - snapshot_path: Path.join(snapshot_path, "relationship_context_polymorphic_delete"), - migration_path: Path.join(migration_path, "relationship_context_polymorphic_delete"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + RelationshipContextPolymorphicDeleteDomain, + snapshot_path, + migration_path, + "relationship_context_polymorphic_delete" ) current_migration_path = Path.join(migration_path, "relationship_context_polymorphic_delete") From 7647b974fe781ccbc1a61fb10f557df3896f158b Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Mon, 2 Nov 2026 20:40:25 -0700 Subject: [PATCH 24/47] test: Apply helpers to the rest of the new tests. --- test/migration_generator_test.exs | 295 ++++++++++++------------------ 1 file changed, 121 insertions(+), 174 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 0594ba08..87d1b303 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -5708,22 +5708,17 @@ defmodule AshPostgres.MigrationGeneratorTest do Code.compiler_options(ignore_module_conflict: false) - legacy_snapshot_path = Path.join(snapshot_path, "legacy_polymorphic") - legacy_migration_path = Path.join(migration_path, "legacy_polymorphic") - - AshPostgres.MigrationGenerator.generate(LegacyPolymorphicDomain, - snapshot_path: legacy_snapshot_path, - migration_path: legacy_migration_path, - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + LegacyPolymorphicDomain, + snapshot_path, + migration_path, + "legacy_polymorphic" ) - assert [file] = - Path.wildcard("#{legacy_migration_path}/**/*_migrate_resources*.exs") - |> Enum.reject(&String.contains?(&1, "extensions")) + current_migration_path = Path.join(migration_path, "legacy_polymorphic") + migration = read_resource_migration!(current_migration_path) - assert File.read!(file) =~ + assert migration =~ ~r/references\(:legacy_polymorphic_posts\b[^)]*on_delete: :delete_all/ end @@ -5840,12 +5835,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - AshPostgres.MigrationGenerator.generate(MultipleLegacyPolymorphicDeleteDomain, - snapshot_path: Path.join(snapshot_path, "multiple_legacy_polymorphic_delete"), - migration_path: Path.join(migration_path, "multiple_legacy_polymorphic_delete"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + MultipleLegacyPolymorphicDeleteDomain, + snapshot_path, + migration_path, + "multiple_legacy_polymorphic_delete" ) end end @@ -5984,12 +5978,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - AshPostgres.MigrationGenerator.generate(LegacyAndNewPolymorphicDeleteDomain, - snapshot_path: Path.join(snapshot_path, "legacy_and_new_polymorphic_delete"), - migration_path: Path.join(migration_path, "legacy_and_new_polymorphic_delete"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + LegacyAndNewPolymorphicDeleteDomain, + snapshot_path, + migration_path, + "legacy_and_new_polymorphic_delete" ) end end @@ -6082,12 +6075,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - AshPostgres.MigrationGenerator.generate(CrossResourcePolymorphicDeleteDomain, - snapshot_path: Path.join(snapshot_path, "cross_resource_polymorphic_delete"), - migration_path: Path.join(migration_path, "cross_resource_polymorphic_delete"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + CrossResourcePolymorphicDeleteDomain, + snapshot_path, + migration_path, + "cross_resource_polymorphic_delete" ) end end @@ -6179,12 +6171,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - AshPostgres.MigrationGenerator.generate(MultipleRelationshipContextPolymorphicDeleteDomain, - snapshot_path: Path.join(snapshot_path, "multiple_relationship_context_polymorphic_delete"), - migration_path: Path.join(migration_path, "multiple_relationship_context_polymorphic_delete"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + MultipleRelationshipContextPolymorphicDeleteDomain, + snapshot_path, + migration_path, + "multiple_relationship_context_polymorphic_delete" ) end end @@ -6243,21 +6234,15 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - AshPostgres.MigrationGenerator.generate(DefaultPolymorphicDeleteDomain, - snapshot_path: Path.join(snapshot_path, "default_polymorphic_delete"), - migration_path: Path.join(migration_path, "default_polymorphic_delete"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + DefaultPolymorphicDeleteDomain, + snapshot_path, + migration_path, + "default_polymorphic_delete" ) - assert [file] = - Path.wildcard( - "#{migration_path}/default_polymorphic_delete/**/*_migrate_resources*.exs" - ) - |> Enum.reject(&String.contains?(&1, "extensions")) - - migration = File.read!(file) + current_migration_path = Path.join(migration_path, "default_polymorphic_delete") + migration = read_resource_migration!(current_migration_path) assert migration =~ ~r/references\(:default_polymorphic_delete_posts\b[^)]*name: "default_polymorphic_delete_comments_resource_id_fkey"/ @@ -6328,22 +6313,17 @@ defmodule AshPostgres.MigrationGeneratorTest do Code.compiler_options(ignore_module_conflict: false) - legacy_snapshot_path = Path.join(snapshot_path, "legacy_polymorphic_update") - legacy_migration_path = Path.join(migration_path, "legacy_polymorphic_update") - - AshPostgres.MigrationGenerator.generate(LegacyPolymorphicUpdateDomain, - snapshot_path: legacy_snapshot_path, - migration_path: legacy_migration_path, - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + LegacyPolymorphicUpdateDomain, + snapshot_path, + migration_path, + "legacy_polymorphic_update" ) - assert [file] = - Path.wildcard("#{legacy_migration_path}/**/*_migrate_resources*.exs") - |> Enum.reject(&String.contains?(&1, "extensions")) + current_migration_path = Path.join(migration_path, "legacy_polymorphic_update") + migration = read_resource_migration!(current_migration_path) - assert File.read!(file) =~ + assert migration =~ ~r/references\(:legacy_polymorphic_update_posts\b[^)]*on_update: :update_all/ end @@ -6460,12 +6440,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - AshPostgres.MigrationGenerator.generate(MultipleLegacyPolymorphicUpdateDomain, - snapshot_path: Path.join(snapshot_path, "multiple_legacy_polymorphic_update"), - migration_path: Path.join(migration_path, "multiple_legacy_polymorphic_update"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + MultipleLegacyPolymorphicUpdateDomain, + snapshot_path, + migration_path, + "multiple_legacy_polymorphic_update" ) end end @@ -6528,21 +6507,17 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - AshPostgres.MigrationGenerator.generate(RelationshipContextPolymorphicUpdateDomain, - snapshot_path: Path.join(snapshot_path, "relationship_context_polymorphic_update"), - migration_path: Path.join(migration_path, "relationship_context_polymorphic_update"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + RelationshipContextPolymorphicUpdateDomain, + snapshot_path, + migration_path, + "relationship_context_polymorphic_update" ) - assert [file] = - Path.wildcard( - "#{migration_path}/relationship_context_polymorphic_update/**/*_migrate_resources*.exs" - ) - |> Enum.reject(&String.contains?(&1, "extensions")) + current_migration_path = Path.join(migration_path, "relationship_context_polymorphic_update") + migration = read_resource_migration!(current_migration_path) - assert File.read!(file) =~ + assert migration =~ ~r/references\(:relationship_context_polymorphic_update_posts\b[^)]*on_update: :update_all/ end @@ -6609,12 +6584,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - AshPostgres.MigrationGenerator.generate(LegacyAndNewPolymorphicUpdateDomain, - snapshot_path: Path.join(snapshot_path, "legacy_and_new_polymorphic_update"), - migration_path: Path.join(migration_path, "legacy_and_new_polymorphic_update"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + LegacyAndNewPolymorphicUpdateDomain, + snapshot_path, + migration_path, + "legacy_and_new_polymorphic_update" ) end end @@ -6707,12 +6681,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - AshPostgres.MigrationGenerator.generate(CrossResourcePolymorphicUpdateDomain, - snapshot_path: Path.join(snapshot_path, "cross_resource_polymorphic_update"), - migration_path: Path.join(migration_path, "cross_resource_polymorphic_update"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + CrossResourcePolymorphicUpdateDomain, + snapshot_path, + migration_path, + "cross_resource_polymorphic_update" ) end end @@ -6804,12 +6777,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - AshPostgres.MigrationGenerator.generate(MultipleRelationshipContextPolymorphicUpdateDomain, - snapshot_path: Path.join(snapshot_path, "multiple_relationship_context_polymorphic_update"), - migration_path: Path.join(migration_path, "multiple_relationship_context_polymorphic_update"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + MultipleRelationshipContextPolymorphicUpdateDomain, + snapshot_path, + migration_path, + "multiple_relationship_context_polymorphic_update" ) end end @@ -6868,21 +6840,15 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - AshPostgres.MigrationGenerator.generate(DefaultPolymorphicUpdateDomain, - snapshot_path: Path.join(snapshot_path, "default_polymorphic_update"), - migration_path: Path.join(migration_path, "default_polymorphic_update"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + DefaultPolymorphicUpdateDomain, + snapshot_path, + migration_path, + "default_polymorphic_update" ) - assert [file] = - Path.wildcard( - "#{migration_path}/default_polymorphic_update/**/*_migrate_resources*.exs" - ) - |> Enum.reject(&String.contains?(&1, "extensions")) - - migration = File.read!(file) + current_migration_path = Path.join(migration_path, "default_polymorphic_update") + migration = read_resource_migration!(current_migration_path) assert migration =~ ~r/references\(:default_polymorphic_update_posts\b[^)]*name: "default_polymorphic_update_comments_resource_id_fkey"/ @@ -6953,22 +6919,17 @@ defmodule AshPostgres.MigrationGeneratorTest do Code.compiler_options(ignore_module_conflict: false) - legacy_snapshot_path = Path.join(snapshot_path, "legacy_polymorphic_name") - legacy_migration_path = Path.join(migration_path, "legacy_polymorphic_name") - - AshPostgres.MigrationGenerator.generate(LegacyPolymorphicNameDomain, - snapshot_path: legacy_snapshot_path, - migration_path: legacy_migration_path, - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + LegacyPolymorphicNameDomain, + snapshot_path, + migration_path, + "legacy_polymorphic_name" ) - assert [file] = - Path.wildcard("#{legacy_migration_path}/**/*_migrate_resources*.exs") - |> Enum.reject(&String.contains?(&1, "extensions")) + current_migration_path = Path.join(migration_path, "legacy_polymorphic_name") + migration = read_resource_migration!(current_migration_path) - assert File.read!(file) =~ + assert migration =~ ~r/references\(:legacy_polymorphic_name_posts\b[^)]*name: "custom_polymorphic_name_fkey"/ end @@ -7085,12 +7046,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - AshPostgres.MigrationGenerator.generate(MultipleLegacyPolymorphicNameDomain, - snapshot_path: Path.join(snapshot_path, "multiple_legacy_polymorphic_name"), - migration_path: Path.join(migration_path, "multiple_legacy_polymorphic_name"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + MultipleLegacyPolymorphicNameDomain, + snapshot_path, + migration_path, + "multiple_legacy_polymorphic_name" ) end end @@ -7153,21 +7113,17 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - AshPostgres.MigrationGenerator.generate(RelationshipContextPolymorphicNameDomain, - snapshot_path: Path.join(snapshot_path, "relationship_context_polymorphic_name"), - migration_path: Path.join(migration_path, "relationship_context_polymorphic_name"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + RelationshipContextPolymorphicNameDomain, + snapshot_path, + migration_path, + "relationship_context_polymorphic_name" ) - assert [file] = - Path.wildcard( - "#{migration_path}/relationship_context_polymorphic_name/**/*_migrate_resources*.exs" - ) - |> Enum.reject(&String.contains?(&1, "extensions")) + current_migration_path = Path.join(migration_path, "relationship_context_polymorphic_name") + migration = read_resource_migration!(current_migration_path) - assert File.read!(file) =~ + assert migration =~ ~r/references\(:relationship_context_polymorphic_name_posts\b[^)]*name: "relationship_context_polymorphic_name_fkey"/ end @@ -7234,12 +7190,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - AshPostgres.MigrationGenerator.generate(LegacyAndNewPolymorphicNameDomain, - snapshot_path: Path.join(snapshot_path, "legacy_and_new_polymorphic_name"), - migration_path: Path.join(migration_path, "legacy_and_new_polymorphic_name"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + LegacyAndNewPolymorphicNameDomain, + snapshot_path, + migration_path, + "legacy_and_new_polymorphic_name" ) end end @@ -7332,12 +7287,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - AshPostgres.MigrationGenerator.generate(CrossResourcePolymorphicNameDomain, - snapshot_path: Path.join(snapshot_path, "cross_resource_polymorphic_name"), - migration_path: Path.join(migration_path, "cross_resource_polymorphic_name"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + CrossResourcePolymorphicNameDomain, + snapshot_path, + migration_path, + "cross_resource_polymorphic_name" ) end end @@ -7429,12 +7383,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - AshPostgres.MigrationGenerator.generate(MultipleRelationshipContextPolymorphicNameDomain, - snapshot_path: Path.join(snapshot_path, "multiple_relationship_context_polymorphic_name"), - migration_path: Path.join(migration_path, "multiple_relationship_context_polymorphic_name"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + MultipleRelationshipContextPolymorphicNameDomain, + snapshot_path, + migration_path, + "multiple_relationship_context_polymorphic_name" ) end end @@ -7493,21 +7446,15 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - AshPostgres.MigrationGenerator.generate(DefaultPolymorphicNameDomain, - snapshot_path: Path.join(snapshot_path, "default_polymorphic_name"), - migration_path: Path.join(migration_path, "default_polymorphic_name"), - quiet: true, - format: false, - auto_name: true + generate_resource_migration( + DefaultPolymorphicNameDomain, + snapshot_path, + migration_path, + "default_polymorphic_name" ) - assert [file] = - Path.wildcard( - "#{migration_path}/default_polymorphic_name/**/*_migrate_resources*.exs" - ) - |> Enum.reject(&String.contains?(&1, "extensions")) - - migration = File.read!(file) + current_migration_path = Path.join(migration_path, "default_polymorphic_name") + migration = read_resource_migration!(current_migration_path) assert migration =~ ~r/references\(:default_polymorphic_name_posts\b[^)]*name: "default_polymorphic_name_comments_resource_id_fkey"/ From 339c3fcb590cdd5c72f766313a21c7dfe52f02cf Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Mon, 2 Nov 2026 21:27:42 -0700 Subject: [PATCH 25/47] test: Macro for test set 1. --- test/migration_generator_test.exs | 266 +++++++++++------------------- 1 file changed, 97 insertions(+), 169 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 87d1b303..ef8cf01b 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -132,6 +132,79 @@ defmodule AshPostgres.MigrationGeneratorTest do end end + defmacrop deflegacy_polymorphic_resources( + module_suffix, + post_table, + relationship_table, + reference_option, + reference_value + ) do + module_suffix = Macro.expand(module_suffix, __CALLER__) + post_table = Macro.expand(post_table, __CALLER__) + relationship_table = Macro.expand(relationship_table, __CALLER__) + reference_option = Macro.expand(reference_option, __CALLER__) + reference_value = Macro.expand(reference_value, __CALLER__) + + comment = Module.concat([:Elixir, "LegacyPolymorphic#{module_suffix}Comment"]) + post = Module.concat([:Elixir, "LegacyPolymorphic#{module_suffix}Post"]) + domain = Module.concat([:Elixir, "LegacyPolymorphic#{module_suffix}Domain"]) + reference = {reference_option, [], [reference_value]} + + quote do + defresource unquote(comment) do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource unquote(post) do + postgres do + table unquote(post_table) + repo(AshPostgres.TestRepo) + + references do + unquote(reference) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, unquote(comment), + public?: true, + destination_attribute: :resource_id, + relationship_context: %{data_layer: %{table: unquote(relationship_table)}} + ) + end + end + + defmodule unquote(domain) do + use Ash.Domain + + resources do + resource(unquote(post)) + resource(unquote(comment)) + end + end + end + end + defp position_of_substring(string, substring) do case :binary.match(string, substring) do {pos, _len} -> pos @@ -5651,65 +5724,16 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - - defresource LegacyPolymorphicComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource LegacyPolymorphicPost do - postgres do - table "legacy_polymorphic_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_on_delete(:delete) - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, LegacyPolymorphicComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{data_layer: %{table: "legacy_post_comments"}} - ) - end - end - - Code.compiler_options(ignore_module_conflict: true) - - defmodule LegacyPolymorphicDomain do - use Ash.Domain - - resources do - resource(LegacyPolymorphicPost) - resource(LegacyPolymorphicComment) - end - end - - Code.compiler_options(ignore_module_conflict: false) + deflegacy_polymorphic_resources( + "Delete", + "legacy_polymorphic_posts", + "legacy_post_comments", + :polymorphic_on_delete, + :delete + ) generate_resource_migration( - LegacyPolymorphicDomain, + Elixir.LegacyPolymorphicDeleteDomain, snapshot_path, migration_path, "legacy_polymorphic" @@ -6257,64 +6281,16 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource LegacyPolymorphicUpdateComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource LegacyPolymorphicUpdatePost do - postgres do - table "legacy_polymorphic_update_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_on_update(:update) - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, LegacyPolymorphicUpdateComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{data_layer: %{table: "legacy_update_post_comments"}} - ) - end - end - - Code.compiler_options(ignore_module_conflict: true) - - defmodule LegacyPolymorphicUpdateDomain do - use Ash.Domain - - resources do - resource(LegacyPolymorphicUpdatePost) - resource(LegacyPolymorphicUpdateComment) - end - end - - Code.compiler_options(ignore_module_conflict: false) + deflegacy_polymorphic_resources( + "Update", + "legacy_polymorphic_update_posts", + "legacy_update_post_comments", + :polymorphic_on_update, + :update + ) generate_resource_migration( - LegacyPolymorphicUpdateDomain, + Elixir.LegacyPolymorphicUpdateDomain, snapshot_path, migration_path, "legacy_polymorphic_update" @@ -6863,64 +6839,16 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource LegacyPolymorphicNameComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource LegacyPolymorphicNamePost do - postgres do - table "legacy_polymorphic_name_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_name("custom_polymorphic_name_fkey") - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, LegacyPolymorphicNameComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{data_layer: %{table: "legacy_polymorphic_name_comments"}} - ) - end - end - - Code.compiler_options(ignore_module_conflict: true) - - defmodule LegacyPolymorphicNameDomain do - use Ash.Domain - - resources do - resource(LegacyPolymorphicNamePost) - resource(LegacyPolymorphicNameComment) - end - end - - Code.compiler_options(ignore_module_conflict: false) + deflegacy_polymorphic_resources( + "Name", + "legacy_polymorphic_name_posts", + "legacy_polymorphic_name_comments", + :polymorphic_name, + "custom_polymorphic_name_fkey" + ) generate_resource_migration( - LegacyPolymorphicNameDomain, + Elixir.LegacyPolymorphicNameDomain, snapshot_path, migration_path, "legacy_polymorphic_name" From 9821988fdb391206d5e8d165b73e0d110e459d2f Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Mon, 2 Nov 2026 21:43:02 -0700 Subject: [PATCH 26/47] test: Apply macros to depreciation warning tests. --- test/migration_generator_test.exs | 106 ++++++++++++------------------ 1 file changed, 43 insertions(+), 63 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index ef8cf01b..193fb724 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -205,6 +205,42 @@ defmodule AshPostgres.MigrationGeneratorTest do end end + defmacrop assert_legacy_polymorphic_deprecation(module_suffix, reference_option, reference_value) do + module_suffix = Macro.expand(module_suffix, __CALLER__) + reference_option = Macro.expand(reference_option, __CALLER__) + reference_value = Macro.expand(reference_value, __CALLER__) + + option_name = reference_option |> Atom.to_string() |> String.replace_prefix("polymorphic_", "") + post = Module.concat([:Elixir, "DeprecatedPolymorphic#{module_suffix}Post"]) + reference = {reference_option, [], [reference_value]} + warning_regex = ~r/#{reference_option}[^\n]*deprecated/i + warning = + "Use `relationship_context: %{data_layer: %{references: %{#{option_name}: ...}}}` on the relationship instead." + + quote do + warning = + ExUnit.CaptureIO.capture_io(:stderr, fn -> + defresource unquote(post) do + postgres do + table unquote("deprecated_polymorphic_#{option_name}_posts") + repo(AshPostgres.TestRepo) + + references do + unquote(reference) + end + end + + attributes do + uuid_primary_key(:id) + end + end + end) + + assert warning =~ unquote(Macro.escape(warning_regex)) + assert warning =~ unquote(warning) + end + end + defp position_of_substring(string, substring) do case :binary.match(string, substring) do {pos, _len} -> pos @@ -5747,27 +5783,7 @@ defmodule AshPostgres.MigrationGeneratorTest do end test "Legacy polymorphic_on_delete emits a deprecation warning." do - warning = - ExUnit.CaptureIO.capture_io(:stderr, fn -> - defresource DeprecatedPolymorphicDeletePost do - postgres do - table "deprecated_polymorphic_delete_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_on_delete(:delete) - end - end - - attributes do - uuid_primary_key(:id) - end - end - end) - - assert warning =~ ~r/polymorphic_on_delete[^\n]*deprecated/i - assert warning =~ - "Use `relationship_context: %{data_layer: %{references: %{on_delete: ...}}}` on the relationship instead." + assert_legacy_polymorphic_deprecation("Delete", :polymorphic_on_delete, :delete) end test "Multiple legacy polymorphic_on_delete configurations result in rejection.", %{ @@ -6304,27 +6320,7 @@ defmodule AshPostgres.MigrationGeneratorTest do end test "Legacy polymorphic_on_update emits a deprecation warning." do - warning = - ExUnit.CaptureIO.capture_io(:stderr, fn -> - defresource DeprecatedPolymorphicUpdatePost do - postgres do - table "deprecated_polymorphic_update_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_on_update(:update) - end - end - - attributes do - uuid_primary_key(:id) - end - end - end) - - assert warning =~ ~r/polymorphic_on_update[^\n]*deprecated/i - assert warning =~ - "Use `relationship_context: %{data_layer: %{references: %{on_update: ...}}}` on the relationship instead." + assert_legacy_polymorphic_deprecation("Update", :polymorphic_on_update, :update) end test "Multiple legacy polymorphic_on_update configurations result in rejection.", %{ @@ -6862,27 +6858,11 @@ defmodule AshPostgres.MigrationGeneratorTest do end test "Legacy polymorphic_name emits a deprecation warning." do - warning = - ExUnit.CaptureIO.capture_io(:stderr, fn -> - defresource DeprecatedPolymorphicNamePost do - postgres do - table "deprecated_polymorphic_name_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_name("deprecated_polymorphic_name_fkey") - end - end - - attributes do - uuid_primary_key(:id) - end - end - end) - - assert warning =~ ~r/polymorphic_name[^\n]*deprecated/i - assert warning =~ - "Use `relationship_context: %{data_layer: %{references: %{name: ...}}}` on the relationship instead." + assert_legacy_polymorphic_deprecation( + "Name", + :polymorphic_name, + "deprecated_polymorphic_name_fkey" + ) end test "Multiple legacy polymorphic_name configurations result in rejection.", %{ From 7ec2f31014836b459a4b33d347af9ad0f2373182 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:02:28 -0700 Subject: [PATCH 27/47] test: apply macros to duplicate legacy configuration tests. --- test/migration_generator_test.exs | 415 ++++++++++-------------------- 1 file changed, 139 insertions(+), 276 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 193fb724..422790bf 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -241,6 +241,121 @@ defmodule AshPostgres.MigrationGeneratorTest do end end + defmacrop assert_multiple_legacy_polymorphic_rejection( + module_suffix, + reference_option, + first_value, + second_value, + snapshot_path, + migration_path + ) do + module_suffix = Macro.expand(module_suffix, __CALLER__) + reference_option = Macro.expand(reference_option, __CALLER__) + first_value = Macro.expand(first_value, __CALLER__) + second_value = Macro.expand(second_value, __CALLER__) + + option_name = reference_option |> Atom.to_string() |> String.replace_prefix("polymorphic_", "") + comment = Module.concat([:Elixir, "MultipleLegacyPolymorphic#{module_suffix}Comment"]) + first_post = Module.concat([:Elixir, "MultipleLegacyPolymorphic#{module_suffix}FirstPost"]) + second_post = Module.concat([:Elixir, "MultipleLegacyPolymorphic#{module_suffix}SecondPost"]) + domain = Module.concat([:Elixir, "MultipleLegacyPolymorphic#{module_suffix}Domain"]) + post_table = "multiple_legacy_polymorphic_#{option_name}_posts" + relationship_table = "multiple_legacy_polymorphic_#{option_name}_comments" + first_reference = {reference_option, [], [first_value]} + second_reference = {reference_option, [], [second_value]} + + quote do + defresource unquote(comment) do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource unquote(first_post) do + postgres do + table unquote(post_table) + repo(AshPostgres.TestRepo) + + references do + unquote(first_reference) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, unquote(comment), + public?: true, + destination_attribute: :resource_id, + relationship_context: %{data_layer: %{table: unquote(relationship_table)}} + ) + end + end + + defresource unquote(second_post) do + postgres do + table unquote(post_table) + repo(AshPostgres.TestRepo) + + references do + unquote(second_reference) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, unquote(comment), + public?: true, + destination_attribute: :resource_id, + relationship_context: %{data_layer: %{table: unquote(relationship_table)}} + ) + end + end + + defmodule unquote(domain) do + use Ash.Domain + + resources do + resource(unquote(first_post)) + resource(unquote(second_post)) + resource(unquote(comment)) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + generate_resource_migration( + unquote(domain), + unquote(snapshot_path), + unquote(migration_path), + unquote("multiple_legacy_polymorphic_#{option_name}") + ) + end + end + end + defp position_of_substring(string, substring) do case :binary.match(string, substring) do {pos, _len} -> pos @@ -5790,98 +5905,14 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource MultipleLegacyPolymorphicDeleteComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource MultipleLegacyPolymorphicDeletePost do - postgres do - table "multiple_legacy_polymorphic_delete_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_on_delete(:delete) - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, MultipleLegacyPolymorphicDeleteComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: "multiple_legacy_polymorphic_delete_comments"} - } - ) - end - end - - defresource MultipleLegacyPolymorphicNilifyPost do - postgres do - table "multiple_legacy_polymorphic_delete_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_on_delete(:nilify) - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, MultipleLegacyPolymorphicDeleteComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: "multiple_legacy_polymorphic_delete_comments"} - } - ) - end - end - - defmodule MultipleLegacyPolymorphicDeleteDomain do - use Ash.Domain - - resources do - resource(MultipleLegacyPolymorphicDeletePost) - resource(MultipleLegacyPolymorphicNilifyPost) - resource(MultipleLegacyPolymorphicDeleteComment) - end - end - - assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - generate_resource_migration( - MultipleLegacyPolymorphicDeleteDomain, - snapshot_path, - migration_path, - "multiple_legacy_polymorphic_delete" - ) - end + assert_multiple_legacy_polymorphic_rejection( + "Delete", + :polymorphic_on_delete, + :delete, + :nilify, + snapshot_path, + migration_path + ) end test "on_delete within relationship_context generates the expected foreign-key delete behavior.", %{ @@ -6327,98 +6358,14 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource MultipleLegacyPolymorphicUpdateComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource MultipleLegacyPolymorphicUpdatePost do - postgres do - table "multiple_legacy_polymorphic_update_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_on_update(:update) - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, MultipleLegacyPolymorphicUpdateComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: "multiple_legacy_polymorphic_update_comments"} - } - ) - end - end - - defresource MultipleLegacyPolymorphicRestrictUpdatePost do - postgres do - table "multiple_legacy_polymorphic_update_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_on_update(:restrict) - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, MultipleLegacyPolymorphicUpdateComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: "multiple_legacy_polymorphic_update_comments"} - } - ) - end - end - - defmodule MultipleLegacyPolymorphicUpdateDomain do - use Ash.Domain - - resources do - resource(MultipleLegacyPolymorphicUpdatePost) - resource(MultipleLegacyPolymorphicRestrictUpdatePost) - resource(MultipleLegacyPolymorphicUpdateComment) - end - end - - assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - generate_resource_migration( - MultipleLegacyPolymorphicUpdateDomain, - snapshot_path, - migration_path, - "multiple_legacy_polymorphic_update" - ) - end + assert_multiple_legacy_polymorphic_rejection( + "Update", + :polymorphic_on_update, + :update, + :restrict, + snapshot_path, + migration_path + ) end test "on_update within relationship_context generates the expected foreign-key update behavior.", @@ -6869,98 +6816,14 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource MultipleLegacyPolymorphicNameComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource MultipleLegacyPolymorphicNamePost do - postgres do - table "multiple_legacy_polymorphic_name_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_name("multiple_legacy_polymorphic_name_delete_fkey") - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, MultipleLegacyPolymorphicNameComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: "multiple_legacy_polymorphic_name_comments"} - } - ) - end - end - - defresource MultipleLegacyPolymorphicCustomNamePost do - postgres do - table "multiple_legacy_polymorphic_name_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_name("multiple_legacy_polymorphic_name_update_fkey") - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, MultipleLegacyPolymorphicNameComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: "multiple_legacy_polymorphic_name_comments"} - } - ) - end - end - - defmodule MultipleLegacyPolymorphicNameDomain do - use Ash.Domain - - resources do - resource(MultipleLegacyPolymorphicNamePost) - resource(MultipleLegacyPolymorphicCustomNamePost) - resource(MultipleLegacyPolymorphicNameComment) - end - end - - assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - generate_resource_migration( - MultipleLegacyPolymorphicNameDomain, - snapshot_path, - migration_path, - "multiple_legacy_polymorphic_name" - ) - end + assert_multiple_legacy_polymorphic_rejection( + "Name", + :polymorphic_name, + "multiple_legacy_polymorphic_name_delete_fkey", + "multiple_legacy_polymorphic_name_update_fkey", + snapshot_path, + migration_path + ) end test "name within relationship_context uses the configured name for generated polymorphic foreign-key constraints.", From 886dff5682cd8839b80012a937d60e7f41c34f0a Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:28:45 -0700 Subject: [PATCH 28/47] test: apply macros to multi configuration rejection test. --- test/migration_generator_test.exs | 619 ++++++++++++------------------ 1 file changed, 235 insertions(+), 384 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 422790bf..1978d420 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -356,6 +356,199 @@ defmodule AshPostgres.MigrationGeneratorTest do end end + defmacrop assert_relationship_context_polymorphic_reference( + module_suffix, + reference_option, + reference_value, + snapshot_path, + migration_path + ) do + module_suffix = Macro.expand(module_suffix, __CALLER__) + reference_option = Macro.expand(reference_option, __CALLER__) + reference_value = Macro.expand(reference_value, __CALLER__) + + option_name = Atom.to_string(reference_option) + comment = Module.concat([:Elixir, "RelationshipContextPolymorphic#{module_suffix}Comment"]) + post = Module.concat([:Elixir, "RelationshipContextPolymorphic#{module_suffix}Post"]) + domain = Module.concat([:Elixir, "RelationshipContextPolymorphic#{module_suffix}Domain"]) + table = "relationship_context_polymorphic_#{option_name}_posts" + relationship_table = "relationship_context_polymorphic_#{option_name}_comments" + references = {:%{}, [], [{reference_option, reference_value}]} + + expected_value = + case reference_option do + :on_delete -> :delete_all + :on_update -> :update_all + :name -> reference_value + end + + expected = + Regex.compile!( + "references\\(:#{table}\\b[^)]*#{option_name}: #{inspect(expected_value)}" + ) + + quote do + defresource unquote(comment) do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource unquote(post) do + postgres do + table unquote(table) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, unquote(comment), + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: unquote(relationship_table), + references: unquote(references) + } + } + ) + end + end + + defmodule unquote(domain) do + use Ash.Domain + + resources do + resource(unquote(post)) + resource(unquote(comment)) + end + end + + generate_resource_migration( + unquote(domain), + unquote(snapshot_path), + unquote(migration_path), + unquote("relationship_context_polymorphic_#{option_name}") + ) + + current_migration_path = + Path.join(unquote(migration_path), unquote("relationship_context_polymorphic_#{option_name}")) + + migration = read_resource_migration!(current_migration_path) + assert migration =~ unquote(Macro.escape(expected)) + end + end + + defmacrop assert_legacy_and_relationship_context_conflict( + module_suffix, + legacy_option, + legacy_value, + relationship_option, + relationship_value, + snapshot_path, + migration_path + ) do + module_suffix = Macro.expand(module_suffix, __CALLER__) + legacy_option = Macro.expand(legacy_option, __CALLER__) + legacy_value = Macro.expand(legacy_value, __CALLER__) + relationship_option = Macro.expand(relationship_option, __CALLER__) + relationship_value = Macro.expand(relationship_value, __CALLER__) + + option_name = relationship_option |> Atom.to_string() + comment = Module.concat([:Elixir, "LegacyAndNewPolymorphic#{module_suffix}Comment"]) + post = Module.concat([:Elixir, "LegacyAndNewPolymorphic#{module_suffix}Post"]) + domain = Module.concat([:Elixir, "LegacyAndNewPolymorphic#{module_suffix}Domain"]) + post_table = "legacy_and_new_polymorphic_#{option_name}_posts" + relationship_table = "legacy_and_new_polymorphic_#{option_name}_comments" + legacy_reference = {legacy_option, [], [legacy_value]} + relationship_references = {:%{}, [], [{relationship_option, relationship_value}]} + + quote do + defresource unquote(comment) do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource unquote(post) do + postgres do + table unquote(post_table) + repo(AshPostgres.TestRepo) + + references do + unquote(legacy_reference) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, unquote(comment), + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: unquote(relationship_table), + references: unquote(relationship_references) + } + } + ) + end + end + + defmodule unquote(domain) do + use Ash.Domain + + resources do + resource(unquote(post)) + resource(unquote(comment)) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + generate_resource_migration( + unquote(domain), + unquote(snapshot_path), + unquote(migration_path), + unquote("legacy_and_new_polymorphic_#{option_name}") + ) + end + end + end + defp position_of_substring(string, substring) do case :binary.match(string, substring) do {pos, _len} -> pos @@ -5919,71 +6112,13 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource RelationshipContextPolymorphicDeleteComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource RelationshipContextPolymorphicDeletePost do - postgres do - table "relationship_context_polymorphic_delete_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, RelationshipContextPolymorphicDeleteComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "relationship_context_polymorphic_delete_comments", - references: %{on_delete: :delete} - } - } - ) - end - end - - defmodule RelationshipContextPolymorphicDeleteDomain do - use Ash.Domain - - resources do - resource(RelationshipContextPolymorphicDeletePost) - resource(RelationshipContextPolymorphicDeleteComment) - end - end - - generate_resource_migration( - RelationshipContextPolymorphicDeleteDomain, + assert_relationship_context_polymorphic_reference( + "Delete", + :on_delete, + :delete, snapshot_path, - migration_path, - "relationship_context_polymorphic_delete" + migration_path ) - - current_migration_path = Path.join(migration_path, "relationship_context_polymorphic_delete") - migration = read_resource_migration!(current_migration_path) - - assert migration =~ - ~r/references\(:relationship_context_polymorphic_delete_posts\b[^)]*on_delete: :delete_all/ end test "Configuring polymorphic_on_delete through both legacy and relationship-context options on one resource results in rejection.", @@ -5991,71 +6126,15 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource LegacyAndNewPolymorphicDeleteComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource LegacyAndNewPolymorphicDeletePost do - postgres do - table "legacy_and_new_polymorphic_delete_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_on_delete(:delete) - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, LegacyAndNewPolymorphicDeleteComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "legacy_and_new_polymorphic_delete_comments", - references: %{on_delete: :nilify} - } - } - ) - end - end - - defmodule LegacyAndNewPolymorphicDeleteDomain do - use Ash.Domain - - resources do - resource(LegacyAndNewPolymorphicDeletePost) - resource(LegacyAndNewPolymorphicDeleteComment) - end - end - - assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - generate_resource_migration( - LegacyAndNewPolymorphicDeleteDomain, - snapshot_path, - migration_path, - "legacy_and_new_polymorphic_delete" - ) - end + assert_legacy_and_relationship_context_conflict( + "Delete", + :polymorphic_on_delete, + :delete, + :on_delete, + :nilify, + snapshot_path, + migration_path + ) end test "Conflicting polymorphic_on_delete configurations across resources result in rejection.", %{ @@ -6373,71 +6452,13 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource RelationshipContextPolymorphicUpdateComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource RelationshipContextPolymorphicUpdatePost do - postgres do - table "relationship_context_polymorphic_update_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, RelationshipContextPolymorphicUpdateComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "relationship_context_polymorphic_update_comments", - references: %{on_update: :update} - } - } - ) - end - end - - defmodule RelationshipContextPolymorphicUpdateDomain do - use Ash.Domain - - resources do - resource(RelationshipContextPolymorphicUpdatePost) - resource(RelationshipContextPolymorphicUpdateComment) - end - end - - generate_resource_migration( - RelationshipContextPolymorphicUpdateDomain, + assert_relationship_context_polymorphic_reference( + "Update", + :on_update, + :update, snapshot_path, - migration_path, - "relationship_context_polymorphic_update" + migration_path ) - - current_migration_path = Path.join(migration_path, "relationship_context_polymorphic_update") - migration = read_resource_migration!(current_migration_path) - - assert migration =~ - ~r/references\(:relationship_context_polymorphic_update_posts\b[^)]*on_update: :update_all/ end test "Configuring polymorphic_on_update through both legacy and relationship-context options on one resource results in rejection.", @@ -6445,71 +6466,15 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource LegacyAndNewPolymorphicUpdateComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource LegacyAndNewPolymorphicUpdatePost do - postgres do - table "legacy_and_new_polymorphic_update_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_on_update(:update) - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, LegacyAndNewPolymorphicUpdateComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "legacy_and_new_polymorphic_update_comments", - references: %{on_update: :restrict} - } - } - ) - end - end - - defmodule LegacyAndNewPolymorphicUpdateDomain do - use Ash.Domain - - resources do - resource(LegacyAndNewPolymorphicUpdatePost) - resource(LegacyAndNewPolymorphicUpdateComment) - end - end - - assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - generate_resource_migration( - LegacyAndNewPolymorphicUpdateDomain, - snapshot_path, - migration_path, - "legacy_and_new_polymorphic_update" - ) - end + assert_legacy_and_relationship_context_conflict( + "Update", + :polymorphic_on_update, + :update, + :on_update, + :restrict, + snapshot_path, + migration_path + ) end test "Conflicting polymorphic_on_update configurations across resources result in rejection.", %{ @@ -6831,71 +6796,13 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource RelationshipContextPolymorphicNameComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource RelationshipContextPolymorphicNamePost do - postgres do - table "relationship_context_polymorphic_name_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, RelationshipContextPolymorphicNameComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "relationship_context_polymorphic_name_comments", - references: %{name: "relationship_context_polymorphic_name_fkey"} - } - } - ) - end - end - - defmodule RelationshipContextPolymorphicNameDomain do - use Ash.Domain - - resources do - resource(RelationshipContextPolymorphicNamePost) - resource(RelationshipContextPolymorphicNameComment) - end - end - - generate_resource_migration( - RelationshipContextPolymorphicNameDomain, + assert_relationship_context_polymorphic_reference( + "Name", + :name, + "relationship_context_polymorphic_name_fkey", snapshot_path, - migration_path, - "relationship_context_polymorphic_name" + migration_path ) - - current_migration_path = Path.join(migration_path, "relationship_context_polymorphic_name") - migration = read_resource_migration!(current_migration_path) - - assert migration =~ - ~r/references\(:relationship_context_polymorphic_name_posts\b[^)]*name: "relationship_context_polymorphic_name_fkey"/ end test "Configuring polymorphic_name through both legacy and relationship-context options on one resource results in rejection.", @@ -6903,71 +6810,15 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource LegacyAndNewPolymorphicNameComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource LegacyAndNewPolymorphicNamePost do - postgres do - table "legacy_and_new_polymorphic_name_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_name("legacy_polymorphic_name_fkey") - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, LegacyAndNewPolymorphicNameComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "legacy_and_new_polymorphic_name_comments", - references: %{name: "relationship_context_polymorphic_name_fkey"} - } - } - ) - end - end - - defmodule LegacyAndNewPolymorphicNameDomain do - use Ash.Domain - - resources do - resource(LegacyAndNewPolymorphicNamePost) - resource(LegacyAndNewPolymorphicNameComment) - end - end - - assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - generate_resource_migration( - LegacyAndNewPolymorphicNameDomain, - snapshot_path, - migration_path, - "legacy_and_new_polymorphic_name" - ) - end + assert_legacy_and_relationship_context_conflict( + "Name", + :polymorphic_name, + "legacy_polymorphic_name_fkey", + :name, + "relationship_context_polymorphic_name_fkey", + snapshot_path, + migration_path + ) end test "Conflicting polymorphic_name configurations across resources result in rejection.", %{ From 85810a95f642fb57fa2288f530b4e509c0147dd9 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:44:51 -0700 Subject: [PATCH 29/47] tests: Create macros for conflicting configurations. --- test/migration_generator_test.exs | 423 +++++++++++------------------- 1 file changed, 150 insertions(+), 273 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 1978d420..5f41cca4 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -549,6 +549,129 @@ defmodule AshPostgres.MigrationGeneratorTest do end end + defmacrop assert_cross_resource_polymorphic_conflict( + module_suffix, + legacy_option, + legacy_value, + relationship_option, + relationship_value, + snapshot_path, + migration_path + ) do + module_suffix = Macro.expand(module_suffix, __CALLER__) + legacy_option = Macro.expand(legacy_option, __CALLER__) + legacy_value = Macro.expand(legacy_value, __CALLER__) + relationship_option = Macro.expand(relationship_option, __CALLER__) + relationship_value = Macro.expand(relationship_value, __CALLER__) + + comment = Module.concat([:Elixir, "CrossResourcePolymorphic#{module_suffix}Comment"]) + legacy_post = Module.concat([:Elixir, "CrossResourceLegacyPolymorphic#{module_suffix}Post"]) + + relationship_post = + Module.concat([:Elixir, "CrossResourceRelationshipContextPolymorphic#{module_suffix}Post"]) + + domain = Module.concat([:Elixir, "CrossResourcePolymorphic#{module_suffix}Domain"]) + migration_name = "cross_resource_polymorphic_#{String.downcase(module_suffix)}" + post_table = "#{migration_name}_posts" + relationship_table = "#{migration_name}_comments" + legacy_reference = {legacy_option, [], [legacy_value]} + relationship_references = {:%{}, [], [{relationship_option, relationship_value}]} + + quote do + defresource unquote(comment) do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource unquote(legacy_post) do + postgres do + table unquote(post_table) + repo(AshPostgres.TestRepo) + + references do + unquote(legacy_reference) + end + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, unquote(comment), + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: unquote(relationship_table)} + } + ) + end + end + + defresource unquote(relationship_post) do + postgres do + table unquote(post_table) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, unquote(comment), + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: unquote(relationship_table), + references: unquote(relationship_references) + } + } + ) + end + end + + defmodule unquote(domain) do + use Ash.Domain + + resources do + resource(unquote(legacy_post)) + resource(unquote(relationship_post)) + resource(unquote(comment)) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + generate_resource_migration( + unquote(domain), + unquote(snapshot_path), + unquote(migration_path), + unquote(migration_name) + ) + end + end + end + defp position_of_substring(string, substring) do case :binary.match(string, substring) do {pos, _len} -> pos @@ -6141,97 +6264,15 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource CrossResourcePolymorphicDeleteComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource CrossResourceLegacyPolymorphicDeletePost do - postgres do - table "cross_resource_polymorphic_delete_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_on_delete(:delete) - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, CrossResourcePolymorphicDeleteComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: "cross_resource_polymorphic_delete_comments"} - } - ) - end - end - - defresource CrossResourceRelationshipContextPolymorphicDeletePost do - postgres do - table "cross_resource_polymorphic_delete_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, CrossResourcePolymorphicDeleteComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "cross_resource_polymorphic_delete_comments", - references: %{on_delete: :nilify} - } - } - ) - end - end - - defmodule CrossResourcePolymorphicDeleteDomain do - use Ash.Domain - - resources do - resource(CrossResourceLegacyPolymorphicDeletePost) - resource(CrossResourceRelationshipContextPolymorphicDeletePost) - resource(CrossResourcePolymorphicDeleteComment) - end - end - - assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - generate_resource_migration( - CrossResourcePolymorphicDeleteDomain, - snapshot_path, - migration_path, - "cross_resource_polymorphic_delete" - ) - end + assert_cross_resource_polymorphic_conflict( + "Delete", + :polymorphic_on_delete, + :delete, + :on_delete, + :nilify, + snapshot_path, + migration_path + ) end test "Multiple on_delete configurations within relationship_context result in rejection.", %{ @@ -6481,97 +6522,15 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource CrossResourcePolymorphicUpdateComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource CrossResourceLegacyPolymorphicUpdatePost do - postgres do - table "cross_resource_polymorphic_update_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_on_update(:update) - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, CrossResourcePolymorphicUpdateComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: "cross_resource_polymorphic_update_comments"} - } - ) - end - end - - defresource CrossResourceRelationshipContextPolymorphicUpdatePost do - postgres do - table "cross_resource_polymorphic_update_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, CrossResourcePolymorphicUpdateComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "cross_resource_polymorphic_update_comments", - references: %{on_update: :restrict} - } - } - ) - end - end - - defmodule CrossResourcePolymorphicUpdateDomain do - use Ash.Domain - - resources do - resource(CrossResourceLegacyPolymorphicUpdatePost) - resource(CrossResourceRelationshipContextPolymorphicUpdatePost) - resource(CrossResourcePolymorphicUpdateComment) - end - end - - assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - generate_resource_migration( - CrossResourcePolymorphicUpdateDomain, - snapshot_path, - migration_path, - "cross_resource_polymorphic_update" - ) - end + assert_cross_resource_polymorphic_conflict( + "Update", + :polymorphic_on_update, + :update, + :on_update, + :restrict, + snapshot_path, + migration_path + ) end test "Multiple on_update configurations within relationship_context result in rejection.", %{ @@ -6825,97 +6784,15 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource CrossResourcePolymorphicNameComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource CrossResourceLegacyPolymorphicNamePost do - postgres do - table "cross_resource_polymorphic_name_posts" - repo(AshPostgres.TestRepo) - - references do - polymorphic_name("legacy_polymorphic_name_fkey") - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, CrossResourcePolymorphicNameComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: "cross_resource_polymorphic_name_comments"} - } - ) - end - end - - defresource CrossResourceRelationshipContextPolymorphicNamePost do - postgres do - table "cross_resource_polymorphic_name_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, CrossResourcePolymorphicNameComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "cross_resource_polymorphic_name_comments", - references: %{name: "relationship_context_polymorphic_name_fkey"} - } - } - ) - end - end - - defmodule CrossResourcePolymorphicNameDomain do - use Ash.Domain - - resources do - resource(CrossResourceLegacyPolymorphicNamePost) - resource(CrossResourceRelationshipContextPolymorphicNamePost) - resource(CrossResourcePolymorphicNameComment) - end - end - - assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - generate_resource_migration( - CrossResourcePolymorphicNameDomain, - snapshot_path, - migration_path, - "cross_resource_polymorphic_name" - ) - end + assert_cross_resource_polymorphic_conflict( + "Name", + :polymorphic_name, + "legacy_polymorphic_name_fkey", + :name, + "relationship_context_polymorphic_name_fkey", + snapshot_path, + migration_path + ) end test "Multiple name configurations within relationship_context result in rejection.", %{ From 58ad9c2ade00d73630a54ee4dd98c79e9d89b483 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Tue, 3 Nov 2026 17:30:46 -0700 Subject: [PATCH 30/47] Test: Finish updating tests with macros. --- test/migration_generator_test.exs | 766 +++++++++++------------------- 1 file changed, 280 insertions(+), 486 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 5f41cca4..ec435faf 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -672,6 +672,220 @@ defmodule AshPostgres.MigrationGeneratorTest do end end + defmacrop assert_conflicting_relationship_context_polymorphic_references( + module_suffix, + reference_option, + first_value, + second_value, + snapshot_path, + migration_path + ) do + module_suffix = Macro.expand(module_suffix, __CALLER__) + reference_option = Macro.expand(reference_option, __CALLER__) + first_value = Macro.expand(first_value, __CALLER__) + second_value = Macro.expand(second_value, __CALLER__) + + comment = + Module.concat([:Elixir, "MultipleRelationshipContextPolymorphic#{module_suffix}Comment"]) + + first_post = Module.concat([:Elixir, "FirstRelationshipContextPolymorphic#{module_suffix}Post"]) + second_post = Module.concat([:Elixir, "SecondRelationshipContextPolymorphic#{module_suffix}Post"]) + + domain = + Module.concat([:Elixir, "MultipleRelationshipContextPolymorphic#{module_suffix}Domain"]) + + migration_name = "multiple_relationship_context_polymorphic_#{String.downcase(module_suffix)}" + post_table = "#{migration_name}_posts" + relationship_table = "#{migration_name}_comments" + first_references = {:%{}, [], [{reference_option, first_value}]} + second_references = {:%{}, [], [{reference_option, second_value}]} + + quote do + defresource unquote(comment) do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource unquote(first_post) do + postgres do + table unquote(post_table) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, unquote(comment), + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: unquote(relationship_table), + references: unquote(first_references) + } + } + ) + end + end + + defresource unquote(second_post) do + postgres do + table unquote(post_table) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, unquote(comment), + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{ + table: unquote(relationship_table), + references: unquote(second_references) + } + } + ) + end + end + + defmodule unquote(domain) do + use Ash.Domain + + resources do + resource(unquote(first_post)) + resource(unquote(second_post)) + resource(unquote(comment)) + end + end + + assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> + generate_resource_migration( + unquote(domain), + unquote(snapshot_path), + unquote(migration_path), + unquote(migration_name) + ) + end + end + end + + defmacrop assert_default_polymorphic_reference( + module_suffix, + reference_option, + snapshot_path, + migration_path + ) do + module_suffix = Macro.expand(module_suffix, __CALLER__) + reference_option = Macro.expand(reference_option, __CALLER__) + + comment = Module.concat([:Elixir, "DefaultPolymorphic#{module_suffix}Comment"]) + post = Module.concat([:Elixir, "DefaultPolymorphic#{module_suffix}Post"]) + domain = Module.concat([:Elixir, "DefaultPolymorphic#{module_suffix}Domain"]) + migration_name = "default_polymorphic_#{String.downcase(module_suffix)}" + post_table = "#{migration_name}_posts" + relationship_table = "#{migration_name}_comments" + + expected_name = + Regex.compile!( + "references\\(:#{post_table}\\b[^)]*name: \"#{relationship_table}_resource_id_fkey\"" + ) + + explicit_option = Regex.compile!("references\\(:#{post_table}\\b[^)]*#{reference_option}:") + + quote do + defresource unquote(comment) do + postgres do + polymorphic?(true) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + attribute(:resource_id, :uuid, public?: true) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + end + + defresource unquote(post) do + postgres do + table unquote(post_table) + repo(AshPostgres.TestRepo) + end + + attributes do + uuid_primary_key(:id) + end + + actions do + defaults([:create, :read, :update, :destroy]) + end + + relationships do + has_many(:comments, unquote(comment), + public?: true, + destination_attribute: :resource_id, + relationship_context: %{ + data_layer: %{table: unquote(relationship_table)} + } + ) + end + end + + defmodule unquote(domain) do + use Ash.Domain + + resources do + resource(unquote(post)) + resource(unquote(comment)) + end + end + + generate_resource_migration( + unquote(domain), + unquote(snapshot_path), + unquote(migration_path), + unquote(migration_name) + ) + + current_migration_path = Path.join(unquote(migration_path), unquote(migration_name)) + migration = read_resource_migration!(current_migration_path) + + assert migration =~ unquote(Macro.escape(expected_name)) + + if unquote(reference_option in [:on_delete, :on_update]) do + refute migration =~ unquote(Macro.escape(explicit_option)) + end + end + end + defp position_of_substring(string, substring) do case :binary.match(string, substring) do {pos, _len} -> pos @@ -6279,192 +6493,51 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource MultipleRelationshipContextPolymorphicDeleteComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end + assert_conflicting_relationship_context_polymorphic_references( + "Delete", + :on_delete, + :delete, + :nilify, + snapshot_path, + migration_path + ) + end - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end + test "Omitting both polymorphic_on_delete and on_delete in relationship_context retains default behavior.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + assert_default_polymorphic_reference( + "Delete", + :on_delete, + snapshot_path, + migration_path + ) + end - actions do - defaults([:create, :read, :update, :destroy]) - end - end + # -- polymorphic_on_update -- - defresource DeleteRelationshipContextPolymorphicDeletePost do - postgres do - table "multiple_relationship_context_polymorphic_delete_posts" - repo(AshPostgres.TestRepo) - end + test "Legacy polymorphic_on_update generates the expected foreign-key update behavior.", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + deflegacy_polymorphic_resources( + "Update", + "legacy_polymorphic_update_posts", + "legacy_update_post_comments", + :polymorphic_on_update, + :update + ) - attributes do - uuid_primary_key(:id) - end + generate_resource_migration( + Elixir.LegacyPolymorphicUpdateDomain, + snapshot_path, + migration_path, + "legacy_polymorphic_update" + ) - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, MultipleRelationshipContextPolymorphicDeleteComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "multiple_relationship_context_polymorphic_delete_comments", - references: %{on_delete: :delete} - } - } - ) - end - end - - defresource NilifyRelationshipContextPolymorphicDeletePost do - postgres do - table "multiple_relationship_context_polymorphic_delete_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, MultipleRelationshipContextPolymorphicDeleteComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "multiple_relationship_context_polymorphic_delete_comments", - references: %{on_delete: :nilify} - } - } - ) - end - end - - defmodule MultipleRelationshipContextPolymorphicDeleteDomain do - use Ash.Domain - - resources do - resource(DeleteRelationshipContextPolymorphicDeletePost) - resource(NilifyRelationshipContextPolymorphicDeletePost) - resource(MultipleRelationshipContextPolymorphicDeleteComment) - end - end - - assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - generate_resource_migration( - MultipleRelationshipContextPolymorphicDeleteDomain, - snapshot_path, - migration_path, - "multiple_relationship_context_polymorphic_delete" - ) - end - end - - test "Omitting both polymorphic_on_delete and on_delete in relationship_context retains default behavior.", %{ - snapshot_path: snapshot_path, - migration_path: migration_path - } do - defresource DefaultPolymorphicDeleteComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource DefaultPolymorphicDeletePost do - postgres do - table "default_polymorphic_delete_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, DefaultPolymorphicDeleteComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: "default_polymorphic_delete_comments"} - } - ) - end - end - - defmodule DefaultPolymorphicDeleteDomain do - use Ash.Domain - - resources do - resource(DefaultPolymorphicDeletePost) - resource(DefaultPolymorphicDeleteComment) - end - end - - generate_resource_migration( - DefaultPolymorphicDeleteDomain, - snapshot_path, - migration_path, - "default_polymorphic_delete" - ) - - current_migration_path = Path.join(migration_path, "default_polymorphic_delete") - migration = read_resource_migration!(current_migration_path) - - assert migration =~ - ~r/references\(:default_polymorphic_delete_posts\b[^)]*name: "default_polymorphic_delete_comments_resource_id_fkey"/ - - refute migration =~ - ~r/references\(:default_polymorphic_delete_posts\b[^)]*on_delete:/ - end - - # -- polymorphic_on_update -- - - test "Legacy polymorphic_on_update generates the expected foreign-key update behavior.", %{ - snapshot_path: snapshot_path, - migration_path: migration_path - } do - deflegacy_polymorphic_resources( - "Update", - "legacy_polymorphic_update_posts", - "legacy_update_post_comments", - :polymorphic_on_update, - :update - ) - - generate_resource_migration( - Elixir.LegacyPolymorphicUpdateDomain, - snapshot_path, - migration_path, - "legacy_polymorphic_update" - ) - - current_migration_path = Path.join(migration_path, "legacy_polymorphic_update") - migration = read_resource_migration!(current_migration_path) + current_migration_path = Path.join(migration_path, "legacy_polymorphic_update") + migration = read_resource_migration!(current_migration_path) assert migration =~ ~r/references\(:legacy_polymorphic_update_posts\b[^)]*on_update: :update_all/ @@ -6522,7 +6595,7 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - assert_cross_resource_polymorphic_conflict( + assert_conflicting_cross_resource_polymorphic_references( "Update", :polymorphic_on_update, :update, @@ -6537,167 +6610,26 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource MultipleRelationshipContextPolymorphicUpdateComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource UpdateRelationshipContextPolymorphicUpdatePost do - postgres do - table "multiple_relationship_context_polymorphic_update_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, MultipleRelationshipContextPolymorphicUpdateComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "multiple_relationship_context_polymorphic_update_comments", - references: %{on_update: :update} - } - } - ) - end - end - - defresource RestrictRelationshipContextPolymorphicUpdatePost do - postgres do - table "multiple_relationship_context_polymorphic_update_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, MultipleRelationshipContextPolymorphicUpdateComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "multiple_relationship_context_polymorphic_update_comments", - references: %{on_update: :restrict} - } - } - ) - end - end - - defmodule MultipleRelationshipContextPolymorphicUpdateDomain do - use Ash.Domain - - resources do - resource(UpdateRelationshipContextPolymorphicUpdatePost) - resource(RestrictRelationshipContextPolymorphicUpdatePost) - resource(MultipleRelationshipContextPolymorphicUpdateComment) - end - end - - assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - generate_resource_migration( - MultipleRelationshipContextPolymorphicUpdateDomain, - snapshot_path, - migration_path, - "multiple_relationship_context_polymorphic_update" - ) - end + assert_conflicting_relationship_context_polymorphic_references( + "Update", + :on_update, + :update, + :restrict, + snapshot_path, + migration_path + ) end test "Omitting both polymorphic_on_update and on_update in relationship_context retains default behavior.", %{ snapshot_path: snapshot_path, migration_path: migration_path } do - defresource DefaultPolymorphicUpdateComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource DefaultPolymorphicUpdatePost do - postgres do - table "default_polymorphic_update_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, DefaultPolymorphicUpdateComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: "default_polymorphic_update_comments"} - } - ) - end - end - - defmodule DefaultPolymorphicUpdateDomain do - use Ash.Domain - - resources do - resource(DefaultPolymorphicUpdatePost) - resource(DefaultPolymorphicUpdateComment) - end - end - - generate_resource_migration( - DefaultPolymorphicUpdateDomain, + assert_default_polymorphic_reference( + "Update", + :on_update, snapshot_path, - migration_path, - "default_polymorphic_update" + migration_path ) - - current_migration_path = Path.join(migration_path, "default_polymorphic_update") - migration = read_resource_migration!(current_migration_path) - - assert migration =~ - ~r/references\(:default_polymorphic_update_posts\b[^)]*name: "default_polymorphic_update_comments_resource_id_fkey"/ - - refute migration =~ - ~r/references\(:default_polymorphic_update_posts\b[^)]*on_update:/ end # -- polymorphic_name -- @@ -6784,7 +6716,7 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - assert_cross_resource_polymorphic_conflict( + assert_conflicting_cross_resource_polymorphic_references( "Name", :polymorphic_name, "legacy_polymorphic_name_fkey", @@ -6799,164 +6731,26 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - defresource MultipleRelationshipContextPolymorphicNameComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource FirstRelationshipContextPolymorphicNamePost do - postgres do - table "multiple_relationship_context_polymorphic_name_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, MultipleRelationshipContextPolymorphicNameComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "multiple_relationship_context_polymorphic_name_comments", - references: %{name: "first_polymorphic_name_fkey"} - } - } - ) - end - end - - defresource SecondRelationshipContextPolymorphicNamePost do - postgres do - table "multiple_relationship_context_polymorphic_name_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, MultipleRelationshipContextPolymorphicNameComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: "multiple_relationship_context_polymorphic_name_comments", - references: %{name: "second_polymorphic_name_fkey"} - } - } - ) - end - end - - defmodule MultipleRelationshipContextPolymorphicNameDomain do - use Ash.Domain - - resources do - resource(FirstRelationshipContextPolymorphicNamePost) - resource(SecondRelationshipContextPolymorphicNamePost) - resource(MultipleRelationshipContextPolymorphicNameComment) - end - end - - assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> - generate_resource_migration( - MultipleRelationshipContextPolymorphicNameDomain, - snapshot_path, - migration_path, - "multiple_relationship_context_polymorphic_name" - ) - end + assert_conflicting_relationship_context_polymorphic_references( + "Name", + :name, + "first_polymorphic_name_fkey", + "second_polymorphic_name_fkey", + snapshot_path, + migration_path + ) end test "Omitting both polymorphic_name and name in relationship_context retains default behavior.", %{ snapshot_path: snapshot_path, migration_path: migration_path } do - defresource DefaultPolymorphicNameComment do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource DefaultPolymorphicNamePost do - postgres do - table "default_polymorphic_name_posts" - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, DefaultPolymorphicNameComment, - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: "default_polymorphic_name_comments"} - } - ) - end - end - - defmodule DefaultPolymorphicNameDomain do - use Ash.Domain - - resources do - resource(DefaultPolymorphicNamePost) - resource(DefaultPolymorphicNameComment) - end - end - - generate_resource_migration( - DefaultPolymorphicNameDomain, + assert_default_polymorphic_reference( + "Name", + :name, snapshot_path, - migration_path, - "default_polymorphic_name" + migration_path ) - - current_migration_path = Path.join(migration_path, "default_polymorphic_name") - migration = read_resource_migration!(current_migration_path) - - assert migration =~ - ~r/references\(:default_polymorphic_name_posts\b[^)]*name: "default_polymorphic_name_comments_resource_id_fkey"/ end end From 4b3783e183623a9c60bec9c00fb23043eee7e5e0 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Tue, 3 Nov 2026 17:43:32 -0700 Subject: [PATCH 31/47] test: Correct function names --- test/migration_generator_test.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index ec435faf..2059dfe0 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -6595,7 +6595,7 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - assert_conflicting_cross_resource_polymorphic_references( + assert_cross_resource_polymorphic_conflict( "Update", :polymorphic_on_update, :update, @@ -6716,7 +6716,7 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - assert_conflicting_cross_resource_polymorphic_references( + assert_cross_resource_polymorphic_conflict( "Name", :polymorphic_name, "legacy_polymorphic_name_fkey", From 13b349701980c85755089d966cd10ad4b6373e91 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Tue, 3 Nov 2026 19:41:00 -0700 Subject: [PATCH 32/47] test: Update default test with true pre-exsisting default for name. --- test/migration_generator_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 2059dfe0..ba010cf7 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -812,7 +812,7 @@ defmodule AshPostgres.MigrationGeneratorTest do expected_name = Regex.compile!( - "references\\(:#{post_table}\\b[^)]*name: \"#{relationship_table}_resource_id_fkey\"" + "references\\(:#{post_table}\\b[^)]*name: (nil|\"#{relationship_table}_resource_id_fkey\")" ) explicit_option = Regex.compile!("references\\(:#{post_table}\\b[^)]*#{reference_option}:") From d0eaced3f459a79a663340c68075a1c16bb23e37 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Tue, 3 Nov 2026 20:19:11 -0700 Subject: [PATCH 33/47] test: Update default test. --- test/migration_generator_test.exs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index ba010cf7..9e83d061 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -810,10 +810,7 @@ defmodule AshPostgres.MigrationGeneratorTest do post_table = "#{migration_name}_posts" relationship_table = "#{migration_name}_comments" - expected_name = - Regex.compile!( - "references\\(:#{post_table}\\b[^)]*name: (nil|\"#{relationship_table}_resource_id_fkey\")" - ) + expected_name = Regex.compile!("references\\(:#{post_table}\\b[^)]*name: nil") explicit_option = Regex.compile!("references\\(:#{post_table}\\b[^)]*#{reference_option}:") @@ -878,7 +875,9 @@ defmodule AshPostgres.MigrationGeneratorTest do current_migration_path = Path.join(unquote(migration_path), unquote(migration_name)) migration = read_resource_migration!(current_migration_path) - assert migration =~ unquote(Macro.escape(expected_name)) + if unquote(reference_option == :name) do + assert migration =~ unquote(Macro.escape(expected_name)) + end if unquote(reference_option in [:on_delete, :on_update]) do refute migration =~ unquote(Macro.escape(explicit_option)) From cef660490392327c7d2e67e0763874216856ee99 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Tue, 3 Nov 2026 20:42:01 -0700 Subject: [PATCH 34/47] test: Re update default test --- test/migration_generator_test.exs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 9e83d061..733894b8 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -810,7 +810,10 @@ defmodule AshPostgres.MigrationGeneratorTest do post_table = "#{migration_name}_posts" relationship_table = "#{migration_name}_comments" - expected_name = Regex.compile!("references\\(:#{post_table}\\b[^)]*name: nil") + expected_name = + Regex.compile!( + "references\\(:#{post_table}\\b[^)]*name: \\\"#{relationship_table}_resource_id_fkey\\\"" + ) explicit_option = Regex.compile!("references\\(:#{post_table}\\b[^)]*#{reference_option}:") From dbd964a45c476a82b76a434774f67c097b156f3c Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Tue, 3 Nov 2026 21:57:32 -0700 Subject: [PATCH 35/47] test: Refactor tests to consolidate common logic. --- test/migration_generator_test.exs | 761 +++++++++++------------------- 1 file changed, 274 insertions(+), 487 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 733894b8..ac0763de 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -132,24 +132,7 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - defmacrop deflegacy_polymorphic_resources( - module_suffix, - post_table, - relationship_table, - reference_option, - reference_value - ) do - module_suffix = Macro.expand(module_suffix, __CALLER__) - post_table = Macro.expand(post_table, __CALLER__) - relationship_table = Macro.expand(relationship_table, __CALLER__) - reference_option = Macro.expand(reference_option, __CALLER__) - reference_value = Macro.expand(reference_value, __CALLER__) - - comment = Module.concat([:Elixir, "LegacyPolymorphic#{module_suffix}Comment"]) - post = Module.concat([:Elixir, "LegacyPolymorphic#{module_suffix}Post"]) - domain = Module.concat([:Elixir, "LegacyPolymorphic#{module_suffix}Domain"]) - reference = {reference_option, [], [reference_value]} - + defmacrop defpolymorphic_comment(comment) do quote do defresource unquote(comment) do postgres do @@ -166,16 +149,53 @@ defmodule AshPostgres.MigrationGeneratorTest do defaults([:create, :read, :update, :destroy]) end end + end + end - defresource unquote(post) do - postgres do - table unquote(post_table) - repo(AshPostgres.TestRepo) + defmacrop defpolymorphic_post( + post, + table, + comment, + relationship_table, + opts \\ [] + ) do + reference = Keyword.get(opts, :reference) + relationship_references = Keyword.get(opts, :relationship_references) + references_block = + if reference do + quote do references do unquote(reference) end end + else + [] + end + + relationship_context = + if relationship_references do + quote do + %{ + data_layer: %{ + table: unquote(relationship_table), + references: unquote(relationship_references) + } + } + end + else + quote do + %{data_layer: %{table: unquote(relationship_table)}} + end + end + + quote do + defresource unquote(post) do + postgres do + table unquote(table) + repo(AshPostgres.TestRepo) + unquote(references_block) + end attributes do uuid_primary_key(:id) @@ -189,31 +209,76 @@ defmodule AshPostgres.MigrationGeneratorTest do has_many(:comments, unquote(comment), public?: true, destination_attribute: :resource_id, - relationship_context: %{data_layer: %{table: unquote(relationship_table)}} + relationship_context: unquote(relationship_context) ) end end + end + end + defmacrop defpolymorphic_domain(domain, resources) do + quote do defmodule unquote(domain) do use Ash.Domain resources do - resource(unquote(post)) - resource(unquote(comment)) + for resource <- unquote(resources) do + resource(resource) + end end end end end - defmacrop assert_legacy_polymorphic_deprecation(module_suffix, reference_option, reference_value) do + defmacrop deflegacy_polymorphic_resources( + module_suffix, + post_table, + relationship_table, + reference_option, + reference_value + ) do + module_suffix = Macro.expand(module_suffix, __CALLER__) + post_table = Macro.expand(post_table, __CALLER__) + relationship_table = Macro.expand(relationship_table, __CALLER__) + reference_option = Macro.expand(reference_option, __CALLER__) + reference_value = Macro.expand(reference_value, __CALLER__) + + comment = Module.concat([:"Elixir", "LegacyPolymorphic#{module_suffix}Comment"]) + post = Module.concat([:"Elixir", "LegacyPolymorphic#{module_suffix}Post"]) + domain = Module.concat([:"Elixir", "LegacyPolymorphic#{module_suffix}Domain"]) + reference = {reference_option, [], [reference_value]} + + quote do + defpolymorphic_comment(unquote(comment)) + + defpolymorphic_post( + unquote(post), + unquote(post_table), + unquote(comment), + unquote(relationship_table), + reference: unquote(reference) + ) + + defpolymorphic_domain(unquote(domain), [unquote(post), unquote(comment)]) + end + end + + defmacrop assert_legacy_polymorphic_deprecation( + module_suffix, + reference_option, + reference_value + ) do module_suffix = Macro.expand(module_suffix, __CALLER__) reference_option = Macro.expand(reference_option, __CALLER__) reference_value = Macro.expand(reference_value, __CALLER__) - option_name = reference_option |> Atom.to_string() |> String.replace_prefix("polymorphic_", "") - post = Module.concat([:Elixir, "DeprecatedPolymorphic#{module_suffix}Post"]) + option_name = + reference_option |> Atom.to_string() |> String.replace_prefix("polymorphic_", "") + + post = Module.concat([:"Elixir", "DeprecatedPolymorphic#{module_suffix}Post"]) reference = {reference_option, [], [reference_value]} warning_regex = ~r/#{reference_option}[^\n]*deprecated/i + warning = "Use `relationship_context: %{data_layer: %{references: %{#{option_name}: ...}}}` on the relationship instead." @@ -242,108 +307,57 @@ defmodule AshPostgres.MigrationGeneratorTest do end defmacrop assert_multiple_legacy_polymorphic_rejection( - module_suffix, - reference_option, - first_value, - second_value, - snapshot_path, - migration_path - ) do + module_suffix, + reference_option, + first_value, + second_value, + snapshot_path, + migration_path + ) do module_suffix = Macro.expand(module_suffix, __CALLER__) reference_option = Macro.expand(reference_option, __CALLER__) first_value = Macro.expand(first_value, __CALLER__) second_value = Macro.expand(second_value, __CALLER__) - option_name = reference_option |> Atom.to_string() |> String.replace_prefix("polymorphic_", "") - comment = Module.concat([:Elixir, "MultipleLegacyPolymorphic#{module_suffix}Comment"]) - first_post = Module.concat([:Elixir, "MultipleLegacyPolymorphic#{module_suffix}FirstPost"]) - second_post = Module.concat([:Elixir, "MultipleLegacyPolymorphic#{module_suffix}SecondPost"]) - domain = Module.concat([:Elixir, "MultipleLegacyPolymorphic#{module_suffix}Domain"]) + option_name = + reference_option |> Atom.to_string() |> String.replace_prefix("polymorphic_", "") + + comment = Module.concat([:"Elixir", "MultipleLegacyPolymorphic#{module_suffix}Comment"]) + first_post = Module.concat([:"Elixir", "MultipleLegacyPolymorphic#{module_suffix}FirstPost"]) + + second_post = + Module.concat([:"Elixir", "MultipleLegacyPolymorphic#{module_suffix}SecondPost"]) + + domain = Module.concat([:"Elixir", "MultipleLegacyPolymorphic#{module_suffix}Domain"]) post_table = "multiple_legacy_polymorphic_#{option_name}_posts" relationship_table = "multiple_legacy_polymorphic_#{option_name}_comments" first_reference = {reference_option, [], [first_value]} second_reference = {reference_option, [], [second_value]} quote do - defresource unquote(comment) do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource unquote(first_post) do - postgres do - table unquote(post_table) - repo(AshPostgres.TestRepo) - - references do - unquote(first_reference) - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, unquote(comment), - public?: true, - destination_attribute: :resource_id, - relationship_context: %{data_layer: %{table: unquote(relationship_table)}} - ) - end - end - - defresource unquote(second_post) do - postgres do - table unquote(post_table) - repo(AshPostgres.TestRepo) + defpolymorphic_comment(unquote(comment)) - references do - unquote(second_reference) - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, unquote(comment), - public?: true, - destination_attribute: :resource_id, - relationship_context: %{data_layer: %{table: unquote(relationship_table)}} - ) - end - end + defpolymorphic_post( + unquote(first_post), + unquote(post_table), + unquote(comment), + unquote(relationship_table), + reference: unquote(first_reference) + ) - defmodule unquote(domain) do - use Ash.Domain + defpolymorphic_post( + unquote(second_post), + unquote(post_table), + unquote(comment), + unquote(relationship_table), + reference: unquote(second_reference) + ) - resources do - resource(unquote(first_post)) - resource(unquote(second_post)) - resource(unquote(comment)) - end - end + defpolymorphic_domain(unquote(domain), [ + unquote(first_post), + unquote(second_post), + unquote(comment) + ]) assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> generate_resource_migration( @@ -357,20 +371,20 @@ defmodule AshPostgres.MigrationGeneratorTest do end defmacrop assert_relationship_context_polymorphic_reference( - module_suffix, - reference_option, - reference_value, - snapshot_path, - migration_path - ) do + module_suffix, + reference_option, + reference_value, + snapshot_path, + migration_path + ) do module_suffix = Macro.expand(module_suffix, __CALLER__) reference_option = Macro.expand(reference_option, __CALLER__) reference_value = Macro.expand(reference_value, __CALLER__) option_name = Atom.to_string(reference_option) - comment = Module.concat([:Elixir, "RelationshipContextPolymorphic#{module_suffix}Comment"]) - post = Module.concat([:Elixir, "RelationshipContextPolymorphic#{module_suffix}Post"]) - domain = Module.concat([:Elixir, "RelationshipContextPolymorphic#{module_suffix}Domain"]) + comment = Module.concat([:"Elixir", "RelationshipContextPolymorphic#{module_suffix}Comment"]) + post = Module.concat([:"Elixir", "RelationshipContextPolymorphic#{module_suffix}Post"]) + domain = Module.concat([:"Elixir", "RelationshipContextPolymorphic#{module_suffix}Domain"]) table = "relationship_context_polymorphic_#{option_name}_posts" relationship_table = "relationship_context_polymorphic_#{option_name}_comments" references = {:%{}, [], [{reference_option, reference_value}]} @@ -383,63 +397,20 @@ defmodule AshPostgres.MigrationGeneratorTest do end expected = - Regex.compile!( - "references\\(:#{table}\\b[^)]*#{option_name}: #{inspect(expected_value)}" - ) + Regex.compile!("references\\(:#{table}\\b[^)]*#{option_name}: #{inspect(expected_value)}") quote do - defresource unquote(comment) do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource unquote(post) do - postgres do - table unquote(table) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end + defpolymorphic_comment(unquote(comment)) - relationships do - has_many(:comments, unquote(comment), - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: unquote(relationship_table), - references: unquote(references) - } - } - ) - end - end - - defmodule unquote(domain) do - use Ash.Domain + defpolymorphic_post( + unquote(post), + unquote(table), + unquote(comment), + unquote(relationship_table), + relationship_references: unquote(references) + ) - resources do - resource(unquote(post)) - resource(unquote(comment)) - end - end + defpolymorphic_domain(unquote(domain), [unquote(post), unquote(comment)]) generate_resource_migration( unquote(domain), @@ -449,7 +420,10 @@ defmodule AshPostgres.MigrationGeneratorTest do ) current_migration_path = - Path.join(unquote(migration_path), unquote("relationship_context_polymorphic_#{option_name}")) + Path.join( + unquote(migration_path), + unquote("relationship_context_polymorphic_#{option_name}") + ) migration = read_resource_migration!(current_migration_path) assert migration =~ unquote(Macro.escape(expected)) @@ -457,14 +431,14 @@ defmodule AshPostgres.MigrationGeneratorTest do end defmacrop assert_legacy_and_relationship_context_conflict( - module_suffix, - legacy_option, - legacy_value, - relationship_option, - relationship_value, - snapshot_path, - migration_path - ) do + module_suffix, + legacy_option, + legacy_value, + relationship_option, + relationship_value, + snapshot_path, + migration_path + ) do module_suffix = Macro.expand(module_suffix, __CALLER__) legacy_option = Macro.expand(legacy_option, __CALLER__) legacy_value = Macro.expand(legacy_value, __CALLER__) @@ -472,71 +446,27 @@ defmodule AshPostgres.MigrationGeneratorTest do relationship_value = Macro.expand(relationship_value, __CALLER__) option_name = relationship_option |> Atom.to_string() - comment = Module.concat([:Elixir, "LegacyAndNewPolymorphic#{module_suffix}Comment"]) - post = Module.concat([:Elixir, "LegacyAndNewPolymorphic#{module_suffix}Post"]) - domain = Module.concat([:Elixir, "LegacyAndNewPolymorphic#{module_suffix}Domain"]) + comment = Module.concat([:"Elixir", "LegacyAndNewPolymorphic#{module_suffix}Comment"]) + post = Module.concat([:"Elixir", "LegacyAndNewPolymorphic#{module_suffix}Post"]) + domain = Module.concat([:"Elixir", "LegacyAndNewPolymorphic#{module_suffix}Domain"]) post_table = "legacy_and_new_polymorphic_#{option_name}_posts" relationship_table = "legacy_and_new_polymorphic_#{option_name}_comments" legacy_reference = {legacy_option, [], [legacy_value]} relationship_references = {:%{}, [], [{relationship_option, relationship_value}]} quote do - defresource unquote(comment) do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource unquote(post) do - postgres do - table unquote(post_table) - repo(AshPostgres.TestRepo) - - references do - unquote(legacy_reference) - end - end + defpolymorphic_comment(unquote(comment)) - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, unquote(comment), - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: unquote(relationship_table), - references: unquote(relationship_references) - } - } - ) - end - end - - defmodule unquote(domain) do - use Ash.Domain + defpolymorphic_post( + unquote(post), + unquote(post_table), + unquote(comment), + unquote(relationship_table), + reference: unquote(legacy_reference), + relationship_references: unquote(relationship_references) + ) - resources do - resource(unquote(post)) - resource(unquote(comment)) - end - end + defpolymorphic_domain(unquote(domain), [unquote(post), unquote(comment)]) assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> generate_resource_migration( @@ -564,13 +494,16 @@ defmodule AshPostgres.MigrationGeneratorTest do relationship_option = Macro.expand(relationship_option, __CALLER__) relationship_value = Macro.expand(relationship_value, __CALLER__) - comment = Module.concat([:Elixir, "CrossResourcePolymorphic#{module_suffix}Comment"]) - legacy_post = Module.concat([:Elixir, "CrossResourceLegacyPolymorphic#{module_suffix}Post"]) + comment = Module.concat([:"Elixir", "CrossResourcePolymorphic#{module_suffix}Comment"]) + legacy_post = Module.concat([:"Elixir", "CrossResourceLegacyPolymorphic#{module_suffix}Post"]) relationship_post = - Module.concat([:Elixir, "CrossResourceRelationshipContextPolymorphic#{module_suffix}Post"]) + Module.concat([ + :"Elixir", + "CrossResourceRelationshipContextPolymorphic#{module_suffix}Post" + ]) - domain = Module.concat([:Elixir, "CrossResourcePolymorphic#{module_suffix}Domain"]) + domain = Module.concat([:"Elixir", "CrossResourcePolymorphic#{module_suffix}Domain"]) migration_name = "cross_resource_polymorphic_#{String.downcase(module_suffix)}" post_table = "#{migration_name}_posts" relationship_table = "#{migration_name}_comments" @@ -578,88 +511,29 @@ defmodule AshPostgres.MigrationGeneratorTest do relationship_references = {:%{}, [], [{relationship_option, relationship_value}]} quote do - defresource unquote(comment) do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource unquote(legacy_post) do - postgres do - table unquote(post_table) - repo(AshPostgres.TestRepo) - - references do - unquote(legacy_reference) - end - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, unquote(comment), - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: unquote(relationship_table)} - } - ) - end - end - - defresource unquote(relationship_post) do - postgres do - table unquote(post_table) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end + defpolymorphic_comment(unquote(comment)) - relationships do - has_many(:comments, unquote(comment), - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: unquote(relationship_table), - references: unquote(relationship_references) - } - } - ) - end - end + defpolymorphic_post( + unquote(legacy_post), + unquote(post_table), + unquote(comment), + unquote(relationship_table), + reference: unquote(legacy_reference) + ) - defmodule unquote(domain) do - use Ash.Domain + defpolymorphic_post( + unquote(relationship_post), + unquote(post_table), + unquote(comment), + unquote(relationship_table), + relationship_references: unquote(relationship_references) + ) - resources do - resource(unquote(legacy_post)) - resource(unquote(relationship_post)) - resource(unquote(comment)) - end - end + defpolymorphic_domain(unquote(domain), [ + unquote(legacy_post), + unquote(relationship_post), + unquote(comment) + ]) assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> generate_resource_migration( @@ -686,13 +560,16 @@ defmodule AshPostgres.MigrationGeneratorTest do second_value = Macro.expand(second_value, __CALLER__) comment = - Module.concat([:Elixir, "MultipleRelationshipContextPolymorphic#{module_suffix}Comment"]) + Module.concat([:"Elixir", "MultipleRelationshipContextPolymorphic#{module_suffix}Comment"]) + + first_post = + Module.concat([:"Elixir", "FirstRelationshipContextPolymorphic#{module_suffix}Post"]) - first_post = Module.concat([:Elixir, "FirstRelationshipContextPolymorphic#{module_suffix}Post"]) - second_post = Module.concat([:Elixir, "SecondRelationshipContextPolymorphic#{module_suffix}Post"]) + second_post = + Module.concat([:"Elixir", "SecondRelationshipContextPolymorphic#{module_suffix}Post"]) domain = - Module.concat([:Elixir, "MultipleRelationshipContextPolymorphic#{module_suffix}Domain"]) + Module.concat([:"Elixir", "MultipleRelationshipContextPolymorphic#{module_suffix}Domain"]) migration_name = "multiple_relationship_context_polymorphic_#{String.downcase(module_suffix)}" post_table = "#{migration_name}_posts" @@ -701,87 +578,29 @@ defmodule AshPostgres.MigrationGeneratorTest do second_references = {:%{}, [], [{reference_option, second_value}]} quote do - defresource unquote(comment) do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource unquote(first_post) do - postgres do - table unquote(post_table) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end + defpolymorphic_comment(unquote(comment)) - relationships do - has_many(:comments, unquote(comment), - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: unquote(relationship_table), - references: unquote(first_references) - } - } - ) - end - end - - defresource unquote(second_post) do - postgres do - table unquote(post_table) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, unquote(comment), - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{ - table: unquote(relationship_table), - references: unquote(second_references) - } - } - ) - end - end + defpolymorphic_post( + unquote(first_post), + unquote(post_table), + unquote(comment), + unquote(relationship_table), + relationship_references: unquote(first_references) + ) - defmodule unquote(domain) do - use Ash.Domain + defpolymorphic_post( + unquote(second_post), + unquote(post_table), + unquote(comment), + unquote(relationship_table), + relationship_references: unquote(second_references) + ) - resources do - resource(unquote(first_post)) - resource(unquote(second_post)) - resource(unquote(comment)) - end - end + defpolymorphic_domain(unquote(domain), [ + unquote(first_post), + unquote(second_post), + unquote(comment) + ]) assert_raise RuntimeError, ~r/Conflicting configurations for references/, fn -> generate_resource_migration( @@ -803,9 +622,9 @@ defmodule AshPostgres.MigrationGeneratorTest do module_suffix = Macro.expand(module_suffix, __CALLER__) reference_option = Macro.expand(reference_option, __CALLER__) - comment = Module.concat([:Elixir, "DefaultPolymorphic#{module_suffix}Comment"]) - post = Module.concat([:Elixir, "DefaultPolymorphic#{module_suffix}Post"]) - domain = Module.concat([:Elixir, "DefaultPolymorphic#{module_suffix}Domain"]) + comment = Module.concat([:"Elixir", "DefaultPolymorphic#{module_suffix}Comment"]) + post = Module.concat([:"Elixir", "DefaultPolymorphic#{module_suffix}Post"]) + domain = Module.concat([:"Elixir", "DefaultPolymorphic#{module_suffix}Domain"]) migration_name = "default_polymorphic_#{String.downcase(module_suffix)}" post_table = "#{migration_name}_posts" relationship_table = "#{migration_name}_comments" @@ -818,55 +637,16 @@ defmodule AshPostgres.MigrationGeneratorTest do explicit_option = Regex.compile!("references\\(:#{post_table}\\b[^)]*#{reference_option}:") quote do - defresource unquote(comment) do - postgres do - polymorphic?(true) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - attribute(:resource_id, :uuid, public?: true) - end - - actions do - defaults([:create, :read, :update, :destroy]) - end - end - - defresource unquote(post) do - postgres do - table unquote(post_table) - repo(AshPostgres.TestRepo) - end - - attributes do - uuid_primary_key(:id) - end + defpolymorphic_comment(unquote(comment)) - actions do - defaults([:create, :read, :update, :destroy]) - end - - relationships do - has_many(:comments, unquote(comment), - public?: true, - destination_attribute: :resource_id, - relationship_context: %{ - data_layer: %{table: unquote(relationship_table)} - } - ) - end - end + defpolymorphic_post( + unquote(post), + unquote(post_table), + unquote(comment), + unquote(relationship_table) + ) - defmodule unquote(domain) do - use Ash.Domain - - resources do - resource(unquote(post)) - resource(unquote(comment)) - end - end + defpolymorphic_domain(unquote(domain), [unquote(post), unquote(comment)]) generate_resource_migration( unquote(domain), @@ -6447,10 +6227,11 @@ defmodule AshPostgres.MigrationGeneratorTest do ) end - test "on_delete within relationship_context generates the expected foreign-key delete behavior.", %{ - snapshot_path: snapshot_path, - migration_path: migration_path - } do + test "on_delete within relationship_context generates the expected foreign-key delete behavior.", + %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do assert_relationship_context_polymorphic_reference( "Delete", :on_delete, @@ -6476,10 +6257,11 @@ defmodule AshPostgres.MigrationGeneratorTest do ) end - test "Conflicting polymorphic_on_delete configurations across resources result in rejection.", %{ - snapshot_path: snapshot_path, - migration_path: migration_path - } do + test "Conflicting polymorphic_on_delete configurations across resources result in rejection.", + %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do assert_cross_resource_polymorphic_conflict( "Delete", :polymorphic_on_delete, @@ -6505,10 +6287,11 @@ defmodule AshPostgres.MigrationGeneratorTest do ) end - test "Omitting both polymorphic_on_delete and on_delete in relationship_context retains default behavior.", %{ - snapshot_path: snapshot_path, - migration_path: migration_path - } do + test "Omitting both polymorphic_on_delete and on_delete in relationship_context retains default behavior.", + %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do assert_default_polymorphic_reference( "Delete", :on_delete, @@ -6593,10 +6376,11 @@ defmodule AshPostgres.MigrationGeneratorTest do ) end - test "Conflicting polymorphic_on_update configurations across resources result in rejection.", %{ - snapshot_path: snapshot_path, - migration_path: migration_path - } do + test "Conflicting polymorphic_on_update configurations across resources result in rejection.", + %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do assert_cross_resource_polymorphic_conflict( "Update", :polymorphic_on_update, @@ -6622,10 +6406,11 @@ defmodule AshPostgres.MigrationGeneratorTest do ) end - test "Omitting both polymorphic_on_update and on_update in relationship_context retains default behavior.", %{ - snapshot_path: snapshot_path, - migration_path: migration_path - } do + test "Omitting both polymorphic_on_update and on_update in relationship_context retains default behavior.", + %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do assert_default_polymorphic_reference( "Update", :on_update, @@ -6636,10 +6421,11 @@ defmodule AshPostgres.MigrationGeneratorTest do # -- polymorphic_name -- - test "Legacy polymorphic_name uses the configured name for generated polymorphic foreign-key constraints.", %{ - snapshot_path: snapshot_path, - migration_path: migration_path - } do + test "Legacy polymorphic_name uses the configured name for generated polymorphic foreign-key constraints.", + %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do deflegacy_polymorphic_resources( "Name", "legacy_polymorphic_name_posts", @@ -6743,10 +6529,11 @@ defmodule AshPostgres.MigrationGeneratorTest do ) end - test "Omitting both polymorphic_name and name in relationship_context retains default behavior.", %{ - snapshot_path: snapshot_path, - migration_path: migration_path - } do + test "Omitting both polymorphic_name and name in relationship_context retains default behavior.", + %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do assert_default_polymorphic_reference( "Name", :name, From 85f8ee2290d6d5e9bde1c248593f7960719825d1 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 5 Nov 2026 16:35:44 -0700 Subject: [PATCH 36/47] tests: Update comments --- test/migration_generator_test.exs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index ac0763de..bd4285c3 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -6181,7 +6181,7 @@ defmodule AshPostgres.MigrationGeneratorTest do ~S[references(:post_comments, column: :id, name: "posts_best_comment_id_fkey", type: :uuid, prefix: "public")] end - # -- polymorphic_on_delete -- + # Tests for polymorphic_on_delete test "Legacy polymorphic_on_delete generates the expected foreign-key delete behavior.", %{ snapshot_path: snapshot_path, @@ -6300,7 +6300,7 @@ defmodule AshPostgres.MigrationGeneratorTest do ) end - # -- polymorphic_on_update -- + # Tests for polymorphic_on_update test "Legacy polymorphic_on_update generates the expected foreign-key update behavior.", %{ snapshot_path: snapshot_path, @@ -6419,7 +6419,7 @@ defmodule AshPostgres.MigrationGeneratorTest do ) end - # -- polymorphic_name -- + # Tests for polymorphic_name test "Legacy polymorphic_name uses the configured name for generated polymorphic foreign-key constraints.", %{ From 7b5c6d7fae837382a2716df563bb5fb979e4e785 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 5 Nov 2026 17:19:14 -0700 Subject: [PATCH 37/47] test: Refactor Legacy tests to use macros. --- test/migration_generator_test.exs | 96 ++++++++++++++----------------- 1 file changed, 42 insertions(+), 54 deletions(-) diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index bd4285c3..3ba02bea 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -230,24 +230,40 @@ defmodule AshPostgres.MigrationGeneratorTest do end end - defmacrop deflegacy_polymorphic_resources( + defmacrop assert_legacy_polymorphic_reference( module_suffix, - post_table, - relationship_table, reference_option, - reference_value + reference_value, + snapshot_path, + migration_path ) do module_suffix = Macro.expand(module_suffix, __CALLER__) - post_table = Macro.expand(post_table, __CALLER__) - relationship_table = Macro.expand(relationship_table, __CALLER__) reference_option = Macro.expand(reference_option, __CALLER__) reference_value = Macro.expand(reference_value, __CALLER__) + option_name = + reference_option |> Atom.to_string() |> String.replace_prefix("polymorphic_", "") + comment = Module.concat([:"Elixir", "LegacyPolymorphic#{module_suffix}Comment"]) post = Module.concat([:"Elixir", "LegacyPolymorphic#{module_suffix}Post"]) domain = Module.concat([:"Elixir", "LegacyPolymorphic#{module_suffix}Domain"]) + migration_name = "legacy_polymorphic_#{option_name}" + post_table = "#{migration_name}_posts" + relationship_table = "#{migration_name}_comments" reference = {reference_option, [], [reference_value]} + expected_value = + case reference_option do + :polymorphic_on_delete -> :delete_all + :polymorphic_on_update -> :update_all + :polymorphic_name -> reference_value + end + + expected = + Regex.compile!( + "references\\(:#{post_table}\\b[^)]*#{option_name}: #{inspect(expected_value)}" + ) + quote do defpolymorphic_comment(unquote(comment)) @@ -260,6 +276,17 @@ defmodule AshPostgres.MigrationGeneratorTest do ) defpolymorphic_domain(unquote(domain), [unquote(post), unquote(comment)]) + + generate_resource_migration( + unquote(domain), + unquote(snapshot_path), + unquote(migration_path), + unquote(migration_name) + ) + + current_migration_path = Path.join(unquote(migration_path), unquote(migration_name)) + migration = read_resource_migration!(current_migration_path) + assert migration =~ unquote(Macro.escape(expected)) end end @@ -6187,26 +6214,13 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - deflegacy_polymorphic_resources( + assert_legacy_polymorphic_reference( "Delete", - "legacy_polymorphic_posts", - "legacy_post_comments", :polymorphic_on_delete, - :delete - ) - - generate_resource_migration( - Elixir.LegacyPolymorphicDeleteDomain, + :delete, snapshot_path, - migration_path, - "legacy_polymorphic" + migration_path ) - - current_migration_path = Path.join(migration_path, "legacy_polymorphic") - migration = read_resource_migration!(current_migration_path) - - assert migration =~ - ~r/references\(:legacy_polymorphic_posts\b[^)]*on_delete: :delete_all/ end test "Legacy polymorphic_on_delete emits a deprecation warning." do @@ -6306,26 +6320,13 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - deflegacy_polymorphic_resources( + assert_legacy_polymorphic_reference( "Update", - "legacy_polymorphic_update_posts", - "legacy_update_post_comments", :polymorphic_on_update, - :update - ) - - generate_resource_migration( - Elixir.LegacyPolymorphicUpdateDomain, + :update, snapshot_path, - migration_path, - "legacy_polymorphic_update" + migration_path ) - - current_migration_path = Path.join(migration_path, "legacy_polymorphic_update") - migration = read_resource_migration!(current_migration_path) - - assert migration =~ - ~r/references\(:legacy_polymorphic_update_posts\b[^)]*on_update: :update_all/ end test "Legacy polymorphic_on_update emits a deprecation warning." do @@ -6426,26 +6427,13 @@ defmodule AshPostgres.MigrationGeneratorTest do snapshot_path: snapshot_path, migration_path: migration_path } do - deflegacy_polymorphic_resources( + assert_legacy_polymorphic_reference( "Name", - "legacy_polymorphic_name_posts", - "legacy_polymorphic_name_comments", :polymorphic_name, - "custom_polymorphic_name_fkey" - ) - - generate_resource_migration( - Elixir.LegacyPolymorphicNameDomain, + "custom_polymorphic_name_fkey", snapshot_path, - migration_path, - "legacy_polymorphic_name" + migration_path ) - - current_migration_path = Path.join(migration_path, "legacy_polymorphic_name") - migration = read_resource_migration!(current_migration_path) - - assert migration =~ - ~r/references\(:legacy_polymorphic_name_posts\b[^)]*name: "custom_polymorphic_name_fkey"/ end test "Legacy polymorphic_name emits a deprecation warning." do From 4315805472735715516645071d19998b3f808668 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Thu, 5 Nov 2026 18:41:27 -0700 Subject: [PATCH 38/47] fix: Add documentation for polymorphic name and add polymorphic name to .formatter.exs. --- .formatter.exs | 1 + documentation/dsls/DSL-AshPostgres.DataLayer.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.formatter.exs b/.formatter.exs index 28d567b0..c351e201 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -44,6 +44,7 @@ spark_locals_without_parens = [ on_update: 1, on_update_message: 1, polymorphic?: 1, + polymorphic_name: 1, polymorphic_on_delete: 1, polymorphic_on_update: 1, prefix: 1, diff --git a/documentation/dsls/DSL-AshPostgres.DataLayer.md b/documentation/dsls/DSL-AshPostgres.DataLayer.md index fe9c3e4a..4b759805 100644 --- a/documentation/dsls/DSL-AshPostgres.DataLayer.md +++ b/documentation/dsls/DSL-AshPostgres.DataLayer.md @@ -283,6 +283,7 @@ end |------|------|---------|------| | [`polymorphic_on_delete`](#postgres-references-polymorphic_on_delete){: #postgres-references-polymorphic_on_delete } | `:delete \| :nilify \| :nothing \| :restrict \| {:nilify, atom \| list(atom)}` | | For polymorphic resources, configures the on_delete behavior of the automatically generated foreign keys to source tables. | | [`polymorphic_on_update`](#postgres-references-polymorphic_on_update){: #postgres-references-polymorphic_on_update } | `:update \| :nilify \| :nothing \| :restrict` | | For polymorphic resources, configures the on_update behavior of the automatically generated foreign keys to source tables. | +| [`polymorphic_name`](#postgres-references-polymorphic_name){: #postgres-references-polymorphic_name } | `String.t` | | For polymorphic resources, configures the name of the generated foreign-key constraint to the source table. | From 4ed262c711165f1a7a657629a4ca617ac3e5828e Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Sat, 7 Nov 2026 20:27:43 -0700 Subject: [PATCH 39/47] fix: Update data_layer.ex with more depreciation documentation. --- lib/data_layer.ex | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/data_layer.ex b/lib/data_layer.ex index 9594b433..f0b75f5e 100644 --- a/lib/data_layer.ex +++ b/lib/data_layer.ex @@ -183,17 +183,17 @@ defmodule AshPostgres.DataLayer do {:tagged_tuple, :nilify, {:wrap_list, :atom}} ]}, doc: - "For polymorphic resources, configures the on_delete behavior of the automatically generated foreign keys to source tables." + "Deprecated. For polymorphic resources, configures the on_delete behavior of the automatically generated foreign keys to source tables. Use relationship_context: %{data_layer: %{references: %{on_delete: ...}}} on the relationship instead." ], polymorphic_on_update: [ type: {:one_of, [:update, :nilify, :nothing, :restrict]}, doc: - "For polymorphic resources, configures the on_update behavior of the automatically generated foreign keys to source tables." + "Deprecated. For polymorphic resources, configures the on_update behavior of the automatically generated foreign keys to source tables. Use relationship_context: %{data_layer: %{references: %{on_update: ...}}} on the relationship instead." ], polymorphic_name: [ type: :string, doc: - "For polymorphic resources, then index name to use for the foreign key to the source table." + "Deprecated. For polymorphic resources, configures the name of the foreign-key constraint to the source table. Use relationship_context: %{data_layer: %{references: %{name: ...}}} on the relationship instead." ] ] } @@ -269,17 +269,17 @@ defmodule AshPostgres.DataLayer do {:tagged_tuple, :nilify, {:wrap_list, :atom}} ]}, doc: - "For polymorphic resources, configures the on_delete behavior of the automatically generated foreign keys to source tables." + "Deprecated. For polymorphic resources, configures the on_delete behavior of the automatically generated foreign keys to source tables. Use relationship_context: %{data_layer: %{references: %{on_delete: ...}}} on the relationship instead." ], polymorphic_on_update: [ type: {:one_of, [:update, :nilify, :nothing, :restrict]}, doc: - "For polymorphic resources, configures the on_update behavior of the automatically generated foreign keys to source tables." + "Deprecated. For polymorphic resources, configures the on_update behavior of the automatically generated foreign keys to source tables. Use relationship_context: %{data_layer: %{references: %{on_update: ...}}} on the relationship instead." ], polymorphic_name: [ type: :string, doc: - "For polymorphic resources, configures the name of the generated foreign-key constraint to the source table." + "Deprecated. For polymorphic resources, configures the name of the generated foreign-key constraint to the source table. Use relationship_context: %{data_layer: %{references: %{name: ...}}} on the relationship instead." ] ] } From 210ca3ae88caf5c7d1df51e7b39834c46911054f Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Sat, 7 Nov 2026 21:48:43 -0700 Subject: [PATCH 40/47] fix: Update deprecation descriptions. --- lib/data_layer.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/data_layer.ex b/lib/data_layer.ex index f0b75f5e..0b728de2 100644 --- a/lib/data_layer.ex +++ b/lib/data_layer.ex @@ -269,17 +269,17 @@ defmodule AshPostgres.DataLayer do {:tagged_tuple, :nilify, {:wrap_list, :atom}} ]}, doc: - "Deprecated. For polymorphic resources, configures the on_delete behavior of the automatically generated foreign keys to source tables. Use relationship_context: %{data_layer: %{references: %{on_delete: ...}}} on the relationship instead." + "For polymorphic resources, configures the `on_delete` behavior of the automatically generated foreign keys to source tables. This option is deprecated; use `relationship_context: %{data_layer: %{references: %{on_delete: ...}}}` on the relationship instead." ], polymorphic_on_update: [ type: {:one_of, [:update, :nilify, :nothing, :restrict]}, doc: - "Deprecated. For polymorphic resources, configures the on_update behavior of the automatically generated foreign keys to source tables. Use relationship_context: %{data_layer: %{references: %{on_update: ...}}} on the relationship instead." + "For polymorphic resources, configures the `on_update` behavior of the automatically generated foreign keys to source tables. This option is deprecated; use `relationship_context: %{data_layer: %{references: %{on_update: ...}}}` on the relationship instead." ], polymorphic_name: [ type: :string, doc: - "Deprecated. For polymorphic resources, configures the name of the generated foreign-key constraint to the source table. Use relationship_context: %{data_layer: %{references: %{name: ...}}} on the relationship instead." + "For polymorphic resources, configures the name of the generated foreign-key constraint to the source table. This option is deprecated; use `relationship_context: %{data_layer: %{references: %{name: ...}}}` on the relationship instead." ] ] } From 6cef26a76b08c978bee6f098bf57bb20b0aa942a Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Sat, 7 Nov 2026 22:02:02 -0700 Subject: [PATCH 41/47] fix: Update DSL-AshPostgres.DataLayer.md with deprecation notices. --- documentation/dsls/DSL-AshPostgres.DataLayer.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/dsls/DSL-AshPostgres.DataLayer.md b/documentation/dsls/DSL-AshPostgres.DataLayer.md index 4b759805..8ae2c8dd 100644 --- a/documentation/dsls/DSL-AshPostgres.DataLayer.md +++ b/documentation/dsls/DSL-AshPostgres.DataLayer.md @@ -281,9 +281,9 @@ end | Name | Type | Default | Docs | |------|------|---------|------| -| [`polymorphic_on_delete`](#postgres-references-polymorphic_on_delete){: #postgres-references-polymorphic_on_delete } | `:delete \| :nilify \| :nothing \| :restrict \| {:nilify, atom \| list(atom)}` | | For polymorphic resources, configures the on_delete behavior of the automatically generated foreign keys to source tables. | -| [`polymorphic_on_update`](#postgres-references-polymorphic_on_update){: #postgres-references-polymorphic_on_update } | `:update \| :nilify \| :nothing \| :restrict` | | For polymorphic resources, configures the on_update behavior of the automatically generated foreign keys to source tables. | -| [`polymorphic_name`](#postgres-references-polymorphic_name){: #postgres-references-polymorphic_name } | `String.t` | | For polymorphic resources, configures the name of the generated foreign-key constraint to the source table. | +| [`polymorphic_on_delete`](#postgres-references-polymorphic_on_delete){: #postgres-references-polymorphic_on_delete } | `:delete \| :nilify \| :nothing \| :restrict \| {:nilify, atom \| list(atom)}` | | For polymorphic resources, configures the `on_delete` behavior of the automatically generated foreign keys to source tables. This option is deprecated; use `relationship_context: %{data_layer: %{references: %{on_delete: ...}}}` on the relationship instead. | +| [`polymorphic_on_update`](#postgres-references-polymorphic_on_update){: #postgres-references-polymorphic_on_update } | `:update \| :nilify \| :nothing \| :restrict` | | For polymorphic resources, configures the `on_update` behavior of the automatically generated foreign keys to source tables. This option is deprecated; use `relationship_context: %{data_layer: %{references: %{on_update: ...}}}` on the relationship instead. | +| [`polymorphic_name`](#postgres-references-polymorphic_name){: #postgres-references-polymorphic_name } | `String.t` | | For polymorphic resources, configures the name of the generated foreign-key constraint to the source table. This option is deprecated; use `relationship_context: %{data_layer: %{references: %{name: ...}}}` on the relationship instead. | From db094b675a9103ddac13eba93f0dab1c267f0b70 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Sat, 7 Nov 2026 22:23:01 -0700 Subject: [PATCH 42/47] fix: Add deprecation warning in data_layer.ex. --- lib/data_layer.ex | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/data_layer.ex b/lib/data_layer.ex index 0b728de2..fe62025f 100644 --- a/lib/data_layer.ex +++ b/lib/data_layer.ex @@ -165,6 +165,12 @@ defmodule AshPostgres.DataLayer do This section is only relevant if you are using the migration generator with this resource. Otherwise, it has no effect. + + > ### Deprecated polymorphic options {: .warning} + > + > `polymorphic_on_delete`, `polymorphic_on_update`, and `polymorphic_name` are + > deprecated but remain supported. Use the relationship context described in + > [Polymorphic references](polymorphic-resources.md#polymorphic-references) instead. """, examples: [ """ From b19f5a065943687de50a2144410ca59473ef6b3c Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Sat, 7 Nov 2026 23:03:28 -0700 Subject: [PATCH 43/47] fix: Move deprecation descriptions to correct locations. --- .../dsls/DSL-AshPostgres.DataLayer.md | 6 ++++++ lib/data_layer.ex | 18 +++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/documentation/dsls/DSL-AshPostgres.DataLayer.md b/documentation/dsls/DSL-AshPostgres.DataLayer.md index 8ae2c8dd..a73079a5 100644 --- a/documentation/dsls/DSL-AshPostgres.DataLayer.md +++ b/documentation/dsls/DSL-AshPostgres.DataLayer.md @@ -261,6 +261,12 @@ A section for configuring the references (foreign keys) in resource migrations. This section is only relevant if you are using the migration generator with this resource. Otherwise, it has no effect. +> ### Deprecated polymorphic options {: .warning} +> +> `polymorphic_on_delete`, `polymorphic_on_update`, and `polymorphic_name` are +> deprecated but remain supported. Use the relationship context described in +> [Polymorphic references](./../topics/resources/polymorphic-resources.md#polymorphic-references) instead. + ### Nested DSLs * [reference](#postgres-references-reference) diff --git a/lib/data_layer.ex b/lib/data_layer.ex index fe62025f..c50f0379 100644 --- a/lib/data_layer.ex +++ b/lib/data_layer.ex @@ -165,12 +165,6 @@ defmodule AshPostgres.DataLayer do This section is only relevant if you are using the migration generator with this resource. Otherwise, it has no effect. - - > ### Deprecated polymorphic options {: .warning} - > - > `polymorphic_on_delete`, `polymorphic_on_update`, and `polymorphic_name` are - > deprecated but remain supported. Use the relationship context described in - > [Polymorphic references](polymorphic-resources.md#polymorphic-references) instead. """, examples: [ """ @@ -189,17 +183,17 @@ defmodule AshPostgres.DataLayer do {:tagged_tuple, :nilify, {:wrap_list, :atom}} ]}, doc: - "Deprecated. For polymorphic resources, configures the on_delete behavior of the automatically generated foreign keys to source tables. Use relationship_context: %{data_layer: %{references: %{on_delete: ...}}} on the relationship instead." + "For polymorphic resources, configures the on_delete behavior of the automatically generated foreign keys to source tables." ], polymorphic_on_update: [ type: {:one_of, [:update, :nilify, :nothing, :restrict]}, doc: - "Deprecated. For polymorphic resources, configures the on_update behavior of the automatically generated foreign keys to source tables. Use relationship_context: %{data_layer: %{references: %{on_update: ...}}} on the relationship instead." + "For polymorphic resources, configures the on_update behavior of the automatically generated foreign keys to source tables." ], polymorphic_name: [ type: :string, doc: - "Deprecated. For polymorphic resources, configures the name of the foreign-key constraint to the source table. Use relationship_context: %{data_layer: %{references: %{name: ...}}} on the relationship instead." + "For polymorphic resources, configures the name of the foreign-key constraint to the source table." ] ] } @@ -257,6 +251,12 @@ defmodule AshPostgres.DataLayer do This section is only relevant if you are using the migration generator with this resource. Otherwise, it has no effect. + + > ### Deprecated polymorphic options {: .warning} + > + > `polymorphic_on_delete`, `polymorphic_on_update`, and `polymorphic_name` are + > deprecated but remain supported. Use the relationship context described in + > [Polymorphic references](./../topics/resources/polymorphic-resources.md#polymorphic-references) instead. """, examples: [ """ From ec73af6aab0d10b9fe8d06116a26f0cf968ff5af Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Mon, 9 Nov 2026 17:18:01 -0700 Subject: [PATCH 44/47] documentation: add 'Polymorphic references' section in documentation/topics/resources/polymorphic-resources.md. --- .../topics/resources/polymorphic-resources.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/documentation/topics/resources/polymorphic-resources.md b/documentation/topics/resources/polymorphic-resources.md index 9f6d11ac..ca03260f 100644 --- a/documentation/topics/resources/polymorphic-resources.md +++ b/documentation/topics/resources/polymorphic-resources.md @@ -62,6 +62,41 @@ With this, when loading or editing related data, ash will automatically set that For managing related data, see `Ash.Changeset.manage_relationship/4` and other relationship functions in `Ash.Changeset` +## Polymorphic references + +For each relationship from a source resource to a polymorphic resource, AshPostgres generates a foreign key that points to the source resource’s table. This foreign key is created in the `table` specified by the relationship’s `data_layer` context. + +To configure the foreign key, add a `references` map alongside `table` in the relationship’s `data_layer` context: + +```elixir +relationships do + has_many :event_locations, MyApp.EventLocation, + relationship_context: %{ + data_layer: %{ + table: "event_locations", + references: %{ + on_delete: :delete, + on_update: :update, + name: "event_locations_event_id_fkey" + } + } + }, + destination_attribute: :event_id +end +``` + +### Options + +The following options go inside `relationship_context.data_layer.references`: + +| Name | Type | Default | Docs | +|------|------|---------|------| +| `on_delete` | `:delete \| :nilify \| :nothing \| :restrict \| {:nilify, atom \| list(atom)}` | `:nothing` | Configures what happens when a source-table row is deleted. | +| `on_update` | `:update \| :nilify \| :nothing \| :restrict` | `:nothing` | Configures what happens when a source-table row is updated. | +| `name` | `String.t` | generated | Sets the name of the generated foreign-key constraint. | + +The legacy `polymorphic_on_delete`, `polymorphic_on_update`, and `polymorphic_name` options are deprecated but remain supported for backwards compatibility. New configurations should use relationship_context.data_layer.references. See the [DSL reference](../../dsls/DSL-AshPostgres.DataLayer.md#postgres-references) for the legacy options. + ## Table specific actions To make actions use a specific table, you can use the `set_context` query preparation/change. @@ -90,3 +125,6 @@ all resources that are related to it, that set the `%{data_layer: %{table: "tabl For each of those, a migration is generated/managed automatically. This means that adding reactions to a new resource is as easy as adding the relationship and table context, and then running `mix ash.codegen`. + +To configure the foreign keys generated for these tables, see +[Polymorphic references](#polymorphic-references). From 23ecbff6f3c12d2fc96697273ccd0b2decc38b8b Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Mon, 9 Nov 2026 17:41:01 -0700 Subject: [PATCH 45/47] documentation: add 'Polymorphic references' documentation. --- documentation/topics/resources/polymorphic-resources.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/documentation/topics/resources/polymorphic-resources.md b/documentation/topics/resources/polymorphic-resources.md index ca03260f..89e4de6c 100644 --- a/documentation/topics/resources/polymorphic-resources.md +++ b/documentation/topics/resources/polymorphic-resources.md @@ -64,7 +64,7 @@ in `Ash.Changeset` ## Polymorphic references -For each relationship from a source resource to a polymorphic resource, AshPostgres generates a foreign key that points to the source resource’s table. This foreign key is created in the `table` specified by the relationship’s `data_layer` context. +For each `has_many` or `has_one` relationship from a source resource to a polymorphic resource that supplies a table context, AshPostgres generates a foreign key that points to the source resource’s table. This foreign key is created in the `table` specified by the relationship’s `data_layer` context. To configure the foreign key, add a `references` map alongside `table` in the relationship’s `data_layer` context: @@ -92,10 +92,12 @@ The following options go inside `relationship_context.data_layer.references`: | Name | Type | Default | Docs | |------|------|---------|------| | `on_delete` | `:delete \| :nilify \| :nothing \| :restrict \| {:nilify, atom \| list(atom)}` | `:nothing` | Configures what happens when a source-table row is deleted. | -| `on_update` | `:update \| :nilify \| :nothing \| :restrict` | `:nothing` | Configures what happens when a source-table row is updated. | +| `on_update` | `:update \| :nilify \| :nothing \| :restrict` | `:nothing` | Configures what happens when the referenced column in the source table is updated. | | `name` | `String.t` | generated | Sets the name of the generated foreign-key constraint. | -The legacy `polymorphic_on_delete`, `polymorphic_on_update`, and `polymorphic_name` options are deprecated but remain supported for backwards compatibility. New configurations should use relationship_context.data_layer.references. See the [DSL reference](../../dsls/DSL-AshPostgres.DataLayer.md#postgres-references) for the legacy options. +The legacy `polymorphic_on_delete`, `polymorphic_on_update`, and `polymorphic_name` options are deprecated but remain supported for backwards compatibility. New configurations should use `relationship_context.data_layer.references`. See the [DSL reference](../../dsls/DSL-AshPostgres.DataLayer.md#postgres-references) for the legacy options. + +Remove the corresponding legacy option when moving a setting into the relationship context. Configuring both forms for the same setting raises an error, even when their values match. ## Table specific actions From b1128d0ac1eb7cdbb8695ed04902c82d11820e44 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Mon, 9 Nov 2026 17:50:07 -0700 Subject: [PATCH 46/47] documentation: Move 'Polymorphic references' section to a better location. --- .../topics/resources/polymorphic-resources.md | 61 +++++++++---------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/documentation/topics/resources/polymorphic-resources.md b/documentation/topics/resources/polymorphic-resources.md index 89e4de6c..f677b0a6 100644 --- a/documentation/topics/resources/polymorphic-resources.md +++ b/documentation/topics/resources/polymorphic-resources.md @@ -62,6 +62,35 @@ With this, when loading or editing related data, ash will automatically set that For managing related data, see `Ash.Changeset.manage_relationship/4` and other relationship functions in `Ash.Changeset` +## Table specific actions + +To make actions use a specific table, you can use the `set_context` query preparation/change. + +For example: + +```elixir +defmodule MyApp.Reaction do + # ... + actions do + read :for_comments do + prepare set_context(%{data_layer: %{table: "comment_reactions"}}) + end + + read :for_posts do + prepare set_context(%{data_layer: %{table: "post_reactions"}}) + end + end +end +``` + +## Migrations + +When a migration is marked as `polymorphic? true`, the migration generator will look at +all resources that are related to it, that set the `%{data_layer: %{table: "table"}}` context. +For each of those, a migration is generated/managed automatically. This means that adding reactions +to a new resource is as easy as adding the relationship and table context, and then running +`mix ash.codegen`. + ## Polymorphic references For each `has_many` or `has_one` relationship from a source resource to a polymorphic resource that supplies a table context, AshPostgres generates a foreign key that points to the source resource’s table. This foreign key is created in the `table` specified by the relationship’s `data_layer` context. @@ -98,35 +127,3 @@ The following options go inside `relationship_context.data_layer.references`: The legacy `polymorphic_on_delete`, `polymorphic_on_update`, and `polymorphic_name` options are deprecated but remain supported for backwards compatibility. New configurations should use `relationship_context.data_layer.references`. See the [DSL reference](../../dsls/DSL-AshPostgres.DataLayer.md#postgres-references) for the legacy options. Remove the corresponding legacy option when moving a setting into the relationship context. Configuring both forms for the same setting raises an error, even when their values match. - -## Table specific actions - -To make actions use a specific table, you can use the `set_context` query preparation/change. - -For example: - -```elixir -defmodule MyApp.Reaction do - # ... - actions do - read :for_comments do - prepare set_context(%{data_layer: %{table: "comment_reactions"}}) - end - - read :for_posts do - prepare set_context(%{data_layer: %{table: "post_reactions"}}) - end - end -end -``` - -## Migrations - -When a migration is marked as `polymorphic? true`, the migration generator will look at -all resources that are related to it, that set the `%{data_layer: %{table: "table"}}` context. -For each of those, a migration is generated/managed automatically. This means that adding reactions -to a new resource is as easy as adding the relationship and table context, and then running -`mix ash.codegen`. - -To configure the foreign keys generated for these tables, see -[Polymorphic references](#polymorphic-references). From 9b5c59bd6acf924ea53285d3d52412fdda235a06 Mon Sep 17 00:00:00 2001 From: Andrew <84246113+arstamm@users.noreply.github.com> Date: Tue, 10 Nov 2026 17:28:08 -0700 Subject: [PATCH 47/47] fix: Remove polymorphic_on_delete from label.ex. --- test/support/resources/label.ex | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/support/resources/label.ex b/test/support/resources/label.ex index 100dcf83..b33aad26 100644 --- a/test/support/resources/label.ex +++ b/test/support/resources/label.ex @@ -17,10 +17,6 @@ defmodule AshPostgres.Test.Label do postgres do table "labels" repo AshPostgres.TestRepo - - references do - polymorphic_on_delete :delete - end end actions do