Skip to content

Commit 7cd3019

Browse files
authored
test: Add distinct sort tests (#605)
* Add support for sorting with filtered relationship tests * Add filter relationship tests
1 parent e199189 commit 7cd3019

File tree

9 files changed

+348
-2
lines changed

9 files changed

+348
-2
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
{
2+
"attributes": [
3+
{
4+
"allow_nil?": false,
5+
"default": "nil",
6+
"generated?": false,
7+
"precision": null,
8+
"primary_key?": true,
9+
"references": {
10+
"deferrable": false,
11+
"destination_attribute": "id",
12+
"destination_attribute_default": null,
13+
"destination_attribute_generated": null,
14+
"index?": false,
15+
"match_type": null,
16+
"match_with": null,
17+
"multitenancy": {
18+
"attribute": null,
19+
"global": null,
20+
"strategy": null
21+
},
22+
"name": "post_tags_post_id_fkey",
23+
"on_delete": null,
24+
"on_update": null,
25+
"primary_key?": true,
26+
"schema": "public",
27+
"table": "posts"
28+
},
29+
"scale": null,
30+
"size": null,
31+
"source": "post_id",
32+
"type": "uuid"
33+
},
34+
{
35+
"allow_nil?": false,
36+
"default": "nil",
37+
"generated?": false,
38+
"precision": null,
39+
"primary_key?": true,
40+
"references": {
41+
"deferrable": false,
42+
"destination_attribute": "id",
43+
"destination_attribute_default": null,
44+
"destination_attribute_generated": null,
45+
"index?": false,
46+
"match_type": null,
47+
"match_with": null,
48+
"multitenancy": {
49+
"attribute": null,
50+
"global": null,
51+
"strategy": null
52+
},
53+
"name": "post_tags_tag_id_fkey",
54+
"on_delete": null,
55+
"on_update": null,
56+
"primary_key?": true,
57+
"schema": "public",
58+
"table": "tags"
59+
},
60+
"scale": null,
61+
"size": null,
62+
"source": "tag_id",
63+
"type": "uuid"
64+
}
65+
],
66+
"base_filter": null,
67+
"check_constraints": [],
68+
"custom_indexes": [],
69+
"custom_statements": [],
70+
"has_create_action": true,
71+
"hash": "BC5024D4E17EDD4ABDD0EF3D81EE5932FB63DF036E1BA81989D21A12496FE4A9",
72+
"identities": [
73+
{
74+
"all_tenants?": false,
75+
"base_filter": null,
76+
"index_name": "post_tags_unique_post_tag_index",
77+
"keys": [
78+
{
79+
"type": "atom",
80+
"value": "post_id"
81+
},
82+
{
83+
"type": "atom",
84+
"value": "tag_id"
85+
}
86+
],
87+
"name": "unique_post_tag",
88+
"nils_distinct?": true,
89+
"where": null
90+
}
91+
],
92+
"multitenancy": {
93+
"attribute": null,
94+
"global": null,
95+
"strategy": null
96+
},
97+
"repo": "Elixir.AshPostgres.TestRepo",
98+
"schema": null,
99+
"table": "post_tags"
100+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"attributes": [
3+
{
4+
"allow_nil?": false,
5+
"default": "fragment(\"gen_random_uuid()\")",
6+
"generated?": false,
7+
"precision": null,
8+
"primary_key?": true,
9+
"references": null,
10+
"scale": null,
11+
"size": null,
12+
"source": "id",
13+
"type": "uuid"
14+
},
15+
{
16+
"allow_nil?": false,
17+
"default": "0",
18+
"generated?": false,
19+
"precision": null,
20+
"primary_key?": false,
21+
"references": null,
22+
"scale": null,
23+
"size": null,
24+
"source": "importance",
25+
"type": "bigint"
26+
}
27+
],
28+
"base_filter": null,
29+
"check_constraints": [],
30+
"custom_indexes": [],
31+
"custom_statements": [],
32+
"has_create_action": true,
33+
"hash": "9770C678D127B7ECA9FEAF174305101CC111417744E5D5712CD7F0A865D9877A",
34+
"identities": [],
35+
"multitenancy": {
36+
"attribute": null,
37+
"global": null,
38+
"strategy": null
39+
},
40+
"repo": "Elixir.AshPostgres.TestRepo",
41+
"schema": null,
42+
"table": "tags"
43+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
defmodule AshPostgres.TestRepo.Migrations.MigrateResources57 do
2+
@moduledoc """
3+
Updates resources based on their most recent snapshots.
4+
5+
This file was autogenerated with `mix ash_postgres.generate_migrations`
6+
"""
7+
8+
use Ecto.Migration
9+
10+
def up do
11+
create table(:tags, primary_key: false) do
12+
add(:id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true)
13+
add(:importance, :bigint, null: false, default: 0)
14+
end
15+
16+
create table(:post_tags, primary_key: false) do
17+
add(
18+
:post_id,
19+
references(:posts,
20+
column: :id,
21+
name: "post_tags_post_id_fkey",
22+
type: :uuid,
23+
prefix: "public"
24+
),
25+
primary_key: true,
26+
null: false
27+
)
28+
29+
add(
30+
:tag_id,
31+
references(:tags,
32+
column: :id,
33+
name: "post_tags_tag_id_fkey",
34+
type: :uuid,
35+
prefix: "public"
36+
),
37+
primary_key: true,
38+
null: false
39+
)
40+
end
41+
42+
create(unique_index(:post_tags, [:post_id, :tag_id], name: "post_tags_unique_post_tag_index"))
43+
end
44+
45+
def down do
46+
drop_if_exists(
47+
unique_index(:post_tags, [:post_id, :tag_id], name: "post_tags_unique_post_tag_index")
48+
)
49+
50+
drop(constraint(:post_tags, "post_tags_post_id_fkey"))
51+
52+
drop(constraint(:post_tags, "post_tags_tag_id_fkey"))
53+
54+
drop(table(:post_tags))
55+
56+
drop(table(:tags))
57+
end
58+
end

test/calculation_test.exs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
defmodule AshPostgres.CalculationTest do
22
alias AshPostgres.Test.RecordTempEntity
33
use AshPostgres.RepoCase, async: false
4-
alias AshPostgres.Test.{Account, Author, Comedian, Comment, Post, Record, TempEntity, User}
4+
5+
alias AshPostgres.Test.{
6+
Account,
7+
Author,
8+
Comedian,
9+
Comment,
10+
Post,
11+
PostTag,
12+
Record,
13+
Tag,
14+
TempEntity,
15+
User
16+
}
517

618
require Ash.Query
719
import Ash.Expr
@@ -1045,4 +1057,23 @@ defmodule AshPostgres.CalculationTest do
10451057
|> Ash.Query.sort("posts_with_matching_title.relevance_score")
10461058
|> Ash.read!()
10471059
end
1060+
1061+
test "sorting with filtered relationship by calculated field" do
1062+
tag = Ash.Changeset.for_create(Tag, :create) |> Ash.create!()
1063+
scores = [0, 3, 111, 22, 9, 4, 2, 33, 10]
1064+
1065+
scores
1066+
|> Enum.each(fn score ->
1067+
post =
1068+
Ash.Changeset.for_create(Post, :create, %{score: score})
1069+
|> Ash.create!()
1070+
1071+
Ash.Changeset.for_create(PostTag, :create, post_id: post.id, tag_id: tag.id)
1072+
|> Ash.create!()
1073+
end)
1074+
1075+
post_with_highest_score = Ash.load!(tag, :post_with_highest_score).post_with_highest_score
1076+
highest_score = hd(Enum.sort(scores, :desc))
1077+
assert post_with_highest_score.score == highest_score
1078+
end
10481079
end

test/sort_test.exs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule AshPostgres.SortTest do
22
@moduledoc false
33
use AshPostgres.RepoCase, async: false
4-
alias AshPostgres.Test.{Comment, Post, PostLink, PostView}
4+
alias AshPostgres.Test.{Comment, Post, PostLink, PostTag, PostView, Tag}
55

66
require Ash.Query
77
require Ash.Sort
@@ -267,4 +267,26 @@ defmodule AshPostgres.SortTest do
267267
|> Ash.Query.sort({Ash.Sort.expr_sort(views.time, :datetime), :desc}, title: :asc)
268268
)
269269
end
270+
271+
test "sorting with filtered relationship" do
272+
tag = Ash.Changeset.for_create(Tag, :create) |> Ash.create!()
273+
dates = [~D[2025-01-03], ~D[2025-01-05], ~D[2025-01-01], ~D[2025-01-10], ~D[2025-01-02]]
274+
275+
dates
276+
|> Enum.each(fn date ->
277+
{:ok, datetime} = DateTime.new(date, ~T[00:00:00])
278+
279+
post =
280+
Ash.Changeset.for_create(Post, :create, %{created_at: datetime})
281+
|> Ash.create!()
282+
283+
Ash.Changeset.for_create(PostTag, :create, post_id: post.id, tag_id: tag.id)
284+
|> Ash.create!()
285+
end)
286+
287+
latest_post = Ash.load!(tag, :latest_post).latest_post
288+
expected_date = hd(Enum.sort(dates, :desc))
289+
290+
assert DateTime.to_date(latest_post.created_at) == expected_date
291+
end
270292
end

test/support/domain.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ defmodule AshPostgres.Test.Domain do
4242
resource(AshPostgres.Test.CSV)
4343
resource(AshPostgres.Test.StandupClub)
4444
resource(AshPostgres.Test.Punchline)
45+
resource(AshPostgres.Test.Tag)
46+
resource(AshPostgres.Test.PostTag)
4547
end
4648

4749
authorization do

test/support/resources/post.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,12 @@ defmodule AshPostgres.Test.Post do
777777
sort: [Ash.Sort.expr_sort(parent(post_followers.order), :integer)]
778778
)
779779

