appsignal 0.11.18 → 0.12.beta.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/.gitignore +6 -0
  2. data/CHANGELOG.md +4 -38
  3. data/Rakefile +14 -6
  4. data/appsignal.gemspec +3 -1
  5. data/benchmark.rake +12 -16
  6. data/ext/appsignal_extension.c +183 -0
  7. data/ext/extconf.rb +39 -0
  8. data/gemfiles/capistrano2.gemfile +0 -1
  9. data/gemfiles/capistrano3.gemfile +0 -1
  10. data/gemfiles/rails-4.2.gemfile +1 -1
  11. data/lib/appsignal.rb +23 -61
  12. data/lib/appsignal/capistrano.rb +1 -2
  13. data/lib/appsignal/config.rb +13 -1
  14. data/lib/appsignal/event_formatter.rb +67 -0
  15. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +23 -0
  16. data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +74 -0
  17. data/lib/appsignal/event_formatter/moped/query_formatter.rb +80 -0
  18. data/lib/appsignal/event_formatter/net_http/request_formatter.rb +13 -0
  19. data/lib/appsignal/instrumentations/net_http.rb +6 -4
  20. data/lib/appsignal/integrations/resque.rb +2 -10
  21. data/lib/appsignal/integrations/sidekiq.rb +2 -2
  22. data/lib/appsignal/integrations/sinatra.rb +1 -0
  23. data/lib/appsignal/js_exception_transaction.rb +44 -28
  24. data/lib/appsignal/marker.rb +11 -13
  25. data/lib/appsignal/params_sanitizer.rb +5 -8
  26. data/lib/appsignal/rack/instrumentation.rb +2 -0
  27. data/lib/appsignal/rack/js_exception_catcher.rb +1 -0
  28. data/lib/appsignal/rack/listener.rb +1 -1
  29. data/lib/appsignal/rack/sinatra_instrumentation.rb +2 -12
  30. data/lib/appsignal/subscriber.rb +59 -0
  31. data/lib/appsignal/transaction.rb +117 -174
  32. data/lib/appsignal/transmitter.rb +8 -37
  33. data/lib/appsignal/version.rb +2 -1
  34. data/spec/lib/appsignal/config_spec.rb +25 -4
  35. data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +42 -0
  36. data/spec/lib/appsignal/{aggregator/middleware/active_record_sanitizer_spec.rb → event_formatter/active_record/sql_formatter_spec.rb} +61 -61
  37. data/spec/lib/appsignal/{event/moped_event_spec.rb → event_formatter/moped/query_formatter_spec.rb} +32 -78
  38. data/spec/lib/appsignal/event_formatter/net_http/request_formatter_spec.rb +26 -0
  39. data/spec/lib/appsignal/event_formatter_spec.rb +102 -0
  40. data/spec/lib/appsignal/extension_spec.rb +75 -0
  41. data/spec/lib/appsignal/instrumentations/net_http_spec.rb +20 -4
  42. data/spec/lib/appsignal/integrations/delayed_job_spec.rb +3 -2
  43. data/spec/lib/appsignal/integrations/rails_spec.rb +0 -7
  44. data/spec/lib/appsignal/integrations/resque_spec.rb +51 -55
  45. data/spec/lib/appsignal/integrations/sequel_spec.rb +8 -3
  46. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +4 -21
  47. data/spec/lib/appsignal/integrations/sinatra_spec.rb +0 -6
  48. data/spec/lib/appsignal/js_exception_transaction_spec.rb +57 -60
  49. data/spec/lib/appsignal/params_sanitizer_spec.rb +11 -27
  50. data/spec/lib/appsignal/rack/listener_spec.rb +6 -6
  51. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +2 -43
  52. data/spec/lib/appsignal/subscriber_spec.rb +162 -0
  53. data/spec/lib/appsignal/transaction_spec.rb +283 -615
  54. data/spec/lib/appsignal/transmitter_spec.rb +3 -32
  55. data/spec/lib/appsignal_spec.rb +41 -90
  56. data/spec/lib/generators/appsignal/appsignal_generator_spec.rb +0 -17
  57. data/spec/spec_helper.rb +18 -22
  58. data/spec/support/helpers/notification_helpers.rb +1 -1
  59. data/spec/support/helpers/time_helpers.rb +11 -0
  60. data/spec/support/helpers/transaction_helpers.rb +6 -18
  61. data/spec/support/project_fixture/config/appsignal.yml +1 -2
  62. metadata +68 -78
  63. checksums.yaml +0 -7
  64. data/gemfiles/padrino-0.13.gemfile +0 -7
  65. data/gemfiles/resque.gemfile +0 -5
  66. data/lib/appsignal/agent.rb +0 -217
  67. data/lib/appsignal/aggregator.rb +0 -67
  68. data/lib/appsignal/aggregator/middleware.rb +0 -4
  69. data/lib/appsignal/aggregator/middleware/action_view_sanitizer.rb +0 -23
  70. data/lib/appsignal/aggregator/middleware/active_record_sanitizer.rb +0 -65
  71. data/lib/appsignal/aggregator/middleware/chain.rb +0 -101
  72. data/lib/appsignal/aggregator/middleware/delete_blanks.rb +0 -16
  73. data/lib/appsignal/aggregator/post_processor.rb +0 -32
  74. data/lib/appsignal/event.rb +0 -20
  75. data/lib/appsignal/event/moped_event.rb +0 -90
  76. data/lib/appsignal/integrations/padrino.rb +0 -64
  77. data/lib/appsignal/integrations/passenger.rb +0 -13
  78. data/lib/appsignal/integrations/rake.rb +0 -29
  79. data/lib/appsignal/integrations/unicorn.rb +0 -25
  80. data/lib/appsignal/ipc.rb +0 -68
  81. data/lib/appsignal/transaction/formatter.rb +0 -85
  82. data/lib/appsignal/transaction/params_sanitizer.rb +0 -4
  83. data/lib/appsignal/zipped_payload.rb +0 -37
  84. data/spec/lib/appsignal/agent_spec.rb +0 -592
  85. data/spec/lib/appsignal/aggregator/middleware/action_view_sanitizer_spec.rb +0 -44
  86. data/spec/lib/appsignal/aggregator/middleware/chain_spec.rb +0 -168
  87. data/spec/lib/appsignal/aggregator/middleware/delete_blanks_spec.rb +0 -37
  88. data/spec/lib/appsignal/aggregator/post_processor_spec.rb +0 -99
  89. data/spec/lib/appsignal/aggregator_spec.rb +0 -186
  90. data/spec/lib/appsignal/event_spec.rb +0 -48
  91. data/spec/lib/appsignal/integrations/padrino_spec.rb +0 -171
  92. data/spec/lib/appsignal/integrations/passenger_spec.rb +0 -22
  93. data/spec/lib/appsignal/integrations/rake_spec.rb +0 -92
  94. data/spec/lib/appsignal/integrations/unicorn_spec.rb +0 -48
  95. data/spec/lib/appsignal/ipc_spec.rb +0 -128
  96. data/spec/lib/appsignal/transaction/formatter_spec.rb +0 -247
  97. data/spec/lib/appsignal/zipped_payload_spec.rb +0 -42
