babik 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +16 -0
  3. data/README.md +718 -0
  4. data/Rakefile +18 -0
  5. data/lib/babik.rb +122 -0
  6. data/lib/babik/database.rb +16 -0
  7. data/lib/babik/queryset.rb +154 -0
  8. data/lib/babik/queryset/components/aggregation.rb +172 -0
  9. data/lib/babik/queryset/components/limit.rb +22 -0
  10. data/lib/babik/queryset/components/order.rb +161 -0
  11. data/lib/babik/queryset/components/projection.rb +118 -0
  12. data/lib/babik/queryset/components/select_related.rb +78 -0
  13. data/lib/babik/queryset/components/sql_renderer.rb +99 -0
  14. data/lib/babik/queryset/components/where.rb +43 -0
  15. data/lib/babik/queryset/lib/association/foreign_association_chain.rb +97 -0
  16. data/lib/babik/queryset/lib/association/select_related_association_chain.rb +32 -0
  17. data/lib/babik/queryset/lib/condition.rb +103 -0
  18. data/lib/babik/queryset/lib/field.rb +34 -0
  19. data/lib/babik/queryset/lib/join/association_joiner.rb +39 -0
  20. data/lib/babik/queryset/lib/join/join.rb +86 -0
  21. data/lib/babik/queryset/lib/selection/config.rb +19 -0
  22. data/lib/babik/queryset/lib/selection/foreign_selection.rb +39 -0
  23. data/lib/babik/queryset/lib/selection/local_selection.rb +40 -0
  24. data/lib/babik/queryset/lib/selection/operation/base.rb +126 -0
  25. data/lib/babik/queryset/lib/selection/operation/date.rb +178 -0
  26. data/lib/babik/queryset/lib/selection/operation/operations.rb +201 -0
  27. data/lib/babik/queryset/lib/selection/operation/regex.rb +58 -0
  28. data/lib/babik/queryset/lib/selection/path/foreign_path.rb +50 -0
  29. data/lib/babik/queryset/lib/selection/path/local_path.rb +44 -0
  30. data/lib/babik/queryset/lib/selection/path/path.rb +23 -0
  31. data/lib/babik/queryset/lib/selection/select_related_selection.rb +38 -0
  32. data/lib/babik/queryset/lib/selection/selection.rb +19 -0
  33. data/lib/babik/queryset/lib/update/assignment.rb +108 -0
  34. data/lib/babik/queryset/mixins/aggregatable.rb +17 -0
  35. data/lib/babik/queryset/mixins/bounded.rb +38 -0
  36. data/lib/babik/queryset/mixins/clonable.rb +52 -0
  37. data/lib/babik/queryset/mixins/countable.rb +44 -0
  38. data/lib/babik/queryset/mixins/deletable.rb +13 -0
  39. data/lib/babik/queryset/mixins/distinguishable.rb +27 -0
  40. data/lib/babik/queryset/mixins/filterable.rb +51 -0
  41. data/lib/babik/queryset/mixins/limitable.rb +88 -0
  42. data/lib/babik/queryset/mixins/lockable.rb +31 -0
  43. data/lib/babik/queryset/mixins/none.rb +16 -0
  44. data/lib/babik/queryset/mixins/projectable.rb +34 -0
  45. data/lib/babik/queryset/mixins/related_selector.rb +28 -0
  46. data/lib/babik/queryset/mixins/set_operations.rb +32 -0
  47. data/lib/babik/queryset/mixins/sortable.rb +49 -0
  48. data/lib/babik/queryset/mixins/sql_renderizable.rb +17 -0
  49. data/lib/babik/queryset/mixins/updatable.rb +14 -0
  50. data/lib/babik/queryset/templates/default/delete/main.sql.erb +14 -0
  51. data/lib/babik/queryset/templates/default/select/components/aggregation.sql.erb +5 -0
  52. data/lib/babik/queryset/templates/default/select/components/from.sql.erb +16 -0
  53. data/lib/babik/queryset/templates/default/select/components/from_set.sql.erb +3 -0
  54. data/lib/babik/queryset/templates/default/select/components/from_table.sql.erb +2 -0
  55. data/lib/babik/queryset/templates/default/select/components/limit.sql.erb +10 -0
  56. data/lib/babik/queryset/templates/default/select/components/order_by.sql.erb +9 -0
  57. data/lib/babik/queryset/templates/default/select/components/projection.sql.erb +7 -0
  58. data/lib/babik/queryset/templates/default/select/components/select_related.sql.erb +26 -0
  59. data/lib/babik/queryset/templates/default/select/components/where.sql.erb +39 -0
  60. data/lib/babik/queryset/templates/default/select/main.sql.erb +42 -0
  61. data/lib/babik/queryset/templates/default/update/main.sql.erb +15 -0
  62. data/lib/babik/queryset/templates/mssql/select/components/limit.sql.erb +8 -0
  63. data/lib/babik/queryset/templates/mssql/select/components/order_by.sql.erb +21 -0
  64. data/lib/babik/queryset/templates/mysql2/delete/main.sql.erb +15 -0
  65. data/lib/babik/queryset/templates/mysql2/update/main.sql.erb +18 -0
  66. data/lib/babik/queryset/templates/sqlite3/select/components/from_set.sql.erb +5 -0
  67. data/test/config/db/schema.rb +83 -0
  68. data/test/config/models/bad_post.rb +5 -0
  69. data/test/config/models/bad_tag.rb +5 -0
  70. data/test/config/models/category.rb +4 -0
  71. data/test/config/models/geozone.rb +6 -0
  72. data/test/config/models/group.rb +5 -0
  73. data/test/config/models/group_user.rb +5 -0
  74. data/test/config/models/post.rb +24 -0
  75. data/test/config/models/post_tag.rb +5 -0
  76. data/test/config/models/tag.rb +5 -0
  77. data/test/config/models/user.rb +6 -0
  78. data/test/delete/delete_test.rb +60 -0
  79. data/test/delete/foreign_conditions_delete_test.rb +57 -0
  80. data/test/delete/local_conditions_delete_test.rb +20 -0
  81. data/test/enable_coverage.rb +17 -0
  82. data/test/lib/selection/operation/log/test-queries.log +1 -0
  83. data/test/lib/selection/operation/test_date.rb +131 -0
  84. data/test/lib/selection/operation/test_regex.rb +55 -0
  85. data/test/other/clone_test.rb +129 -0
  86. data/test/other/escape_test.rb +21 -0
  87. data/test/other/inverse_of_required_test.rb +33 -0
  88. data/test/select/aggregate_test.rb +151 -0
  89. data/test/select/bounds_test.rb +46 -0
  90. data/test/select/count_test.rb +147 -0
  91. data/test/select/distinct_test.rb +38 -0
  92. data/test/select/exclude_test.rb +72 -0
  93. data/test/select/filter_from_object_test.rb +125 -0
  94. data/test/select/filter_test.rb +207 -0
  95. data/test/select/for_update_test.rb +19 -0
  96. data/test/select/foreign_selection_test.rb +60 -0
  97. data/test/select/get_test.rb +40 -0
  98. data/test/select/limit_test.rb +109 -0
  99. data/test/select/local_selection_test.rb +24 -0
  100. data/test/select/lookup_test.rb +208 -0
  101. data/test/select/none_test.rb +40 -0
  102. data/test/select/order_test.rb +165 -0
  103. data/test/select/project_test.rb +107 -0
  104. data/test/select/select_related_test.rb +124 -0
  105. data/test/select/subquery_test.rb +50 -0
  106. data/test/set_operations/basic_usage_test.rb +121 -0
  107. data/test/test_helper.rb +55 -0
  108. data/test/update/update_test.rb +93 -0
  109. metadata +278 -0
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../test_helper'
5
+
6
+ # Limit method tests
7
+ class LimitTest < Minitest::Test
8
+
9
+ def setup
10
+ i = 0
11
+ while i < 100
12
+ User.create!(first_name: "User #{i + 1}", last_name: 'LimitTest user')
13
+ i += 1
14
+ end
15
+ end
16
+
17
+ def teardown
18
+ User.delete_all
19
+ end
20
+
21
+ def test_limit
22
+ page_size = 5
23
+
24
+ # Check the first page of users
25
+ first_page = User.objects.filter(last_name: 'LimitTest user').limit(page_size)
26
+ # Check with each
27
+ loops = 0
28
+ first_page.each do |user|
29
+ assert_equal "User #{loops + 1}", user.first_name
30
+ loops += 1
31
+ end
32
+ assert_equal page_size, loops
33
+
34
+ # Check the second page of users
35
+ second_page_offset = page_size
36
+ second_page = User.objects.filter(last_name: 'LimitTest user').limit(page_size, second_page_offset)
37
+ assert_equal 5, second_page.count
38
+ second_page.each_with_index do |user, user_index|
39
+ assert_equal "User #{user_index + second_page_offset + 1}", user.first_name
40
+ end
41
+ end
42
+
43
+ def test_limit_brackets
44
+ page_size = 5
45
+ first_page = User.objects.filter(last_name: 'LimitTest user').limit(page_size)
46
+ first_page_with_brackets = User.objects.filter(last_name: 'LimitTest user')[0..page_size]
47
+ limit_test_user = User.objects.filter(last_name: 'LimitTest user')[page_size]
48
+ assert_equal 'No user', User.objects.filter(last_name: 'LimitTest user').fetch(10_000, 'No user')
49
+
50
+ non_existent_index = User.objects.count + 100
51
+ exception = assert_raises IndexError do
52
+ User.objects.filter(last_name: 'LimitTest user').fetch(non_existent_index)
53
+ end
54
+ limit_test_count = User.objects.filter(last_name: 'LimitTest user').count
55
+
56
+ assert_equal("Index #{non_existent_index} outside of QuerySet bounds", exception.message)
57
+ assert_equal User, limit_test_user.class
58
+ assert_equal 'LimitTest user', limit_test_user.last_name
59
+ assert_equal first_page.count, first_page_with_brackets.count
60
+
61
+ second_page_offset = page_size
62
+ second_page = User.objects.filter(last_name: 'LimitTest user').limit(page_size, second_page_offset)
63
+ second_page_with_brackets = User.objects.filter(last_name: 'LimitTest user')[second_page_offset..(second_page_offset+page_size)]
64
+ assert_equal page_size, second_page.count
65
+ assert_equal second_page.count, second_page_with_brackets.count
66
+ end
67
+
68
+ def test_limit_brackets_index
69
+ first_user = User.objects.filter(last_name: 'LimitTest user').order_by(created_at: :ASC)[0]
70
+ expected_first_user = User.where(last_name: 'LimitTest user').order('created_at ASC').first
71
+ assert_equal expected_first_user.id, first_user.id
72
+ assert_equal expected_first_user.first_name, first_user.first_name
73
+ end
74
+
75
+ def test_is_limited
76
+ assert User.objects.filter!(last_name: 'LimitTest user').limit!(5, 10).limit?
77
+ end
78
+
79
+ def test_unlimit
80
+ refute User.objects.filter!(last_name: 'LimitTest user').limit!(5, 10).unlimit!.limit?
81
+ end
82
+
83
+ def test_exists
84
+ assert User.objects.filter(last_name: 'LimitTest user').exists?
85
+ assert User.objects.filter(last_name: 'LimitTest user').exist?
86
+
87
+ refute User.objects.filter(last_name: 'This user does not exist').exists?
88
+ refute User.objects.filter(last_name: 'This user does not exist').exist?
89
+ end
90
+
91
+ def test_brackets_index_out_of_range
92
+ assert_nil User.objects.filter(last_name: 'LimitTest user').order_by(created_at: :ASC)[1000]
93
+ end
94
+
95
+ def test_brackets_invalid_value
96
+ exception = assert_raises RuntimeError do
97
+ assert_raises User.objects.filter(last_name: 'LimitTest user').order_by(created_at: :ASC)['INVALID VALUE']
98
+ end
99
+ assert_equal('Invalid limit passed to query: INVALID VALUE', exception.message)
100
+ end
101
+
102
+ def test_fetch_index_out_of_range
103
+ exception = assert_raises IndexError do
104
+ assert_raises User.objects.filter(last_name: 'LimitTest user').order_by(created_at: :ASC).fetch(1000)
105
+ end
106
+ assert_equal('Index 1000 outside of QuerySet bounds', exception.message)
107
+ end
108
+
109
+ end
@@ -0,0 +1,24 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../test_helper'
3
+
4
+ class LocalSelectionTest < Minitest::Test
5
+
6
+ def test_equal_selection
7
+ local_selection = Babik::Selection::LocalSelection.new(User, 'first_name', 'Pepe')
8
+ assert_equal local_selection.model, User
9
+ assert_equal local_selection.selection_path, 'first_name'
10
+ assert_equal local_selection.selected_field, 'first_name'
11
+ assert_equal local_selection.value, 'Pepe'
12
+ assert_equal local_selection.operator, 'equal'
13
+ end
14
+
15
+ def test_selection_icontains
16
+ local_selection = Babik::Selection::LocalSelection.new(User, 'firstname__icontains', 'Pepe')
17
+ assert_equal local_selection.model, User
18
+ assert_equal local_selection.selection_path, 'firstname__icontains'
19
+ assert_equal local_selection.selected_field, 'firstname'
20
+ assert_equal local_selection.value, 'Pepe'
21
+ assert_equal local_selection.operator, 'icontains'
22
+ end
23
+
24
+ end
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../test_helper'
5
+
6
+ # Tests of different lookups available in both methods filter and exclude
7
+ class LookupTest < Minitest::Test
8
+
9
+ def setup
10
+ Post.create!(title: 'This other post of 5 stars', stars: 5)
11
+ Post.create!(title: 'This a post of 5 stars', stars: 5)
12
+ Post.create!(title: 'This a post of 4 stars', stars: 4)
13
+ Post.create!(title: 'This a post of 3 stars', stars: 3)
14
+ Post.create!(title: 'This other post with 3 stars', stars: 3)
15
+
16
+ Post.create!(title: 'Old post 1', stars: 0, created_at: Time.now - 1.month)
17
+ Post.create!(title: 'Old post 2', stars: 0, created_at: Time.now - 10.months)
18
+
19
+ Post.create!(title: 'Yesteryear post 1', created_at: Time.now - 1.year)
20
+ Post.create!(title: 'Yesteryear post 2', created_at: Time.now - 10.year)
21
+ end
22
+
23
+ def teardown
24
+ Post.destroy_all
25
+ end
26
+
27
+ def test_gt
28
+ posts_with_more_than_4_starts = Post.objects.filter(stars__gt: 4).order_by(created_at: :ASC)
29
+ assert_equal 2, posts_with_more_than_4_starts.count
30
+ assert_equal 'This other post of 5 stars', posts_with_more_than_4_starts[0].title
31
+ assert_equal 'This a post of 5 stars', posts_with_more_than_4_starts[1].title
32
+ end
33
+
34
+ def test_gte
35
+ posts_with_more_or_equal_than_4_starts = Post.objects.filter(stars__gte: 4).order_by(created_at: :ASC)
36
+ assert_equal 3, posts_with_more_or_equal_than_4_starts.count
37
+ assert_equal 'This other post of 5 stars', posts_with_more_or_equal_than_4_starts[0].title
38
+
39
+ assert_nil posts_with_more_or_equal_than_4_starts[100]
40
+ end
41
+
42
+ def test_lt
43
+ posts_with_less_than_4_starts = Post.objects.filter(stars__lt: 4).order_by(created_at: :ASC)
44
+ assert_equal 4, posts_with_less_than_4_starts.count
45
+ assert_equal 'Old post 2', posts_with_less_than_4_starts[0].title
46
+ assert_equal 'Old post 1', posts_with_less_than_4_starts[1].title
47
+ end
48
+
49
+ def test_lte
50
+ posts_with_less_or_equal_than_4_starts = Post.objects.filter(stars__lte: 4).order_by(created_at: :ASC)
51
+ assert_equal 5, posts_with_less_or_equal_than_4_starts.count
52
+ assert_equal 'Old post 2', posts_with_less_or_equal_than_4_starts[0].title
53
+ assert_equal 'Old post 1', posts_with_less_or_equal_than_4_starts[1].title
54
+ assert_equal 'This a post of 4 stars', posts_with_less_or_equal_than_4_starts[2].title
55
+ assert_equal 'This a post of 3 stars', posts_with_less_or_equal_than_4_starts[3].title
56
+ assert_equal 'This a post of 3 stars', posts_with_less_or_equal_than_4_starts[3].title
57
+ end
58
+
59
+ def test_in
60
+ posts_with_odd_stars = Post.objects.filter(stars__in: [5, 3, 1])
61
+ posts_with_5_stars = Post.objects.filter(stars: 5)
62
+ posts_with_3_stars = Post.objects.filter(stars: 3)
63
+ posts_with_1_star = Post.objects.filter(stars: 1)
64
+
65
+ assert_equal 2, posts_with_5_stars.count
66
+ assert_equal 2, posts_with_3_stars.count
67
+ assert_equal 0, posts_with_1_star.count
68
+ assert_equal 4, posts_with_odd_stars.count
69
+ end
70
+
71
+ def test_in_and_equal
72
+ assert_equal Post.objects.filter(stars: 3).count, Post.objects.filter(stars__in: 3).count
73
+ end
74
+
75
+ def test_between
76
+ posts_3_4_stars = Post.objects.filter(stars__between: [3, 4]).order_by(created_at: :ASC)
77
+ posts_3_4_stars_range = Post.objects.filter(stars__range: [3, 4]).order_by(created_at: :ASC)
78
+ assert_equal 3, posts_3_4_stars.count
79
+ assert_equal 3, posts_3_4_stars_range.count
80
+ assert_equal 'This a post of 4 stars', posts_3_4_stars[0].title
81
+ assert_equal 'This a post of 3 stars', posts_3_4_stars[1].title
82
+ assert_equal 'This other post with 3 stars', posts_3_4_stars[2].title
83
+ end
84
+
85
+ def test_between_dates
86
+ old_posts = Post.objects.filter(created_at__between: [Time.now - 2.months, Time.now - 1.day])
87
+ old_posts_range = Post.objects.filter(created_at__range: [Time.now - 2.months, Time.now - 1.day])
88
+ assert_equal 1, old_posts.count
89
+ assert_equal 1, old_posts_range.count
90
+ assert_equal 'Old post 1', old_posts[0].title
91
+ assert_equal 'Old post 1', old_posts_range[0].title
92
+ end
93
+
94
+ def test_date
95
+ today_posts = Post.objects.filter(created_at__date: Date.today)
96
+ assert_equal 5, today_posts.count
97
+ end
98
+
99
+ def test_year
100
+ younger_than_1998_year_posts = Post.objects.filter(created_at__year__gt: 1998)
101
+ assert_equal Post.objects.count, younger_than_1998_year_posts.count
102
+ assert_equal 0, Post.objects.filter(created_at__year: 1998).count
103
+ end
104
+
105
+ def test_quarter
106
+ # Get a quarter with posts
107
+ quarters = Post.objects.map { |post| (post.created_at.strftime('%m').to_i + 2) / 3 }
108
+ grouped_quarters = quarters.group_by(&:itself)
109
+ quarter = grouped_quarters.keys[0]
110
+ first_quarter = quarter.to_i
111
+ this_quarter_posts = Post.objects.filter(created_at__quarter: first_quarter)
112
+ assert_equal grouped_quarters[quarter].length, this_quarter_posts.count
113
+ end
114
+
115
+ def test_month
116
+ this_month_posts = Post.objects.filter(created_at__month: Time.now.utc.month)
117
+ assert_equal 7, this_month_posts.count
118
+ end
119
+
120
+ def test_day
121
+ this_day_posts = Post.objects.filter(created_at__day: Time.now.utc.day)
122
+ assert_equal 9, this_day_posts.count
123
+ end
124
+
125
+ def test_hour
126
+ this_hour_posts = Post.objects.filter(created_at__hour: Time.now.utc.hour)
127
+ assert_equal 9, this_hour_posts.count
128
+ end
129
+
130
+ def test_hour_wrong_lookup
131
+ exception = assert_raises RuntimeError do
132
+ Post.objects.filter(created_at__hour__xx: Time.now.utc.hour)[0]
133
+ end
134
+ assert_equal('Unknown lookup xx', exception.message)
135
+ end
136
+
137
+ def test_minute
138
+ this_minute_posts = Post.objects.filter(created_at__minute: Time.now.utc.strftime('%M'))
139
+ assert_equal 9, this_minute_posts.count
140
+ end
141
+
142
+ def test_second
143
+ # Get a second with posts
144
+ seconds = Post.objects.map { |post| post.created_at.strftime('%S') }
145
+ grouped_seconds = seconds.group_by(&:itself)
146
+ second = grouped_seconds.keys[0]
147
+ first_second = second.to_i
148
+ this_second_posts = Post.objects.filter(created_at__second: first_second)
149
+ assert_equal grouped_seconds[second].length, this_second_posts.count
150
+ end
151
+
152
+ # Check ISO week
153
+ def test_week
154
+ weeks = Post.objects.map { |post| post.created_at.strftime('%V').to_i }
155
+ grouped_weeks = weeks.group_by(&:itself)
156
+ first_week = grouped_weeks.keys[0]
157
+ first_week_int = first_week.to_i
158
+ this_week_posts = Post.objects.filter(created_at__week: first_week_int)
159
+ assert_equal grouped_weeks[first_week].length, this_week_posts.count
160
+ end
161
+
162
+ # Check 0-6 (sunday to monday) week day
163
+ def test_weekday
164
+ # Get a week day (0-6, sunday to monday) with posts
165
+ week_days = Post.objects.map { |post| post.created_at.strftime('%w') }
166
+ grouped_week_days = week_days.group_by(&:itself)
167
+ first_week_day = grouped_week_days.keys[0]
168
+ first_week_day_int = first_week_day.to_i
169
+ this_week_day_posts = Post.objects.filter(created_at__week_day: first_week_day_int)
170
+ assert_equal grouped_week_days[first_week_day].length, this_week_day_posts.count
171
+ end
172
+
173
+ def test_time
174
+ # Get a time (HH:MM:SS) with posts
175
+ times = Post.objects.map { |post| post.created_at.strftime('%H:%M:%S') }
176
+ grouped_times = times.group_by(&:itself)
177
+ first_time = grouped_times.keys[0]
178
+ this_time_posts = Post.objects.filter(created_at__time: first_time)
179
+ assert_equal grouped_times[first_time].length, this_time_posts.count
180
+ end
181
+
182
+ def test_regex
183
+ other_posts = Post.objects.filter(title__regex: /This a post of \d+ stars/).order_by(stars: :ASC)
184
+ if Babik::Database.config[:adapter] == 'sqlite3'
185
+ assert other_posts.sql.select.include?("posts.title REGEXP 'This a post of \\d+ stars'")
186
+ return
187
+ end
188
+ assert_equal 3, other_posts.count
189
+ assert_equal 'This a post of 3 stars', other_posts[0].title
190
+ assert_equal 'This a post of 4 stars', other_posts[1].title
191
+ assert_equal 'This a post of 5 stars', other_posts[2].title
192
+ assert_equal 3, other_posts.count
193
+ end
194
+
195
+ def test_iregex
196
+ other_posts = Post.objects.filter(title__iregex: /This a post of \d+ stars/).order_by(stars: :ASC)
197
+ if Babik::Database.config[:adapter] == 'sqlite3'
198
+ assert other_posts.sql.select.include?("posts.title REGEXP '(?i)This a post of \\d+ stars'")
199
+ return
200
+ end
201
+ assert_equal 3, other_posts.count
202
+ assert_equal 'This a post of 3 stars', other_posts[0].title
203
+ assert_equal 'This a post of 4 stars', other_posts[1].title
204
+ assert_equal 'This a post of 5 stars', other_posts[2].title
205
+ assert_equal 3, other_posts.count
206
+ end
207
+
208
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../test_helper'
5
+
6
+ # Tests of none method
7
+ class NoneTest < Minitest::Test
8
+
9
+ def setup
10
+ [1..100].each do |i|
11
+ Post.create!(title: "Post #{i}", stars: 5)
12
+ end
13
+ end
14
+
15
+ def teardown
16
+ Post.destroy_all
17
+ end
18
+
19
+ def test_none
20
+ no_posts = Post.objects.none
21
+ assert_equal 0, no_posts.count
22
+ i = 100
23
+ no_posts.each do |_post|
24
+ i -= 1
25
+ end
26
+ assert_equal 100, i
27
+ end
28
+
29
+ def test_deep_none_call
30
+ no_posts = Post.objects.filter(stars: 5).none
31
+ assert_equal 0, no_posts.count
32
+ i = 100
33
+ no_posts.each do |_post|
34
+ i -= 1
35
+ end
36
+ assert_equal 100, i
37
+ end
38
+
39
+
40
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../test_helper'
5
+
6
+ # Ordering tests
7
+ class OrderTest < Minitest::Test
8
+
9
+ def setup
10
+
11
+ @frankish_kingdom = GeoZone.create!(name: 'Frankish Kingdom')
12
+
13
+ frankish_king_names = [
14
+ 'Merovech',
15
+ 'Childeric I',
16
+ 'Clovis I'
17
+ ]
18
+
19
+ @frankish_kings = []
20
+ frankish_king_names.each do |frankish_king|
21
+ @frankish_kings << User.create!(first_name: frankish_king, last_name: 'Merovingian', zone: @frankish_kingdom)
22
+ end
23
+
24
+ @hispania = GeoZone.create!(name: 'Hispania')
25
+ goth_king_names = ['Alarico I',
26
+ 'Ataulfo',
27
+ 'Sigerico',
28
+ 'Teodorico I',
29
+ 'Turismundo',
30
+ 'Teodorico II',
31
+ 'Eurico',
32
+ 'Alarico II']
33
+
34
+ @goth_kings = []
35
+ goth_king_names.each do |goth_king|
36
+ @goth_kings << User.create!(first_name: goth_king, zone: @hispania)
37
+ end
38
+
39
+ end
40
+
41
+ def teardown
42
+ User.destroy_all
43
+ GeoZone.destroy_all
44
+ end
45
+
46
+ def test_first
47
+ first_user = User.objects.filter('zone::name': 'Hispania').order_by(%i[first_name ASC]).first
48
+ assert_equal 'Alarico I', first_user.first_name
49
+ end
50
+
51
+ def test_last_asc
52
+ last_user = User.objects.filter('zone::name': 'Hispania').order_by(%i[first_name ASC]).last
53
+ assert_equal 'Turismundo', last_user.first_name
54
+ end
55
+
56
+ def test_last_desc
57
+ last_user = User.objects.filter('zone::name': 'Hispania').order_by(%i[first_name DESC]).last
58
+ assert_equal 'Alarico I', last_user.first_name
59
+ end
60
+
61
+ def test_first_last
62
+ first = User.objects.filter('zone::name': 'Hispania').order_by(%i[first_name ASC]).first
63
+ inverted_last = User.objects.filter('zone::name': 'Hispania').order_by(%i[first_name DESC]).last
64
+ assert_equal first.id, inverted_last.id
65
+ assert_equal first.first_name, inverted_last.first_name
66
+ end
67
+
68
+ def test_basic_order
69
+ users = User.objects.filter('zone::name': 'Hispania').order_by(%i[first_name ASC])
70
+ _test_basic_order(users)
71
+ end
72
+
73
+ def test_basic_order_hash
74
+ users = User.objects.filter('zone::name': 'Hispania').order_by(first_name: :ASC)
75
+ _test_basic_order(users)
76
+ end
77
+
78
+ def test_basic_order_string
79
+ users = User.objects.filter('zone::name': 'Hispania').order_by('first_name')
80
+ _test_basic_order(users)
81
+ end
82
+
83
+ def test_basic_order_symbol
84
+ users = User.objects.filter('zone::name': 'Hispania').order_by(:first_name)
85
+ _test_basic_order(users)
86
+ end
87
+
88
+ def _test_basic_order(users)
89
+ goth_king_names = [
90
+ 'Alarico I', 'Alarico II', 'Ataulfo', 'Eurico', 'Sigerico', 'Teodorico I', 'Teodorico II', 'Turismundo'
91
+ ]
92
+ users.each_with_index do |user, user_index|
93
+ assert_equal goth_king_names[user_index], user.first_name
94
+ end
95
+ assert users.ordered?
96
+ end
97
+
98
+ def test_deep_order
99
+ users = User.objects.order_by(%i[zone::name ASC], %i[first_name ASC])
100
+ reversed_users = users.reverse
101
+ king_names = [
102
+ 'Childeric I',
103
+ 'Clovis I',
104
+ 'Merovech',
105
+ 'Alarico I',
106
+ 'Alarico II',
107
+ 'Ataulfo',
108
+ 'Eurico',
109
+ 'Sigerico',
110
+ 'Teodorico I',
111
+ 'Teodorico II',
112
+ 'Turismundo'
113
+ ]
114
+ users.each_with_index do |user, user_index|
115
+ assert_equal king_names[user_index], user.first_name
116
+ end
117
+ assert users.ordered?
118
+
119
+ reversed_king_names = king_names.reverse
120
+ reversed_users.each_with_index do |reversed_order_user, user_index|
121
+ assert_equal reversed_king_names[user_index], reversed_order_user.first_name
122
+ end
123
+ assert reversed_users.ordered?
124
+
125
+ king_names.each_with_index do |king_name, king_index|
126
+ assert_equal king_name, users[king_index].first_name
127
+ assert_equal king_name, reversed_users[king_names.length - king_index - 1].first_name
128
+ end
129
+ end
130
+
131
+ def test_disorder
132
+ users = User.objects.order_by(%i[zone::name ASC], %i[first_name ASC])
133
+ users.disorder!
134
+ refute users.ordered?
135
+ end
136
+
137
+ def test_order_with_prefixed_direction
138
+ users = User.objects.order_by('-zone::name', '-first_name')
139
+ first_user = users.first
140
+ assert_equal 'Hispania', first_user.zone.name
141
+ assert_equal 'Turismundo', first_user.first_name
142
+ end
143
+
144
+ def test_wrong_order
145
+ exception = assert_raises RuntimeError do
146
+ User.objects.filter('zone::name': 'Hispania').order_by([:first_name, 'XXXX'])
147
+ end
148
+ assert_equal('Invalid order type XXXX in first_name: Expecting :ASC or :DESC', exception.message)
149
+ end
150
+
151
+ def test_wrong_order_param_type
152
+ exception = assert_raises RuntimeError do
153
+ User.objects.filter('zone::name': 'Hispania').order_by(2222)
154
+ end
155
+ assert_equal('Invalid type of order: 2222', exception.message)
156
+ end
157
+
158
+ def test_wrong_field_path
159
+ exception = assert_raises RuntimeError do
160
+ User.objects.filter('zone::name': 'Hispania').order_by([1111, :ASC])
161
+ end
162
+ assert_equal('field_path of class Integer not valid. A Symbol/String/Babik::Selection::Base expected', exception.message)
163
+ end
164
+
165
+ end