bullet 6.1.0 → 7.0.7
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/.github/workflows/main.yml +82 -0
- data/CHANGELOG.md +66 -0
- data/Gemfile.rails-6.0 +1 -1
- data/Gemfile.rails-6.1 +15 -0
- data/Gemfile.rails-7.0 +10 -0
- data/MIT-LICENSE +1 -1
- data/README.md +41 -27
- data/lib/bullet/active_job.rb +5 -1
- data/lib/bullet/active_record41.rb +1 -0
- data/lib/bullet/active_record42.rb +1 -0
- data/lib/bullet/active_record5.rb +10 -8
- data/lib/bullet/active_record52.rb +32 -25
- data/lib/bullet/active_record60.rb +30 -23
- data/lib/bullet/active_record61.rb +274 -0
- data/lib/bullet/active_record70.rb +284 -0
- data/lib/bullet/bullet_xhr.js +18 -17
- data/lib/bullet/dependency.rb +16 -0
- data/lib/bullet/detector/association.rb +8 -0
- data/lib/bullet/detector/base.rb +2 -1
- data/lib/bullet/detector/counter_cache.rb +2 -2
- data/lib/bullet/detector/n_plus_one_query.rb +24 -13
- data/lib/bullet/detector/unused_eager_loading.rb +3 -3
- data/lib/bullet/mongoid4x.rb +1 -1
- data/lib/bullet/mongoid5x.rb +1 -1
- data/lib/bullet/mongoid6x.rb +1 -1
- data/lib/bullet/mongoid7x.rb +32 -17
- data/lib/bullet/notification.rb +2 -1
- data/lib/bullet/rack.rb +64 -23
- data/lib/bullet/registry/call_stack.rb +12 -0
- data/lib/bullet/registry.rb +1 -0
- data/lib/bullet/stack_trace_filter.rb +15 -15
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +35 -29
- data/lib/generators/bullet/install_generator.rb +22 -25
- data/perf/benchmark.rb +4 -1
- data/spec/bullet/detector/counter_cache_spec.rb +1 -1
- data/spec/bullet/detector/n_plus_one_query_spec.rb +1 -33
- data/spec/bullet/detector/unused_eager_loading_spec.rb +15 -10
- data/spec/bullet/ext/object_spec.rb +1 -1
- data/spec/bullet/notification/base_spec.rb +4 -4
- data/spec/bullet/notification/n_plus_one_query_spec.rb +1 -3
- data/spec/bullet/rack_spec.rb +152 -9
- data/spec/bullet/stack_trace_filter_spec.rb +26 -0
- data/spec/bullet_spec.rb +15 -15
- data/spec/integration/active_record/association_spec.rb +95 -12
- data/spec/integration/counter_cache_spec.rb +4 -4
- data/spec/integration/mongoid/association_spec.rb +4 -4
- data/spec/models/attachment.rb +5 -0
- data/spec/models/deal.rb +5 -0
- data/spec/models/folder.rb +2 -1
- data/spec/models/group.rb +2 -1
- data/spec/models/page.rb +2 -1
- data/spec/models/post.rb +2 -0
- data/spec/models/role.rb +7 -0
- data/spec/models/submission.rb +1 -0
- data/spec/models/user.rb +2 -0
- data/spec/models/writer.rb +2 -1
- data/spec/spec_helper.rb +0 -2
- data/spec/support/mongo_seed.rb +1 -0
- data/spec/support/sqlite_seed.rb +38 -0
- data/test.sh +2 -0
- metadata +20 -7
- data/.travis.yml +0 -33
@@ -10,40 +10,37 @@ module Bullet
|
|
10
10
|
|
11
11
|
def enable_in_development
|
12
12
|
environment(nil, env: 'development') do
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
13
|
+
<<~FILE
|
14
|
+
config.after_initialize do
|
15
|
+
Bullet.enable = true
|
16
|
+
Bullet.alert = true
|
17
|
+
Bullet.bullet_logger = true
|
18
|
+
Bullet.console = true
|
19
|
+
Bullet.rails_logger = true
|
20
|
+
Bullet.add_footer = true
|
21
|
+
end
|
22
|
+
|
24
23
|
FILE
|
25
|
-
.strip
|
26
24
|
end
|
27
25
|
|
28
26
|
say 'Enabled bullet in config/environments/development.rb'
|
29
27
|
end
|
30
28
|
|
31
29
|
def enable_in_test
|
32
|
-
|
33
|
-
environment(nil, env: 'test') do
|
34
|
-
<<-"FILE"
|
35
|
-
|
36
|
-
config.after_initialize do
|
37
|
-
Bullet.enable = true
|
38
|
-
Bullet.bullet_logger = true
|
39
|
-
Bullet.raise = true # raise an error if n+1 query occurs
|
40
|
-
end
|
41
|
-
FILE
|
42
|
-
.strip
|
43
|
-
end
|
30
|
+
return unless yes?('Would you like to enable bullet in test environment? (y/n)')
|
44
31
|
|
45
|
-
|
32
|
+
environment(nil, env: 'test') do
|
33
|
+
<<~FILE
|
34
|
+
config.after_initialize do
|
35
|
+
Bullet.enable = true
|
36
|
+
Bullet.bullet_logger = true
|
37
|
+
Bullet.raise = true # raise an error if n+1 query occurs
|
38
|
+
end
|
39
|
+
|
40
|
+
FILE
|
46
41
|
end
|
42
|
+
|
43
|
+
say 'Enabled bullet in config/environments/test.rb'
|
47
44
|
end
|
48
45
|
end
|
49
46
|
end
|
data/perf/benchmark.rb
CHANGED
@@ -30,7 +30,10 @@ end
|
|
30
30
|
|
31
31
|
# create database bullet_benchmark;
|
32
32
|
ActiveRecord::Base.establish_connection(
|
33
|
-
adapter: 'mysql2',
|
33
|
+
adapter: 'mysql2',
|
34
|
+
database: 'bullet_benchmark',
|
35
|
+
server: '/tmp/mysql.socket',
|
36
|
+
username: 'root'
|
34
37
|
)
|
35
38
|
|
36
39
|
ActiveRecord::Base.connection.tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
|
@@ -47,7 +47,7 @@ module Bullet
|
|
47
47
|
expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
|
48
48
|
end
|
49
49
|
|
50
|
-
it 'should be
|
50
|
+
it 'should be false when object is possible, and impossible' do
|
51
51
|
CounterCache.add_possible_objects(@post1)
|
52
52
|
CounterCache.add_impossible_object(@post1)
|
53
53
|
expect(CounterCache.conditions_met?(@post1, :associations)).to eq false
|
@@ -39,7 +39,7 @@ module Bullet
|
|
39
39
|
|
40
40
|
it 'should be false if object, association pair is not existed' do
|
41
41
|
NPlusOneQuery.add_object_associations(@post, :association1)
|
42
|
-
expect(NPlusOneQuery.association?(@post, :
|
42
|
+
expect(NPlusOneQuery.association?(@post, :association2)).to eq false
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
@@ -127,38 +127,6 @@ module Bullet
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
-
context '.caller_in_project' do
|
131
|
-
it 'should include only paths that are in the project' do
|
132
|
-
in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
|
133
|
-
not_in_project = OpenStruct.new(absolute_path: '/def/def.rb')
|
134
|
-
|
135
|
-
expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, not_in_project])
|
136
|
-
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
137
|
-
expect(NPlusOneQuery).to receive(:create_notification).with([in_project], 'Post', :association)
|
138
|
-
NPlusOneQuery.call_association(@post, :association)
|
139
|
-
end
|
140
|
-
|
141
|
-
context 'stacktrace_includes' do
|
142
|
-
before { Bullet.stacktrace_includes = ['def', /xyz/] }
|
143
|
-
after { Bullet.stacktrace_includes = nil }
|
144
|
-
|
145
|
-
it 'should include paths that are in the stacktrace_include list' do
|
146
|
-
in_project = OpenStruct.new(absolute_path: File.join(Dir.pwd, 'abc', 'abc.rb'))
|
147
|
-
included_gems = [OpenStruct.new(absolute_path: '/def/def.rb'), OpenStruct.new(absolute_path: 'xyz/xyz.rb')]
|
148
|
-
excluded_gem = OpenStruct.new(absolute_path: '/ghi/ghi.rb')
|
149
|
-
|
150
|
-
expect(NPlusOneQuery).to receive(:caller_locations).and_return([in_project, *included_gems, excluded_gem])
|
151
|
-
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post, :association).and_return(true)
|
152
|
-
expect(NPlusOneQuery).to receive(:create_notification).with(
|
153
|
-
[in_project, *included_gems],
|
154
|
-
'Post',
|
155
|
-
:association
|
156
|
-
)
|
157
|
-
NPlusOneQuery.call_association(@post, :association)
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
130
|
context '.add_possible_objects' do
|
163
131
|
it 'should add possible objects' do
|
164
132
|
NPlusOneQuery.add_possible_objects([@post, @post2])
|
@@ -13,27 +13,27 @@ module Bullet
|
|
13
13
|
|
14
14
|
context '.call_associations' do
|
15
15
|
it 'should get empty array if eager_loadings' do
|
16
|
-
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new(
|
16
|
+
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'should get call associations if object and association are both in eager_loadings and call_object_associations' do
|
20
20
|
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
21
21
|
UnusedEagerLoading.add_call_object_associations(@post, :association)
|
22
|
-
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new(
|
23
|
-
|
22
|
+
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq(
|
23
|
+
[:association]
|
24
24
|
)
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'should not get call associations if not exist in call_object_associations' do
|
28
28
|
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
29
|
-
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new(
|
29
|
+
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
33
|
context '.diff_object_associations' do
|
34
34
|
it 'should return associations not exist in call_association' do
|
35
|
-
expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new(
|
36
|
-
|
35
|
+
expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq(
|
36
|
+
[:association]
|
37
37
|
)
|
38
38
|
end
|
39
39
|
|
@@ -41,7 +41,7 @@ module Bullet
|
|
41
41
|
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
42
42
|
UnusedEagerLoading.add_call_object_associations(@post, :association)
|
43
43
|
expect(
|
44
|
-
UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new(
|
44
|
+
UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
|
45
45
|
).to be_empty
|
46
46
|
end
|
47
47
|
end
|
@@ -51,7 +51,7 @@ module Bullet
|
|
51
51
|
it 'should create notification if object_association_diff is not empty' do
|
52
52
|
UnusedEagerLoading.add_object_associations(@post, :association)
|
53
53
|
allow(UnusedEagerLoading).to receive(:caller_in_project).and_return(paths)
|
54
|
-
expect(UnusedEagerLoading).to receive(:create_notification).with(paths, 'Post',
|
54
|
+
expect(UnusedEagerLoading).to receive(:create_notification).with(paths, 'Post', [:association])
|
55
55
|
UnusedEagerLoading.check_unused_preload_associations
|
56
56
|
end
|
57
57
|
|
@@ -60,11 +60,16 @@ module Bullet
|
|
60
60
|
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
61
61
|
UnusedEagerLoading.add_call_object_associations(@post, :association)
|
62
62
|
expect(
|
63
|
-
UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new(
|
63
|
+
UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
|
64
64
|
).to be_empty
|
65
|
-
expect(UnusedEagerLoading).not_to receive(:create_notification).with('Post',
|
65
|
+
expect(UnusedEagerLoading).not_to receive(:create_notification).with('Post', [:association])
|
66
66
|
UnusedEagerLoading.check_unused_preload_associations
|
67
67
|
end
|
68
|
+
|
69
|
+
it 'should create call stack for notification' do
|
70
|
+
UnusedEagerLoading.add_object_associations(@post, :association)
|
71
|
+
expect(UnusedEagerLoading.send(:call_stacks).registry).not_to be_empty
|
72
|
+
end
|
68
73
|
end
|
69
74
|
|
70
75
|
context '.add_eager_loadings' do
|
@@ -10,7 +10,7 @@ describe Object do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
if mongoid?
|
13
|
-
it 'should return class with
|
13
|
+
it 'should return class with namespace and id composition' do
|
14
14
|
post = Mongoid::Post.first
|
15
15
|
expect(post.bullet_key).to eq("Mongoid::Post:#{post.id}")
|
16
16
|
end
|
@@ -74,8 +74,8 @@ module Bullet
|
|
74
74
|
it 'should send full_notice to notifier' do
|
75
75
|
notifier = double
|
76
76
|
allow(subject).to receive(:notifier).and_return(notifier)
|
77
|
-
allow(subject).to receive(:notification_data).and_return(foo: :bar)
|
78
|
-
expect(notifier).to receive(:inline_notify).with(foo: :bar)
|
77
|
+
allow(subject).to receive(:notification_data).and_return({ foo: :bar })
|
78
|
+
expect(notifier).to receive(:inline_notify).with({ foo: :bar })
|
79
79
|
subject.notify_inline
|
80
80
|
end
|
81
81
|
end
|
@@ -84,8 +84,8 @@ module Bullet
|
|
84
84
|
it 'should send full_out_of_channel to notifier' do
|
85
85
|
notifier = double
|
86
86
|
allow(subject).to receive(:notifier).and_return(notifier)
|
87
|
-
allow(subject).to receive(:notification_data).and_return(foo: :bar)
|
88
|
-
expect(notifier).to receive(:out_of_channel_notify).with(foo: :bar)
|
87
|
+
allow(subject).to receive(:notification_data).and_return({ foo: :bar })
|
88
|
+
expect(notifier).to receive(:out_of_channel_notify).with({ foo: :bar })
|
89
89
|
subject.notify_out_of_channel
|
90
90
|
end
|
91
91
|
end
|
@@ -21,9 +21,7 @@ module Bullet
|
|
21
21
|
)
|
22
22
|
end
|
23
23
|
it do
|
24
|
-
expect(subject.body).to eq(
|
25
|
-
" Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])"
|
26
|
-
)
|
24
|
+
expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your query: .includes([:comments, :votes])")
|
27
25
|
end
|
28
26
|
it { expect(subject.title).to eq('USE eager loading in path') }
|
29
27
|
end
|
data/spec/bullet/rack_spec.rb
CHANGED
@@ -31,12 +31,6 @@ module Bullet
|
|
31
31
|
response = double(body: '<html><head></head><body></body></html>')
|
32
32
|
expect(middleware).not_to be_html_request(headers, response)
|
33
33
|
end
|
34
|
-
|
35
|
-
it "should be false if response body doesn't contain html tag" do
|
36
|
-
headers = { 'Content-Type' => 'text/html' }
|
37
|
-
response = double(body: '<div>Partial</div>')
|
38
|
-
expect(middleware).not_to be_html_request(headers, response)
|
39
|
-
end
|
40
34
|
end
|
41
35
|
|
42
36
|
context 'empty?' do
|
@@ -54,6 +48,11 @@ module Bullet
|
|
54
48
|
response = double(body: '')
|
55
49
|
expect(middleware).to be_empty(response)
|
56
50
|
end
|
51
|
+
|
52
|
+
it 'should be true if no response body' do
|
53
|
+
response = double
|
54
|
+
expect(middleware).to be_empty(response)
|
55
|
+
end
|
57
56
|
end
|
58
57
|
|
59
58
|
context '#call' do
|
@@ -67,9 +66,8 @@ module Bullet
|
|
67
66
|
|
68
67
|
it 'should change response body if notification is active' do
|
69
68
|
expect(Bullet).to receive(:notification?).and_return(true)
|
70
|
-
|
69
|
+
expect(Bullet).to receive(:console_enabled?).and_return(true)
|
71
70
|
expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
|
72
|
-
expect(middleware).to receive(:xhr_script).and_return('')
|
73
71
|
expect(Bullet).to receive(:perform_out_of_channel_notifications)
|
74
72
|
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
75
73
|
expect(headers['Content-Length']).to eq('56')
|
@@ -81,9 +79,140 @@ module Bullet
|
|
81
79
|
response.body = '<html><head></head><body>é</body></html>'
|
82
80
|
app.response = response
|
83
81
|
expect(Bullet).to receive(:notification?).and_return(true)
|
82
|
+
allow(Bullet).to receive(:console_enabled?).and_return(true)
|
84
83
|
expect(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
|
85
84
|
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
86
|
-
expect(headers['Content-Length']).to eq(
|
85
|
+
expect(headers['Content-Length']).to eq('58')
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'with injection notifiers' do
|
89
|
+
before do
|
90
|
+
expect(Bullet).to receive(:notification?).and_return(true)
|
91
|
+
allow(Bullet).to receive(:gather_inline_notifications).and_return('<bullet></bullet>')
|
92
|
+
allow(middleware).to receive(:xhr_script).and_return('<script></script>')
|
93
|
+
allow(middleware).to receive(:footer_note).and_return('footer')
|
94
|
+
expect(Bullet).to receive(:perform_out_of_channel_notifications)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should change response body if add_footer is true' do
|
98
|
+
expect(Bullet).to receive(:add_footer).exactly(3).times.and_return(true)
|
99
|
+
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
100
|
+
|
101
|
+
expect(headers['Content-Length']).to eq((73 + middleware.send(:footer_note).length).to_s)
|
102
|
+
expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet><script></script></body></html>])
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should change response body for html safe string if add_footer is true' do
|
106
|
+
expect(Bullet).to receive(:add_footer).exactly(3).times.and_return(true)
|
107
|
+
app.response =
|
108
|
+
Support::ResponseDouble.new.tap do |response|
|
109
|
+
response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
|
110
|
+
end
|
111
|
+
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
112
|
+
|
113
|
+
expect(headers['Content-Length']).to eq((73 + middleware.send(:footer_note).length).to_s)
|
114
|
+
expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet><script></script></body></html>])
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should add the footer-text header for non-html requests when add_footer is true' do
|
118
|
+
allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
|
119
|
+
allow(Bullet).to receive(:footer_info).and_return(['footer text'])
|
120
|
+
app.headers = { 'Content-Type' => 'application/json' }
|
121
|
+
_, headers, _response = middleware.call({})
|
122
|
+
expect(headers).to include('X-bullet-footer-text' => '["footer text"]')
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should change response body if console_enabled is true' do
|
126
|
+
expect(Bullet).to receive(:console_enabled?).and_return(true)
|
127
|
+
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
128
|
+
expect(headers['Content-Length']).to eq('56')
|
129
|
+
expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should include CSP nonce in inline script if console_enabled and a CSP is applied' do
|
133
|
+
allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
|
134
|
+
expect(Bullet).to receive(:console_enabled?).and_return(true)
|
135
|
+
allow(middleware).to receive(:xhr_script).and_call_original
|
136
|
+
|
137
|
+
nonce = '+t9/wTlgG6xbHxXYUaDNzQ=='
|
138
|
+
app.headers = {
|
139
|
+
'Content-Type' => 'text/html',
|
140
|
+
'Content-Security-Policy' => "default-src 'self' https:; script-src 'self' https: 'nonce-#{nonce}'"
|
141
|
+
}
|
142
|
+
|
143
|
+
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
144
|
+
|
145
|
+
size = 56 + middleware.send(:footer_note).length + middleware.send(:xhr_script, nonce).length
|
146
|
+
expect(headers['Content-Length']).to eq(size.to_s)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should change response body for html safe string if console_enabled is true' do
|
150
|
+
expect(Bullet).to receive(:console_enabled?).and_return(true)
|
151
|
+
app.response =
|
152
|
+
Support::ResponseDouble.new.tap do |response|
|
153
|
+
response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
|
154
|
+
end
|
155
|
+
_, headers, response = middleware.call('Content-Type' => 'text/html')
|
156
|
+
expect(headers['Content-Length']).to eq('56')
|
157
|
+
expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'should add headers for non-html requests when console_enabled is true' do
|
161
|
+
allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true)
|
162
|
+
allow(Bullet).to receive(:text_notifications).and_return(['text notifications'])
|
163
|
+
app.headers = { 'Content-Type' => 'application/json' }
|
164
|
+
_, headers, _response = middleware.call({})
|
165
|
+
expect(headers).to include('X-bullet-console-text' => '["text notifications"]')
|
166
|
+
end
|
167
|
+
|
168
|
+
it "shouldn't change response body unnecessarily" do
|
169
|
+
expected_response = Support::ResponseDouble.new 'Actual body'
|
170
|
+
app.response = expected_response
|
171
|
+
_, _, response = middleware.call({})
|
172
|
+
expect(response).to eq(expected_response)
|
173
|
+
end
|
174
|
+
|
175
|
+
it "shouldn't add headers unnecessarily" do
|
176
|
+
app.headers = { 'Content-Type' => 'application/json' }
|
177
|
+
_, headers, _response = middleware.call({})
|
178
|
+
expect(headers).not_to include('X-bullet-footer-text')
|
179
|
+
expect(headers).not_to include('X-bullet-console-text')
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'when skip_http_headers is enabled' do
|
183
|
+
before do
|
184
|
+
allow(Bullet).to receive(:skip_http_headers).and_return(true)
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'should include the footer but not the xhr script tag if add_footer is true' do
|
188
|
+
expect(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
|
189
|
+
_, headers, response = middleware.call({})
|
190
|
+
|
191
|
+
expect(headers['Content-Length']).to eq((56 + middleware.send(:footer_note).length).to_s)
|
192
|
+
expect(response).to eq(%w[<html><head></head><body>footer<bullet></bullet></body></html>])
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'should not include the xhr script tag if console_enabled is true' do
|
196
|
+
expect(Bullet).to receive(:console_enabled?).and_return(true)
|
197
|
+
_, headers, response = middleware.call({})
|
198
|
+
expect(headers['Content-Length']).to eq('56')
|
199
|
+
expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'should not add the footer-text header for non-html requests when add_footer is true' do
|
203
|
+
allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
|
204
|
+
app.headers = { 'Content-Type' => 'application/json' }
|
205
|
+
_, headers, _response = middleware.call({})
|
206
|
+
expect(headers).not_to include('X-bullet-footer-text')
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'should not add headers for non-html requests when console_enabled is true' do
|
210
|
+
allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true)
|
211
|
+
app.headers = { 'Content-Type' => 'application/json' }
|
212
|
+
_, headers, _response = middleware.call({})
|
213
|
+
expect(headers).not_to include('X-bullet-console-text')
|
214
|
+
end
|
215
|
+
end
|
87
216
|
end
|
88
217
|
|
89
218
|
context 'when skip_html_injection is enabled' do
|
@@ -148,6 +277,20 @@ module Bullet
|
|
148
277
|
expect(middleware.response_body(response)).to eq body_string
|
149
278
|
end
|
150
279
|
end
|
280
|
+
|
281
|
+
begin
|
282
|
+
require 'rack/files'
|
283
|
+
|
284
|
+
context 'when `response` is a Rack::Files::Iterator' do
|
285
|
+
let(:response) { instance_double(::Rack::Files::Iterator) }
|
286
|
+
before { allow(response).to receive(:is_a?).with(::Rack::Files::Iterator) { true } }
|
287
|
+
|
288
|
+
it 'should return nil' do
|
289
|
+
expect(middleware.response_body(response)).to be_nil
|
290
|
+
end
|
291
|
+
end
|
292
|
+
rescue LoadError
|
293
|
+
end
|
151
294
|
end
|
152
295
|
end
|
153
296
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
module Bullet
|
6
|
+
RSpec.describe StackTraceFilter do
|
7
|
+
let(:dummy_class) { Class.new { extend StackTraceFilter } }
|
8
|
+
let(:root_path) { Dir.pwd }
|
9
|
+
let(:bundler_path) { Bundler.bundle_path }
|
10
|
+
|
11
|
+
describe '#caller_in_project' do
|
12
|
+
it 'gets the caller in the project' do
|
13
|
+
expect(dummy_class).to receive(:call_stacks).and_return({
|
14
|
+
'Post:1' => [
|
15
|
+
File.join(root_path, 'lib/bullet.rb'),
|
16
|
+
File.join(root_path, 'vendor/uniform_notifier.rb'),
|
17
|
+
File.join(bundler_path, 'rack.rb')
|
18
|
+
]
|
19
|
+
})
|
20
|
+
expect(dummy_class.caller_in_project('Post:1')).to eq([
|
21
|
+
File.join(root_path, 'lib/bullet.rb')
|
22
|
+
])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/spec/bullet_spec.rb
CHANGED
@@ -74,31 +74,31 @@ describe Bullet, focused: true do
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
-
describe '#
|
77
|
+
describe '#add_safelist' do
|
78
78
|
context "for 'special' class names" do
|
79
|
-
it 'is added to the
|
80
|
-
Bullet.
|
81
|
-
expect(Bullet.
|
79
|
+
it 'is added to the safelist successfully' do
|
80
|
+
Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
81
|
+
expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
-
describe '#
|
86
|
+
describe '#delete_safelist' do
|
87
87
|
context "for 'special' class names" do
|
88
|
-
it 'is deleted from the
|
89
|
-
Bullet.
|
90
|
-
Bullet.
|
91
|
-
expect(Bullet.
|
88
|
+
it 'is deleted from the safelist successfully' do
|
89
|
+
Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
90
|
+
Bullet.delete_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
91
|
+
expect(Bullet.safelist[:n_plus_one_query]).to eq({})
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
95
|
context 'when exists multiple definitions' do
|
96
|
-
it 'is deleted from the
|
97
|
-
Bullet.
|
98
|
-
Bullet.
|
99
|
-
Bullet.
|
100
|
-
expect(Bullet.
|
101
|
-
expect(Bullet.
|
96
|
+
it 'is deleted from the safelist successfully' do
|
97
|
+
Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
|
98
|
+
Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
|
99
|
+
Bullet.delete_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
|
100
|
+
expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
|
101
|
+
expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
|
102
102
|
end
|
103
103
|
end
|
104
104
|
end
|