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,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../test_helper'
5
+
6
+ # Check the quotes and strings ar correctly escaped.
7
+ class EscapeTest < Minitest::Test
8
+
9
+ def setup
10
+ User.create!(first_name: 'Athos')
11
+ User.create!(first_name: 'Porthos')
12
+ User.create!(first_name: 'Aramis')
13
+ User.create!(first_name: "D'Artagnan")
14
+ end
15
+
16
+ def test_escape_value
17
+ users = User.objects.filter(first_name: "D'Artagnan")
18
+ assert_equal 1, users.count
19
+ end
20
+
21
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../test_helper'
5
+
6
+ # Check that inverse_of is required in all associations
7
+ class InverseOfRequiredTest < Minitest::Test
8
+
9
+ def setup
10
+ main_category = Category.create!(name: 'Category not used')
11
+ bad_post = BadPost.create!(title: 'This is a bad post', category: main_category)
12
+ BadTag.create!(name: 'bad tag 1', bad_post: bad_post)
13
+ BadTag.create!(name: 'bad tag 2', bad_post: bad_post)
14
+ end
15
+
16
+ def teardown
17
+ BadTag.delete_all
18
+ BadPost.delete_all
19
+ Category.delete_all
20
+ end
21
+
22
+ def test_association_without_inverse
23
+ exception = assert_raises RuntimeError do
24
+ bad_post = BadPost.all.first
25
+ bad_post.objects(:bad_tags)
26
+ end
27
+ assert_equal(
28
+ 'Relationship bad_tags of model BadPost has no inverse_of option.',
29
+ exception.message
30
+ )
31
+ end
32
+
33
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../test_helper'
5
+
6
+ # Tests of aggregate method
7
+ class AggregateTest < Minitest::Test
8
+
9
+ def setup
10
+ @caesar = User.create!(first_name: 'Julius', last_name: 'Caesar', email: 'backstabbed@example.com')
11
+ (1..7).each do |book_i|
12
+ post = Post.create!(title: "Commentarii de Bello Gallico #{book_i}", author: @caesar, stars: [book_i, 5].min)
13
+ post.add_tag_by_name('war')
14
+ post.add_tag_by_name('gallic')
15
+ post.add_tag_by_name('tribes')
16
+ post.add_tag_by_name('victory')
17
+ post.add_tag_by_name('campaign')
18
+ post.add_tag_by_name('biography')
19
+ end
20
+
21
+ @aulus = User.create!(first_name: 'Aulus', last_name: 'Hirtius', email: 'aulushirtius@example.com')
22
+ last_post = Post.create!(title: 'Commentarii de Bello Gallico 8', author: @aulus)
23
+ last_post.add_tag_by_name('war')
24
+ last_post.add_tag_by_name('last_book')
25
+ end
26
+
27
+ def teardown
28
+ Tag.destroy_all
29
+ Post.destroy_all
30
+ User.destroy_all
31
+ end
32
+
33
+ def test_local_avg
34
+ stars = @caesar.objects(:posts).map(&:stars)
35
+ avg_stars = stars.reduce(:+) / stars.length.to_f
36
+ avg_starts_aggregation = @caesar
37
+ .objects(:posts)
38
+ .aggregate(avg_stars: Babik::QuerySet.agg(:avg, 'stars'))
39
+ assert_equal avg_stars.round(4), avg_starts_aggregation[:avg_stars].round(4)
40
+ end
41
+
42
+ def test_foreign_avg
43
+ stars = @caesar.objects(:posts).map(&:stars)
44
+ avg_stars = stars.reduce(:+) / stars.length.to_f
45
+ avg_starts_aggregation = User
46
+ .objects
47
+ .filter(id: @caesar.id)
48
+ .aggregate(avg_stars: Babik::QuerySet.agg(:avg, 'posts::stars'))
49
+ assert_equal avg_stars.round(4), avg_starts_aggregation[:avg_stars].round(4)
50
+ end
51
+
52
+ def test_simplified_foreign_avg
53
+ stars = @caesar.objects(:posts).map(&:stars)
54
+ avg_stars = stars.reduce(:+) / stars.length.to_f
55
+ avg_starts_aggregation = @caesar
56
+ .objects
57
+ .aggregate(avg_stars: Babik::QuerySet.agg(:avg, 'posts::stars'))
58
+ assert_equal avg_stars.round(4), avg_starts_aggregation[:avg_stars].round(4)
59
+ end
60
+
61
+ def test_local_distinct_count
62
+ # Julius Caesar has all types of value of stars
63
+ # As he has posts with all stars, it should be 5
64
+ count_aggregation = @caesar
65
+ .objects
66
+ .project(:stars)
67
+ .aggregate(
68
+ caesar_count_distinct_stars:
69
+ Babik::QuerySet.agg(:count_distinct, 'posts::stars')
70
+ )
71
+ assert_equal 5, count_aggregation[:caesar_count_distinct_stars]
72
+ end
73
+
74
+ def test_foreign_distinct_count
75
+ # Julius Caesar has 6 tags in his posts, it should be 6
76
+ count_aggregation = @caesar
77
+ .objects
78
+ .aggregate(
79
+ caesar_count_distinct_tags:
80
+ Babik::QuerySet.agg(:count_distinct, 'posts::tags::id')
81
+ )
82
+ assert_equal 6, count_aggregation[:caesar_count_distinct_tags]
83
+ end
84
+
85
+ def test_foreign_count
86
+ # Julius Caesar has 6 tags in his posts, it should be 6
87
+ count_aggregation = @caesar
88
+ .objects
89
+ .aggregate(
90
+ caesar_count_tags:
91
+ Babik::QuerySet.agg(:count, 'posts::tags::id')
92
+ )
93
+ assert_equal @caesar.objects(:posts).count * 6, count_aggregation[:caesar_count_tags]
94
+ end
95
+
96
+ def test_max
97
+ max_stars = @caesar.objects(:posts).map(&:stars).max
98
+ max_stars_agg = @caesar.objects(:posts)
99
+ .aggregate(max_stars: Babik::QuerySet.agg(:max, 'stars'))[:max_stars]
100
+ assert_equal max_stars, max_stars_agg
101
+ end
102
+
103
+ def test_min
104
+ min_stars = @caesar.objects(:posts).map(&:stars).min
105
+ min_stars_agg = @caesar.objects(:posts)
106
+ .aggregate(min_stars: Babik::QuerySet::Min.new('stars'))[:min_stars]
107
+ assert_equal min_stars, min_stars_agg
108
+ end
109
+
110
+ def test_sum
111
+ sum_stars = @caesar.objects(:posts).map(&:stars).inject(:+)
112
+ sum_stars_agg = @caesar.objects(:posts)
113
+ .aggregate(sum_stars: Babik::QuerySet::Sum.new('stars'))[:sum_stars]
114
+ assert_equal sum_stars, sum_stars_agg
115
+ end
116
+
117
+ def test_sum_max_min
118
+ sum_stars = @caesar.objects(:posts).map(&:stars).inject(:+)
119
+ max_stars = @caesar.objects(:posts).map(&:stars).max
120
+ min_stars = @caesar.objects(:posts).map(&:stars).min
121
+
122
+
123
+ max_min_stars_agg = @caesar.objects(:posts)
124
+ .aggregate(
125
+ sum_stars: Babik::QuerySet::Sum.new('stars'),
126
+ max_stars: Babik::QuerySet::Max.new('stars'),
127
+ min_stars: Babik::QuerySet::Min.new('stars')
128
+ )
129
+ assert_equal sum_stars, max_min_stars_agg[:sum_stars]
130
+ assert_equal max_stars, max_min_stars_agg[:max_stars]
131
+ assert_equal min_stars, max_min_stars_agg[:min_stars]
132
+ end
133
+
134
+ def test_std_dev
135
+ if %w[postgres, mysql2].include?(Babik::Database.config[:adapter])
136
+ std_dev_var_agg = @caesar.objects(:posts)
137
+ .aggregate(
138
+ std_dev_stars: Babik::QuerySet::StdDev.new('stars'),
139
+ std_dev_sample_stars: Babik::QuerySet::StdDevSample.new('stars'),
140
+ var_stars: Babik::QuerySet::Var.new('stars'),
141
+ var_sample_stars: Babik::QuerySet::VarSample.new('stars'),
142
+ )
143
+
144
+ assert_in_delta 1.4982983545287878, std_dev_var_agg[:std_dev_stars]
145
+ assert_in_delta 1.618347187425374, std_dev_var_agg[:std_dev_sample_stars]
146
+ assert_in_delta 2.2448979591836733, std_dev_var_agg[:var_stars]
147
+ assert_in_delta 2.244897959183673, std_dev_var_agg[:var_sample_stars]
148
+ end
149
+ end
150
+
151
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../test_helper'
5
+
6
+ # Tests of first, last, earliest and latest methods
7
+ class BoundsTest < Minitest::Test
8
+
9
+ def setup
10
+ @cervantes = User.create!(first_name: 'Miguel', last_name: 'De Cervantes y Saavedra')
11
+ Post.create!(title: 'La Galatea', author: @cervantes, stars: 3)
12
+ Post.create!(title: 'La gitanilla', author: @cervantes, stars: 3)
13
+ Post.create!(title: 'El ingenioso hidalgo Don Quijote de La Mancha', author: @cervantes, stars: 5)
14
+ Post.create!(title: 'Rinconete y Cortadillo', author: @cervantes, stars: 4)
15
+ end
16
+
17
+ def teardown
18
+ Post.destroy_all
19
+ User.destroy_all
20
+ end
21
+
22
+ def test_first_and_earliest
23
+ first_with_less_stars = Post.objects.order_by('stars').first
24
+ earliest_with_less_stars = Post.objects.earliest('stars')
25
+ latest_with_more_stars = Post.objects.latest('-stars')
26
+ assert_equal latest_with_more_stars.id, first_with_less_stars.id
27
+ assert_equal earliest_with_less_stars.id, first_with_less_stars.id
28
+ assert_equal 'La Galatea', first_with_less_stars.title
29
+ assert_equal 'La Galatea', earliest_with_less_stars.title
30
+ assert_equal 3, first_with_less_stars.stars
31
+ assert_equal 3, earliest_with_less_stars.stars
32
+ end
33
+
34
+ def test_last_and_latest
35
+ last_with_less_stars = Post.objects.order_by('stars').last
36
+ latest_with_less_stars = Post.objects.latest('stars')
37
+ earliest_with_more_stars = Post.objects.earliest('-stars')
38
+ assert_equal earliest_with_more_stars.id, last_with_less_stars.id
39
+ assert_equal latest_with_less_stars.id, last_with_less_stars.id
40
+ assert_equal 'El ingenioso hidalgo Don Quijote de La Mancha', last_with_less_stars.title
41
+ assert_equal 'El ingenioso hidalgo Don Quijote de La Mancha', latest_with_less_stars.title
42
+ assert_equal 5, last_with_less_stars.stars
43
+ assert_equal 5, latest_with_less_stars.stars
44
+ end
45
+
46
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../test_helper'
5
+
6
+ # Tests of count method
7
+ class CountTest < Minitest::Test
8
+
9
+ def setup
10
+ @parthian_empire = GeoZone.create!(name: 'Parthian Empire')
11
+ @syria = GeoZone.create!(name: 'Syria', parent_zone: @parthian_empire)
12
+ @antioch = GeoZone.create!(name: 'Antioch', parent_zone: @syria)
13
+ @seleucus = User.create!(first_name: 'Seleucus', email: 'seleucus@example.com', zone: @antioch)
14
+
15
+ @roman_empire = GeoZone.create!(name: 'Roman Empire')
16
+
17
+ @italia = GeoZone.create!(name: 'Italia', parent_zone: @roman_empire)
18
+ @rome = GeoZone.create!(name: 'Rome', parent_zone: @italia)
19
+
20
+ @judea = GeoZone.create!(name: 'Judea', parent_zone: @roman_empire)
21
+ @jerusalem = GeoZone.create!(name: 'Jerusalem', parent_zone: @judea)
22
+
23
+ @tiberius = User.create!(first_name: 'Tiberius', email: 'tiberius@example.com', zone: @rome)
24
+ @pilate = User.create!(first_name: 'Pontius', last_name: 'Pilate', email: 'pontious@example.com', zone: @jerusalem)
25
+ @flavio = User.create!(first_name: 'Flavio', last_name: 'Josefo', email: 'flaviojosefo@example.com', zone: @jerusalem)
26
+
27
+ @noah = User.create!(first_name: 'Noah')
28
+ @noah.created_at = Time.now - 1000.days
29
+ @noah.save!
30
+ end
31
+
32
+ def teardown
33
+ @parthian_empire.destroy
34
+ @seleucus.destroy
35
+ @roman_empire.destroy
36
+ @tiberius.destroy
37
+ @pilate.destroy
38
+ @flavio.destroy
39
+ @noah.destroy
40
+ end
41
+
42
+ # Test the count is well implemented when the returned value is 0
43
+ def test_count_0
44
+ zone_non_existant_description = 'This description does not appear in any zone'
45
+ queryset = User.objects.filter(
46
+ first_name: 'Flavio',
47
+ last_name: 'Josefo',
48
+ "zone::name__different": 'Rome',
49
+ "zone::description": zone_non_existant_description
50
+ )
51
+ active_record_set = User
52
+ .joins(:zone)
53
+ .where(first_name: 'Flavio', last_name: 'Josefo', "geo_zones.description": zone_non_existant_description)
54
+ .where.not("geo_zones.name": 'Rome')
55
+ assert_equal active_record_set.count, queryset.count
56
+ assert_equal queryset.length,queryset.count
57
+ assert_equal queryset.length,queryset.size
58
+ assert queryset.empty?
59
+ assert_equal false, queryset.exists?
60
+ end
61
+
62
+ # Test the count is well implemented when the returned value is 1
63
+ def test_count_1
64
+ queryset = User.objects.filter(
65
+ first_name: 'Flavio',
66
+ last_name: 'Josefo',
67
+ "zone::name__different": 'Madrid'
68
+ )
69
+ active_record_set = User
70
+ .joins(:zone)
71
+ .where(first_name: 'Flavio', last_name: 'Josefo')
72
+ .where.not("geo_zones.name": 'Madrid')
73
+ assert_equal active_record_set.count, queryset.count
74
+ assert_equal queryset.length, queryset.count
75
+ assert_equal queryset.length, queryset.size
76
+ assert_equal false, queryset.empty?
77
+ assert_equal true, queryset.exists?
78
+ end
79
+
80
+ # Test the count from a deep belongs to relationship
81
+ def test_from_deep_belongs_to
82
+ queryset = User.objects.filter(
83
+ "zone::parent_zone::parent_zone::name__equals": 'Roman Empire'
84
+ )
85
+ assert_equal 3, queryset.count
86
+ assert_equal 3, queryset.size
87
+ end
88
+
89
+ # Test the count from a deep belongs to relationship
90
+ def test_or
91
+ queryset = User.objects.filter(
92
+ [
93
+ { "zone::parent_zone::parent_zone::name__equals": 'Roman Empire' },
94
+ { "zone::parent_zone::parent_zone::name__equals": 'Parthian Empire' }
95
+ ]
96
+ )
97
+ number_of_users = 0
98
+ queryset.each do |user|
99
+ assert ['Roman Empire', 'Parthian Empire'].include?(user.zone.parent_zone.parent_zone.name)
100
+ number_of_users += 1
101
+ end
102
+ assert_equal 4, queryset.count
103
+ assert_equal 4, number_of_users
104
+ end
105
+
106
+ # Count the objects using contains
107
+ def test_lookups
108
+ assert_equal User.where('first_name LIKE ?', '%avi%').length, User.objects.filter(first_name__contains: 'avi').length
109
+ assert_equal User.where('first_name LIKE ?', 'Fla%').length, User.objects.filter(first_name__startswith: 'Fla').length
110
+ assert_equal User.where('first_name LIKE ?', '%vio').length, User.objects.filter(first_name__startswith: 'Fla').length
111
+ assert_equal User.where(first_name: 'Flavio').length, User.objects.filter(first_name: 'Flavio').length
112
+ assert_equal User.where(first_name: 'Flavio').length, User.objects.filter(first_name__exact: 'Flavio').length
113
+ assert_equal User.where(first_name: 'Flavio').length, User.objects.filter(first_name__equal: 'Flavio').length
114
+ end
115
+
116
+ def test_lookup_isnull
117
+ assert_equal 0, User.objects.filter('zone::name': 'Rome', email__isnull: true).count
118
+ assert_equal 0, User.objects.filter('zone::name': 'Rome', email__exact: nil).count
119
+ assert_equal 1, User.objects.filter('zone::name': 'Rome', email__isnull: false).count
120
+ assert_equal 2, User.objects.filter('zone::name': 'Jerusalem', email__isnull: false).count
121
+ assert_equal 2, User.objects.filter('zone::name': 'Jerusalem').exclude(email__exact: nil).count
122
+ end
123
+
124
+ def test_lookup_in
125
+ assert_equal 2, User.objects.filter(first_name__in: %w[Tiberius Pontius Julius Marcus]).length
126
+ end
127
+
128
+ def test_date
129
+ today_start = Time.now.beginning_of_day
130
+ today_end = Time.now.end_of_day
131
+ today = Date.today
132
+
133
+ number_of_users = User.where('created_at >= ?', today_start).where('created_at <= ?', today_end).count
134
+
135
+ assert_equal number_of_users, User.objects.filter(created_at__gte: today_start, created_at__lte: today_end).count
136
+ assert_equal number_of_users, User.objects.filter(created_at__between: [today_start, today_end]).count
137
+ assert_equal number_of_users, User.objects.filter(created_at__date: today).count
138
+
139
+ assert_equal(
140
+ 2, User
141
+ .objects
142
+ .filter(created_at__date: today)
143
+ .filter([{ first_name: 'Tiberius' }, { first_name: 'Pontius' }]).count
144
+ )
145
+ end
146
+
147
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../test_helper'
5
+
6
+ # Tests of distinct method
7
+ class DistinctTest < Minitest::Test
8
+
9
+ def setup
10
+ User.create!(first_name: 'Diego', last_name: 'de Siloé')
11
+ User.create!(first_name: 'Diego', last_name: 'Velazquez')
12
+ end
13
+
14
+ def teardown
15
+ User.delete_all
16
+ end
17
+
18
+ def test_local_distinct
19
+ assert_equal 2, User.objects.filter(first_name: 'Diego').project(:first_name).count
20
+ assert_equal 1, User.objects.distinct.filter(first_name: 'Diego').project(:first_name).count
21
+ assert User.objects.distinct.filter(first_name: 'Diego').distinct?
22
+ end
23
+
24
+ def test_local_distinct_from_queryset
25
+ diegos = User.objects.filter(first_name: 'Diego').project(:first_name).distinct
26
+ assert diegos.distinct?
27
+ refute diegos.undistinct.distinct?
28
+ assert_equal 1, diegos.count
29
+ end
30
+
31
+ def test_local_undistinct
32
+ diegos = User.objects.filter(first_name: 'Diego').project(:first_name)
33
+ refute diegos.distinct?
34
+ assert_equal 2, diegos.count
35
+ assert_equal diegos.count, diegos.distinct.undistinct.count
36
+ end
37
+
38
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../test_helper'
5
+
6
+ # Tests of exclude method
7
+ class ExcludeTest < Minitest::Test
8
+
9
+ def setup
10
+ @asturias = GeoZone.new(name: 'Asturias')
11
+ @cantabria = GeoZone.new(name: 'Cantabria')
12
+ @cangas_de_onis = GeoZone.new(name: 'Cangas de Onís', parent_zone: @asturias)
13
+ @oviedo = GeoZone.new(name: 'Oviedo', parent_zone: @asturias)
14
+
15
+ User.create!(first_name: 'Pelayo', biography: 'Unknown origin', zone: @cangas_de_onis)
16
+ User.create!(first_name: 'Favila', biography: 'Short reign of two years, seven months and ten days', zone: @cangas_de_onis)
17
+ User.create!(first_name: 'Alfonso I', last_name: 'El Católico', biography: 'Son-in-law of Don Pelayo', zone: @cantabria)
18
+ User.create!(first_name: 'Fruela I', last_name: 'Hombre de Hierro', biography: 'Killed by conspirators', zone: @cangas_de_onis)
19
+ User.create!(first_name: 'Aurelio', biography: 'Chosen by Asturian nobility', zone: @asturias)
20
+ User.create!(first_name: 'Silo', biography: 'Peace period', zone: @asturias)
21
+ User.create!(first_name: 'Alfonso II', last_name: 'El Casto', biography: 'Discovered Saint James tomb', zone: @oviedo)
22
+ User.create!(first_name: 'Mauregato', biography: 'Paid the 100 maidens tribute to Cordova Emirate', zone: @asturias)
23
+ User.create!(first_name: 'Bermudo I', last_name: 'El Diácono', biography: 'Cultured, magnanimous and illustrated man')
24
+ User.create!(first_name: 'Nepociano', last_name: 'El usurpador')
25
+ User.create!(first_name: 'Ramiro I', last_name: 'Vara de la Justicia', zone: @oviedo)
26
+ User.create!(first_name: 'Ordoño I', biography: 'Failed first attemp to reconquer the kingdom', zone: @oviedo)
27
+ User.create!(first_name: 'Alfonso III', last_name: 'El Magno', biography: 'Last king of Independent Asturian kingdom')
28
+ User.create!(first_name: 'Fruela II', last_name: 'El leproso', biography: 'Last king of Asturian kingdom')
29
+ end
30
+
31
+ def teardown
32
+ User.delete_all
33
+ end
34
+
35
+ def test_local_exclude
36
+ kings_with_bio_out_without_ordinal = User.objects
37
+ .filter(biography__isnull: false)
38
+ .exclude(first_name__endswith: 'I')
39
+ .order_by([:first_name, :ASC])
40
+ asturian_kings = ['Aurelio', 'Favila', 'Mauregato', 'Pelayo', 'Silo']
41
+ kings_with_bio_out_without_ordinal.each_with_index do |king, king_i|
42
+ assert_equal asturian_kings[king_i], king.first_name
43
+ end
44
+ end
45
+
46
+ def test_foreign_exclude
47
+ kings_with_bio_not_from_oviedo = User.objects
48
+ .filter(biography__isnull: false)
49
+ .exclude('zone::name': 'Oviedo')
50
+ .order_by([:first_name, :ASC])
51
+ asturian_kings = ['Alfonso I', 'Aurelio', 'Favila', 'Fruela I', 'Mauregato', 'Pelayo', 'Silo']
52
+ kings_with_bio_not_from_oviedo.each_with_index do |king, king_i|
53
+ assert_equal asturian_kings[king_i], king.first_name
54
+ end
55
+ end
56
+
57
+ def test_foreign_complex_exclude
58
+ kings_not_from_oviedo_not_named_alfonso = User.objects
59
+ .exclude(
60
+ [
61
+ { 'zone::name': 'Oviedo' },
62
+ { first_name__startswith: 'Alfonso' }
63
+ ]
64
+ )
65
+ .order_by([:first_name, :ASC])
66
+ asturian_kings = ['Aurelio', 'Favila', 'Fruela I', 'Mauregato', 'Pelayo', 'Silo']
67
+ kings_not_from_oviedo_not_named_alfonso.each_with_index do |king, king_i|
68
+ assert_equal asturian_kings[king_i], king.first_name
69
+ end
70
+ end
71
+
72
+ end