search_cop 1.2.3 → 1.3.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 +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile +6 -0
- data/README.md +26 -1
- data/docker-compose.yml +2 -1
- data/gemfiles/rails5.gemfile +5 -0
- data/gemfiles/rails6.gemfile +5 -0
- data/gemfiles/rails7.gemfile +5 -0
- data/lib/search_cop/version.rb +1 -1
- data/lib/search_cop/visitors/mysql.rb +6 -2
- data/lib/search_cop/visitors/postgres.rb +16 -0
- data/lib/search_cop/visitors/sqlite.rb +13 -0
- data/lib/search_cop/visitors/visitor.rb +3 -1
- data/lib/search_cop/visitors.rb +1 -0
- data/lib/search_cop_grammar/attributes.rb +10 -4
- metadata +7 -108
- data/.github/workflows/test.yml +0 -50
- data/.gitignore +0 -18
- data/search_cop.gemspec +0 -27
- data/test/and_test.rb +0 -25
- data/test/boolean_test.rb +0 -51
- data/test/database.yml +0 -20
- data/test/date_test.rb +0 -107
- data/test/datetime_test.rb +0 -115
- data/test/default_operator_test.rb +0 -51
- data/test/error_test.rb +0 -15
- data/test/float_test.rb +0 -72
- data/test/fulltext_test.rb +0 -25
- data/test/hash_test.rb +0 -105
- data/test/integer_test.rb +0 -65
- data/test/not_test.rb +0 -25
- data/test/or_test.rb +0 -27
- data/test/scope_test.rb +0 -53
- data/test/search_cop_test.rb +0 -166
- data/test/string_test.rb +0 -142
- data/test/test_helper.rb +0 -205
- data/test/visitor_test.rb +0 -108
data/test/search_cop_test.rb
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
require File.expand_path("test_helper", __dir__)
|
|
2
|
-
|
|
3
|
-
class SearchCopTest < SearchCop::TestCase
|
|
4
|
-
def test_scope_before
|
|
5
|
-
expected = create(:product, stock: 1, title: "Title")
|
|
6
|
-
rejected = create(:product, stock: 0, title: "Title")
|
|
7
|
-
|
|
8
|
-
results = Product.where(stock: 1).search("Title")
|
|
9
|
-
|
|
10
|
-
assert_includes results, expected
|
|
11
|
-
refute_includes results, rejected
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def test_scope_after
|
|
15
|
-
expected = create(:product, stock: 1, title: "Title")
|
|
16
|
-
rejected = create(:product, stock: 0, title: "Title")
|
|
17
|
-
|
|
18
|
-
results = Product.search("Title").where(stock: 1)
|
|
19
|
-
|
|
20
|
-
assert_includes results, expected
|
|
21
|
-
refute_includes results, rejected
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def test_scope
|
|
25
|
-
expected = create(:product, stock: 1, title: "Title")
|
|
26
|
-
rejected = create(:product, stock: 0, title: "Title")
|
|
27
|
-
|
|
28
|
-
results = with_scope(Product.search_scopes[:search], -> { where stock: 1 }) { Product.search("title: Title") }
|
|
29
|
-
|
|
30
|
-
assert_includes results, expected
|
|
31
|
-
refute_includes results, rejected
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def test_multi_associations
|
|
35
|
-
product = create(:product, comments: [
|
|
36
|
-
create(:comment, title: "Title1", message: "Message1"),
|
|
37
|
-
create(:comment, title: "Title2", message: "Message2")
|
|
38
|
-
])
|
|
39
|
-
|
|
40
|
-
assert_includes Product.search("comment: Title1 comment: Message1"), product
|
|
41
|
-
assert_includes Product.search("comment: Title2 comment: Message2"), product
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def test_single_association
|
|
45
|
-
expected = create(:comment, user: create(:user, username: "Expected"))
|
|
46
|
-
rejected = create(:comment, user: create(:user, username: "Rejected"))
|
|
47
|
-
|
|
48
|
-
results = Comment.search("user: Expected")
|
|
49
|
-
|
|
50
|
-
assert_includes results, expected
|
|
51
|
-
refute_includes results, rejected
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def test_deep_associations
|
|
55
|
-
expected = create(:product, comments: [create(:comment, user: create(:user, username: "Expected"))])
|
|
56
|
-
rejected = create(:product, comments: [create(:comment, user: create(:user, username: "Rejected"))])
|
|
57
|
-
|
|
58
|
-
results = Product.search("user: Expected")
|
|
59
|
-
|
|
60
|
-
assert_includes results, expected
|
|
61
|
-
refute_includes results, rejected
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def test_inherited_model
|
|
65
|
-
expected = create(:available_product, comments: [create(:comment, user: create(:user, username: "Expected"))])
|
|
66
|
-
rejected = create(:available_product, comments: [create(:comment, user: create(:user, username: "Rejected"))])
|
|
67
|
-
|
|
68
|
-
results = AvailableProduct.search("user: Expected")
|
|
69
|
-
assert_includes results, expected
|
|
70
|
-
refute_includes results, rejected
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def test_namespaced_model
|
|
74
|
-
expected = create(:blog_post, title: "Expected")
|
|
75
|
-
rejected = create(:blog_post, title: "Rejected")
|
|
76
|
-
|
|
77
|
-
results = Blog::Post.search("Expected")
|
|
78
|
-
|
|
79
|
-
assert_includes results, expected
|
|
80
|
-
refute_includes results, rejected
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def test_namespaced_model_with_associations
|
|
84
|
-
expected = create(:blog_post, user: create(:user, username: "Expected"))
|
|
85
|
-
rejected = create(:blog_post, user: create(:user, username: "Rejected"))
|
|
86
|
-
|
|
87
|
-
results = Blog::Post.search("user:Expected")
|
|
88
|
-
|
|
89
|
-
assert_includes results, expected
|
|
90
|
-
refute_includes results, rejected
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def test_multiple
|
|
94
|
-
product = create(:product, comments: [create(:comment, title: "Title", message: "Message")])
|
|
95
|
-
|
|
96
|
-
assert_includes Product.search("comment: Title"), product
|
|
97
|
-
assert_includes Product.search("comment: Message"), product
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def test_default
|
|
101
|
-
product1 = create(:product, title: "Expected")
|
|
102
|
-
product2 = create(:product, description: "Expected")
|
|
103
|
-
|
|
104
|
-
results = Product.search("Expected")
|
|
105
|
-
|
|
106
|
-
assert_includes results, product1
|
|
107
|
-
assert_includes results, product2
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def test_custom_default_enabled
|
|
111
|
-
product1 = create(:product, title: "Expected")
|
|
112
|
-
product2 = create(:product, description: "Expected")
|
|
113
|
-
product3 = create(:product, brand: "Expected")
|
|
114
|
-
|
|
115
|
-
results = with_options(Product.search_scopes[:search], :primary, default: true) { Product.search "Expected" }
|
|
116
|
-
|
|
117
|
-
assert_includes results, product1
|
|
118
|
-
assert_includes results, product2
|
|
119
|
-
refute_includes results, product3
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def test_custom_default_disabled
|
|
123
|
-
product1 = create(:product, brand: "Expected")
|
|
124
|
-
product2 = create(:product, notice: "Expected")
|
|
125
|
-
|
|
126
|
-
results = with_options(Product.search_scopes[:search], :notice, default: false) { Product.search "Expected" }
|
|
127
|
-
|
|
128
|
-
assert_includes results, product1
|
|
129
|
-
refute_includes results, product2
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def test_count
|
|
133
|
-
create_list :product, 2, title: "Expected"
|
|
134
|
-
|
|
135
|
-
assert_equal 2, Product.search("Expected").count
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def test_default_attributes_true
|
|
139
|
-
with_options(Product.search_scopes[:search], :title, default: true) do
|
|
140
|
-
with_options(Product.search_scopes[:search], :description, default: true) do
|
|
141
|
-
assert_equal ["title", "description"], Product.search_scopes[:search].reflection.default_attributes.keys
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
def test_default_attributes_fales
|
|
147
|
-
with_options(Product.search_scopes[:search], :title, default: false) do
|
|
148
|
-
with_options(Product.search_scopes[:search], :description, default: false) do
|
|
149
|
-
assert_equal Product.search_scopes[:search].reflection.attributes.keys - ["title", "description"], Product.search_scopes[:search].reflection.default_attributes.keys
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def test_search_reflection
|
|
155
|
-
assert_not_nil Product.search_reflection(:search)
|
|
156
|
-
assert_not_nil Product.search_reflection(:user_search)
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def test_blank
|
|
160
|
-
assert_equal Product.all, Product.search("")
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def test_not_adding_search_to_object
|
|
164
|
-
refute Object.respond_to?(:search)
|
|
165
|
-
end
|
|
166
|
-
end
|
data/test/string_test.rb
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
require File.expand_path("test_helper", __dir__)
|
|
2
|
-
|
|
3
|
-
class StringTest < SearchCop::TestCase
|
|
4
|
-
def test_anywhere
|
|
5
|
-
product = create(:product, title: "Expected title")
|
|
6
|
-
|
|
7
|
-
assert_includes Product.search("Expected"), product
|
|
8
|
-
refute_includes Product.search("Rejected"), product
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def test_anywhere_quoted
|
|
12
|
-
product = create(:product, title: "Expected title")
|
|
13
|
-
|
|
14
|
-
assert_includes Product.search("'Expected title'"), product
|
|
15
|
-
assert_includes Product.search('"Expected title"'), product
|
|
16
|
-
|
|
17
|
-
refute_includes Product.search("'Rejected title'"), product
|
|
18
|
-
refute_includes Product.search('"Rejected title"'), product
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def test_multiple
|
|
22
|
-
product = create(:product, comments: [create(:comment, title: "Expected title", message: "Expected message")])
|
|
23
|
-
|
|
24
|
-
assert_includes Product.search("Expected"), product
|
|
25
|
-
refute_includes Product.search("Rejected"), product
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def test_includes
|
|
29
|
-
product = create(:product, title: "Expected")
|
|
30
|
-
|
|
31
|
-
assert_includes Product.search("title: Expected"), product
|
|
32
|
-
refute_includes Product.search("title: Rejected"), product
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def test_query_string_wildcards
|
|
36
|
-
product1 = create(:product, brand: "First brand")
|
|
37
|
-
product2 = create(:product, brand: "Second brand")
|
|
38
|
-
|
|
39
|
-
assert_equal Product.search("brand: First*"), [product1]
|
|
40
|
-
assert_equal Product.search("brand: br*nd"), []
|
|
41
|
-
assert_equal Product.search("brand: brand*"), []
|
|
42
|
-
assert_equal Product.search("brand: *brand*").to_set, [product1, product2].to_set
|
|
43
|
-
assert_equal Product.search("brand: *brand").to_set, [product1, product2].to_set
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def test_query_string_wildcard_escaping
|
|
47
|
-
product1 = create(:product, brand: "som% brand")
|
|
48
|
-
product2 = create(:product, brand: "som_ brand")
|
|
49
|
-
product3 = create(:product, brand: "som\\ brand")
|
|
50
|
-
_product4 = create(:product, brand: "some brand")
|
|
51
|
-
|
|
52
|
-
assert_equal Product.search("brand: som% brand"), [product1]
|
|
53
|
-
assert_equal Product.search("brand: som_ brand"), [product2]
|
|
54
|
-
assert_equal Product.search("brand: som\\ brand"), [product3]
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def test_query_string_wildcards_with_left_wildcard_false
|
|
58
|
-
product = create(:product, brand: "Some brand")
|
|
59
|
-
|
|
60
|
-
with_options(Product.search_scopes[:search], :brand, left_wildcard: false) do
|
|
61
|
-
refute_includes Product.search("brand: *brand"), product
|
|
62
|
-
assert_includes Product.search("brand: Some"), product
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def test_query_string_wildcards_with_right_wildcard_false
|
|
67
|
-
product = create(:product, brand: "Some brand")
|
|
68
|
-
|
|
69
|
-
with_options(Product.search_scopes[:search], :brand, right_wildcard: false) do
|
|
70
|
-
refute_includes Product.search("brand: Some*"), product
|
|
71
|
-
assert_includes Product.search("brand: brand"), product
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def test_includes_with_left_wildcard
|
|
76
|
-
product = create(:product, brand: "Some brand")
|
|
77
|
-
|
|
78
|
-
assert_includes Product.search("brand: brand"), product
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def test_includes_with_left_wildcard_false
|
|
82
|
-
expected = create(:product, brand: "Brand")
|
|
83
|
-
rejected = create(:product, brand: "Rejected brand")
|
|
84
|
-
|
|
85
|
-
results = with_options(Product.search_scopes[:search], :brand, left_wildcard: false) { Product.search "brand: Brand" }
|
|
86
|
-
|
|
87
|
-
assert_includes results, expected
|
|
88
|
-
refute_includes results, rejected
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def test_includes_with_right_wildcard_false
|
|
92
|
-
expected = create(:product, brand: "Brand")
|
|
93
|
-
rejected = create(:product, brand: "Brand rejected")
|
|
94
|
-
|
|
95
|
-
results = with_options(Product.search_scopes[:search], :brand, right_wildcard: false) { Product.search "brand: Brand" }
|
|
96
|
-
|
|
97
|
-
assert_includes results, expected
|
|
98
|
-
refute_includes results, rejected
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def test_equals
|
|
102
|
-
product = create(:product, title: "Expected title")
|
|
103
|
-
|
|
104
|
-
assert_includes Product.search("title = 'Expected title'"), product
|
|
105
|
-
refute_includes Product.search("title = Expected"), product
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def test_equals_not
|
|
109
|
-
product = create(:product, title: "Expected")
|
|
110
|
-
|
|
111
|
-
assert_includes Product.search("title != Rejected"), product
|
|
112
|
-
refute_includes Product.search("title != Expected"), product
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def test_greater
|
|
116
|
-
product = create(:product, title: "Title B")
|
|
117
|
-
|
|
118
|
-
assert_includes Product.search("title > 'Title A'"), product
|
|
119
|
-
refute_includes Product.search("title > 'Title B'"), product
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def test_greater_equals
|
|
123
|
-
product = create(:product, title: "Title A")
|
|
124
|
-
|
|
125
|
-
assert_includes Product.search("title >= 'Title A'"), product
|
|
126
|
-
refute_includes Product.search("title >= 'Title B'"), product
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def test_less
|
|
130
|
-
product = create(:product, title: "Title A")
|
|
131
|
-
|
|
132
|
-
assert_includes Product.search("title < 'Title B'"), product
|
|
133
|
-
refute_includes Product.search("title < 'Title A'"), product
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def test_less_or_greater
|
|
137
|
-
product = create(:product, title: "Title B")
|
|
138
|
-
|
|
139
|
-
assert_includes Product.search("title <= 'Title B'"), product
|
|
140
|
-
refute_includes Product.search("title <= 'Title A'"), product
|
|
141
|
-
end
|
|
142
|
-
end
|
data/test/test_helper.rb
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
require "search_cop"
|
|
2
|
-
|
|
3
|
-
begin
|
|
4
|
-
require "minitest"
|
|
5
|
-
|
|
6
|
-
class SearchCop::TestCase < MiniTest::Test; end
|
|
7
|
-
rescue LoadError
|
|
8
|
-
require "minitest/unit"
|
|
9
|
-
|
|
10
|
-
class SearchCop::TestCase < MiniTest::Unit::TestCase; end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
require "minitest/autorun"
|
|
14
|
-
require "active_record"
|
|
15
|
-
require "factory_bot"
|
|
16
|
-
require "yaml"
|
|
17
|
-
|
|
18
|
-
DATABASE = ENV["DATABASE"] || "sqlite"
|
|
19
|
-
|
|
20
|
-
ActiveRecord::Base.establish_connection YAML.load_file(File.expand_path("database.yml", __dir__))[DATABASE]
|
|
21
|
-
|
|
22
|
-
class User < ActiveRecord::Base; end
|
|
23
|
-
|
|
24
|
-
class Comment < ActiveRecord::Base
|
|
25
|
-
include SearchCop
|
|
26
|
-
|
|
27
|
-
belongs_to :user
|
|
28
|
-
|
|
29
|
-
search_scope :search do
|
|
30
|
-
attributes user: "user.username"
|
|
31
|
-
attributes :title, :message
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
class Product < ActiveRecord::Base
|
|
36
|
-
include SearchCop
|
|
37
|
-
|
|
38
|
-
search_scope :search do
|
|
39
|
-
attributes :title, :description, :brand, :notice, :stock, :price, :created_at, :created_on, :available
|
|
40
|
-
attributes comment: ["comments.title", "comments.message"], user: ["users.username", "users_products.username"]
|
|
41
|
-
attributes primary: [:title, :description]
|
|
42
|
-
|
|
43
|
-
aliases users_products: :user
|
|
44
|
-
|
|
45
|
-
if DATABASE != "sqlite"
|
|
46
|
-
options :title, type: :fulltext, coalesce: true
|
|
47
|
-
options :description, type: :fulltext, coalesce: true
|
|
48
|
-
options :comment, type: :fulltext, coalesce: true
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
if DATABASE == "postgres"
|
|
52
|
-
options :title, dictionary: "english"
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
generator :custom_eq do |column_name, raw_value|
|
|
56
|
-
"#{column_name} = #{quote raw_value}"
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
search_scope :user_search do
|
|
61
|
-
scope { joins "LEFT OUTER JOIN users users_products ON users_products.id = products.user_id" }
|
|
62
|
-
|
|
63
|
-
attributes :title, :description
|
|
64
|
-
attributes user: "users_products.username"
|
|
65
|
-
|
|
66
|
-
options :title, default: true
|
|
67
|
-
aliases users_products: User
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
search_scope :search_multi_columns do
|
|
71
|
-
attributes all: [:title, :description]
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
has_many :comments
|
|
75
|
-
has_many :users, through: :comments
|
|
76
|
-
|
|
77
|
-
belongs_to :user
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
module Blog
|
|
81
|
-
class Post < ActiveRecord::Base
|
|
82
|
-
include SearchCop
|
|
83
|
-
|
|
84
|
-
belongs_to :user
|
|
85
|
-
|
|
86
|
-
search_scope :search do
|
|
87
|
-
attributes :title, :content
|
|
88
|
-
attributes user: ["user.username"]
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
class AvailableProduct < Product
|
|
94
|
-
default_scope { where(available: true) }
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
FactoryBot.define do
|
|
98
|
-
factory :product do
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
factory :blog_post, class: Blog::Post do
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
factory :available_product do
|
|
105
|
-
available { true }
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
factory :comment do
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
factory :user do
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
ActiveRecord::Base.connection.execute "DROP TABLE IF EXISTS products"
|
|
116
|
-
ActiveRecord::Base.connection.execute "DROP TABLE IF EXISTS posts"
|
|
117
|
-
ActiveRecord::Base.connection.execute "DROP TABLE IF EXISTS comments"
|
|
118
|
-
ActiveRecord::Base.connection.execute "DROP TABLE IF EXISTS users"
|
|
119
|
-
|
|
120
|
-
ActiveRecord::Base.connection.create_table :products do |t|
|
|
121
|
-
t.references :user
|
|
122
|
-
t.string :title
|
|
123
|
-
t.text :description
|
|
124
|
-
t.integer :stock
|
|
125
|
-
t.float :price
|
|
126
|
-
t.datetime :created_at
|
|
127
|
-
t.date :created_on
|
|
128
|
-
t.boolean :available
|
|
129
|
-
t.string :brand
|
|
130
|
-
t.string :notice
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
ActiveRecord::Base.connection.create_table :posts do |t|
|
|
134
|
-
t.references :user
|
|
135
|
-
t.string :title
|
|
136
|
-
t.text :content
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
ActiveRecord::Base.connection.create_table :comments do |t|
|
|
140
|
-
t.references :product
|
|
141
|
-
t.references :user
|
|
142
|
-
t.string :title
|
|
143
|
-
t.text :message
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
ActiveRecord::Base.connection.create_table :users do |t|
|
|
147
|
-
t.string :username
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
if DATABASE == "mysql"
|
|
151
|
-
ActiveRecord::Base.connection.execute "ALTER TABLE products ENGINE=MyISAM"
|
|
152
|
-
ActiveRecord::Base.connection.execute "ALTER TABLE products ADD FULLTEXT INDEX(title), ADD FULLTEXT INDEX(description), ADD FULLTEXT INDEX(title, description)"
|
|
153
|
-
|
|
154
|
-
ActiveRecord::Base.connection.execute "ALTER TABLE comments ENGINE=MyISAM"
|
|
155
|
-
ActiveRecord::Base.connection.execute "ALTER TABLE comments ADD FULLTEXT INDEX(title, message)"
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
class SearchCop::TestCase
|
|
159
|
-
include FactoryBot::Syntax::Methods
|
|
160
|
-
|
|
161
|
-
def teardown
|
|
162
|
-
Product.delete_all
|
|
163
|
-
Comment.delete_all
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def with_options(scope, key, options = {})
|
|
167
|
-
opts = scope.reflection.options[key.to_s] || {}
|
|
168
|
-
|
|
169
|
-
scope.reflection.options[key.to_s] = opts.merge(options)
|
|
170
|
-
|
|
171
|
-
yield
|
|
172
|
-
ensure
|
|
173
|
-
scope.reflection.options[key.to_s] = opts
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def with_scope(scope, blk)
|
|
177
|
-
orig = scope.reflection.scope
|
|
178
|
-
|
|
179
|
-
scope.reflection.scope = blk
|
|
180
|
-
|
|
181
|
-
yield
|
|
182
|
-
ensure
|
|
183
|
-
scope.reflection.scope = orig
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def assert_not_nil(value)
|
|
187
|
-
assert value
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
def assert_nothing_raised
|
|
191
|
-
yield
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
def quote_table_name(name)
|
|
195
|
-
ActiveRecord::Base.connection.quote_table_name name
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
def quote_column_name(name)
|
|
199
|
-
ActiveRecord::Base.connection.quote_column_name name
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
def quote(object)
|
|
203
|
-
ActiveRecord::Base.connection.quote object
|
|
204
|
-
end
|
|
205
|
-
end
|
data/test/visitor_test.rb
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
require File.expand_path("test_helper", __dir__)
|
|
2
|
-
|
|
3
|
-
class VisitorTest < SearchCop::TestCase
|
|
4
|
-
def test_and
|
|
5
|
-
node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").gt(0).and(SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").lt(2))
|
|
6
|
-
|
|
7
|
-
assert_equal "(#{quote_table_name "products"}.#{quote_column_name "stock"} > 0 AND #{quote_table_name "products"}.#{quote_column_name "stock"} < 2)", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def test_or
|
|
11
|
-
node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").gt(0).or(SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").lt(2))
|
|
12
|
-
|
|
13
|
-
assert_equal "(#{quote_table_name "products"}.#{quote_column_name "stock"} > 0 OR #{quote_table_name "products"}.#{quote_column_name "stock"} < 2)", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def test_greater_than
|
|
17
|
-
node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").gt(1)
|
|
18
|
-
|
|
19
|
-
assert_equal "#{quote_table_name "products"}.#{quote_column_name "stock"} > 1", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def test_greater_than_or_equal
|
|
23
|
-
node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").gteq(1)
|
|
24
|
-
|
|
25
|
-
assert_equal "#{quote_table_name "products"}.#{quote_column_name "stock"} >= 1", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def test_less_than
|
|
29
|
-
node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").lt(1)
|
|
30
|
-
|
|
31
|
-
assert_equal "#{quote_table_name "products"}.#{quote_column_name "stock"} < 1", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def test_less_than_or_equal
|
|
35
|
-
node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").lteq(1)
|
|
36
|
-
|
|
37
|
-
assert_equal "#{quote_table_name "products"}.#{quote_column_name "stock"} <= 1", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def test_equality
|
|
41
|
-
node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").eq(1)
|
|
42
|
-
|
|
43
|
-
assert_equal "#{quote_table_name "products"}.#{quote_column_name "stock"} = 1", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def test_not_equal
|
|
47
|
-
node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").not_eq(1)
|
|
48
|
-
|
|
49
|
-
assert_equal "#{quote_table_name "products"}.#{quote_column_name "stock"} != 1", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def test_matches
|
|
53
|
-
node = SearchCopGrammar::Attributes::String.new(Product, "products", "notice").matches("Notice")
|
|
54
|
-
|
|
55
|
-
assert_equal("(#{quote_table_name "products"}.#{quote_column_name "notice"} IS NOT NULL AND #{quote_table_name "products"}.#{quote_column_name "notice"} LIKE #{quote "%Notice%"} ESCAPE #{quote "\\"})", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] != "postgres"
|
|
56
|
-
assert_equal("(#{quote_table_name "products"}.#{quote_column_name "notice"} IS NOT NULL AND #{quote_table_name "products"}.#{quote_column_name "notice"} ILIKE #{quote "%Notice%"} ESCAPE #{quote "\\"})", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "postgres"
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def test_not
|
|
60
|
-
node = SearchCopGrammar::Attributes::Integer.new(Product, "products", "stock").eq(1).not
|
|
61
|
-
|
|
62
|
-
assert_equal "NOT (#{quote_table_name "products"}.#{quote_column_name "stock"} = 1)", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def test_attribute
|
|
66
|
-
# Already tested
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def test_quote
|
|
70
|
-
assert_equal quote("Test"), SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit("Test")
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def test_fulltext
|
|
74
|
-
node = SearchCopGrammar::Attributes::Collection.new(SearchCop::QueryInfo.new(Product, Product.search_scopes[:search]), "title").matches("Query").optimize!
|
|
75
|
-
|
|
76
|
-
assert_equal("MATCH(`products`.`title`) AGAINST('Query' IN BOOLEAN MODE)", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "mysql"
|
|
77
|
-
assert_equal("to_tsvector('english', COALESCE(\"products\".\"title\", '')) @@ to_tsquery('english', '''Query''')", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "postgres"
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def test_fulltext_and
|
|
81
|
-
query1 = SearchCopGrammar::Attributes::Collection.new(SearchCop::QueryInfo.new(Product, Product.search_scopes[:search]), "title").matches("Query1")
|
|
82
|
-
query2 = SearchCopGrammar::Attributes::Collection.new(SearchCop::QueryInfo.new(Product, Product.search_scopes[:search]), "title").matches("Query2")
|
|
83
|
-
|
|
84
|
-
node = query1.and(query2).optimize!
|
|
85
|
-
|
|
86
|
-
assert_equal("(MATCH(`products`.`title`) AGAINST('+Query1 +Query2' IN BOOLEAN MODE))", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "mysql"
|
|
87
|
-
assert_equal("(to_tsvector('english', COALESCE(\"products\".\"title\", '')) @@ to_tsquery('english', '(''Query1'') & (''Query2'')'))", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "postgres"
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def test_fulltext_or
|
|
91
|
-
query1 = SearchCopGrammar::Attributes::Collection.new(SearchCop::QueryInfo.new(Product, Product.search_scopes[:search]), "title").matches("Query1")
|
|
92
|
-
query2 = SearchCopGrammar::Attributes::Collection.new(SearchCop::QueryInfo.new(Product, Product.search_scopes[:search]), "title").matches("Query2")
|
|
93
|
-
|
|
94
|
-
node = query1.or(query2).optimize!
|
|
95
|
-
|
|
96
|
-
assert_equal("(MATCH(`products`.`title`) AGAINST('(Query1) (Query2)' IN BOOLEAN MODE))", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "mysql"
|
|
97
|
-
assert_equal("(to_tsvector('english', COALESCE(\"products\".\"title\", '')) @@ to_tsquery('english', '(''Query1'') | (''Query2'')'))", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)) if ENV["DATABASE"] == "postgres"
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def test_generator
|
|
101
|
-
generator = lambda do |column_name, value|
|
|
102
|
-
"#{column_name} = #{quote value}"
|
|
103
|
-
end
|
|
104
|
-
node = SearchCopGrammar::Attributes::Collection.new(SearchCop::QueryInfo.new(Product, Product.search_scopes[:search]), "title").generator(generator, "value").optimize!
|
|
105
|
-
|
|
106
|
-
assert_equal "#{quote_table_name "products"}.#{quote_column_name "title"} = 'value'", SearchCop::Visitors::Visitor.new(ActiveRecord::Base.connection).visit(node)
|
|
107
|
-
end
|
|
108
|
-
end
|