logical-insight 0.4.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.
- data/History.txt +45 -0
- data/MIT-LICENSE.txt +19 -0
- data/README.md +123 -0
- data/Rakefile +24 -0
- data/Thorfile +113 -0
- data/lib/insight.rb +17 -0
- data/lib/insight/app.rb +189 -0
- data/lib/insight/database.rb +186 -0
- data/lib/insight/enable-button.rb +43 -0
- data/lib/insight/filtered_backtrace.rb +45 -0
- data/lib/insight/instrumentation.rb +9 -0
- data/lib/insight/instrumentation/backstage.rb +10 -0
- data/lib/insight/instrumentation/client.rb +20 -0
- data/lib/insight/instrumentation/instrument.rb +109 -0
- data/lib/insight/instrumentation/package-definition.rb +58 -0
- data/lib/insight/instrumentation/probe-definition.rb +20 -0
- data/lib/insight/instrumentation/probe.rb +199 -0
- data/lib/insight/instrumentation/setup.rb +32 -0
- data/lib/insight/logger.rb +55 -0
- data/lib/insight/options.rb +102 -0
- data/lib/insight/panel.rb +119 -0
- data/lib/insight/panel_app.rb +31 -0
- data/lib/insight/panels-content.rb +22 -0
- data/lib/insight/panels-header.rb +18 -0
- data/lib/insight/panels/active_record_panel.rb +46 -0
- data/lib/insight/panels/cache_panel.rb +69 -0
- data/lib/insight/panels/cache_panel/panel_app.rb +46 -0
- data/lib/insight/panels/cache_panel/stats.rb +98 -0
- data/lib/insight/panels/log_panel.rb +54 -0
- data/lib/insight/panels/memory_panel.rb +32 -0
- data/lib/insight/panels/rails_info_panel.rb +19 -0
- data/lib/insight/panels/redis_panel.rb +42 -0
- data/lib/insight/panels/redis_panel/redis_extension.rb +23 -0
- data/lib/insight/panels/redis_panel/stats.rb +50 -0
- data/lib/insight/panels/request_variables_panel.rb +70 -0
- data/lib/insight/panels/speedtracer_panel.rb +89 -0
- data/lib/insight/panels/speedtracer_panel/trace-app.rb +52 -0
- data/lib/insight/panels/speedtracer_panel/tracer.rb +212 -0
- data/lib/insight/panels/sql_panel.rb +53 -0
- data/lib/insight/panels/sql_panel/panel_app.rb +37 -0
- data/lib/insight/panels/sql_panel/query.rb +94 -0
- data/lib/insight/panels/templates_panel.rb +58 -0
- data/lib/insight/panels/templates_panel/rendering.rb +81 -0
- data/lib/insight/panels/timer_panel.rb +40 -0
- data/lib/insight/params_signature.rb +61 -0
- data/lib/insight/public/__insight__/bookmarklet.html +10 -0
- data/lib/insight/public/__insight__/bookmarklet.js +223 -0
- data/lib/insight/public/__insight__/insight.css +235 -0
- data/lib/insight/public/__insight__/insight.js +123 -0
- data/lib/insight/public/__insight__/jquery-1.3.2.js +4376 -0
- data/lib/insight/public/__insight__/jquery.tablesorter.min.js +1 -0
- data/lib/insight/public/__insight__/spinner.gif +0 -0
- data/lib/insight/rack_static_bug_avoider.rb +16 -0
- data/lib/insight/redirect_interceptor.rb +25 -0
- data/lib/insight/render.rb +72 -0
- data/lib/insight/request-recorder.rb +23 -0
- data/lib/insight/toolbar.rb +63 -0
- data/lib/insight/views/enable-button.html.erb +1 -0
- data/lib/insight/views/error.html.erb +17 -0
- data/lib/insight/views/headers_fragment.html.erb +20 -0
- data/lib/insight/views/panels/active_record.html.erb +17 -0
- data/lib/insight/views/panels/cache.html.erb +93 -0
- data/lib/insight/views/panels/execute_sql.html.erb +32 -0
- data/lib/insight/views/panels/explain_sql.html.erb +32 -0
- data/lib/insight/views/panels/log.html.erb +21 -0
- data/lib/insight/views/panels/profile_sql.html.erb +32 -0
- data/lib/insight/views/panels/rails_info.html.erb +19 -0
- data/lib/insight/views/panels/redis.html.erb +46 -0
- data/lib/insight/views/panels/request_variables.html.erb +25 -0
- data/lib/insight/views/panels/speedtracer/serverevent.html.erb +10 -0
- data/lib/insight/views/panels/speedtracer/servertrace.html.erb +12 -0
- data/lib/insight/views/panels/speedtracer/traces.html.erb +18 -0
- data/lib/insight/views/panels/sql.html.erb +43 -0
- data/lib/insight/views/panels/templates.html.erb +6 -0
- data/lib/insight/views/panels/timer.html.erb +19 -0
- data/lib/insight/views/panels/view_cache.html.erb +19 -0
- data/lib/insight/views/redirect.html.erb +16 -0
- data/lib/insight/views/request_fragment.html.erb +25 -0
- data/lib/insight/views/toolbar.html.erb +29 -0
- data/lib/logical-insight.rb +1 -0
- data/spec/custom_matchers.rb +31 -0
- data/spec/fixtures/config.ru +8 -0
- data/spec/fixtures/dummy_panel.rb +2 -0
- data/spec/fixtures/sample_app.rb +72 -0
- data/spec/insight/panels/active_record_panel_spec.rb +42 -0
- data/spec/insight/panels/cache_panel_spec.rb +176 -0
- data/spec/insight/panels/log_panel_spec.rb +44 -0
- data/spec/insight/panels/memory_panel_spec.rb +19 -0
- data/spec/insight/panels/mongo_panel_spec_pending.rb +50 -0
- data/spec/insight/panels/rails_info_panel_spec.rb +27 -0
- data/spec/insight/panels/redis_panel_spec.rb +66 -0
- data/spec/insight/panels/sql_panel_spec.rb +145 -0
- data/spec/insight/panels/templates_panel_spec.rb +84 -0
- data/spec/insight/panels/timer_panel_spec.rb +36 -0
- data/spec/insight_spec.rb +141 -0
- data/spec/instrumentation_spec.rb +188 -0
- data/spec/rcov.opts +1 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +93 -0
- metadata +187 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require File::expand_path("../../../spec_helper", __FILE__)
|
|
2
|
+
module Insight
|
|
3
|
+
describe "ActiveRecordPanel" do
|
|
4
|
+
before do
|
|
5
|
+
mock_constant("ActiveRecord::Base")
|
|
6
|
+
reset_insight :panel_classes => [ActiveRecordPanel]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def mock_model(name)
|
|
10
|
+
model = stub("model")
|
|
11
|
+
model.stub!(:name => name)
|
|
12
|
+
obj = stub(name)
|
|
13
|
+
obj.stub!(:base_class => model)
|
|
14
|
+
obj
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe "heading" do
|
|
18
|
+
it "displays the total number of instantiated AR objects" do
|
|
19
|
+
app.before_return do
|
|
20
|
+
mock_method_call("ActiveRecord::Base", "allocate", [], :class, mock_model("User"))
|
|
21
|
+
mock_method_call("ActiveRecord::Base", "allocate", [], :class, mock_model("Group"))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
response = get_via_rack "/"
|
|
25
|
+
response.should have_heading("2 AR Objects")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe "content" do
|
|
30
|
+
it "displays the count of instantiated objects for each class" do
|
|
31
|
+
app.before_return do
|
|
32
|
+
mock_method_call("ActiveRecord::Base", "allocate", [], :class, mock_model("User"))
|
|
33
|
+
mock_method_call("ActiveRecord::Base", "allocate", [], :class, mock_model("User"))
|
|
34
|
+
mock_method_call("ActiveRecord::Base", "allocate", [], :class, mock_model("Group"))
|
|
35
|
+
end
|
|
36
|
+
response = get_via_rack "/"
|
|
37
|
+
response.should have_row("#active_record", "User", "2")
|
|
38
|
+
response.should have_row("#active_record", "Group", "1")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
module Insight
|
|
2
|
+
describe "CachePanel" do
|
|
3
|
+
before do
|
|
4
|
+
mock_constant("Rails")
|
|
5
|
+
mock_constant("Memcached")
|
|
6
|
+
mock_constant("MemCache")
|
|
7
|
+
reset_insight :panel_classes => [CachePanel]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe "heading" do
|
|
11
|
+
it "displays the total memcache time" do
|
|
12
|
+
response = get_via_rack "/"
|
|
13
|
+
response.should have_heading("Cache: 0.00ms")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe "content" do
|
|
18
|
+
describe "usage table" do
|
|
19
|
+
it "displays the total number of memcache calls" do
|
|
20
|
+
app.before_return do
|
|
21
|
+
mock_method_call("Memcached", "get", ["user:1"])
|
|
22
|
+
end
|
|
23
|
+
response = get_via_rack "/"
|
|
24
|
+
|
|
25
|
+
# This causes a bus error:
|
|
26
|
+
# response.should have_selector("th:content('Total Calls') + td", :content => "1")
|
|
27
|
+
|
|
28
|
+
response.should have_row("#cache_usage", "Total Calls", "1")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "displays the total memcache time" do
|
|
32
|
+
response = get_via_rack "/"
|
|
33
|
+
response.should have_row("#cache_usage", "Total Time", "0.00ms")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "dispays the number of memcache hits" do
|
|
37
|
+
app.before_return do
|
|
38
|
+
mock_method_call("Memcached", "get", ["user:1"])
|
|
39
|
+
end
|
|
40
|
+
response = get_via_rack "/"
|
|
41
|
+
response.should have_row("#cache_usage", "Hits", "0")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "displays the number of memcache misses" do
|
|
45
|
+
app.before_return do
|
|
46
|
+
mock_method_call("Memcached", "get", ["user:1"])
|
|
47
|
+
end
|
|
48
|
+
response = get_via_rack "/"
|
|
49
|
+
response.should have_row("#cache_usage", "Misses", "1")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "displays the number of memcache gets" do
|
|
53
|
+
app.before_return do
|
|
54
|
+
mock_method_call("Memcached", "get", ["user:1"])
|
|
55
|
+
end
|
|
56
|
+
response = get_via_rack "/"
|
|
57
|
+
response.should have_row("#cache_usage", "gets", "1")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "displays the number of memcache sets" do
|
|
61
|
+
|
|
62
|
+
app.before_return do
|
|
63
|
+
mock_method_call("Memcached", "set", ["user:1"])
|
|
64
|
+
end
|
|
65
|
+
response = get_via_rack "/"
|
|
66
|
+
response.should have_row("#cache_usage", "sets", "1")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "displays the number of memcache deletes" do
|
|
70
|
+
app.before_return do
|
|
71
|
+
mock_method_call("Memcached", "delete", ["user:1"])
|
|
72
|
+
end
|
|
73
|
+
response = get_via_rack "/"
|
|
74
|
+
response.should have_row("#cache_usage", "deletes", "1")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "displays the number of memcache get_multis" do
|
|
78
|
+
app.before_return do
|
|
79
|
+
mock_method_call("MemCache", "get_multi", ["user:1", "user:2"])
|
|
80
|
+
end
|
|
81
|
+
response = get_via_rack "/"
|
|
82
|
+
response.should have_row("#cache_usage", "get_multis", "1")
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
describe "breakdown" do
|
|
87
|
+
it "displays each memcache operation" do
|
|
88
|
+
app.before_return do
|
|
89
|
+
mock_method_call("Memcached", "get", ["user:1"])
|
|
90
|
+
end
|
|
91
|
+
response = get_via_rack "/"
|
|
92
|
+
response.should have_row("#cache_breakdown", "get")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "displays the time for each memcache call" do
|
|
96
|
+
app.before_return do
|
|
97
|
+
mock_method_call("Memcached", "get", ["user:1"])
|
|
98
|
+
end
|
|
99
|
+
response = get_via_rack "/"
|
|
100
|
+
response.should have_row("#cache_breakdown", "user:1", TIME_MS_REGEXP)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "displays the keys for each memcache call" do
|
|
104
|
+
app.before_return do
|
|
105
|
+
mock_method_call("Memcached", "get", ["user:1"])
|
|
106
|
+
end
|
|
107
|
+
response = get_via_rack "/"
|
|
108
|
+
response.should have_row("#cache_breakdown", "user:1", "get")
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe "cache operations" do
|
|
114
|
+
before do
|
|
115
|
+
rack_env "insight.secret_key", 'abc'
|
|
116
|
+
response = get_via_rack "/"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
describe "expire_all" do
|
|
121
|
+
it "expires the cache keys" do
|
|
122
|
+
Rails.stub!(:cache => mock("cache"))
|
|
123
|
+
Rails.cache.should_receive(:delete).with("user:1")
|
|
124
|
+
Rails.cache.should_receive(:delete).with("user:2")
|
|
125
|
+
Rails.cache.should_receive(:delete).with("user:3")
|
|
126
|
+
Rails.cache.should_receive(:delete).with("user:4")
|
|
127
|
+
|
|
128
|
+
get_via_rack "/__insight__/delete_cache_list",
|
|
129
|
+
:keys_1 => "user:1", :keys_2 => "user:2", :keys_3 => "user:3", :keys_4 => "user:4",
|
|
130
|
+
:hash => Digest::SHA1.hexdigest("abc:user:1:user:2:user:3:user:4")
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "returns OK" do
|
|
134
|
+
Rails.stub!(:cache => mock("cache", :delete => nil))
|
|
135
|
+
response = get_via_rack "/__insight__/delete_cache_list",
|
|
136
|
+
:keys_1 => "user:1", :keys_2 => "user:2", :keys_3 => "user:3", :keys_4 => "user:4",
|
|
137
|
+
:hash => Digest::SHA1.hexdigest("abc:user:1:user:2:user:3:user:4")
|
|
138
|
+
response.should contain("OK")
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
describe "expire" do
|
|
143
|
+
it "expires the cache key" do
|
|
144
|
+
Rails.stub!(:cache => mock("cache"))
|
|
145
|
+
Rails.cache.should_receive(:delete).with("user:1")
|
|
146
|
+
get_via_rack "/__insight__/delete_cache", :key => "user:1",
|
|
147
|
+
:hash => Digest::SHA1.hexdigest("abc:user:1")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "returns OK" do
|
|
151
|
+
Rails.stub!(:cache => mock("cache", :delete => nil))
|
|
152
|
+
response = get_via_rack "/__insight__/delete_cache", :key => "user:1",
|
|
153
|
+
:hash => Digest::SHA1.hexdigest("abc:user:1")
|
|
154
|
+
response.should contain("OK")
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
describe "view_cache" do
|
|
159
|
+
it "renders the cache key" do
|
|
160
|
+
Rails.stub!(:cache => mock("cache", :read => "cache body"))
|
|
161
|
+
response = get_via_rack "/__insight__/view_cache", :key => "user:1",
|
|
162
|
+
:hash => Digest::SHA1.hexdigest("abc:user:1")
|
|
163
|
+
response.should contain("cache body")
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "renders non-String cache values properly" do
|
|
167
|
+
Rails.stub!(:cache => mock("cache", :read => [1, 2]))
|
|
168
|
+
response = get_via_rack "/__insight__/view_cache", :key => "user:1",
|
|
169
|
+
:hash => Digest::SHA1.hexdigest("abc:user:1")
|
|
170
|
+
response.should contain("[1, 2]")
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require File::expand_path('../../../spec_helper', __FILE__)
|
|
2
|
+
|
|
3
|
+
module Insight
|
|
4
|
+
describe "LogPanel" do
|
|
5
|
+
before do
|
|
6
|
+
reset_insight :panel_classes => [LogPanel]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
describe "heading" do
|
|
10
|
+
it "displays 'Log'" do
|
|
11
|
+
response = get_via_rack "/"
|
|
12
|
+
response.should have_heading("Log")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe "content" do
|
|
17
|
+
it "displays recorded log lines" do
|
|
18
|
+
app.before_return do
|
|
19
|
+
mock_method_call("Logger", "add", [0, "This is a logged message"])
|
|
20
|
+
end
|
|
21
|
+
response = get_via_rack "/"
|
|
22
|
+
response.should contain("This is a logged message")
|
|
23
|
+
response.should contain("DEBUG")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe "Extended Logger" do
|
|
28
|
+
it "does still return true/false for #add if class Logger" do
|
|
29
|
+
#AS::BufferedLogger returns the added string, Logger returns true/false
|
|
30
|
+
LOGGER.add(0, "foo").should == true
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
describe "With no logger defined" do
|
|
36
|
+
it "does not err out" do
|
|
37
|
+
logger = LOGGER
|
|
38
|
+
Object.send :remove_const, :LOGGER
|
|
39
|
+
lambda{ load("insight/panels/log_panel.rb") }.should_not raise_error
|
|
40
|
+
::LOGGER = logger
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Insight
|
|
2
|
+
describe "MemoryPanel" do
|
|
3
|
+
before do
|
|
4
|
+
reset_insight :panel => [MemoryPanel]
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
describe "heading" do
|
|
8
|
+
it "displays the total memory" do
|
|
9
|
+
response = get_via_rack "/"
|
|
10
|
+
response.should have_heading(/\d+ KB total/)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "displays the memory change during the request" do
|
|
14
|
+
response = get_via_rack "/"
|
|
15
|
+
response.should have_heading(/\d+ KB/)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
if defined? Mongo
|
|
2
|
+
module Insight
|
|
3
|
+
describe "MongoPanel" do
|
|
4
|
+
before do
|
|
5
|
+
MongoPanel.reset
|
|
6
|
+
rack_env "insight.panel_classes", [MongoPanel]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
describe "heading" do
|
|
10
|
+
it "displays the total mongo time" do
|
|
11
|
+
response = get_via_rack "/"
|
|
12
|
+
response.should have_heading("Mongo: 0.00ms")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe "content" do
|
|
17
|
+
describe "usage table" do
|
|
18
|
+
it "displays the total number of mongo calls" do
|
|
19
|
+
MongoPanel.record("db.user.user.find()") { }
|
|
20
|
+
response = get_via_rack "/"
|
|
21
|
+
|
|
22
|
+
# This causes a bus error:
|
|
23
|
+
# response.should have_selector("th:content('Total Calls') + td", :content => "1")
|
|
24
|
+
|
|
25
|
+
response.should have_row("#mongo_usage", "Total Calls", "1")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "displays the total mongo time" do
|
|
29
|
+
response = get_via_rack "/"
|
|
30
|
+
response.should have_row("#mongo_usage", "Total Time", "0.00ms")
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe "breakdown" do
|
|
35
|
+
it "displays each mongo operation" do
|
|
36
|
+
MongoPanel.record("db.user.user.find()") { }
|
|
37
|
+
response = get_via_rack "/"
|
|
38
|
+
response.should have_row("#mongo_breakdown", "db.user.user.find()")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "displays the time for mongo call" do
|
|
42
|
+
MongoPanel.record("db.user.user.find()") { }
|
|
43
|
+
response = get_via_rack "/"
|
|
44
|
+
response.should have_row("#mongo_breakdown", "db.user.user.find()", TIME_MS_REGEXP)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Insight
|
|
2
|
+
describe "RailsInfoPanel" do
|
|
3
|
+
before do
|
|
4
|
+
mock_constant("Rails::Info")
|
|
5
|
+
reset_insight :panel_classes => [RailsInfoPanel]
|
|
6
|
+
|
|
7
|
+
Rails::Info.stub!(:properties => [])
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe "heading" do
|
|
11
|
+
it "displays the Rails version" do
|
|
12
|
+
Rails.stub!(:version => "v2.3.0")
|
|
13
|
+
response = get_via_rack "/"
|
|
14
|
+
response.should have_heading("Rails v2.3.0")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe "content" do
|
|
19
|
+
it "displays the Rails::Info properties" do
|
|
20
|
+
Rails.stub!(:version => "v2.3.0")
|
|
21
|
+
Rails::Info.stub!(:properties => [["Name", "Value"]])
|
|
22
|
+
response = get_via_rack "/"
|
|
23
|
+
response.should have_row("#rails_info", "Name", "Value")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require File::expand_path("../../../spec_helper", __FILE__)
|
|
2
|
+
module Insight
|
|
3
|
+
describe "RedisPanel" do
|
|
4
|
+
before do
|
|
5
|
+
reset_insight :panel_files => %w[redis_panel]
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
describe "heading" do
|
|
9
|
+
it "displays the total redis time" do
|
|
10
|
+
response = get_via_rack "/"
|
|
11
|
+
response.should have_heading("Redis: 0.00ms")
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe "content" do
|
|
16
|
+
describe "usage table" do
|
|
17
|
+
it "displays the total number of redis calls" do
|
|
18
|
+
RedisPanel.record(["get, user:1"], Kernel.caller) { }
|
|
19
|
+
response = get_via_rack "/"
|
|
20
|
+
|
|
21
|
+
# This causes a bus error:
|
|
22
|
+
# response.should have_selector("th:content('Total Calls') + td", :content => "1")
|
|
23
|
+
|
|
24
|
+
response.should have_row("#redis_usage", "Total Calls", "1")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "displays the total redis time" do
|
|
28
|
+
response = get_via_rack "/"
|
|
29
|
+
response.should have_row("#redis_usage", "Total Time", "0.00ms")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe "breakdown" do
|
|
34
|
+
it "displays each redis operation" do
|
|
35
|
+
RedisPanel.record(["get, user:1"], Kernel.caller) { }
|
|
36
|
+
response = get_via_rack "/"
|
|
37
|
+
response.should have_row("#redis_breakdown", "get")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "displays the time for redis call" do
|
|
41
|
+
RedisPanel.record(["get, user:1"], Kernel.caller) { }
|
|
42
|
+
response = get_via_rack "/"
|
|
43
|
+
response.should have_row("#redis_breakdown", "user:1", TIME_MS_REGEXP)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "displays the arguments for each redis call" do
|
|
47
|
+
RedisPanel.record(["get, user:1"], Kernel.caller) { }
|
|
48
|
+
response = get_via_rack "/"
|
|
49
|
+
response.should have_row("#redis_breakdown", "user:1", "get")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "displays a link to show the backtrace when it's available" do
|
|
53
|
+
RedisPanel.record(["get, user:1"], Kernel.caller) { }
|
|
54
|
+
response = get_via_rack "/"
|
|
55
|
+
response.should have_row("#redis_breakdown", "user:1", "Show Backtrace")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "does not display a link to show the backtrace when it's not available" do
|
|
59
|
+
RedisPanel.record(["get, user:1"], []) { }
|
|
60
|
+
response = get_via_rack "/"
|
|
61
|
+
response.should_not contain("Show Backtrace")
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
require File::expand_path("../../../spec_helper", __FILE__)
|
|
2
|
+
module Insight
|
|
3
|
+
describe "SQLPanel" do
|
|
4
|
+
before do
|
|
5
|
+
mock_constant("ActiveRecord::ConnectionAdapters::MysqlAdapter")
|
|
6
|
+
reset_insight :panel_files => %w{sql_panel}
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
describe "heading" do
|
|
10
|
+
it "displays the total SQL query count" do
|
|
11
|
+
app.before_return do
|
|
12
|
+
mock_method_call("ActiveRecord::ConnectionAdapters::MysqlAdapter", "execute", ["SELECT NOW();"])
|
|
13
|
+
end
|
|
14
|
+
response = get_via_rack "/"
|
|
15
|
+
response.should have_heading("1 Queries")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "displays the total SQL time" do
|
|
19
|
+
app.before_return do
|
|
20
|
+
mock_method_call("ActiveRecord::ConnectionAdapters::MysqlAdapter", "execute", ["SELECT NOW();"])
|
|
21
|
+
end
|
|
22
|
+
response = get_via_rack "/"
|
|
23
|
+
response.should have_heading(/Queries \(\d+\.\d{2}ms\)/)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe "content" do
|
|
28
|
+
it "displays each executed SQL query" do
|
|
29
|
+
app.before_return do
|
|
30
|
+
mock_method_call("ActiveRecord::ConnectionAdapters::MysqlAdapter", "execute", ["SELECT NOW();"])
|
|
31
|
+
end
|
|
32
|
+
response = get_via_rack "/"
|
|
33
|
+
response.should have_row("#sql", "SELECT NOW();")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "displays the time of each executed SQL query" do
|
|
37
|
+
app.before_return do
|
|
38
|
+
mock_method_call("ActiveRecord::ConnectionAdapters::MysqlAdapter", "execute", ["SELECT NOW();"])
|
|
39
|
+
end
|
|
40
|
+
response = get_via_rack "/"
|
|
41
|
+
response.should have_row("#sql", "SELECT NOW();", TIME_MS_REGEXP)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def stub_result(results = [[]])
|
|
46
|
+
columns = results.first
|
|
47
|
+
fields = columns.map { |c| stub("field", :name => c) }
|
|
48
|
+
rows = results[1..-1]
|
|
49
|
+
|
|
50
|
+
result = stub("result", :fetch_fields => fields)
|
|
51
|
+
result.stub!(:map).and_yield(rows)
|
|
52
|
+
return result
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def expect_query(sql, results)
|
|
56
|
+
conn = stub("connection")
|
|
57
|
+
mock_constant("ActiveRecord::Base")
|
|
58
|
+
ActiveRecord::Base.stub!(:connection => conn)
|
|
59
|
+
conn.should_receive(:execute).with(sql).and_return(stub_result(results))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe "sql/execute" do
|
|
63
|
+
it "displays the query results" do
|
|
64
|
+
rack_env "insight.secret_key", "abc"
|
|
65
|
+
expect_query "SELECT username FROM users",
|
|
66
|
+
[["username"],
|
|
67
|
+
["bryan"]]
|
|
68
|
+
|
|
69
|
+
response = get_via_rack "/__insight__/sql/execute", {:query => "SELECT username FROM users",
|
|
70
|
+
:hash => Digest::SHA1.hexdigest("abc:SELECT username FROM users")}, {:xhr => true}
|
|
71
|
+
response.should contain("SELECT username FROM users")
|
|
72
|
+
response.should be_ok
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "is forbidden when the hash is missing or wrong" do
|
|
76
|
+
rack_env "insight.secret_key", 'abc'
|
|
77
|
+
|
|
78
|
+
lambda {
|
|
79
|
+
get_via_rack "/__insight__/sql/execute", {:query => "SELECT username FROM users",
|
|
80
|
+
:hash => "foobar"}, {:xhr => true}
|
|
81
|
+
}.should raise_error(SecurityError)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "is not available when the insight.secret_key is nil" do
|
|
85
|
+
rack_env "insight.secret_key", nil
|
|
86
|
+
|
|
87
|
+
lambda {
|
|
88
|
+
get_via_rack "/__insight__/sql/execute", {:query => "SELECT username FROM users",
|
|
89
|
+
:hash => "6f286f55b75716e5c91f16d77d09fa73b353ebc1"}, {:xhr => true}
|
|
90
|
+
}.should raise_error(SecurityError)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "is not available when the insight.secret_key is an empty string" do
|
|
94
|
+
rack_env "insight.secret_key", ""
|
|
95
|
+
|
|
96
|
+
lambda {
|
|
97
|
+
get_via_rack "/__insight__/sql/execute", {:query => "SELECT username FROM users",
|
|
98
|
+
:hash => "6f286f55b75716e5c91f16d77d09fa73b353ebc1"}, {:xhr => true}
|
|
99
|
+
}.should raise_error(SecurityError)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
describe "sql/explain" do
|
|
104
|
+
it "displays the query explain plan" do
|
|
105
|
+
rack_env "insight.secret_key", "abc"
|
|
106
|
+
expect_query "EXPLAIN SELECT username FROM users",
|
|
107
|
+
[["table"],
|
|
108
|
+
["users"]]
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
response = get_via_rack "/__insight__/sql/explain", :query => "SELECT username FROM users",
|
|
112
|
+
:hash => Digest::SHA1.hexdigest("abc:SELECT username FROM users")
|
|
113
|
+
response.should contain("SELECT username FROM users")
|
|
114
|
+
response.should be_ok
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "is forbidden when the hash is missing or wrong" do
|
|
118
|
+
rack_env "insight.secret_key", 'abc'
|
|
119
|
+
|
|
120
|
+
lambda {
|
|
121
|
+
get_via_rack "/__insight__/sql/explain", :query => "SELECT username FROM users",
|
|
122
|
+
:hash => "foobar"
|
|
123
|
+
}.should raise_error(SecurityError)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "is not available when the insight.secret_key is nil" do
|
|
127
|
+
rack_env "insight.secret_key", nil
|
|
128
|
+
|
|
129
|
+
lambda {
|
|
130
|
+
get_via_rack "/__insight__/sql/explain", :query => "SELECT username FROM users",
|
|
131
|
+
:hash => "6f286f55b75716e5c91f16d77d09fa73b353ebc1"
|
|
132
|
+
}.should raise_error(SecurityError)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "is not available when the insight.secret_key is an empty string" do
|
|
136
|
+
rack_env "insight.secret_key", ""
|
|
137
|
+
|
|
138
|
+
lambda {
|
|
139
|
+
get_via_rack "/__insight__/sql/explain", :query => "SELECT username FROM users",
|
|
140
|
+
:hash => "6f286f55b75716e5c91f16d77d09fa73b353ebc1"
|
|
141
|
+
}.should raise_error(SecurityError)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|