780+
many_to_many :tags, AshPostgres.Test.Tag do
781+
public?(true)
782+
through(AshPostgres.Test.PostTag)
783+
sort(importance: :desc)
784+
end
785+
780786
has_many(:views, AshPostgres.Test.PostView) do
781787
public?(true)
782788
end

test/support/resources/post_tag.ex

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
defmodule AshPostgres.Test.PostTag do
2+
@moduledoc false
3+
use Ash.Resource,
4+
domain: AshPostgres.Test.Domain,
5+
data_layer: AshPostgres.DataLayer
6+
7+
require Ash.Sort
8+
9+
postgres do
10+
table "post_tags"
11+
repo AshPostgres.TestRepo
12+
end
13+
14+
actions do
15+
default_accept(:*)
16+
17+
defaults([:create, :read, :update, :destroy])
18+
end
19+
20+
identities do
21+
identity(:unique_post_tag, [:post_id, :tag_id])
22+
end
23+
24+
relationships do
25+
belongs_to :post, AshPostgres.Test.Post do
26+
primary_key?(true)
27+
public?(true)
28+
allow_nil?(false)
29+
end
30+
31+
belongs_to :tag, AshPostgres.Test.Tag do
32+
primary_key?(true)
33+
public?(true)
34+
allow_nil?(false)
35+
end
36+
end
37+
end

