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.
- checksums.yaml +7 -0
- data/Gemfile +16 -0
- data/README.md +718 -0
- data/Rakefile +18 -0
- data/lib/babik.rb +122 -0
- data/lib/babik/database.rb +16 -0
- data/lib/babik/queryset.rb +154 -0
- data/lib/babik/queryset/components/aggregation.rb +172 -0
- data/lib/babik/queryset/components/limit.rb +22 -0
- data/lib/babik/queryset/components/order.rb +161 -0
- data/lib/babik/queryset/components/projection.rb +118 -0
- data/lib/babik/queryset/components/select_related.rb +78 -0
- data/lib/babik/queryset/components/sql_renderer.rb +99 -0
- data/lib/babik/queryset/components/where.rb +43 -0
- data/lib/babik/queryset/lib/association/foreign_association_chain.rb +97 -0
- data/lib/babik/queryset/lib/association/select_related_association_chain.rb +32 -0
- data/lib/babik/queryset/lib/condition.rb +103 -0
- data/lib/babik/queryset/lib/field.rb +34 -0
- data/lib/babik/queryset/lib/join/association_joiner.rb +39 -0
- data/lib/babik/queryset/lib/join/join.rb +86 -0
- data/lib/babik/queryset/lib/selection/config.rb +19 -0
- data/lib/babik/queryset/lib/selection/foreign_selection.rb +39 -0
- data/lib/babik/queryset/lib/selection/local_selection.rb +40 -0
- data/lib/babik/queryset/lib/selection/operation/base.rb +126 -0
- data/lib/babik/queryset/lib/selection/operation/date.rb +178 -0
- data/lib/babik/queryset/lib/selection/operation/operations.rb +201 -0
- data/lib/babik/queryset/lib/selection/operation/regex.rb +58 -0
- data/lib/babik/queryset/lib/selection/path/foreign_path.rb +50 -0
- data/lib/babik/queryset/lib/selection/path/local_path.rb +44 -0
- data/lib/babik/queryset/lib/selection/path/path.rb +23 -0
- data/lib/babik/queryset/lib/selection/select_related_selection.rb +38 -0
- data/lib/babik/queryset/lib/selection/selection.rb +19 -0
- data/lib/babik/queryset/lib/update/assignment.rb +108 -0
- data/lib/babik/queryset/mixins/aggregatable.rb +17 -0
- data/lib/babik/queryset/mixins/bounded.rb +38 -0
- data/lib/babik/queryset/mixins/clonable.rb +52 -0
- data/lib/babik/queryset/mixins/countable.rb +44 -0
- data/lib/babik/queryset/mixins/deletable.rb +13 -0
- data/lib/babik/queryset/mixins/distinguishable.rb +27 -0
- data/lib/babik/queryset/mixins/filterable.rb +51 -0
- data/lib/babik/queryset/mixins/limitable.rb +88 -0
- data/lib/babik/queryset/mixins/lockable.rb +31 -0
- data/lib/babik/queryset/mixins/none.rb +16 -0
- data/lib/babik/queryset/mixins/projectable.rb +34 -0
- data/lib/babik/queryset/mixins/related_selector.rb +28 -0
- data/lib/babik/queryset/mixins/set_operations.rb +32 -0
- data/lib/babik/queryset/mixins/sortable.rb +49 -0
- data/lib/babik/queryset/mixins/sql_renderizable.rb +17 -0
- data/lib/babik/queryset/mixins/updatable.rb +14 -0
- data/lib/babik/queryset/templates/default/delete/main.sql.erb +14 -0
- data/lib/babik/queryset/templates/default/select/components/aggregation.sql.erb +5 -0
- data/lib/babik/queryset/templates/default/select/components/from.sql.erb +16 -0
- data/lib/babik/queryset/templates/default/select/components/from_set.sql.erb +3 -0
- data/lib/babik/queryset/templates/default/select/components/from_table.sql.erb +2 -0
- data/lib/babik/queryset/templates/default/select/components/limit.sql.erb +10 -0
- data/lib/babik/queryset/templates/default/select/components/order_by.sql.erb +9 -0
- data/lib/babik/queryset/templates/default/select/components/projection.sql.erb +7 -0
- data/lib/babik/queryset/templates/default/select/components/select_related.sql.erb +26 -0
- data/lib/babik/queryset/templates/default/select/components/where.sql.erb +39 -0
- data/lib/babik/queryset/templates/default/select/main.sql.erb +42 -0
- data/lib/babik/queryset/templates/default/update/main.sql.erb +15 -0
- data/lib/babik/queryset/templates/mssql/select/components/limit.sql.erb +8 -0
- data/lib/babik/queryset/templates/mssql/select/components/order_by.sql.erb +21 -0
- data/lib/babik/queryset/templates/mysql2/delete/main.sql.erb +15 -0
- data/lib/babik/queryset/templates/mysql2/update/main.sql.erb +18 -0
- data/lib/babik/queryset/templates/sqlite3/select/components/from_set.sql.erb +5 -0
- data/test/config/db/schema.rb +83 -0
- data/test/config/models/bad_post.rb +5 -0
- data/test/config/models/bad_tag.rb +5 -0
- data/test/config/models/category.rb +4 -0
- data/test/config/models/geozone.rb +6 -0
- data/test/config/models/group.rb +5 -0
- data/test/config/models/group_user.rb +5 -0
- data/test/config/models/post.rb +24 -0
- data/test/config/models/post_tag.rb +5 -0
- data/test/config/models/tag.rb +5 -0
- data/test/config/models/user.rb +6 -0
- data/test/delete/delete_test.rb +60 -0
- data/test/delete/foreign_conditions_delete_test.rb +57 -0
- data/test/delete/local_conditions_delete_test.rb +20 -0
- data/test/enable_coverage.rb +17 -0
- data/test/lib/selection/operation/log/test-queries.log +1 -0
- data/test/lib/selection/operation/test_date.rb +131 -0
- data/test/lib/selection/operation/test_regex.rb +55 -0
- data/test/other/clone_test.rb +129 -0
- data/test/other/escape_test.rb +21 -0
- data/test/other/inverse_of_required_test.rb +33 -0
- data/test/select/aggregate_test.rb +151 -0
- data/test/select/bounds_test.rb +46 -0
- data/test/select/count_test.rb +147 -0
- data/test/select/distinct_test.rb +38 -0
- data/test/select/exclude_test.rb +72 -0
- data/test/select/filter_from_object_test.rb +125 -0
- data/test/select/filter_test.rb +207 -0
- data/test/select/for_update_test.rb +19 -0
- data/test/select/foreign_selection_test.rb +60 -0
- data/test/select/get_test.rb +40 -0
- data/test/select/limit_test.rb +109 -0
- data/test/select/local_selection_test.rb +24 -0
- data/test/select/lookup_test.rb +208 -0
- data/test/select/none_test.rb +40 -0
- data/test/select/order_test.rb +165 -0
- data/test/select/project_test.rb +107 -0
- data/test/select/select_related_test.rb +124 -0
- data/test/select/subquery_test.rb +50 -0
- data/test/set_operations/basic_usage_test.rb +121 -0
- data/test/test_helper.rb +55 -0
- data/test/update/update_test.rb +93 -0
- metadata +278 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require_relative '../test_helper'
|
5
|
+
|
6
|
+
# Project method test
|
7
|
+
class ProjectTest < Minitest::Test
|
8
|
+
|
9
|
+
def setup
|
10
|
+
if GeoZone.objects.filter(name: 'Castilla').exists?
|
11
|
+
return
|
12
|
+
end
|
13
|
+
@castille = GeoZone.create!(name: 'Castilla')
|
14
|
+
['Juan II', 'Isabel I', 'Juana I'].each do |name|
|
15
|
+
User.create!(first_name: name, last_name: 'de Castilla', email: "#{name.downcase.delete(' ')}@example.com", zone: @castille)
|
16
|
+
end
|
17
|
+
@spain = GeoZone.create!(name: 'España')
|
18
|
+
['Carlos I', 'Felipe II', 'Felipe III'].each do |name|
|
19
|
+
User.create!(first_name: name, last_name: 'de Austria', email: "#{name.downcase.delete(' ')}@example.com", zone: @spain)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def teardown
|
24
|
+
User.destroy_all
|
25
|
+
GeoZone.destroy_all
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_project
|
29
|
+
users_projection = User.objects
|
30
|
+
.filter('zone::name': 'Castilla')
|
31
|
+
.order_by('first_name')
|
32
|
+
.project('first_name', 'email')
|
33
|
+
|
34
|
+
local_projection_expectation = [
|
35
|
+
{ first_name: 'Isabel I', email: 'isabeli@example.com' },
|
36
|
+
{ first_name: 'Juan II', email: 'juanii@example.com' },
|
37
|
+
{ first_name: 'Juana I', email: 'juanai@example.com' }
|
38
|
+
]
|
39
|
+
|
40
|
+
assert users_projection.projection?
|
41
|
+
users_projection.each_with_index do |user_projection, user_projection_index|
|
42
|
+
assert_equal local_projection_expectation[user_projection_index], user_projection.symbolize_keys
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_no_projection
|
47
|
+
users_projection = User.objects
|
48
|
+
.filter('zone::name': 'Castilla')
|
49
|
+
.order_by('first_name')
|
50
|
+
refute users_projection.projection?
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_foreign_field_project
|
54
|
+
users_projection = User.objects
|
55
|
+
.filter('zone::name': 'Castilla')
|
56
|
+
.order_by('first_name')
|
57
|
+
.project('first_name', 'email', %w[zone::name country])
|
58
|
+
foreign_projection_expectation = [
|
59
|
+
{ first_name: 'Isabel I', email: 'isabeli@example.com', country: 'Castilla' },
|
60
|
+
{ first_name: 'Juan II', email: 'juanii@example.com', country: 'Castilla' },
|
61
|
+
{ first_name: 'Juana I', email: 'juanai@example.com', country: 'Castilla' }
|
62
|
+
]
|
63
|
+
|
64
|
+
assert users_projection.projection?
|
65
|
+
users_projection.each_with_index do |user_projection, user_projection_index|
|
66
|
+
assert_equal foreign_projection_expectation[user_projection_index], user_projection.symbolize_keys
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_explicit_transform
|
71
|
+
datetime_format = '%Y-%m-%d %H:%M:%S'
|
72
|
+
datetime_transformer = ->(datetime) { datetime.strftime(datetime_format) }
|
73
|
+
users = User.objects.filter('zone::name': 'Castilla').order_by('first_name')
|
74
|
+
users_projection = users.project(
|
75
|
+
['first_name', ->(s) { s.upcase }], 'email',
|
76
|
+
['created_at', 'creation_date', ->(d) { datetime_transformer.call(Time.parse(d.to_s + 'UTC')) }]
|
77
|
+
)
|
78
|
+
users_projection.each_with_index do |user_projection, user_index|
|
79
|
+
assert_equal users[user_index].first_name.upcase, user_projection[:first_name]
|
80
|
+
assert_equal users[user_index].email, user_projection[:email]
|
81
|
+
assert_equal users[user_index].created_at.strftime(datetime_format), user_projection[:creation_date]
|
82
|
+
assert_equal users[user_index].created_at.strftime(datetime_format).class, user_projection[:creation_date].class
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_wrong_transform_params
|
87
|
+
exception = assert_raises RuntimeError do
|
88
|
+
datetime_format = '%Y-%m-%d %H:%M:%S'
|
89
|
+
datetime_transformer = ->(datetime) { datetime.strftime(datetime_format) }
|
90
|
+
users = User.objects.filter('zone::name': 'Castilla').order_by('first_name')
|
91
|
+
users.project(
|
92
|
+
['first_name', 2], 'email',
|
93
|
+
['created_at', 'creation_date', ->(d) { datetime_transformer.call(Time.parse(d.to_s + 'UTC')) }]
|
94
|
+
)
|
95
|
+
end
|
96
|
+
assert_equal('Babik::QuerySet::ProjectedField.new only accepts String/Symbol or Proc. Passed a Integer.', exception.message)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_wrong_params
|
100
|
+
exception = assert_raises RuntimeError do
|
101
|
+
users = User.objects.filter('zone::name': 'Castilla').order_by('first_name')
|
102
|
+
users.project('first_name', 'email', 2222)
|
103
|
+
end
|
104
|
+
assert_equal('No other parameter type is permitted in Babik::QuerySet::ProjectedField.new than Array, String and Symbol.', exception.message)
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require_relative '../test_helper'
|
5
|
+
|
6
|
+
# Select related method test
|
7
|
+
class SelectRelatedTest < Minitest::Test
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@baetica = GeoZone.create!(name: 'Baetica')
|
11
|
+
@corduba = GeoZone.create!(name: 'Corduba', parent_zone: @baetica)
|
12
|
+
@seneca_sr = User.create!(first_name: 'Marcus Annaeus', last_name: 'Seneca', zone: @corduba)
|
13
|
+
@seneca_jr = User.create!(first_name: 'Lucius Annaeus', last_name: 'Seneca', zone: @corduba)
|
14
|
+
|
15
|
+
@africa = GeoZone.create!(name: 'Africa')
|
16
|
+
@cartago = GeoZone.create!(name: 'Cartago', parent_zone: @africa)
|
17
|
+
@tertullianus = User.create!(first_name: 'Quintus Septimius', last_name: 'Florens Tertullianus', zone: @cartago)
|
18
|
+
|
19
|
+
short_stories = Category.create(name: 'Short stories')
|
20
|
+
legal = Category.create(name: 'Legal texts')
|
21
|
+
oratory = Category.create(name: 'Oratory')
|
22
|
+
Post.create!(title: 'Gesta Romanorum', author: @seneca_sr, category: short_stories)
|
23
|
+
Post.create!(title: 'Controversiae', author: @seneca_sr, category: legal)
|
24
|
+
Post.create!(title: 'Suasoriae', author: @seneca_sr, category: oratory)
|
25
|
+
|
26
|
+
stoic_texts = Category.create(name: 'Stoic texts')
|
27
|
+
Post.create!(title: 'De brevitate vitae', author: @seneca_jr, category: stoic_texts)
|
28
|
+
Post.create!(title: 'Epistulae Morales ad Lucilium', author: @seneca_jr, category: stoic_texts)
|
29
|
+
Post.create!(title: 'De tranquillitate animi', author: @seneca_jr, category: stoic_texts)
|
30
|
+
end
|
31
|
+
|
32
|
+
def teardown
|
33
|
+
GeoZone.destroy_all
|
34
|
+
User.destroy_all
|
35
|
+
Category.destroy_all
|
36
|
+
Post.destroy_all
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_wrong_select_related
|
40
|
+
exception = assert_raises RuntimeError do
|
41
|
+
User
|
42
|
+
.objects
|
43
|
+
.filter(zone__in: [@corduba.id, @cartago.id])
|
44
|
+
.select_related(:xxxxx)[0]
|
45
|
+
end
|
46
|
+
assert_equal(
|
47
|
+
'Bad selection path: xxxxx. xxxxx not found in model User when filtering User objects',
|
48
|
+
exception.message
|
49
|
+
)
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_select_related
|
54
|
+
users = User.objects.order_by('first_name')
|
55
|
+
number_of_users = User.objects.count
|
56
|
+
number_of_returned_users = 0
|
57
|
+
|
58
|
+
User
|
59
|
+
.objects
|
60
|
+
.filter(zone__in: [@corduba.id, @cartago.id])
|
61
|
+
.select_related(:zone)
|
62
|
+
.order_by('first_name')
|
63
|
+
.each_with_index do |user_with_related, user_with_related_index|
|
64
|
+
# Loop through each user with his/her related objects
|
65
|
+
user, select_related = user_with_related
|
66
|
+
# User tests
|
67
|
+
expected_user = users[user_with_related_index]
|
68
|
+
assert_equal User, user.class
|
69
|
+
assert_equal expected_user.id, user.id
|
70
|
+
assert_equal expected_user, user
|
71
|
+
# Zone tests
|
72
|
+
expected_zone = expected_user.zone
|
73
|
+
assert_equal GeoZone, select_related[:zone].class
|
74
|
+
assert_equal user.zone_id, select_related[:zone].id
|
75
|
+
assert_equal expected_zone, select_related[:zone]
|
76
|
+
# Users count
|
77
|
+
number_of_returned_users += 1
|
78
|
+
end
|
79
|
+
assert_equal number_of_users, number_of_returned_users
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_select_related_several_associations
|
83
|
+
expected_posts = Post.objects.filter(author: @seneca_sr).order_by(title: :DESC)
|
84
|
+
posts = Post.objects
|
85
|
+
.filter(author: @seneca_sr)
|
86
|
+
.order_by(title: :DESC)
|
87
|
+
.select_related([:author, :category])
|
88
|
+
_test_select_related_posts(expected_posts, posts)
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_select_related_several_associations_from_instance
|
92
|
+
expected_posts = Post.objects.filter(author: @seneca_sr).order_by(title: :DESC)
|
93
|
+
posts = @seneca_sr.objects(:posts)
|
94
|
+
.order_by(title: :DESC)
|
95
|
+
.select_related([:author, :category])
|
96
|
+
_test_select_related_posts(expected_posts, posts)
|
97
|
+
end
|
98
|
+
|
99
|
+
def _test_select_related_posts(expected_posts, posts)
|
100
|
+
# Load the posts with 4 or more stars with their author and category
|
101
|
+
posts.each_with_index do |post_with_author_category, index|
|
102
|
+
# Load post and its two associated objects
|
103
|
+
post, foreign_objects = post_with_author_category
|
104
|
+
author = foreign_objects[:author]
|
105
|
+
category = foreign_objects[:category]
|
106
|
+
# Check post
|
107
|
+
expected_post = expected_posts[index]
|
108
|
+
assert_equal Post, post.class
|
109
|
+
assert_equal expected_post.id, post.id
|
110
|
+
assert_equal expected_post, post
|
111
|
+
# Check author (user)
|
112
|
+
assert_equal User, author.class
|
113
|
+
assert_equal post.author_id, author.id
|
114
|
+
assert_equal post.author, author
|
115
|
+
# Check category
|
116
|
+
assert_equal Category, category.class
|
117
|
+
assert_equal post.category_id, category.id
|
118
|
+
assert_equal post.category, category
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require_relative '../test_helper'
|
5
|
+
|
6
|
+
# Subquery test
|
7
|
+
class SubqueryTest < Minitest::Test
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@baetica = GeoZone.create!(name: 'Baetica')
|
11
|
+
@corduba = GeoZone.create!(name: 'Corduba', parent_zone: @baetica)
|
12
|
+
@seneca_sr = User.create!(first_name: 'Marcus Annaeus', last_name: 'Seneca', zone: @corduba)
|
13
|
+
short_stories = Category.create(name: 'Short stories')
|
14
|
+
legal = Category.create(name: 'Legal texts')
|
15
|
+
oratory = Category.create(name: 'Oratory')
|
16
|
+
Post.create!(title: 'Gesta Romanorum', author: @seneca_sr, category: short_stories)
|
17
|
+
Post.create!(title: 'Controversiae', author: @seneca_sr, category: legal)
|
18
|
+
Post.create!(title: 'Suasoriae', author: @seneca_sr, category: oratory)
|
19
|
+
end
|
20
|
+
|
21
|
+
def teardown
|
22
|
+
GeoZone.destroy_all
|
23
|
+
User.destroy_all
|
24
|
+
Category.destroy_all
|
25
|
+
Post.destroy_all
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_subquery_with_equal
|
29
|
+
seneca_sr_posts = Post.objects.filter(id: @seneca_sr.objects(:posts).project(:id))
|
30
|
+
assert_equal @seneca_sr.objects(:posts).count, seneca_sr_posts.count
|
31
|
+
seneca_sr_posts_count = 0
|
32
|
+
seneca_sr_posts.each do |post|
|
33
|
+
assert_equal @seneca_sr.id, post.author_id
|
34
|
+
seneca_sr_posts_count += 1
|
35
|
+
end
|
36
|
+
assert_equal seneca_sr_posts_count, seneca_sr_posts.count
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_subquery_with_in
|
40
|
+
seneca_sr_posts = Post.objects.filter(id__in: @seneca_sr.objects(:posts).project(:id))
|
41
|
+
assert_equal @seneca_sr.objects(:posts).count, seneca_sr_posts.count
|
42
|
+
seneca_sr_posts_count = 0
|
43
|
+
seneca_sr_posts.each do |post|
|
44
|
+
assert_equal @seneca_sr.id, post.author_id
|
45
|
+
seneca_sr_posts_count += 1
|
46
|
+
end
|
47
|
+
assert_equal seneca_sr_posts_count, seneca_sr_posts.count
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require_relative '../test_helper'
|
5
|
+
|
6
|
+
# Class for basic set operation tests
|
7
|
+
class BasicUsageTest < Minitest::Test
|
8
|
+
|
9
|
+
def setup
|
10
|
+
|
11
|
+
patrician_families = %w[
|
12
|
+
Aebutia Aemilia Aquillia Atilia Claudia Cloelia Cornelia Curtia
|
13
|
+
Fabia Foslia Furia Gegania Genucia Herminia Horatia Julia Lartia
|
14
|
+
Lucretia Manlia Menenia Metilia Minucia Mucia Nautia Numicia Papiria
|
15
|
+
Pinaria Pollia Postumia Potitia Quinctia Quinctilia Romilia
|
16
|
+
Sempronia Sergia Servilia Sestia Siccia Sulpicia Tarpeia Tarquinia
|
17
|
+
Tarquitia Tullia Valeria Verginia Veturia Vitellia Volumnia
|
18
|
+
]
|
19
|
+
roman_names = %w[
|
20
|
+
Aeliana Albia Antonia Aquilia Argentia Atticus Augusta Augustus Aurelia Aurelius Avita Caesar Camilla
|
21
|
+
Cassia Cassius Cato Cecilia Cicero Claudia Claudius Clemensia Cornelius Crispus Cyprian Decima Decimus
|
22
|
+
Drusilla Dulcia Fabia Faustina Felix Flavia Florentina Fortunata Gaia Galla Hilaria Horatia Julia Julius
|
23
|
+
Junia Justus Laelia Laurentia Livia Lucius Lucretia Magnus Marcella Marcus Marilla Marius Martia Maxima
|
24
|
+
Maximus Mila Nerilla Nero Octavia Octavius Philo Prima Priscilla Quintia Quintus Remus Romulus Rufina
|
25
|
+
Rufus Sabina Seneca Septima Septimus Sergia Tanaquil Tatiana Tauria Tertia Tiberius Tullia Urban Urbana
|
26
|
+
Valentina Varinia Vita
|
27
|
+
]
|
28
|
+
|
29
|
+
random_generator = Random.new(1234)
|
30
|
+
patrician_families.each do |family_name|
|
31
|
+
roman_names.each do |roman_name|
|
32
|
+
User.create!(first_name: roman_name, last_name: family_name) if random_generator.rand(0..1) == 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def teardown
|
39
|
+
User.destroy_all
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_union
|
43
|
+
claudia = User.objects.filter(last_name: 'Claudia')
|
44
|
+
verturia = User.objects.filter(last_name: 'Veturia')
|
45
|
+
both_families = claudia.union(verturia).order_by!({ last_name: :DESC }, { first_name: :ASC })
|
46
|
+
both_families_without_union = User.where(last_name: ['Claudia', 'Veturia']).order(last_name: :DESC, first_name: :ASC)
|
47
|
+
_check_set_operation(both_families_without_union, both_families)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_chained_union
|
51
|
+
families = User.objects.filter(last_name: 'Aebutia')
|
52
|
+
.union(User.objects.filter(last_name: 'Fabia'))
|
53
|
+
.union(User.objects.filter(last_name: 'Atilia'))
|
54
|
+
.union(User.objects.filter(last_name: 'Claudia'))
|
55
|
+
.union(User.objects.filter(last_name: 'Cloelia'))
|
56
|
+
.order_by!({last_name: :DESC}, {first_name: :ASC})
|
57
|
+
families_without_union = User.where(last_name: ['Aebutia', 'Fabia', 'Atilia', 'Claudia', 'Cloelia'])
|
58
|
+
.order(last_name: :DESC, first_name: :ASC)
|
59
|
+
_check_set_operation(families_without_union, families)
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_deep_union
|
63
|
+
claudia = User.objects.filter(last_name: 'Claudia')
|
64
|
+
verturia = User.objects.filter(last_name: 'Veturia')
|
65
|
+
aemilia = User.objects.filter(last_name: 'Aemilia')
|
66
|
+
three_families = claudia.union(verturia).union(aemilia).order_by!({ last_name: :DESC }, { first_name: :ASC })
|
67
|
+
three_families_without_union = User
|
68
|
+
.where(last_name: ['Claudia', 'Veturia', 'Aemilia'])
|
69
|
+
.order(last_name: :DESC, first_name: :ASC)
|
70
|
+
_check_set_operation(three_families_without_union, three_families)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_intersection
|
74
|
+
first_user = User.objects.first
|
75
|
+
qs_with_intersection = User.objects.filter(first_name: first_user.first_name)
|
76
|
+
.intersection(User.objects.filter(last_name: first_user.last_name))
|
77
|
+
.order_by!({ last_name: :DESC }, { first_name: :ASC })
|
78
|
+
qs_without_intersection = User.where(first_name: first_user.first_name, last_name: first_user.last_name)
|
79
|
+
.order(last_name: :DESC, first_name: :ASC)
|
80
|
+
_check_set_operation(qs_without_intersection, qs_with_intersection)
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_deep_intersection
|
84
|
+
first_user = User.objects.first
|
85
|
+
qs_with_intersection = User.objects.filter(first_name: first_user.first_name)
|
86
|
+
.intersection(User.objects.filter(last_name: first_user.last_name))
|
87
|
+
.intersection(User.objects.filter(created_at__lt: Time.now))
|
88
|
+
.order_by!({ last_name: :DESC }, { first_name: :ASC })
|
89
|
+
qs_without_intersection = User.where(first_name: first_user.first_name, last_name: first_user.last_name)
|
90
|
+
.order(last_name: :DESC, first_name: :ASC)
|
91
|
+
_check_set_operation(qs_without_intersection, qs_with_intersection)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_difference
|
95
|
+
first_user = User.objects.first
|
96
|
+
qs_with_minus = User.objects
|
97
|
+
.filter(last_name: first_user.last_name)
|
98
|
+
.difference(User.objects.filter(first_name: first_user.first_name))
|
99
|
+
.order_by!({ last_name: :DESC }, { first_name: :ASC })
|
100
|
+
qs_without_minus = User.where(last_name: first_user.last_name)
|
101
|
+
.where.not(first_name: first_user.first_name)
|
102
|
+
.order(last_name: :DESC, first_name: :ASC)
|
103
|
+
_check_set_operation(qs_without_minus, qs_with_minus)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Check a set-operation based queryset is correct
|
107
|
+
def _check_set_operation(expected_qs, actual_qs)
|
108
|
+
record_count = 0
|
109
|
+
expected_qs.each_with_index do |expected_qs_record, expected_qs_record_index|
|
110
|
+
actual_qs_record = actual_qs[expected_qs_record_index]
|
111
|
+
assert_equal expected_qs_record.id, actual_qs_record.id
|
112
|
+
assert_equal expected_qs_record.last_name, actual_qs_record.last_name
|
113
|
+
assert_equal expected_qs_record.first_name, actual_qs_record.first_name
|
114
|
+
assert_equal expected_qs_record.last_name, actual_qs_record.last_name
|
115
|
+
record_count += 1
|
116
|
+
end
|
117
|
+
assert_equal expected_qs.count, actual_qs.count
|
118
|
+
assert_equal expected_qs.count, record_count
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record'
|
4
|
+
require 'minitest/unit'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
require 'minitest/pride'
|
7
|
+
|
8
|
+
# Setup the log
|
9
|
+
require 'fileutils'
|
10
|
+
FileUtils.mkpath 'log' unless File.directory? 'log'
|
11
|
+
ActiveRecord::Base.logger = Logger.new('log/test-queries.log')
|
12
|
+
|
13
|
+
DEFAULT_DB_ADAPTER = 'sqlite3'
|
14
|
+
#DEFAULT_DB_ADAPTER = 'mysql2'
|
15
|
+
#DEFAULT_DB_ADAPTER = 'postgresql'
|
16
|
+
|
17
|
+
# Make a connection
|
18
|
+
adapter = ENV.fetch('DB', DEFAULT_DB_ADAPTER)
|
19
|
+
case adapter
|
20
|
+
when 'mysql2', 'postgresql'
|
21
|
+
config = {
|
22
|
+
host: '127.0.0.1',
|
23
|
+
database: 'babik_test',
|
24
|
+
encoding: 'utf8',
|
25
|
+
min_messages: 'WARNING',
|
26
|
+
adapter: adapter,
|
27
|
+
username: ENV['DB_USERNAME'] || 'postgres',
|
28
|
+
password: ENV['DB_PASSWORD'] || 'postgres'
|
29
|
+
}
|
30
|
+
ActiveRecord::Tasks::DatabaseTasks.create(config.stringify_keys)
|
31
|
+
ActiveRecord::Base.establish_connection(config)
|
32
|
+
when 'sqlite3'
|
33
|
+
ActiveRecord::Base.establish_connection adapter: adapter, database: ':memory:'
|
34
|
+
else
|
35
|
+
raise "Unknown DB adapter #{adapter}. Valid adapters are: mysql2, postgresql, sqlite3."
|
36
|
+
end
|
37
|
+
|
38
|
+
# Load babik library
|
39
|
+
require 'babik'
|
40
|
+
|
41
|
+
# Create tables
|
42
|
+
load "#{__dir__}/config/db/schema.rb"
|
43
|
+
|
44
|
+
# Load models
|
45
|
+
require 'config/models/geozone'
|
46
|
+
require 'config/models/user'
|
47
|
+
require 'config/models/tag'
|
48
|
+
require 'config/models/category'
|
49
|
+
require 'config/models/post'
|
50
|
+
require 'config/models/post_tag'
|
51
|
+
require 'config/models/bad_tag'
|
52
|
+
require 'config/models/bad_post'
|
53
|
+
|
54
|
+
|
55
|
+
|