From 97119f2ba5aed9711465cb408e928190f1c938c7 Mon Sep 17 00:00:00 2001 From: Oliver Severin Mulelid-Tynes Date: Wed, 3 Dec 2025 15:45:45 +0100 Subject: [PATCH] test: add failing test for expr(has_one.function_calc) nil poisoning in batch --- test/calculation_test.exs | 44 +++++++++++++++++++++++++++++++ test/support/resources/chat.ex | 8 ++++++ test/support/resources/message.ex | 10 +++++++ 3 files changed, 62 insertions(+) diff --git a/test/calculation_test.exs b/test/calculation_test.exs index 4260976c..c38e1847 100644 --- a/test/calculation_test.exs +++ b/test/calculation_test.exs @@ -1603,4 +1603,48 @@ defmodule AshPostgres.CalculationTest do assert loaded3.active_item_name == nil assert loaded3.all_item_name == nil end + + test "expr(has_one.function_based_calculation) in batch with nil relationships" do + alias AshPostgres.Test.{Chat, Message} + + chat1 = Ash.Seed.seed!(Chat, %{name: "Chat 1"}) + + chat2 = Ash.Seed.seed!(Chat, %{name: "Chat 2"}) + + Ash.Seed.seed!(Message, %{ + chat_id: chat2.id, + content: "Unread message", + read_at: nil + }) + + chat3 = Ash.Seed.seed!(Chat, %{name: "Chat 3"}) + + Ash.Seed.seed!(Message, %{ + chat_id: chat3.id, + content: "Read message", + read_at: DateTime.utc_now() + }) + + single = + Chat + |> Ash.Query.filter(id == ^chat2.id) + |> Ash.Query.load([:last_unread_message_formatted_fn]) + |> Ash.read!() + |> hd() + + assert single.last_unread_message_formatted_fn == "FnMessage: Unread message" + + chats = + Chat + |> Ash.Query.filter(id in [^chat1.id, ^chat2.id, ^chat3.id]) + |> Ash.Query.sort(:name) + |> Ash.Query.load([:last_unread_message_formatted_fn]) + |> Ash.read!() + + [loaded1, loaded2, loaded3] = chats + + assert loaded1.last_unread_message_formatted_fn == nil + assert loaded2.last_unread_message_formatted_fn == "FnMessage: Unread message" + assert loaded3.last_unread_message_formatted_fn == nil + end end diff --git a/test/support/resources/chat.ex b/test/support/resources/chat.ex index 4a819f19..1f0954ac 100644 --- a/test/support/resources/chat.ex +++ b/test/support/resources/chat.ex @@ -23,6 +23,14 @@ defmodule AshPostgres.Test.Chat do attribute(:name, :string, public?: true) end + calculations do + calculate( + :last_unread_message_formatted_fn, + :string, + expr(last_unread_message.formatted_content_fn) + ) + end + relationships do belongs_to :last_read_message, AshPostgres.Test.Message do allow_nil?(true) diff --git a/test/support/resources/message.ex b/test/support/resources/message.ex index 1085c337..20aff86b 100644 --- a/test/support/resources/message.ex +++ b/test/support/resources/message.ex @@ -25,6 +25,16 @@ defmodule AshPostgres.Test.Message do attribute(:read_at, :utc_datetime, public?: true) end + calculations do + calculate :formatted_content_fn, :string do + load([:content]) + + calculation(fn records, _context -> + Enum.map(records, &"FnMessage: #{&1.content}") + end) + end + end + relationships do belongs_to :chat, AshPostgres.Test.Chat do public?(true)