bullet_instructure 4.0.2
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/.gitignore +14 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +20 -0
- data/CHANGELOG.md +75 -0
- data/Gemfile +19 -0
- data/Gemfile.mongoid +14 -0
- data/Gemfile.mongoid-2.4 +19 -0
- data/Gemfile.mongoid-2.5 +19 -0
- data/Gemfile.mongoid-2.6 +19 -0
- data/Gemfile.mongoid-2.7 +19 -0
- data/Gemfile.mongoid-2.8 +19 -0
- data/Gemfile.mongoid-3.0 +19 -0
- data/Gemfile.mongoid-3.1 +19 -0
- data/Gemfile.mongoid-4.0 +19 -0
- data/Gemfile.rails-3.0 +19 -0
- data/Gemfile.rails-3.1 +19 -0
- data/Gemfile.rails-3.2 +19 -0
- data/Gemfile.rails-4.0 +19 -0
- data/Gemfile.rails-4.1 +19 -0
- data/Guardfile +8 -0
- data/Hacking.md +74 -0
- data/MIT-LICENSE +20 -0
- data/README.md +428 -0
- data/Rakefile +52 -0
- data/bullet_instructure.gemspec +27 -0
- data/lib/bullet.rb +196 -0
- data/lib/bullet/active_record3.rb +148 -0
- data/lib/bullet/active_record3x.rb +128 -0
- data/lib/bullet/active_record4.rb +128 -0
- data/lib/bullet/active_record41.rb +121 -0
- data/lib/bullet/dependency.rb +81 -0
- data/lib/bullet/detector.rb +9 -0
- data/lib/bullet/detector/association.rb +67 -0
- data/lib/bullet/detector/base.rb +6 -0
- data/lib/bullet/detector/counter_cache.rb +59 -0
- data/lib/bullet/detector/n_plus_one_query.rb +89 -0
- data/lib/bullet/detector/unused_eager_loading.rb +84 -0
- data/lib/bullet/ext/object.rb +9 -0
- data/lib/bullet/ext/string.rb +5 -0
- data/lib/bullet/mongoid2x.rb +56 -0
- data/lib/bullet/mongoid3x.rb +56 -0
- data/lib/bullet/mongoid4x.rb +56 -0
- data/lib/bullet/notification.rb +10 -0
- data/lib/bullet/notification/base.rb +97 -0
- data/lib/bullet/notification/counter_cache.rb +13 -0
- data/lib/bullet/notification/n_plus_one_query.rb +28 -0
- data/lib/bullet/notification/unused_eager_loading.rb +13 -0
- data/lib/bullet/notification_collector.rb +24 -0
- data/lib/bullet/rack.rb +81 -0
- data/lib/bullet/registry.rb +7 -0
- data/lib/bullet/registry/association.rb +13 -0
- data/lib/bullet/registry/base.rb +40 -0
- data/lib/bullet/registry/object.rb +13 -0
- data/lib/bullet/version.rb +4 -0
- data/perf/benchmark.rb +121 -0
- data/rails/init.rb +1 -0
- data/spec/bullet/detector/association_spec.rb +26 -0
- data/spec/bullet/detector/base_spec.rb +8 -0
- data/spec/bullet/detector/counter_cache_spec.rb +56 -0
- data/spec/bullet/detector/n_plus_one_query_spec.rb +138 -0
- data/spec/bullet/detector/unused_eager_loading_spec.rb +88 -0
- data/spec/bullet/ext/object_spec.rb +17 -0
- data/spec/bullet/ext/string_spec.rb +13 -0
- data/spec/bullet/notification/base_spec.rb +83 -0
- data/spec/bullet/notification/counter_cache_spec.rb +12 -0
- data/spec/bullet/notification/n_plus_one_query_spec.rb +14 -0
- data/spec/bullet/notification/unused_eager_loading_spec.rb +12 -0
- data/spec/bullet/notification_collector_spec.rb +32 -0
- data/spec/bullet/rack_spec.rb +97 -0
- data/spec/bullet/registry/association_spec.rb +26 -0
- data/spec/bullet/registry/base_spec.rb +44 -0
- data/spec/bullet/registry/object_spec.rb +24 -0
- data/spec/bullet_spec.rb +41 -0
- data/spec/integration/active_record3/association_spec.rb +651 -0
- data/spec/integration/active_record4/association_spec.rb +649 -0
- data/spec/integration/counter_cache_spec.rb +63 -0
- data/spec/integration/mongoid/association_spec.rb +258 -0
- data/spec/models/address.rb +3 -0
- data/spec/models/author.rb +3 -0
- data/spec/models/base_user.rb +5 -0
- data/spec/models/category.rb +7 -0
- data/spec/models/city.rb +3 -0
- data/spec/models/client.rb +4 -0
- data/spec/models/comment.rb +4 -0
- data/spec/models/company.rb +3 -0
- data/spec/models/country.rb +3 -0
- data/spec/models/document.rb +5 -0
- data/spec/models/entry.rb +3 -0
- data/spec/models/firm.rb +4 -0
- data/spec/models/folder.rb +2 -0
- data/spec/models/mongoid/address.rb +7 -0
- data/spec/models/mongoid/category.rb +8 -0
- data/spec/models/mongoid/comment.rb +7 -0
- data/spec/models/mongoid/company.rb +7 -0
- data/spec/models/mongoid/entry.rb +7 -0
- data/spec/models/mongoid/post.rb +12 -0
- data/spec/models/mongoid/user.rb +5 -0
- data/spec/models/newspaper.rb +3 -0
- data/spec/models/page.rb +2 -0
- data/spec/models/person.rb +3 -0
- data/spec/models/pet.rb +3 -0
- data/spec/models/post.rb +10 -0
- data/spec/models/relationship.rb +4 -0
- data/spec/models/student.rb +3 -0
- data/spec/models/submission.rb +4 -0
- data/spec/models/teacher.rb +3 -0
- data/spec/models/user.rb +4 -0
- data/spec/models/writer.rb +2 -0
- data/spec/spec_helper.rb +103 -0
- data/spec/support/bullet_ext.rb +55 -0
- data/spec/support/mongo_seed.rb +65 -0
- data/spec/support/rack_double.rb +55 -0
- data/spec/support/sqlite_seed.rb +229 -0
- data/tasks/bullet_tasks.rake +9 -0
- data/test.sh +15 -0
- metadata +246 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Bullet
|
4
|
+
module Detector
|
5
|
+
describe NPlusOneQuery do
|
6
|
+
before(:all) do
|
7
|
+
@post = Post.first
|
8
|
+
@post2 = Post.last
|
9
|
+
end
|
10
|
+
|
11
|
+
context ".call_association" do
|
12
|
+
it "should add call_object_associations" do
|
13
|
+
expect(NPlusOneQuery).to receive(:add_call_object_associations).with(@post, :associations)
|
14
|
+
NPlusOneQuery.call_association(@post, :associations)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context ".possible?" do
|
19
|
+
it "should be true if possible_objects contain" do
|
20
|
+
NPlusOneQuery.add_possible_objects(@post)
|
21
|
+
expect(NPlusOneQuery.send(:possible?, @post.bullet_key)).to eq true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context ".impossible?" do
|
26
|
+
it "should be true if impossible_objects contain" do
|
27
|
+
NPlusOneQuery.add_impossible_object(@post)
|
28
|
+
expect(NPlusOneQuery.send(:impossible?, @post.bullet_key)).to eq true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context ".association?" do
|
33
|
+
it "should be true if object, associations pair is already existed" do
|
34
|
+
NPlusOneQuery.add_object_associations(@post, :association)
|
35
|
+
expect(NPlusOneQuery.send(:association?, @post.bullet_key, :association)).to eq true
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should be false if object, association pair is not existed" do
|
39
|
+
NPlusOneQuery.add_object_associations(@post, :association1)
|
40
|
+
expect(NPlusOneQuery.send(:association?, @post.bullet_key, :associatio2)).to eq false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context ".conditions_met?" do
|
45
|
+
it "should be true if object is possible, not impossible and object, associations pair is not already existed" do
|
46
|
+
allow(NPlusOneQuery).to receive(:possible?).with(@post.bullet_key).and_return(true)
|
47
|
+
allow(NPlusOneQuery).to receive(:impossible?).with(@post.bullet_key).and_return(false)
|
48
|
+
allow(NPlusOneQuery).to receive(:association?).with(@post.bullet_key, :associations).and_return(false)
|
49
|
+
expect(NPlusOneQuery.send(:conditions_met?, @post.bullet_key, :associations)).to eq true
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should be false if object is not possible, not impossible and object, associations pair is not already existed" do
|
53
|
+
allow(NPlusOneQuery).to receive(:possible?).with(@post.bullet_key).and_return(false)
|
54
|
+
allow(NPlusOneQuery).to receive(:impossible?).with(@post.bullet_key).and_return(false)
|
55
|
+
allow(NPlusOneQuery).to receive(:association?).with(@post.bullet_key, :associations).and_return(false)
|
56
|
+
expect(NPlusOneQuery.send(:conditions_met?, @post.bullet_key, :associations)).to eq false
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should be false if object is possible, but impossible and object, associations pair is not already existed" do
|
60
|
+
allow(NPlusOneQuery).to receive(:possible?).with(@post.bullet_key).and_return(true)
|
61
|
+
allow(NPlusOneQuery).to receive(:impossible?).with(@post.bullet_key).and_return(true)
|
62
|
+
allow(NPlusOneQuery).to receive(:association?).with(@post.bullet_key, :associations).and_return(false)
|
63
|
+
expect(NPlusOneQuery.send(:conditions_met?, @post.bullet_key, :associations)).to eq false
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should be false if object is possible, not impossible and object, associations pair is already existed" do
|
67
|
+
allow(NPlusOneQuery).to receive(:possible?).with(@post.bullet_key).and_return(true)
|
68
|
+
allow(NPlusOneQuery).to receive(:impossible?).with(@post.bullet_key).and_return(false)
|
69
|
+
allow(NPlusOneQuery).to receive(:association?).with(@post.bullet_key, :associations).and_return(true)
|
70
|
+
expect(NPlusOneQuery.send(:conditions_met?, @post.bullet_key, :associations)).to eq false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context ".call_association" do
|
75
|
+
it "should create notification if conditions met" do
|
76
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post.bullet_key, :association).and_return(true)
|
77
|
+
expect(NPlusOneQuery).to receive(:caller_in_project).and_return(["caller"])
|
78
|
+
expect(NPlusOneQuery).to receive(:create_notification).with(["caller"], "Post", :association)
|
79
|
+
NPlusOneQuery.call_association(@post, :association)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should not create notification if conditions not met" do
|
83
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post.bullet_key, :association).and_return(false)
|
84
|
+
expect(NPlusOneQuery).not_to receive(:caller_in_project!)
|
85
|
+
expect(NPlusOneQuery).not_to receive(:create_notification).with("Post", :association)
|
86
|
+
NPlusOneQuery.call_association(@post, :association)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context ".caller_in_project" do
|
91
|
+
it "should include only paths that are in the project" do
|
92
|
+
in_project = File.join(Dir.pwd, 'abc', 'abc.rb')
|
93
|
+
not_in_project = '/def/def.rb'
|
94
|
+
|
95
|
+
expect(NPlusOneQuery).to receive(:caller).and_return([in_project, not_in_project])
|
96
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post.bullet_key, :association).and_return(true)
|
97
|
+
expect(NPlusOneQuery).to receive(:create_notification).with([in_project], "Post", :association)
|
98
|
+
NPlusOneQuery.call_association(@post, :association)
|
99
|
+
end
|
100
|
+
|
101
|
+
context "stacktrace_includes" do
|
102
|
+
before { Bullet.stacktrace_includes = [ 'def' ] }
|
103
|
+
after { Bullet.stacktrace_includes = nil }
|
104
|
+
|
105
|
+
it "should include paths that are in the stacktrace_include list" do
|
106
|
+
in_project = File.join(Dir.pwd, 'abc', 'abc.rb')
|
107
|
+
included_gem = '/def/def.rb'
|
108
|
+
excluded_gem = '/ghi/ghi.rb'
|
109
|
+
|
110
|
+
expect(NPlusOneQuery).to receive(:caller).and_return([in_project, included_gem, excluded_gem])
|
111
|
+
expect(NPlusOneQuery).to receive(:conditions_met?).with(@post.bullet_key, :association).and_return(true)
|
112
|
+
expect(NPlusOneQuery).to receive(:create_notification).with([in_project, included_gem], "Post", :association)
|
113
|
+
NPlusOneQuery.call_association(@post, :association)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context ".add_possible_objects" do
|
119
|
+
it "should add possible objects" do
|
120
|
+
NPlusOneQuery.add_possible_objects([@post, @post2])
|
121
|
+
expect(NPlusOneQuery.send(:possible_objects)).to be_include(@post.bullet_key)
|
122
|
+
expect(NPlusOneQuery.send(:possible_objects)).to be_include(@post2.bullet_key)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should not raise error if object is nil" do
|
126
|
+
expect { NPlusOneQuery.add_possible_objects(nil) }.not_to raise_error
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context ".add_impossible_object" do
|
131
|
+
it "should add impossible object" do
|
132
|
+
NPlusOneQuery.add_impossible_object(@post)
|
133
|
+
expect(NPlusOneQuery.send(:impossible_objects)).to be_include(@post.bullet_key)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Bullet
|
4
|
+
module Detector
|
5
|
+
describe UnusedEagerLoading do
|
6
|
+
before(:all) do
|
7
|
+
@post = Post.first
|
8
|
+
@post2 = Post.last
|
9
|
+
end
|
10
|
+
|
11
|
+
context ".call_associations" do
|
12
|
+
it "should get empty array if eager_loadings" do
|
13
|
+
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should get call associations if object and association are both in eager_loadings and call_object_associations" do
|
17
|
+
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
18
|
+
UnusedEagerLoading.add_call_object_associations(@post, :association)
|
19
|
+
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq([:association])
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should not get call associations if not exist in call_object_associations" do
|
23
|
+
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
24
|
+
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context ".diff_object_associations" do
|
29
|
+
it "should return associations not exist in call_association" do
|
30
|
+
expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq([:association])
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should return empty if associations exist in call_association" do
|
34
|
+
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
35
|
+
UnusedEagerLoading.add_call_object_associations(@post, :association)
|
36
|
+
expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to be_empty
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context ".check_unused_preload_associations" do
|
41
|
+
it "should create notification if object_association_diff is not empty" do
|
42
|
+
UnusedEagerLoading.add_object_associations(@post, :association)
|
43
|
+
expect(UnusedEagerLoading).to receive(:create_notification).with("Post", [:association])
|
44
|
+
UnusedEagerLoading.check_unused_preload_associations
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should not create notification if object_association_diff is empty" do
|
48
|
+
UnusedEagerLoading.add_object_associations(@post, :association)
|
49
|
+
UnusedEagerLoading.add_eager_loadings([@post], :association)
|
50
|
+
UnusedEagerLoading.add_call_object_associations(@post, :association)
|
51
|
+
expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to be_empty
|
52
|
+
expect(UnusedEagerLoading).not_to receive(:create_notification).with("Post", [:association])
|
53
|
+
UnusedEagerLoading.check_unused_preload_associations
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context ".add_eager_loadings" do
|
58
|
+
it "should add objects, associations pair when eager_loadings are empty" do
|
59
|
+
UnusedEagerLoading.add_eager_loadings([@post, @post2], :associations)
|
60
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key, @post2.bullet_key], :associations)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should add objects, associations pair for existing eager_loadings" do
|
64
|
+
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association1)
|
65
|
+
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association2)
|
66
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key, @post2.bullet_key], :association1)
|
67
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key, @post2.bullet_key], :association2)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should merge objects, associations pair for existing eager_loadings" do
|
71
|
+
UnusedEagerLoading.add_eager_loadings([@post], :association1)
|
72
|
+
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association2)
|
73
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association1)
|
74
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association2)
|
75
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key, @post2.bullet_key], :association2)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should delete objects, associations pair for existing eager_loadings" do
|
79
|
+
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association1)
|
80
|
+
UnusedEagerLoading.add_eager_loadings([@post], :association2)
|
81
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association1)
|
82
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association2)
|
83
|
+
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post2.bullet_key], :association1)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Object do
|
4
|
+
context "bullet_key" do
|
5
|
+
it "should return class and id composition" do
|
6
|
+
post = Post.first
|
7
|
+
expect(post.bullet_key).to eq("Post:#{post.id}")
|
8
|
+
end
|
9
|
+
|
10
|
+
if mongoid?
|
11
|
+
it "should return class with namesapce and id composition" do
|
12
|
+
post = Mongoid::Post.first
|
13
|
+
expect(post.bullet_key).to eq("Mongoid::Post:#{post.id}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe String do
|
4
|
+
context "bullet_class_name" do
|
5
|
+
it "should only return class name" do
|
6
|
+
expect("Post:1".bullet_class_name).to eq("Post")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should return class name with namespace" do
|
10
|
+
expect("Mongoid::Post:1234567890".bullet_class_name).to eq("Mongoid::Post")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Bullet
|
4
|
+
module Notification
|
5
|
+
describe Base do
|
6
|
+
subject { Base.new(Post, [:comments, :votes]) }
|
7
|
+
|
8
|
+
context "#title" do
|
9
|
+
it "should raise NoMethodError" do
|
10
|
+
expect { subject.title }.to raise_error(NoMethodError)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "#body" do
|
15
|
+
it "should raise NoMethodError" do
|
16
|
+
expect { subject.body }.to raise_error(NoMethodError)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "#whoami" do
|
21
|
+
it "should display user name" do
|
22
|
+
user = `whoami`.chomp
|
23
|
+
expect(subject.whoami).to eq("user: #{user}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "#body_with_caller" do
|
28
|
+
it "should return body" do
|
29
|
+
allow(subject).to receive(:body).and_return("body")
|
30
|
+
expect(subject.body_with_caller).to eq("body")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "#standard_notice" do
|
35
|
+
it "should return title + body" do
|
36
|
+
allow(subject).to receive(:title).and_return("title")
|
37
|
+
allow(subject).to receive(:body).and_return("body")
|
38
|
+
expect(subject.standard_notice).to eq("title\nbody")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "#full_notice" do
|
43
|
+
it "should return whoami + url + title + body_with_caller" do
|
44
|
+
allow(subject).to receive(:whoami).and_return("whoami")
|
45
|
+
allow(subject).to receive(:url).and_return("url")
|
46
|
+
allow(subject).to receive(:title).and_return("title")
|
47
|
+
allow(subject).to receive(:body_with_caller).and_return("body_with_caller")
|
48
|
+
expect(subject.full_notice).to eq("whoami\nurl\ntitle\nbody_with_caller")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "#notification_data" do
|
53
|
+
it "should return notification data" do
|
54
|
+
allow(subject).to receive(:whoami).and_return("whoami")
|
55
|
+
allow(subject).to receive(:url).and_return("url")
|
56
|
+
allow(subject).to receive(:title).and_return("title")
|
57
|
+
allow(subject).to receive(:body_with_caller).and_return("body_with_caller")
|
58
|
+
expect(subject.notification_data).to eq(:user => "whoami", :url => "url", :title => "title", :body => "body_with_caller")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "#notify_inline" do
|
63
|
+
it "should send full_notice to notifier" do
|
64
|
+
notifier = double
|
65
|
+
allow(subject).to receive(:notifier).and_return(notifier)
|
66
|
+
allow(subject).to receive(:notification_data).and_return(:foo => :bar)
|
67
|
+
expect(notifier).to receive(:inline_notify).with(:foo => :bar)
|
68
|
+
subject.notify_inline
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "#notify_out_of_channel" do
|
73
|
+
it "should send full_out_of_channel to notifier" do
|
74
|
+
notifier = double
|
75
|
+
allow(subject).to receive(:notifier).and_return(notifier)
|
76
|
+
allow(subject).to receive(:notification_data).and_return(:foo => :bar)
|
77
|
+
expect(notifier).to receive(:out_of_channel_notify).with(:foo => :bar)
|
78
|
+
subject.notify_out_of_channel
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Bullet
|
4
|
+
module Notification
|
5
|
+
describe CounterCache do
|
6
|
+
subject { CounterCache.new(Post, [:comments, :votes]) }
|
7
|
+
|
8
|
+
it { expect(subject.body).to eq(" Post => [:comments, :votes]") }
|
9
|
+
it { expect(subject.title).to eq("Need Counter Cache") }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Bullet
|
4
|
+
module Notification
|
5
|
+
describe NPlusOneQuery do
|
6
|
+
subject { NPlusOneQuery.new([["caller1", "caller2"]], Post, [:comments, :votes], "path") }
|
7
|
+
|
8
|
+
it { expect(subject.body_with_caller).to eq(" Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2") }
|
9
|
+
it { expect([ subject.body_with_caller, subject.body_with_caller]).to eq([ " Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2", " Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2" ]) }
|
10
|
+
it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]") }
|
11
|
+
it { expect(subject.title).to eq("N+1 Query in path") }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Bullet
|
4
|
+
module Notification
|
5
|
+
describe UnusedEagerLoading do
|
6
|
+
subject { UnusedEagerLoading.new(Post, [:comments, :votes], "path") }
|
7
|
+
|
8
|
+
it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Remove from your finder: :include => [:comments, :votes]") }
|
9
|
+
it { expect(subject.title).to eq("Unused Eager Loading in path") }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Bullet
|
4
|
+
describe NotificationCollector do
|
5
|
+
subject { NotificationCollector.new.tap { |collector| collector.add("value") } }
|
6
|
+
|
7
|
+
context "#add" do
|
8
|
+
it "should add a value" do
|
9
|
+
subject.add("value1")
|
10
|
+
expect(subject.collection).to be_include("value1")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "#reset" do
|
15
|
+
it "should reset collector" do
|
16
|
+
subject.reset
|
17
|
+
expect(subject.collection).to be_empty
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "#notifications_present?" do
|
22
|
+
it "should be true if collection is not empty" do
|
23
|
+
expect(subject).to be_notifications_present
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be false if collection is empty" do
|
27
|
+
subject.reset
|
28
|
+
expect(subject).not_to be_notifications_present
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Bullet
|
5
|
+
describe Rack do
|
6
|
+
let(:middleware) { Bullet::Rack.new app }
|
7
|
+
let(:app) { Support::AppDouble.new }
|
8
|
+
|
9
|
+
context "#html_request?" do
|
10
|
+
it "should be true if Content-Type is text/html and http body contains html tag" do
|
11
|
+
headers = {"Content-Type" => "text/html"}
|
12
|
+
response = double(:body => "<html><head></head><body></body></html>")
|
13
|
+
expect(middleware).to be_html_request(headers, response)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should be true if Content-Type is text/html and http body contains html tag with attributes" do
|
17
|
+
headers = {"Content-Type" => "text/html"}
|
18
|
+
response = double(:body => "<html attr='hello'><head></head><body></body></html>")
|
19
|
+
expect(middleware).to be_html_request(headers, response)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should be false if there is no Content-Type header" do
|
23
|
+
headers = {}
|
24
|
+
response = double(:body => "<html><head></head><body></body></html>")
|
25
|
+
expect(middleware).not_to be_html_request(headers, response)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should be false if Content-Type is javascript" do
|
29
|
+
headers = {"Content-Type" => "text/javascript"}
|
30
|
+
response = double(:body => "<html><head></head><body></body></html>")
|
31
|
+
expect(middleware).not_to be_html_request(headers, response)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should be false if response body doesn't contain html tag" do
|
35
|
+
headers = {"Content-Type" => "text/html"}
|
36
|
+
response = double(:body => "<div>Partial</div>")
|
37
|
+
expect(middleware).not_to be_html_request(headers, response)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "empty?" do
|
42
|
+
it "should be false if response is a string and not empty" do
|
43
|
+
response = double(:body => "<html><head></head><body></body></html>")
|
44
|
+
expect(middleware).not_to be_empty(response)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should be true if response is not found" do
|
48
|
+
response = ["Not Found"]
|
49
|
+
expect(middleware).to be_empty(response)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should be true if response body is empty" do
|
53
|
+
response = double(:body => "")
|
54
|
+
expect(middleware).to be_empty(response)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "#call" do
|
59
|
+
context "when Bullet is enabled" do
|
60
|
+
it "should return original response body" do
|
61
|
+
expected_response = Support::ResponseDouble.new "Actual body"
|
62
|
+
app.response = expected_response
|
63
|
+
_, _, response = middleware.call([])
|
64
|
+
expect(response).to eq(expected_response)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should change response body if notification is active" do
|
68
|
+
expect(Bullet).to receive(:notification?).and_return(true).twice
|
69
|
+
expect(Bullet).to receive(:gather_inline_notifications).and_return("<bullet></bullet>")
|
70
|
+
expect(Bullet).to receive(:perform_out_of_channel_notifications)
|
71
|
+
status, headers, response = middleware.call([200, {"Content-Type" => "text/html"}])
|
72
|
+
expect(headers["Content-Length"]).to eq("56")
|
73
|
+
expect(response).to eq(["<html><head></head><body></body></html><bullet></bullet>"])
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should set the right Content-Length if response body contains accents" do
|
77
|
+
response = Support::ResponseDouble.new
|
78
|
+
response.body = "<html><head></head><body>é</body></html>"
|
79
|
+
app.response = response
|
80
|
+
expect(Bullet).to receive(:notification?).and_return(true).twice
|
81
|
+
expect(Bullet).to receive(:gather_inline_notifications).and_return("<bullet></bullet>")
|
82
|
+
status, headers, response = middleware.call([200, {"Content-Type" => "text/html"}])
|
83
|
+
expect(headers["Content-Length"]).to eq("58")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "when Bullet is disabled" do
|
88
|
+
before(:each) { allow(Bullet).to receive(:enable?).and_return(false) }
|
89
|
+
|
90
|
+
it "should not call Bullet.start_request" do
|
91
|
+
expect(Bullet).not_to receive(:start_request)
|
92
|
+
middleware.call([])
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|