Skip to content

Commit d4f7572

Browse files
committed
chore: add tests for the nested filters issue
ash-project/ash_sql#220 Nested filters failed to compile correctly. This can be tested by setting the ASH_SQL_VERSION to `main` and observing the test failure (at least until the fix is merged).
1 parent eae6dae commit d4f7572

File tree

2 files changed

+59
-0
lines changed

2 files changed

+59
-0
lines changed

test/aggregate_test.exs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2256,4 +2256,52 @@ defmodule AshSql.AggregateTest do
22562256
"Calculation was not loaded when using page(count: true) with aggregates"
22572257
end
22582258
end
2259+
2260+
describe "join_filters in aggregate calculations" do
2261+
test "Ash.Filter structs in join_filters are properly converted to Ecto expressions" do
2262+
# This test reproduces the bug where Ash.Filter structs in join_filters
2263+
# are not properly converted to Ecto dynamic expressions, causing:
2264+
# ** (Ecto.Query.CastError) value `#Ash.Filter<...>` in `where` cannot be cast to type :boolean
2265+
#
2266+
# The root cause is in ash_sql/lib/expr.ex - when a BooleanExpression contains
2267+
# an Ash.Filter struct as an operand, the private do_dynamic_expr/default_dynamic_expr
2268+
# functions don't have a clause to handle it, so the Ash.Filter is passed directly
2269+
# to Ecto instead of being converted to a dynamic expression.
2270+
2271+
author =
2272+
Author
2273+
|> Ash.Changeset.for_create(:create, %{first_name: "Test", last_name: "Author"})
2274+
|> Ash.create!()
2275+
2276+
post =
2277+
Post
2278+
|> Ash.Changeset.for_create(:create, %{title: "test"})
2279+
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
2280+
|> Ash.create!()
2281+
2282+
comment =
2283+
Comment
2284+
|> Ash.Changeset.for_create(:create, %{title: "comment", likes: 5})
2285+
|> Ash.Changeset.manage_relationship(:post, post, type: :append_and_remove)
2286+
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
2287+
|> Ash.create!()
2288+
2289+
Rating
2290+
|> Ash.Changeset.for_create(:create, %{score: 10, resource_id: comment.id})
2291+
|> Ash.Changeset.set_context(%{data_layer: %{table: "comment_ratings"}})
2292+
|> Ash.create!()
2293+
2294+
# This triggers the bug - loading a calculation that uses join_filters with actor reference.
2295+
# The join_filter `expr(author_id == ^actor(:id))` gets resolved to an Ash.Filter struct
2296+
# which is then combined with other filters in a BooleanExpression.
2297+
# We use authorize?: false to bypass Post's organization-based authorization policies.
2298+
assert {:ok, [loaded_post]} =
2299+
Post
2300+
|> Ash.Query.filter(id == ^post.id)
2301+
|> Ash.Query.load(:max_rating_with_join_filter)
2302+
|> Ash.read(actor: author, authorize?: false)
2303+
2304+
assert loaded_post.max_rating_with_join_filter == 10
2305+
end
2306+
end
22592307
end

test/support/resources/post.ex

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,17 @@ defmodule AshPostgres.Test.Post do
12111211

12121212
calculate(:past_datetime1?, :boolean, expr(now() > datetime))
12131213
calculate(:past_datetime2?, :boolean, expr(datetime <= now()))
1214+
1215+
# Test case for join_filters bug where Ash.Filter structs are not converted to Ecto expressions
1216+
calculate :max_rating_with_join_filter,
1217+
:integer,
1218+
expr(
1219+
max([:comments, :ratings],
1220+
query: [filter: expr(score > 0)],
1221+
field: :score,
1222+
join_filters: %{comments: expr(author_id == ^actor(:id))}
1223+
)
1224+
)
12141225
end
12151226

12161227
aggregates do

0 commit comments

Comments
 (0)