Skip to content

Commit 854bde5

Browse files
committed
test: Add tests for combination queries and loading calculations
These tests show that loading calculations in queries that have combinations works.
1 parent e963ea0 commit 854bde5

File tree

3 files changed

+200
-0
lines changed

3 files changed

+200
-0
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
defmodule AshPostgres.CombinationNullableCalcTest do
2+
@moduledoc false
3+
use AshPostgres.RepoCase, async: false
4+
alias AshPostgres.Test.{Post, Author}
5+
6+
require Ash.Query
7+
import Ash.Expr
8+
9+
describe "combination_of with nullable calculations" do
10+
test "combination query with allow_nil? calculation loses ORDER BY" do
11+
Post
12+
|> Ash.Changeset.for_create(:create, %{title: "Zebra", score: 5})
13+
|> Ash.create!()
14+
15+
Post
16+
|> Ash.Changeset.for_create(:create, %{title: "Apple", score: 25})
17+
|> Ash.create!()
18+
19+
Post
20+
|> Ash.Changeset.for_create(:create, %{title: "Dog", score: 10})
21+
|> Ash.create!()
22+
23+
Post
24+
|> Ash.Changeset.for_create(:create, %{title: "Cat", score: 20})
25+
|> Ash.create!()
26+
27+
query =
28+
Post
29+
|> Ash.Query.sort([{:title, :asc}])
30+
|> Ash.Query.load([:latest_comment_title])
31+
|> Ash.Query.combination_of([
32+
Ash.Query.Combination.base(
33+
filter: expr(score < 15),
34+
calculations: %{
35+
sort_order: calc(score * 20, type: :integer)
36+
},
37+
sort: [{calc(score * 20, type: :integer), :desc}]
38+
),
39+
Ash.Query.Combination.union(
40+
filter: expr(score >= 15),
41+
calculations: %{
42+
sort_order: calc(score * 5, type: :integer)
43+
},
44+
sort: [{calc(score * 5, type: :integer), :desc}]
45+
)
46+
])
47+
|> Ash.Query.sort([{calc(^combinations(:sort_order)), :desc}], prepend?: true)
48+
49+
result = Ash.read!(query)
50+
titles = Enum.map(result, & &1.title)
51+
# Expected order: sort_order DESC, then title ASC
52+
# Dog(200), Apple(125), Cat(100), Zebra(100)
53+
expected_title_order = ["Dog", "Apple", "Cat", "Zebra"]
54+
assert titles == expected_title_order
55+
end
56+
57+
test "combination query without nullable calc works" do
58+
Post
59+
|> Ash.Changeset.for_create(:create, %{title: "Zebra", score: 5})
60+
|> Ash.create!()
61+
62+
Post
63+
|> Ash.Changeset.for_create(:create, %{title: "Apple", score: 25})
64+
|> Ash.create!()
65+
66+
Post
67+
|> Ash.Changeset.for_create(:create, %{title: "Dog", score: 10})
68+
|> Ash.create!()
69+
70+
Post
71+
|> Ash.Changeset.for_create(:create, %{title: "Cat", score: 20})
72+
|> Ash.create!()
73+
74+
query =
75+
Post
76+
|> Ash.Query.sort([{:title, :asc}])
77+
|> Ash.Query.combination_of([
78+
Ash.Query.Combination.base(
79+
filter: expr(score < 15),
80+
calculations: %{
81+
sort_order: calc(score * 20, type: :integer)
82+
},
83+
sort: [{calc(score * 20, type: :integer), :desc}]
84+
),
85+
Ash.Query.Combination.union(
86+
filter: expr(score >= 15),
87+
calculations: %{
88+
sort_order: calc(score * 5, type: :integer)
89+
},
90+
sort: [{calc(score * 5, type: :integer), :desc}]
91+
)
92+
])
93+
|> Ash.Query.sort([{calc(^combinations(:sort_order)), :desc}], prepend?: true)
94+
95+
result = Ash.read!(query)
96+
titles = Enum.map(result, & &1.title)
97+
# Expected order: sort_order DESC, then title ASC
98+
# Dog(200), Apple(125), Cat(100), Zebra(100)
99+
expected_title_order = ["Dog", "Apple", "Cat", "Zebra"]
100+
assert titles == expected_title_order
101+
end
102+
end
103+
104+
describe "Author combination_of with nullable calculations" do
105+
test "Author combination query with allow_nil? calculation loses ORDER BY" do
106+
Author
107+
|> Ash.Changeset.for_create(:create, %{first_name: "Zebra", last_name: "User"})
108+
|> Ash.create!()
109+
110+
Author
111+
|> Ash.Changeset.for_create(:create, %{first_name: "Apple", last_name: "User"})
112+
|> Ash.create!()
113+
114+
Author
115+
|> Ash.Changeset.for_create(:create, %{first_name: "Dog", last_name: "User"})
116+
|> Ash.create!()
117+
118+
Author
119+
|> Ash.Changeset.for_create(:create, %{first_name: "Cat", last_name: "User"})
120+
|> Ash.create!()
121+
122+
query =
123+
Author
124+
|> Ash.Query.sort([{:first_name, :asc}])
125+
|> Ash.Query.load([:profile_description_calc])
126+
|> Ash.Query.combination_of([
127+
Ash.Query.Combination.base(
128+
filter: expr(first_name in ["Zebra", "Dog"]),
129+
calculations: %{
130+
sort_order: calc(1000, type: :integer)
131+
},
132+
sort: [{calc(1000, type: :integer), :desc}]
133+
),
134+
Ash.Query.Combination.union(
135+
filter: expr(first_name in ["Apple", "Cat"]),
136+
calculations: %{
137+
sort_order: calc(500, type: :integer)
138+
},
139+
sort: [{calc(500, type: :integer), :desc}]
140+
)
141+
])
142+
|> Ash.Query.sort([{calc(^combinations(:sort_order)), :desc}], prepend?: true)
143+
144+
result = Ash.read!(query)
145+
first_names = Enum.map(result, & &1.first_name)
146+
# Expected order: sort_order DESC, then first_name ASC
147+
# [Dog, Zebra] (1000), [Apple, Cat] (500) → Dog, Zebra, Apple, Cat
148+
expected_name_order = ["Dog", "Zebra", "Apple", "Cat"]
149+
assert first_names == expected_name_order
150+
end
151+
152+
test "Author combination query without nullable calc works" do
153+
Author
154+
|> Ash.Changeset.for_create(:create, %{first_name: "Zebra", last_name: "User"})
155+
|> Ash.create!()
156+
157+
Author
158+
|> Ash.Changeset.for_create(:create, %{first_name: "Apple", last_name: "User"})
159+
|> Ash.create!()
160+
161+
Author
162+
|> Ash.Changeset.for_create(:create, %{first_name: "Dog", last_name: "User"})
163+
|> Ash.create!()
164+
165+
Author
166+
|> Ash.Changeset.for_create(:create, %{first_name: "Cat", last_name: "User"})
167+
|> Ash.create!()
168+
169+
query =
170+
Author
171+
|> Ash.Query.sort([{:first_name, :asc}])
172+
|> Ash.Query.combination_of([
173+
Ash.Query.Combination.base(
174+
filter: expr(first_name in ["Zebra", "Dog"]),
175+
calculations: %{
176+
sort_order: calc(1000, type: :integer)
177+
}
178+
),
179+
Ash.Query.Combination.union(
180+
filter: expr(first_name in ["Apple", "Cat"]),
181+
calculations: %{
182+
sort_order: calc(500, type: :integer)
183+
}
184+
)
185+
])
186+
|> Ash.Query.sort([{calc(^combinations(:sort_order)), :desc}], prepend?: true)
187+
188+
result = Ash.read!(query)
189+
first_names = Enum.map(result, & &1.first_name)
190+
# Expected order: sort_order DESC, then first_name ASC
191+
# [Dog, Zebra] (1000), [Apple, Cat] (500) → Dog, Zebra, Apple, Cat
192+
expected_name_order = ["Dog", "Zebra", "Apple", "Cat"]
193+
assert first_names == expected_name_order
194+
end
195+
end
196+
end

test/support/resources/author.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ defmodule AshPostgres.Test.Author do
175175

176176
calculate(:has_posts, :boolean, expr(exists(posts, true == true)))
177177
calculate(:has_no_posts, :boolean, expr(has_posts == false))
178+
179+
calculate(:profile_description_calc, :string, expr(profile.description), allow_nil?: true)
178180
end
179181

180182
aggregates do

test/support/resources/post.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,8 @@ defmodule AshPostgres.Test.Post do
927927
calculate(:author_first_name_ref_agg_calc, :string, expr(author_first_name))
928928

929929
calculate(:author_profile_description_from_agg, :string, expr(author_profile_description))
930+
931+
calculate(:latest_comment_title, :string, expr(latest_comment.title), allow_nil?: true)
930932
end
931933

932934
aggregates do

0 commit comments

Comments
 (0)