@@ -11,48 +11,90 @@ describe Appsignal::Transaction do
11
11
  start_agent
12
12
  end
13
13
 
14
- describe '.create' do
15
- subject { Appsignal::Transaction.create('1', {}) }
14
+ let(:time) { Time.at(fixed_time) }
15
+ let(:transaction) { Appsignal::Transaction.create('1', {}) }
16
16
 
17
- it 'should add the request id to the thread local' do
18
- subject
19
- Thread.current[:appsignal_transaction_id].should == '1'
17
+ before { Timecop.freeze(time) }
18
+ after { Timecop.return }
19
+
20
+ context "class methods" do
21
+ describe '.create' do
22
+ subject { Appsignal::Transaction.create('1', {}) }
23
+
24
+ it 'should add the transaction to thread local' do
25
+ Appsignal::Extension.should_receive(:start_transaction).with('1')
26
+ subject
27
+ Thread.current[:appsignal_transaction].should == subject
28
+ end
29
+
30
+ it "should create a transaction" do
31
+ subject.should be_a Appsignal::Transaction
32
+ subject.request_id.should == '1'
33
+ end
20
34
  end
21
35
 
22
- it "should create a transaction" do
23
- subject.should be_a Appsignal::Transaction
24
- subject.request_id.should == '1'
36
+ describe '.current' do
37
+ let(:transaction) { Appsignal::Transaction.create('1', {}) }
38
+ before { transaction }
39
+ subject { Appsignal::Transaction.current }
40
+
41
+ it 'should return the correct transaction' do
42
+ should == transaction
43
+ end
25
44
  end
26
- end
27
45
 
28
- describe '.current' do
29
- let(:transaction) { Appsignal::Transaction.create('1', {}) }
30
- before { transaction }
31
- subject { Appsignal::Transaction.current }
46
+ describe "complete_current!" do
47
+ before { Thread.current[:appsignal_transaction] = nil }
48
+
49
+ context "with a current transaction" do
50
+ before { Appsignal::Transaction.create('2', {}) }
51
+
52
+ it "should complete the current transaction and set the thread appsignal_transaction to nil" do
53
+ Appsignal::Extension.should_receive(:finish_transaction).with('2')
54
+
55
+ Appsignal::Transaction.complete_current!
32
56
 
33
- it 'should return the correct transaction' do
34
- should eq transaction
57
+ Thread.current[:appsignal_transaction].should be_nil
58
+ end
59
+ end
60
+
61
+ context "without a current transaction" do
62
+ it "should not raise an error" do
63
+ Appsignal::Transaction.complete_current!
64
+ end
65
+ end
35
66
  end
36
67
  end
37
68
 
38
- describe "complete_current!" do
39
- before { Thread.current[:appsignal_transaction_id] = nil }
69
+ describe "#pause!" do
70
+ it "should change the pause flag to true" do
71
+ expect{
72
+ transaction.pause!
73
+ }.to change(transaction, :paused).from(false).to(true)
74
+ end
75
+ end
40
76
 
41
- context "with a current transaction" do
42
- before { Appsignal::Transaction.create('2', {}) }
77
+ describe "#resume!" do
78
+ before { transaction.pause! }
43
79
 
44
- it "should complete the current transaction and reset the thread appsignal_transaction_id" do
45
- Appsignal::Transaction.current.should_receive(:complete!)
80
+ it "should change the pause flag to false" do
81
+ expect{
82
+ transaction.resume!
83
+ }.to change(transaction, :paused).from(true).to(false)
84
+ end
85
+ end
46
86
 
47
- Appsignal::Transaction.complete_current!
87
+ describe "#paused?" do
48
88
 