test/support/resources/tag.ex

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
defmodule AshPostgres.Test.Tag do
2+
@moduledoc false
3+
use Ash.Resource,
4+
domain: AshPostgres.Test.Domain,
5+
data_layer: AshPostgres.DataLayer
6+
7+
require Ash.Sort
8+
9+
postgres do
10+
table "tags"
11+
repo AshPostgres.TestRepo
12+
end
13+
14+
actions do
15+
defaults([:read])
16+
17+
create :create do
18+
accept(:*)
19+
end
20+
end
21+
22+
attributes do
23+
uuid_primary_key(:id)
24+
attribute(:importance, :integer, allow_nil?: false, default: 0, public?: true)
25+
end
26+
27+
relationships do
28+
many_to_many :posts, AshPostgres.Test.Post do
29+
through(AshPostgres.Test.PostTag)
30+
public?(true)
31+
end
32+
33+
has_one :latest_post, AshPostgres.Test.Post do
34+
public?(true)
35+
no_attributes?(true)
36+
filter(expr(tags.id == parent(id)))
37+
sort(created_at: :desc)
38+
end
39+
40+
has_one :post_with_highest_score, AshPostgres.Test.Post do
41+
public?(true)
42+
no_attributes?(true)
43+
filter(expr(tags.id == parent(id)))
44+
sort(score_after_winning: :desc)
45+
end
46+
end
47+
end

0 commit comments

Comments
 (0)