defmodule Mv.Membership.CustomFieldDeletionTest do @moduledoc """ Tests for CustomField deletion with CASCADE behavior. Tests cover: - Deletion of custom fields without assigned values - Deletion of custom fields with assigned values (CASCADE) - assigned_members_count calculation - prepare_deletion action with count loading - CASCADE deletion only affects specific custom field values """ use Mv.DataCase, async: true alias Mv.Membership.{CustomField, CustomFieldValue, Member} describe "assigned_members_count calculation" do test "returns 0 for custom field without any values" do {:ok, custom_field} = CustomField |> Ash.Changeset.for_create(:create, %{ name: "test_field", value_type: :string }) |> Ash.create() custom_field_with_count = Ash.load!(custom_field, :assigned_members_count) assert custom_field_with_count.assigned_members_count == 0 end test "returns correct count for custom field with one member" do {:ok, member} = create_member() {:ok, custom_field} = create_custom_field("test_field", :string) {:ok, _custom_field_value} = CustomFieldValue |> Ash.Changeset.for_create(:create, %{ member_id: member.id, custom_field_id: custom_field.id, value: %{"_union_type" => "string", "_union_value" => "test"} }) |> Ash.create() custom_field_with_count = Ash.load!(custom_field, :assigned_members_count) assert custom_field_with_count.assigned_members_count == 1 end test "returns correct count for custom field with multiple members" do {:ok, member1} = create_member() {:ok, member2} = create_member() {:ok, member3} = create_member() {:ok, custom_field} = create_custom_field("test_field", :string) # Create custom field value for each member for member <- [member1, member2, member3] do {:ok, _} = CustomFieldValue |> Ash.Changeset.for_create(:create, %{ member_id: member.id, custom_field_id: custom_field.id, value: %{"_union_type" => "string", "_union_value" => "test"} }) |> Ash.create() end custom_field_with_count = Ash.load!(custom_field, :assigned_members_count) assert custom_field_with_count.assigned_members_count == 3 end test "counts distinct members (not multiple values per member)" do {:ok, member} = create_member() {:ok, custom_field} = create_custom_field("test_field", :string) # Create custom field value for member {:ok, _} = CustomFieldValue |> Ash.Changeset.for_create(:create, %{ member_id: member.id, custom_field_id: custom_field.id, value: %{"_union_type" => "string", "_union_value" => "test"} }) |> Ash.create() custom_field_with_count = Ash.load!(custom_field, :assigned_members_count) # Should still be 1, not 2, even if we tried to create multiple (which would fail due to uniqueness) assert custom_field_with_count.assigned_members_count == 1 end end describe "prepare_deletion action" do test "loads assigned_members_count for deletion preparation" do {:ok, member} = create_member() {:ok, custom_field} = create_custom_field("test_field", :string) {:ok, _} = CustomFieldValue |> Ash.Changeset.for_create(:create, %{ member_id: member.id, custom_field_id: custom_field.id, value: %{"_union_type" => "string", "_union_value" => "test"} }) |> Ash.create() # Use prepare_deletion action [prepared_custom_field] = CustomField |> Ash.Query.for_read(:prepare_deletion, %{id: custom_field.id}) |> Ash.read!() assert prepared_custom_field.assigned_members_count == 1 assert prepared_custom_field.id == custom_field.id end test "returns empty list for non-existent custom field" do non_existent_id = Ash.UUID.generate() result = CustomField |> Ash.Query.for_read(:prepare_deletion, %{id: non_existent_id}) |> Ash.read!() assert result == [] end end describe "destroy_with_values action" do test "deletes custom field without any values" do {:ok, custom_field} = create_custom_field("test_field", :string) assert :ok = Ash.destroy(custom_field) # Verify custom field is deleted assert {:error, _} = Ash.get(CustomField, custom_field.id) end test "deletes custom field and cascades to all its values" do {:ok, member} = create_member() {:ok, custom_field} = create_custom_field("test_field", :string) {:ok, custom_field_value} = CustomFieldValue |> Ash.Changeset.for_create(:create, %{ member_id: member.id, custom_field_id: custom_field.id, value: %{"_union_type" => "string", "_union_value" => "test"} }) |> Ash.create() # Delete custom field assert :ok = Ash.destroy(custom_field) # Verify custom field is deleted assert {:error, _} = Ash.get(CustomField, custom_field.id) # Verify custom field value is also deleted (CASCADE) assert {:error, _} = Ash.get(CustomFieldValue, custom_field_value.id) # Verify member still exists assert {:ok, _} = Ash.get(Member, member.id) end test "deletes only values of the specific custom field" do {:ok, member} = create_member() {:ok, custom_field1} = create_custom_field("field1", :string) {:ok, custom_field2} = create_custom_field("field2", :string) # Create value for custom_field1 {:ok, value1} = CustomFieldValue |> Ash.Changeset.for_create(:create, %{ member_id: member.id, custom_field_id: custom_field1.id, value: %{"_union_type" => "string", "_union_value" => "value1"} }) |> Ash.create() # Create value for custom_field2 {:ok, value2} = CustomFieldValue |> Ash.Changeset.for_create(:create, %{ member_id: member.id, custom_field_id: custom_field2.id, value: %{"_union_type" => "string", "_union_value" => "value2"} }) |> Ash.create() # Delete custom_field1 assert :ok = Ash.destroy(custom_field1) # Verify custom_field1 and value1 are deleted assert {:error, _} = Ash.get(CustomField, custom_field1.id) assert {:error, _} = Ash.get(CustomFieldValue, value1.id) # Verify custom_field2 and value2 still exist assert {:ok, _} = Ash.get(CustomField, custom_field2.id) assert {:ok, _} = Ash.get(CustomFieldValue, value2.id) end test "deletes custom field with values from multiple members" do {:ok, member1} = create_member() {:ok, member2} = create_member() {:ok, member3} = create_member() {:ok, custom_field} = create_custom_field("test_field", :string) # Create value for each member values = for member <- [member1, member2, member3] do {:ok, value} = CustomFieldValue |> Ash.Changeset.for_create(:create, %{ member_id: member.id, custom_field_id: custom_field.id, value: %{"_union_type" => "string", "_union_value" => "test"} }) |> Ash.create() value end # Delete custom field assert :ok = Ash.destroy(custom_field) # Verify all values are deleted for value <- values do assert {:error, _} = Ash.get(CustomFieldValue, value.id) end # Verify all members still exist for member <- [member1, member2, member3] do assert {:ok, _} = Ash.get(Member, member.id) end end end # Helper functions defp create_member do Member |> Ash.Changeset.for_create(:create_member, %{ first_name: "Test", last_name: "User#{System.unique_integer([:positive])}", email: "test#{System.unique_integer([:positive])}@example.com" }) |> Ash.create() end defp create_custom_field(name, value_type) do CustomField |> Ash.Changeset.for_create(:create, %{ name: "#{name}_#{System.unique_integer([:positive])}", value_type: value_type }) |> Ash.create() end end