49
- Thread.current[:appsignal_transaction_id].should be_nil
50
- end
89
+ it "should return the pasue state" do
90
+ expect( transaction.paused? ).to be_false
51
91
  end
52
92
 
53
- context "without a current transaction" do
54
- it "should not raise an error" do
55
- Appsignal::Transaction.complete_current!
93
+ context "when paused" do
94
+ before { transaction.pause! }
95
+
96
+ it "should return the pasue state" do
97
+ expect( transaction.paused? ).to be_true
56
98
  end
57
99
  end
58
100
  end
@@ -68,85 +110,21 @@ describe Appsignal::Transaction do
68
110
  end
69
111
  let(:transaction) { Appsignal::Transaction.create('3', env) }
70
112
 
71
- it "should add the transaction to the list" do
72
- transaction
73
- Appsignal.transactions['3'].should == transaction
113
+ context "initialization" do
114
+ subject { transaction }
115
+
116
+ its(:request_id) { should == '3' }
117
+ its(:root_event_payload) { should be_nil }
118
+ its(:exception) { should be_nil }
119
+ its(:env) { should == env }
120
+ its(:tags) { should == {} }
121
+ its(:queue_start) { should == -1 }
74
122
  end
75
123
 
76
124
  describe '#request' do
77
125
  subject { transaction.request }
78
126
 
79
127
  it { should be_a ::Rack::Request }
80
-
81
- context "without env" do
82
- let(:env) { nil }
83
-
84
- it { should be_nil }
85
- end
86
- end
87
-
88
- describe '#set_process_action_event' do
89
- before { transaction.set_process_action_event(process_action_event) }
90
-
91
- let(:process_action_event) { notification_event }
92
-
93
- it 'should add a process action event' do
94
- transaction.process_action_event.name.should == process_action_event.name
95
- transaction.process_action_event.payload.should == process_action_event.payload
96
- end
97
-
98
- it "should set the action" do
99
- transaction.action.should == 'BlogPostsController#show'
100
- end
101
-
102
- it "should set the kind" do
103
- transaction.kind.should == 'http_request'
104
- end
105
-
106
- it "should call set_http_queue_start" do
107
- transaction.queue_start.should_not be_nil
108
- end
109
-
110
- context "if there is no controller" do
111
- before do
112
- process_action_event.payload[:action] = 'GET /items/:id'
113
- process_action_event.payload.delete(:controller)
114
- transaction.set_process_action_event(process_action_event)
115
- end
116
-
117
- it "should set the action without a #" do
118
- transaction.action.should == 'GET /items/:id'
119
- end
120
- end
121
- end
122
-
123
- describe "set_perform_job_event" do
124
- before { transaction.set_perform_job_event(perform_job_event) }
125
-
126
- let(:payload) { create_background_payload }
127
- let(:perform_job_event) do
128
- notification_event(
129
- :name => 'perform_job.delayed_job',
130
- :payload => payload
131
- )
132
- end
133
-
134
- it 'should add a perform job event' do
135
- transaction.process_action_event.name.should == perform_job_event.name
136
- transaction.process_action_event.payload.should == perform_job_event.payload
137
- end
138
-
139
- it "should set the action" do
140
- transaction.action.should == 'BackgroundJob#perform'
141
- end
142
-
143
- it "should set the kind" do
144
- transaction.kind.should == 'background_job'
145
- end
146
-
147
- it "should set call set_background_queue_start" do
148
- transaction.queue_start.should_not be_nil
149
- end
150
128
  end
151
129
 
152
130
  describe "#set_tags" do
@@ -157,510 +135,291 @@ describe Appsignal::Transaction do
157
135
  end
158
136
  end
159
137
 
160
- describe '#add_event' do
161
- let(:event) { double(:event, :name => 'test') }
162
-
163
- it 'should add an event' do
164
- expect {
165
- transaction.add_event(event)
166
- }.to change(transaction, :events).to([event])
167
- end
168
-
169
- context "when paused" do
170
- before { transaction.pause! }
171
-
172
- it 'should add an event' do
173
- expect {
174
- transaction.add_event(event)
175
- }.to_not change(transaction, :events)
176
- end
177
- end
178
- end
179
-
180
- describe "#pause!" do
181
- it "should change the pause flag to true" do
182
- expect{
183
- transaction.pause!
184
- }.to change(transaction, :paused).from(false).to(true)
185
- end
186
- end
187
-
188
- describe "#resume!" do
189
- before { transaction.pause! }
190
-
191
- it "should change the pause flag to false" do
192
- expect{
193
- transaction.resume!
194
- }.to change(transaction, :paused).from(true).to(false)
195
- end
196
- end
197
-
198
- context "using exceptions" do
199
- let(:exception) do
200
- double(
201
- :exception,
202
- :class => double(:name => 'test'),
203
- :message => 'Broken',
204
- :backtrace => [
205
- File.join(project_fixture_path, 'app/controllers/somethings_controller.rb:10').to_s,
206
- '/user/local/ruby/path.rb:8'
207
- ]
208
- )
209
- end
138
+ describe '#set_root_event' do
139
+ context "for a process_action event" do
140
+ let(:name) { 'process_action.action_controller' }
141
+ let(:payload) { create_payload }
142
+
143
+ it "should set the meta data in the transaction and native" do
144
+ Appsignal::Extension.should_receive(:set_transaction_basedata).with(
145
+ '3',
146
+ 'http_request',
147
+ 'BlogPostsController#show',
148
+ kind_of(Integer)
149
+ )
210
150
 
