aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/cases/relation/merging_test.rb
blob: feb5604284f496cd17880e45e71581e9547b4c66 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# frozen_string_literal: true
require "cases/helper"
require "models/author"
require "models/comment"
require "models/developer"
require "models/computer"
require "models/post"
require "models/project"
require "models/rating"

class RelationMergingTest < ActiveRecord::TestCase
  fixtures :developers, :comments, :authors, :author_addresses, :posts

  def test_relation_merging
    devs = Developer.where("salary >= 80000").merge(Developer.limit(2)).merge(Developer.order("id ASC").where("id < 3"))
    assert_equal [developers(:david), developers(:jamis)], devs.to_a

    dev_with_count = Developer.limit(1).merge(Developer.order("id DESC")).merge(Developer.select("developers.*"))
    assert_equal [developers(:poor_jamis)], dev_with_count.to_a
  end

  def test_relation_to_sql
    post = Post.first
    sql = post.comments.to_sql
    assert_match(/.?post_id.? = #{post.id}\z/i, sql)
  end

  def test_relation_merging_with_arel_equalities_keeps_last_equality
    devs = Developer.where(Developer.arel_table[:salary].eq(80000)).merge(
      Developer.where(Developer.arel_table[:salary].eq(9000))
    )
    assert_equal [developers(:poor_jamis)], devs.to_a
  end

  def test_relation_merging_with_arel_equalities_keeps_last_equality_with_non_attribute_left_hand
    salary_attr = Developer.arel_table[:salary]
    devs = Developer.where(
      Arel::Nodes::NamedFunction.new("abs", [salary_attr]).eq(80000)
    ).merge(
      Developer.where(
        Arel::Nodes::NamedFunction.new("abs", [salary_attr]).eq(9000)
      )
    )
    assert_equal [developers(:poor_jamis)], devs.to_a
  end

  def test_relation_merging_with_eager_load
    relations = []
    relations << Post.order("comments.id DESC").merge(Post.eager_load(:last_comment)).merge(Post.all)
    relations << Post.eager_load(:last_comment).merge(Post.order("comments.id DESC")).merge(Post.all)

    relations.each do |posts|
      post = posts.find { |p| p.id == 1 }
      assert_equal Post.find(1).last_comment, post.last_comment
    end
  end

  def test_relation_merging_with_locks
    devs = Developer.lock.where("salary >= 80000").order("id DESC").merge(Developer.limit(2))
    assert devs.locked?
  end

  def test_relation_merging_with_preload
    [Post.all.merge(Post.preload(:author)), Post.preload(:author).merge(Post.all)].each do |posts|
      assert_queries(2) { assert posts.first.author }
    end
  end

  def test_relation_merging_with_joins
    comments = Comment.joins(:post).where(body: "Thank you for the welcome").merge(Post.where(body: "Such a lovely day"))
    assert_equal 1, comments.count
  end

  def test_relation_merging_with_association
    assert_queries(2) do  # one for loading post, and another one merged query
      post = Post.where(body: "Such a lovely day").first
      comments = Comment.where(body: "Thank you for the welcome").merge(post.comments)
      assert_equal 1, comments.count
    end
  end

  test "merge collapses wheres from the LHS only" do
    left  = Post.where(title: "omg").where(comments_count: 1)
    right = Post.where(title: "wtf").where(title: "bbq")

    expected = [left.bound_attributes[1]] + right.bound_attributes
    merged   = left.merge(right)

    assert_equal expected, merged.bound_attributes
    assert_not_includes merged.to_sql, "omg"
    assert_includes merged.to_sql, "wtf"
    assert_includes merged.to_sql, "bbq"
  end

  def test_merging_reorders_bind_params
    post  = Post.first
    right = Post.where(id: 1)
    left  = Post.where(title: post.title)

    merged = left.merge(right)
    assert_equal post, merged.first
  end

  def test_merging_compares_symbols_and_strings_as_equal
    post = PostThatLoadsCommentsInAnAfterSaveHook.create!(title: "First Post", body: "Blah blah blah.")
    assert_equal "First comment!", post.comments.where(body: "First comment!").first_or_create.body
  end

  def test_merging_with_from_clause
    relation = Post.all
    assert relation.from_clause.empty?
    relation = relation.merge(Post.from("posts"))
    refute relation.from_clause.empty?
  end
end

class MergingDifferentRelationsTest < ActiveRecord::TestCase
  fixtures :posts, :authors, :author_addresses, :developers

  test "merging where relations" do
    hello_by_bob = Post.where(body: "hello").joins(:author).
      merge(Author.where(name: "Bob")).order("posts.id").pluck("posts.id")

    assert_equal [posts(:misc_by_bob).id,
                  posts(:other_by_bob).id], hello_by_bob
  end

  test "merging order relations" do
    posts_by_author_name = Post.limit(3).joins(:author).
      merge(Author.order(:name)).pluck("authors.name")

    assert_equal ["Bob", "Bob", "David"], posts_by_author_name

    posts_by_author_name = Post.limit(3).joins(:author).
      merge(Author.order("name")).pluck("authors.name")

    assert_equal ["Bob", "Bob", "David"], posts_by_author_name
  end

  test "merging order relations (using a hash argument)" do
    posts_by_author_name = Post.limit(4).joins(:author).
      merge(Author.order(name: :desc)).pluck("authors.name")

    assert_equal ["Mary", "Mary", "Mary", "David"], posts_by_author_name
  end

  test "relation merging (using a proc argument)" do
    dev = Developer.where(name: "Jamis").first

    comment_1 = dev.comments.create!(body: "I'm Jamis", post: Post.first)
    rating_1 = comment_1.ratings.create!

    comment_2 = dev.comments.create!(body: "I'm John", post: Post.first)
    comment_2.ratings.create!

    assert_equal dev.ratings, [rating_1]
  end
end