211
- describe '#add_exception' do
212
- it 'should add an exception', :if => rails_present? do
213
- if Gem::Version.new(Rails.version) >= Gem::Version.new('4.2.0')
214
- expect {
215
- transaction.add_exception(exception)
216
- }.to change(transaction, :exception).to({
217
- :exception => 'test',
218
- :message => 'Broken',
219
- :backtrace => [
220
- 'spec/support/project_fixture/app/controllers/somethings_controller.rb:10',
221
- '/user/local/ruby/path.rb:8'
222
- ]
223
- })
224
- else
225
- expect {
226
- transaction.add_exception(exception)
227
- }.to change(transaction, :exception).to({
228
- :exception => 'test',
229
- :message => 'Broken',
230
- :backtrace => [
231
- 'app/controllers/somethings_controller.rb:10',
232
- '/user/local/ruby/path.rb:8'
233
- ]
234
- })
151
+ metadata = {
152
+ 'path' => '/blog',
153
+ 'request_format' => 'html',
154
+ 'request_method' => 'GET',
155
+ 'status' => '200'
156
+ }
157
+ metadata.each do |key, value|
158
+ transaction.should_receive(:set_metadata).with(key, value).once
235
159
  end
236
- end
237
160
 
238
- it 'should add an exception', :if => !rails_present? do
239
- expect {
240
- transaction.add_exception(exception)
241
- }.to change(transaction, :exception).to({
242
- :exception => 'test',
243
- :message => 'Broken',
244
- :backtrace => [
245
- File.join(project_fixture_path, 'app/controllers/somethings_controller.rb:10'),
246
- '/user/local/ruby/path.rb:8'
247
- ]
248
- })
249
- end
250
- end
251
-
252
- describe "#exception?" do
253
- subject { transaction.exception? }
254
-
255
- context "without an exception" do
256
- it { should be_false }
257
- end
161
+ transaction.set_root_event(name, payload)
258
162
 
259
- context "without an exception" do
260
- before { transaction.add_exception(exception) }
261
-
262
- it { should be_true }
163
+ transaction.root_event_payload.should == payload
164
+ transaction.action.should == 'BlogPostsController#show'
165
+ transaction.kind.should == 'http_request'
166
+ transaction.queue_start.should be_kind_of(Integer)
263
167
  end
264
168
  end
265
- end
266
169
 
267
- describe '#slow_request?' do
268
- let(:start) { Time.now }
269
- subject { transaction.slow_request? }
170
+ context "for a perform_job event" do
171
+ let(:name) { 'perform_job.delayed_job' }
172
+ let(:payload) { create_background_payload }
270
173
 
271
- context "duration" do
272
- before do
273
- transaction.set_process_action_event(
274
- notification_event(:start => start, :ending => start + duration)
174
+ it "should set the meta data in the transaction and native" do
175
+ Appsignal::Extension.should_receive(:set_transaction_basedata).with(
176
+ '3',
177
+ 'background_job',
178
+ 'BackgroundJob#perform',
179
+ kind_of(Integer)
275
180
  )
276
- end
277
-
278
- context "when it reasonably fast" do
279
- let(:duration) { 0.199 } # in seconds
280
-
281
- it { should be_false }
282
- end
283
-
284
- context "when the request took too long" do
285
- let(:duration) { 0.201 } # in seconds
286
-
287
- it { should be_true }
288
- end
289
- end
290
181
 
291
- context "when process action event is empty" do
292
- before { transaction.set_process_action_event(nil) }
182
+ transaction.set_root_event(name, payload)
293
183
 
294
- it { should be_false }
295
- end
296
-
297
- context "when process action event does not have a payload" do
298
- let(:event) { notification_event }
299
- before do
300
- event.instance_variable_set(:@payload, nil)
301
- transaction.set_process_action_event(event)
184
+ transaction.root_event_payload.should == payload
185
+ transaction.action.should == 'BackgroundJob#perform'
186
+ transaction.kind.should == 'background_job'
187
+ transaction.queue_start.should be_kind_of(Integer)
302
188
  end
303
-
304
- it { should be_false }
305
189
  end
306
190
  end
307
191
 
308
- describe "#slower?" do
309
- context "comparing to a slower transaction" do
310
- subject { regular_transaction.slower?(slow_transaction) }
192
+ describe "#set_metadata" do
193
+ it "should set the metdata in native" do
194
+ Appsignal::Extension.should_receive(:set_transaction_metadata).with(
195
+ '3',
196
+ 'request_method',
197
+ 'GET'
198
+ ).once
311
199
 
312
- it { should be_false }
200
+ transaction.set_metadata('request_method', 'GET')
313
201
  end
314
202
 
315
- context "comparing to a faster transaction" do
316
- subject { slow_transaction.slower?(regular_transaction) }
203
+ it "should set the metdata in native when value is nil" do
204
+ Appsignal::Extension.should_not_receive(:set_transaction_metadata)
317
205
 
318
- it { should be_true }
206
+ transaction.set_metadata('request_method', nil)
319
207
  end
320
208
  end
321
209
 
322
- describe "clear_events!" do
323
- let(:transaction) { slow_transaction }
210
+ describe '#set_error' do
211
+ let(:error) { double(:error, :message => 'test message', :backtrace => ['line 1']) }
324
212
 
325
- it "should remove events from the transaction" do
326
- expect {
327
- transaction.clear_events!
328
- }.to change(transaction.events, :length).from(1).to(0)
213
+ it "should also respond to add_exception for backwords compatibility" do
214
+ transaction.should respond_to(:add_exception)
329
215
  end
330
- end
331
-
332
- describe "#truncate!" do
333
- subject { slow_transaction }
334
- before do
335
- subject.set_tags('a' => 'b')
336
- subject.sanitized_environment[:foo] = 'bar'
337
- subject.sanitized_session_data[:foo] = 'bar'
338
- subject.sanitized_params[:foo] = 'bar'
339
- end
340
-
341
- it "should clear the process action payload and events" do
342
- subject.truncate!
343
-
344
- subject.process_action_event.payload.should be_empty
345
- subject.events.should be_empty
346
- subject.tags.should be_empty
347
-
348
- subject.sanitized_environment.should be_empty
349
- subject.sanitized_session_data.should be_empty
350
- subject.sanitized_params.should be_empty
351
-
352
- subject.truncated?.should be_true
353
- end
354
-
355
- it "should not truncate twice" do
356
- subject.process_action_event.should_receive(:truncate!).once
357
- subject.events.should_receive(:clear).once
358
-
359
- subject.truncate!
360
- subject.truncate!
361
- end
362
- end
363
-
364
- describe "#convert_values_to_primitives!" do
365
- let(:transaction) { slow_transaction }
366
- let(:action_event) { transaction.process_action_event }
367
- let(:event) { transaction.events.first }
368
- let(:weird_class) { Class.new }
369
- let(:smash) { Smash.new.merge!(:foo => 'bar') }
370
-
371
- context "with values that need to be converted" do
372
- context "process action event payload" do
373
- subject { action_event.payload }
374
- before do
375
- action_event.payload.clear
376
- action_event.payload.merge!(
377
- :model => {:with => [:weird, weird_class]},
378
- )
379
- transaction.convert_values_to_primitives!
380
- end
381
-
382
- it "should convert all payloads to primitives" do
383
- should == {
384
- :model => {:with => [:weird, '#<Class>']},
385
- }
386
- end
387
- end
388
-
389
- context "payload of events" do
390
- subject { event.payload }
391
- before do
392
- event.payload.clear
393
- event.payload.merge!(
394
- :weird => weird_class,
395
- :smash => smash
396
- )
397
- transaction.convert_values_to_primitives!
398
- end
399
-
400
- its([:weird]) { should be_a(String) }
401
- its([:weird]) { should eql("#<Class>") }
402
- its([:smash]) { should == {:foo => 'bar'} }
403
- end
404
- end
405
-
406
- context "without values that need to be converted" do
407
- subject { transaction.convert_values_to_primitives! }
408
-
409
- it "doesn't change the action event payload" do
410
- before = action_event.payload.dup
411
- subject
412
- action_event.payload.should == before
413
- end
414
-
415
- it " doesn't change the event payloads" do
416
- before = event.payload.dup
417
- subject
418
- event.payload.should == before
419
- end
420
-
421
- it "should not covert to primitives twice" do
422
- transaction.convert_values_to_primitives!
423
- transaction.have_values_been_converted_to_primitives?.should be_true
424
216
 
425
- Appsignal::ParamsSanitizer.should_not_receive(:sanitize!)
426
- transaction.convert_values_to_primitives!
427
- end
428
- end
429
- end
430
-
431
- describe "#type" do
432
- context "with a regular transaction" do
433
- subject { regular_transaction.type }
434
-
435
- it { should == :regular_request }
436
- end
437
-
438
- context "with a slow transaction" do
439
- subject { slow_transaction.type }
440
-
441
- it { should == :slow_request }
442
- end
443
-
444
- context "with an exception transaction" do
445
- subject { transaction_with_exception.type }
446
-
447
- it { should == :exception }
448
- end
449
- end
450
-
451
- describe '#to_hash' do
452
- subject { transaction.to_hash }
453
-
454
- it { should be_instance_of Hash }
455
- end
456
-
457
- describe '#complete!' do
458
- let(:event) { double(:event) }
459
- let(:exception) do
460
- double(
461
- :exception,
462
- :class => double(:name => 'test'),
463
- :message => 'Broken',
464
- :backtrace => [
465
- 'app/controllers/somethings_controller.rb:10',
466
- '/user/local/ruby/path.rb:8'
467
- ]
217
+ it "should set an error and it's data in native" do
218
+ Appsignal::Extension.should_receive(:set_transaction_error).with(
219
+ '3',
220
+ 'RSpec::Mocks::Mock',
221
+ 'test message'
468
222
  )
469
- end
470
- before do
471
- Appsignal::IPC.stub(:current => nil)
472
- transaction.set_process_action_event(notification_event)
473
- end
474
-
475
- it 'should remove transaction from the list' do
476
- expect { transaction.complete! }.
477
- to change(Appsignal.transactions, :length).by(-1)
478
- end
479
-
480
- context 'enqueueing' do
481
- context 'sanity check' do
482
- specify { Appsignal.should respond_to(:enqueue) }
483
- end
484
-
485
- context 'without events and without exception' do
486
- it 'should add transaction to the agent' do
487
- Appsignal.should_receive(:enqueue).with(transaction)
488
- end
489
- end
490
-
491
- context 'with events' do
492
- before { transaction.add_event(event) }
493
-
494
- it 'should add transaction to the agent' do
495
- Appsignal.should_receive(:enqueue).with(transaction)
496
- end
223
+ Appsignal::Extension.should_receive(:set_transaction_error_data).with(
224
+ '3',
225
+ 'environment',
226
+ "{\"SERVER_NAME\":\"localhost\",\"HTTP_X_REQUEST_START\":\"1000000\",\"HTTP_USER_AGENT\":\"IE6\"}"
227
+ ).once
228
+ Appsignal::Extension.should_receive(:set_transaction_error_data).with(
229
+ '3',
230
+ 'session_data',
231
+ "{}"
232
+ ).once
233
+ Appsignal::Extension.should_receive(:set_transaction_error_data).with(
234
+ '3',
235
+ 'backtrace',
236
+ "[\"line 1\"]"
237
+ ).once
238
+ Appsignal::Extension.should_receive(:set_transaction_error_data).with(
239
+ '3',
240
+ 'tags',
241
+ "{}"
242
+ ).once
243
+
244
+ transaction.set_error(error)
245
+ end
246
+
247
+ context "with root event payload" do
248
+ before do
249
+ transaction.set_root_event('process_action.action_controller', create_payload)
497
250
  end
498
251
 
499
- context 'with exception' do
500
- before { transaction.add_exception(exception) }
252
+ it "should also set params" do
253
+ Appsignal::Extension.should_receive(:set_transaction_error_data).with(
254
+ '3',
255
+ 'params',
256
+ '{"controller":"blog_posts","action":"show","id":"1"}'
257
+ ).once
258
+ Appsignal::Extension.should_receive(:set_transaction_error_data).with(
259
+ '3',
260
+ kind_of(String),
261
+ kind_of(String)
262
+ ).exactly(4).times
501
263
 
502
- it 'should add transaction to the agent' do
503
- Appsignal.should_receive(:enqueue).with(transaction)
504
- end
264
+ transaction.set_error(error)
505
265
  end
506
-
507
- after { transaction.complete! }
508
266
  end
509
267
 
510
- context 'when using IPC' do
268
+ context "with a non-json convertable type" do
511
269
  before do
512
- Appsignal::IPC::Client.start
513
- transaction.stub(:convert_values_to_primitives! => true)
514
- end
515
- after do
516
- Appsignal::IPC::Client.stop
270
+ transaction.stub(:sanitized_params => 'a string')
517
271
  end
518
272
 
519
- it "should convert to primitves and send itself trough the pipe" do
520
- transaction.should_receive(:convert_values_to_primitives!)
521
- Appsignal::IPC::Client.should_receive(:enqueue).with(transaction)
522
- end
273
+ it "should skip the field" do
274
+ Appsignal::Extension.should_not_receive(:set_transaction_error_data).with(
275
+ '3',
276
+ 'params',
277
+ kind_of(String)
278
+ )
279
+ Appsignal::Extension.should_receive(:set_transaction_error_data).with(
280
+ '3',
281
+ kind_of(String),
282
+ kind_of(String)
283
+ ).exactly(4).times
523
284
 
524
- after { transaction.complete! }
285
+ transaction.set_error(error)
286
+ end
525
287
  end
526
288
  end
527
289
 
290
+ # protected
291
+
528
292
  describe "#set_background_queue_start" do
529
293
  before do
530
- transaction.stub(:process_action_event =>
531
- notification_event(
532
- :name => 'perform_job.delayed_job',
533
- :payload => payload
534
- )
535
- )
536
- transaction.set_background_queue_start
294
+ transaction.stub(:root_event_payload => payload)
295
+ transaction.send(:set_background_queue_start)
537
296
  end
297
+
538
298
  subject { transaction.queue_start }
539
299
 
540
300
  context "when queue start is nil" do
541
301
  let(:payload) { create_background_payload(:queue_start => nil) }
542
302
 
543
- it { should be_nil }
303
+ it { should == -1 }
544
304
  end
545
305
 
546
306
  context "when queue start is set" do
547
307
  let(:payload) { create_background_payload }
548
308
 
549
- it { should == 1389783590.0 }
309
+ it { should == 1389783590000 }
550
310
  end
551
311
  end
552
312
 
553
- describe "#set_http_queue_start" do
554
- let(:slightly_earlier_time) { fixed_time - 0.4 }
555
- let(:slightly_earlier_time_value) { (slightly_earlier_time * factor).to_i }
556
- before { transaction.set_http_queue_start }
557
- subject { transaction.queue_start }
313
+ describe "#sanitized_params" do
314
+ subject { transaction.send(:sanitized_params) }
558
315
 
559
- shared_examples "http queue start" do
560
- context "without env" do
561
- let(:env) { nil }
316
+ context "without a root event payload" do
317
+ it { should be_nil }
318
+ end
562
319
 
563
- it { should be_nil }
564
- end
320
+ context "with a root event payload" do
321
+ before { transaction.stub(:root_event_payload => create_payload) }
565
322
 
566
- context "with no relevant header set" do
567
- let(:env) { {} }
323
+ it "should call the params sanitizer" do
324
+ Appsignal::ParamsSanitizer.should_receive(:sanitize).with(kind_of(Hash)).and_return({:id => 1})
568
325
 
569
- it { should be_nil }
326
+ subject.should == {:id => 1}
570
327
  end
328
+ end
329
+ end
571
330
 
572
- context "with the HTTP_X_REQUEST_START header set" do
573
- let(:env) { {'HTTP_X_REQUEST_START' => "t=#{slightly_earlier_time_value}"} }
574
-
575
- it { should == 1389783599.6 }
576
-
577
- context "with unparsable content" do
578
- let(:env) { {'HTTP_X_REQUEST_START' => 'something'} }
579
-
580
- it { should be_nil }
581
- end
331
+ describe "#sanitized_environment" do
332
+ let(:whitelisted_keys) { Appsignal::Transaction::ENV_METHODS }
333
+ let(:transaction) { Appsignal::Transaction.create('1', env) }
582
334
 
583
- context "with some cruft" do
584
- let(:env) { {'HTTP_X_REQUEST_START' => "t=#{slightly_earlier_time_value}aaaa"} }
335
+ subject { transaction.send(:sanitized_environment) }
585
336
 
586
- it { should == 1389783599.6 }
587
- end
337
+ context "when env is nil" do
338
+ let(:env) { nil }
588
339
 
589
- context "with the alternate HTTP_X_QUEUE_START header set" do
590
- let(:env) { {'HTTP_X_QUEUE_START' => "t=#{slightly_earlier_time_value}"} }
340
+ it { should be_nil }
341
+ end
591
342
 
592
- it { should == 1389783599.6 }
343
+ context "when env is present" do
344
+ let(:env) do
345
+ Hash.new.tap do |hash|
346
+ whitelisted_keys.each { |o| hash[o] = 1 } # use all whitelisted keys
347
+ hash[whitelisted_keys] = nil # don't add if nil
348
+ hash[:not_whitelisted] = 'I will be sanitized'
593
349
  end
594
350
  end
595
- end
596
-
597
- context "time in miliseconds" do
598
- let(:factor) { 1_000 }
599
351
 
600
- it_should_behave_like "http queue start"
601
- end
602
-
603
- context "time in microseconds" do
604
- let(:factor) { 1_000_000 }
605
-
606
- it_should_behave_like "http queue start"
352
+ its(:keys) { should =~ whitelisted_keys[0, whitelisted_keys.length] }
607
353
  end
608
354
  end
609
355
 
610
- # protected
611
-
612
- describe '#add_sanitized_context!' do
613
- subject { transaction.send(:add_sanitized_context!) }
356
+ describe '#sanitized_session_data' do
357
+ subject { transaction.send(:sanitized_session_data) }
614
358
 
615
- context "for a http request" do
616
- before { transaction.stub(:kind => 'http_request') }
359
+ context "when env is nil" do
360
+ let(:transaction) { Appsignal::Transaction.create('1', nil) }
617
361
 
618
- it "should call sanitize_environment!, sanitize_session_data! and sanitize_tags!" do
619
- transaction.should_receive(:sanitize_environment!)
620
- transaction.should_receive(:sanitize_session_data!)
621
- transaction.should_receive(:sanitize_tags!)
622
- transaction.should_receive(:sanitize_params!)
623
- subject
624
- end
362
+ it { should be_nil }
625
363
  end
626
364
 
627
- context "for a non-web request" do
628
- before { transaction.stub(:kind => 'background_job') }
365
+ context "when env is empty" do
366
+ let(:transaction) { Appsignal::Transaction.create('1', {}) }
629
367
 
630
- it "should not call sanitize_session_data!" do
631
- transaction.should_receive(:sanitize_environment!)
632
- transaction.should_not_receive(:sanitize_session_data!)
633
- transaction.should_receive(:sanitize_tags!)
634
- transaction.should_receive(:sanitize_params!)
635
- subject
636
- end
368
+ it { should == {} }
637
369
  end
638
370
 
639
- specify { expect { subject }.to change(transaction, :env).to(nil) }
640
- end
641
-
642
- describe '#sanitize_environment!' do
643
- let(:whitelisted_keys) { Appsignal::Transaction::ENV_METHODS }
644
- let(:transaction) { Appsignal::Transaction.create('1', env) }
645
- let(:env) do
646
- Hash.new.tap do |hash|
647
- whitelisted_keys.each { |o| hash[o] = 1 } # use all whitelisted keys
648
- hash[:not_whitelisted] = 'I will be sanitized'
371
+ context "when there is a session" do
372
+ let(:transaction) { Appsignal::Transaction.create('1', {}) }
373
+ before do
374
+ transaction.should respond_to(:request)
375
+ transaction.stub_chain(:request, :session => {:foo => :bar})
376
+ transaction.stub_chain(:request, :fullpath => :bar)
377
+ end
378
+
379
+ it "passes the session data into the params sanitizer" do
380
+ Appsignal::ParamsSanitizer.should_receive(:sanitize).with({:foo => :bar}).
381
+ and_return(:sanitized_foo)
382
+ subject.should == :sanitized_foo
383
+ end
384
+
385
+ if defined? ActionDispatch::Request::Session
386
+ context "with ActionDispatch::Request::Session" do
387
+ before do
388
+ transaction.should respond_to(:request)
389
+ transaction.stub_chain(:request, :session => action_dispatch_session)
390
+ transaction.stub_chain(:request, :fullpath => :bar)
391
+ end
392
+
393
+ it "should return an session hash" do
394
+ Appsignal::ParamsSanitizer.should_receive(:sanitize).with({'foo' => :bar}).
395
+ and_return(:sanitized_foo)
396
+ subject
397
+ end
398
+
399
+ def action_dispatch_session
400
+ store = Class.new {
401
+ def load_session(env); [1, {:foo => :bar}]; end
402
+ def session_exists?(env); true; end
403
+ }.new
404
+ ActionDispatch::Request::Session.create(store, {}, {})
405
+ end
406
+ end
649
407
  end
650
408
  end
651
- subject { transaction.sanitized_environment }
652
- before { transaction.send(:sanitize_environment!) }
653
409
 
654
- its(:keys) { should =~ whitelisted_keys }
655
-
656
- context "when env is nil" do
657
- let(:env) { nil }
410
+ context "when skipping session data" do
411
+ before do
412
+ Appsignal.config = {:skip_session_data => true}
413
+ end
658
414
 
659
- it { should be_empty }
415
+ it "does not pass the session data into the params sanitizer" do
416
+ Appsignal::ParamsSanitizer.should_not_receive(:sanitize)
417
+ subject.should be_nil
418
+ end
660
419
  end
661
420
  end
662
421
 
663
- describe '#sanitize_tags!' do
422
+ describe '#sanitized_tags' do
664
423
  let(:transaction) { Appsignal::Transaction.create('1', {}) }
665
424
  before do
666
425
  transaction.set_tags(
@@ -676,9 +435,8 @@ describe Appsignal::Transaction do
676
435
  SecureRandom.urlsafe_base64(101) => 'to_long_key'
677
436
  }
678
437
  )
679
- transaction.send(:sanitize_tags!)
680
438
  end
681
- subject { transaction.tags.keys }
439
+ subject { transaction.send(:sanitized_tags).keys }
682
440
 
683
441
  it "should only return whitelisted data" do
684
442
  should =~ [
@@ -690,102 +448,12 @@ describe Appsignal::Transaction do
690
448
  end
691
449
  end
692
450
 
693
- describe '#sanitize_session_data!' do
694
- subject { transaction.send(:sanitize_session_data!) }
695
- before do
696
- transaction.should respond_to(:request)
697
- transaction.stub_chain(:request, :session => {:foo => :bar})
698
- transaction.stub_chain(:request, :fullpath => :bar)
699
- end
700
-
701
- it "passes the session data into the params sanitizer" do
702
- Appsignal::ParamsSanitizer.should_receive(:sanitize).with({:foo => :bar}).
703
- and_return(:sanitized_foo)
704
- subject
705
- transaction.sanitized_session_data.should == :sanitized_foo
706
- end
707
-
708
- it "sets the fullpath of the request" do
709
- expect { subject }.to change(transaction, :fullpath).to(:bar)
710
- end
711
-
712
- if defined? ActionDispatch::Request::Session
713
- context "with ActionDispatch::Request::Session" do
714
- before do
715
- transaction.should respond_to(:request)
716
- transaction.stub_chain(:request, :session => action_dispatch_session)
717
- transaction.stub_chain(:request, :fullpath => :bar)
718
- end
719
-
720
- it "should return an session hash" do
721
- Appsignal::ParamsSanitizer.should_receive(:sanitize).with({'foo' => :bar}).
722
- and_return(:sanitized_foo)
723
- subject
724
- end
725
-
726
- def action_dispatch_session
727
- store = Class.new {
728
- def load_session(env); [1, {:foo => :bar}]; end
729
- def session_exists?(env); true; end
730
- }.new
731
- ActionDispatch::Request::Session.create(store, {}, {})
732
- end
733
- end
734
- end
735
-
736
- context "when skipping session data" do
737
- before do
738
- Appsignal.config = {:skip_session_data => true}
739
- end
740
-
741
- it "does not pass the session data into the params sanitizer" do
742
- Appsignal::ParamsSanitizer.should_not_receive(:sanitize)
743
- subject
744
- transaction.sanitized_session_data.should == {}
745
- end
746
- end
747
-
748
- context "without a request" do
749
- before do
750
- transaction.stub(:request => nil)
751
- end
752
-
753
- it "does not pass the session data into the params sanitizer" do
754
- Appsignal::ParamsSanitizer.should_not_receive(:sanitize)
755
- subject
756
- transaction.sanitized_session_data.should == {}
757
- end
758
- end
759
- end
760
-
761
- describe '#sanitize_params!' do
762
- let(:params) { {:foo => 'bar'} }
763
- let(:transaction) do
764
- Appsignal::Transaction.create('1', {}, :params => params)
765
- end
766
- before { Appsignal.config = {:send_params => true} }
767
- subject { transaction.sanitized_params }
768
-
769
- it "should call the params sanitizer and set sanitized params" do
770
- Appsignal::ParamsSanitizer.should_receive(:sanitize)
771
- .with(params)
772
- .and_return({'foo' => 'bar'})
451
+ describe "#cleaned_backtrace" do
452
+ subject { transaction.send(:cleaned_backtrace, ['line 1']) }
773
453
 
774
- transaction.send(:sanitize_params!)
454
+ it { should == ['line 1'] }
775
455
 
776
- should == {'foo' => 'bar'}
777
- end
778
-
779
- context "when skipping session data" do
780
- before { Appsignal.config = {:send_params => false} }
781
-
782
- it "should not pass data to the params sanitizer" do
783
- Appsignal::ParamsSanitizer.should_not_receive(:sanitize)
784
- transaction.send(:sanitize_params!)
785
-
786
- should == {}
787
- end
788
- end
456
+ pending "calls Rails backtrace cleaner if Rails is present"
789
457
  end
790
458
  end
791
459
  end