appsignal 0.12.beta.31 → 0.12.beta.32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/Rakefile +1 -0
  4. data/benchmark.rake +20 -20
  5. data/ext/appsignal_extension.c +31 -23
  6. data/gemfiles/padrino.gemfile +7 -0
  7. data/lib/appsignal.rb +50 -27
  8. data/lib/appsignal/capistrano.rb +2 -1
  9. data/lib/appsignal/config.rb +94 -39
  10. data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +12 -17
  11. data/lib/appsignal/integrations/padrino.rb +65 -0
  12. data/lib/appsignal/integrations/rails.rb +4 -2
  13. data/lib/appsignal/integrations/rake.rb +30 -0
  14. data/lib/appsignal/integrations/sidekiq.rb +2 -2
  15. data/lib/appsignal/integrations/sinatra.rb +0 -1
  16. data/lib/appsignal/js_exception_transaction.rb +4 -9
  17. data/lib/appsignal/params_sanitizer.rb +8 -5
  18. data/lib/appsignal/rack/rails_instrumentation.rb +41 -0
  19. data/lib/appsignal/rack/sinatra_instrumentation.rb +31 -24
  20. data/lib/appsignal/subscriber.rb +2 -9
  21. data/lib/appsignal/transaction.rb +86 -75
  22. data/lib/appsignal/transmitter.rb +30 -3
  23. data/lib/appsignal/version.rb +2 -2
  24. data/spec/lib/appsignal/cli_spec.rb +1 -1
  25. data/spec/lib/appsignal/config_spec.rb +38 -131
  26. data/spec/lib/appsignal/event_formatter/active_record/sql_formatter_spec.rb +27 -29
  27. data/spec/lib/appsignal/extension_spec.rb +11 -29
  28. data/spec/lib/appsignal/integrations/padrino_spec.rb +191 -0
  29. data/spec/lib/appsignal/integrations/rails_spec.rb +3 -4
  30. data/spec/lib/appsignal/integrations/rake_spec.rb +78 -0
  31. data/spec/lib/appsignal/integrations/resque_spec.rb +2 -2
  32. data/spec/lib/appsignal/integrations/sequel_spec.rb +2 -3
  33. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +22 -5
  34. data/spec/lib/appsignal/integrations/sinatra_spec.rb +0 -6
  35. data/spec/lib/appsignal/js_exception_transaction_spec.rb +4 -6
  36. data/spec/lib/appsignal/params_sanitizer_spec.rb +27 -11
  37. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +79 -0
  38. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +71 -71
  39. data/spec/lib/appsignal/subscriber_spec.rb +3 -37
  40. data/spec/lib/appsignal/transaction_spec.rb +290 -155
  41. data/spec/lib/appsignal/transmitter_spec.rb +10 -0
  42. data/spec/lib/appsignal_spec.rb +80 -47
  43. data/spec/spec_helper.rb +21 -2
  44. data/spec/support/helpers/env_helpers.rb +31 -0
  45. data/spec/support/helpers/notification_helpers.rb +1 -30
  46. data/spec/support/helpers/transaction_helpers.rb +7 -7
  47. data/spec/support/project_fixture/config/appsignal.yml +2 -0
  48. metadata +14 -8
  49. data/lib/appsignal/rack/instrumentation.rb +0 -32
  50. data/lib/appsignal/rack/listener.rb +0 -32
  51. data/spec/lib/appsignal/rack/instrumentation_spec.rb +0 -72
  52. data/spec/lib/appsignal/rack/listener_spec.rb +0 -104
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+ if defined?(::Rails)
4
+ describe Appsignal::Rack::RailsInstrumentation do
5
+ before :all do
6
+ start_agent
7
+ end
8
+
9
+ let(:app) { double(:call => true) }
10
+ let(:env) { http_request_env_with_data('action_dispatch.request_id' => '1') }
11
+ let(:middleware) { Appsignal::Rack::RailsInstrumentation.new(app, {}) }
12
+
13
+ describe "#call" do
14
+ before do
15
+ middleware.stub(:raw_payload => {})
16
+ end
17
+
18
+ context "when appsignal is active" do
19
+ before { Appsignal.stub(:active? => true) }
20
+
21
+ it "should call with monitoring" do
22
+ expect( middleware ).to receive(:call_with_appsignal_monitoring).with(env)
23
+ end
24
+ end
25
+
26
+ context "when appsignal is not active" do
27
+ before { Appsignal.stub(:active? => false) }
28
+
29
+ it "should not call with monitoring" do
30
+ expect( middleware ).to_not receive(:call_with_appsignal_monitoring)
31
+ end
32
+
33
+ it "should call the app" do
34
+ expect( app ).to receive(:call).with(env)
35
+ end
36
+ end
37
+
38
+ after { middleware.call(env) }
39
+ end
40
+
41
+ describe "#call_with_appsignal_monitoring" do
42
+ it "should create a transaction" do
43
+ Appsignal::Transaction.should_receive(:create).with(
44
+ '1',
45
+ Appsignal::Transaction::HTTP_REQUEST,
46
+ kind_of(ActionDispatch::Request)
47
+ ).and_return(double(:set_http_or_background_action => nil, :set_http_or_background_queue_start => nil, :set_metadata => nil))
48
+ end
49
+
50
+ it "should call the app" do
51
+ app.should_receive(:call).with(env)
52
+ end
53
+
54
+ context "with an error" do
55
+ let(:error) { VerySpecificError.new }
56
+ let(:app) do
57
+ double.tap do |d|
58
+ d.stub(:call).and_raise(error)
59
+ end
60
+ end
61
+
62
+ it "should set the error" do
63
+ Appsignal::Transaction.any_instance.should_receive(:set_error).with(error)
64
+ end
65
+ end
66
+
67
+ it "should set metadata" do
68
+ Appsignal::Transaction.any_instance.should_receive(:set_metadata).twice
69
+ end
70
+
71
+ it "should set the action and queue start" do
72
+ Appsignal::Transaction.any_instance.should_receive(:set_http_or_background_action)
73
+ Appsignal::Transaction.any_instance.should_receive(:set_http_or_background_queue_start)
74
+ end
75
+
76
+ after { middleware.call(env) rescue VerySpecificError }
77
+ end
78
+ end
79
+ end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  begin
4
4
  require 'sinatra'
5
+ require 'appsignal/integrations/sinatra'
5
6
  rescue LoadError
6
7
  end
7
8
 
@@ -9,102 +10,101 @@ if defined?(::Sinatra)
9
10
  describe Appsignal::Rack::SinatraInstrumentation do
10
11
  before :all do
11
12
  start_agent
12
- @events = []
13
- @subscriber = ActiveSupport::Notifications.subscribe do |*args|
14
- @events << ActiveSupport::Notifications::Event.new(*args)
15
- end
16
- end
17
- after :all do
18
- ActiveSupport::Notifications.unsubscribe(@subscriber)
19
13
  end
20
14
 
21
15
  let(:app) { double(:call => true) }
22
- let(:env) { {} }
23
- let(:middleware) { Appsignal::Rack::SinatraInstrumentation.new(app, {}) }
16
+ let(:env) { {'sinatra.route' => 'GET /', :path => '/', :method => 'GET'} }
17
+ let(:options) { {} }
18
+ let(:middleware) { Appsignal::Rack::SinatraInstrumentation.new(app, options) }
24
19
 
25
20
  describe "#call" do
26
21
  before do
27
22
  middleware.stub(:raw_payload => {})
28
- env['sinatra.route'] = 'GET /'
29
23
  end
30
24
 
31
- it "should instrument the call" do
32
- app.should_receive(:call).with(env)
25
+ context "when appsignal is active" do
26
+ before { Appsignal.stub(:active? => true) }
27
+
28
+ it "should call with monitoring" do
29
+ expect( middleware ).to receive(:call_with_appsignal_monitoring).with(env)
30
+ end
31
+ end
33
32
 
34
- middleware.call(env)
33
+ context "when appsignal is not active" do
34
+ before { Appsignal.stub(:active? => false) }
35
35
 
36
- process_action_event = @events.last
37
- process_action_event.name.should == 'process_action.sinatra'
38
- process_action_event.payload[:action].should == 'GET /'
36
+ it "should not call with monitoring" do
37
+ expect( middleware ).to_not receive(:call_with_appsignal_monitoring)
38
+ end
39
+
40
+ it "should call the stack" do
41
+ expect( app ).to receive(:call).with(env)
42
+ end
39
43
  end
40
44
 
41
- it "should still set the action if there was an exception" do
42
- app.should_receive(:call).with(env).and_raise('the roof')
45
+ after { middleware.call(env) }
46
+ end
43
47
 
44
- lambda {
45
- middleware.call(env)
46
- }.should raise_error
48
+ describe "#call_with_appsignal_monitoring" do
49
+ it "should create a transaction" do
50
+ Appsignal::Transaction.should_receive(:create).with(
51
+ kind_of(String),
52
+ Appsignal::Transaction::HTTP_REQUEST,
53
+ kind_of(Sinatra::Request)
54
+ ).and_return(double(:set_action => nil, :set_http_or_background_queue_start => nil, :set_metadata => nil))
55
+ end
47
56
 
48
- process_action_event = @events.last
49
- process_action_event.name.should == 'process_action.sinatra'
50
- process_action_event.payload[:action].should == 'GET /'
57
+ it "should call the app" do
58
+ app.should_receive(:call).with(env)
51
59
  end
52
60
 
53
- it "should add exceptions stored in env under sinatra.error" do
54
- exception = RuntimeError.new('Raise the roof')
55
- env['sinatra.error'] = exception
61
+ context "with an error" do
62
+ let(:error) { VerySpecificError.new }
63
+ let(:app) do
64
+ double.tap do |d|
65
+ d.stub(:call).and_raise(error)
66
+ end
67
+ end
68
+
69
+ it "should set the error" do
70
+ Appsignal::Transaction.any_instance.should_receive(:set_error).with(error)
71
+ end
72
+ end
56
73
 
57
- transaction = double
58
- transaction.stub(:set_process_action_event)
59
- transaction.stub(:add_event)
60
- transaction.should_receive(:add_exception).with(exception)
61
- Appsignal::Transaction.stub(:current => transaction)
74
+ context "with an error in sinatra.error" do
75
+ let(:error) { VerySpecificError.new }
76
+ let(:env) { {'sinatra.error' => error} }
62
77
 
63
- middleware.call(env)
78
+ it "should set the error" do
79
+ Appsignal::Transaction.any_instance.should_receive(:set_error).with(error)
80
+ end
64
81
  end
65
- end
66
82
 
67
- describe "raw_payload" do
68
- let(:env) do
69
- {
70
- 'rack.input' => StringIO.new,
71
- 'REQUEST_METHOD' => 'GET',
72
- 'PATH_INFO' => '/homepage',
73
- 'QUERY_STRING' => 'param=something'
74
- }
83
+ it "should set the action" do
84
+ Appsignal::Transaction.any_instance.should_receive(:set_action).with('GET /')
85
+ end
86
+
87
+ it "should set metadata" do
88
+ Appsignal::Transaction.any_instance.should_receive(:set_metadata).twice
75
89
  end
76
- subject { middleware.raw_payload(env) }
77
-
78
- it { should == {
79
- :params => {'param' => 'something'},
80
- :session => {},
81
- :method => 'GET',
82
- :path => '/homepage'
83
- } }
84
- end
85
90
 
86
- describe "use a custom request class and parameters method" do
87
- let(:request_class) do
88
- double(
89
- new: double(
90
- request_method: 'POST',
91
- path: '/somewhere',
92
- filtered_params: {'param' => 'changed_something'}
93
- )
94
- )
91
+ it "should set the queue start" do
92
+ Appsignal::Transaction.any_instance.should_receive(:set_http_or_background_queue_start)
95
93
  end
96
- let(:options) do
97
- { request_class: request_class, params_method: :filtered_params }
94
+
95
+ context "with overridden request class and params method" do
96
+ let(:options) { {:request_class => ::Rack::Request, :params_method => :filtered_params} }
97
+
98
+ it "should use the overridden request class and params method" do
99
+ request = ::Rack::Request.new(env)
100
+ ::Rack::Request.should_receive(:new).
101
+ with(env.merge(:params_method => :filtered_params)).
102
+ at_least(:once).
103
+ and_return(request)
104
+ end
98
105
  end
99
- let(:middleware) { Appsignal::Rack::Instrumentation.new(app, options) }
100
- subject { middleware.raw_payload(env) }
101
-
102
- it { should == {
103
- :action => 'POST:/somewhere',
104
- :params => {'param' => 'changed_something'},
105
- :method => 'POST',
106
- :path => '/somewhere'
107
- } }
106
+
107
+ after { middleware.call(env) rescue VerySpecificError }
108
108
  end
109
109
  end
110
110
  end
@@ -76,7 +76,7 @@ describe Appsignal::Subscriber do
76
76
  let(:transaction) { Appsignal::Transaction.current }
77
77
 
78
78
  before do
79
- Appsignal::Transaction.create('request-id', {})
79
+ Appsignal::Transaction.create('request-id', Appsignal::Transaction::HTTP_REQUEST, {})
80
80
  end
81
81
 
82
82
  it "should call native start and finish event for every event" do
@@ -113,48 +113,14 @@ describe Appsignal::Subscriber do
113
113
  )
114
114
  end
115
115
 
116
- context "root event" do
117
- it "should not set the root event for normal events" do
118
- transaction.should_not_receive(:set_root_event)
119
-
120
- ActiveSupport::Notifications.instrument 'something'
121
- end
122
-
123
- it "should set the root event when the name starts with process_action" do
124
- transaction.respond_to?(:set_root_event).should be_true
125
- transaction.should_receive(:set_root_event).with(
126
- 'process_action.action_controller',
127
- :params => {}
128
- )
129
-
130
- ActiveSupport::Notifications.instrument 'process_action.action_controller', {:params => {}}
131
- end
132
-
133
- it "should set the root event when the name starts with perform_job" do
134
- transaction.respond_to?(:set_root_event).should be_true
135
- transaction.should_receive(:set_root_event).with(
136
- 'perform_job.resque',
137
- :params => {}
138
- )
139
-
140
- ActiveSupport::Notifications.instrument 'perform_job.resque', {:params => {}}
141
- end
142
- end
143
-
144
116
  context "when paused" do
145
117
  before { transaction.pause! }
146
118
 
147
- it "should set a root event, but no other events" do
119
+ it "should add no events" do
148
120
  Appsignal::Extension.should_not_receive(:start_event)
149
121
  Appsignal::Extension.should_not_receive(:finish_event)
150
122
 
151
- transaction.respond_to?(:set_root_event).should be_true
152
- transaction.should_receive(:set_root_event).with(
153
- 'perform_job.resque',
154
- :params => {}
155
- )
156
-
157
- ActiveSupport::Notifications.instrument 'perform_job.resque', {:params => {}}
123
+ ActiveSupport::Notifications.instrument 'sql.active_record'
158
124
  end
159
125
  end
160
126
  end
@@ -12,29 +12,34 @@ describe Appsignal::Transaction do
12
12
  end
13
13
 
14
14
  let(:time) { Time.at(fixed_time) }
15
- let(:transaction) { Appsignal::Transaction.create('1', {}) }
15
+ let(:namespace) { Appsignal::Transaction::HTTP_REQUEST }
16
+ let(:env) { {} }
17
+ let(:merged_env) { http_request_env_with_data(env) }
18
+ let(:options) { {} }
19
+ let(:request) { Rack::Request.new(merged_env) }
20
+ let(:transaction) { Appsignal::Transaction.create('1', namespace, request, options) }
16
21
 
17
22
  before { Timecop.freeze(time) }
18
23
  after { Timecop.return }
19
24
 
20
25
  context "class methods" do
21
- describe '.create' do
22
- subject { Appsignal::Transaction.create('1', {}) }
26
+ describe ".create" do
27
+ subject { transaction }
23
28
 
24
- it 'should add the transaction to thread local' do
25
- Appsignal::Extension.should_receive(:start_transaction).with('1')
29
+ it "should add the transaction to thread local" do
30
+ Appsignal::Extension.should_receive(:start_transaction).with('1', 'http_request')
26
31
  subject
27
32
  Thread.current[:appsignal_transaction].should == subject
28
33
  end
29
34
 
30
35
  it "should create a transaction" do
31
36
  subject.should be_a Appsignal::Transaction
32
- subject.request_id.should == '1'
37
+ subject.transaction_id.should == '1'
38
+ subject.namespace.should == 'http_request'
33
39
  end
34
40
  end
35
41
 
36
42
  describe '.current' do
37
- let(:transaction) { Appsignal::Transaction.create('1', {}) }
38
43
  before { transaction }
39
44
  subject { Appsignal::Transaction.current }
40
45
 
@@ -47,7 +52,7 @@ describe Appsignal::Transaction do
47
52
  before { Thread.current[:appsignal_transaction] = nil }
48
53
 
49
54
  context "with a current transaction" do
50
- before { Appsignal::Transaction.create('2', {}) }
55
+ before { Appsignal::Transaction.create('2', Appsignal::Transaction::HTTP_REQUEST, {}) }
51
56
 
52
57
  it "should complete the current transaction and set the thread appsignal_transaction to nil" do
53
58
  Appsignal::Extension.should_receive(:finish_transaction).with(kind_of(Integer))
@@ -66,66 +71,64 @@ describe Appsignal::Transaction do
66
71
  end
67
72
  end
68
73
 
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
+ context "pausing" do
75
+ describe "#pause!" do
76
+ it "should change the pause flag to true" do
77
+ expect{
78
+ transaction.pause!
79
+ }.to change(transaction, :paused).from(false).to(true)
80
+ end
74
81
  end
75
- end
76
82
 
77
- describe "#resume!" do
78
- before { transaction.pause! }
83
+ describe "#resume!" do
84
+ before { transaction.pause! }
79
85
 
80
- it "should change the pause flag to false" do
81
- expect{
82
- transaction.resume!
83
- }.to change(transaction, :paused).from(true).to(false)
86
+ it "should change the pause flag to false" do
87
+ expect{
88
+ transaction.resume!
89
+ }.to change(transaction, :paused).from(true).to(false)
90
+ end
84
91
  end
85
- end
86
92
 
87
- describe "#paused?" do
93
+ describe "#paused?" do
88
94
 
89
- it "should return the pasue state" do
90
- expect( transaction.paused? ).to be_false
91
- end
95
+ it "should return the pause state" do
96
+ expect( transaction.paused? ).to be_false
97
+ end
92
98
 
93
- context "when paused" do
94
- before { transaction.pause! }
99
+ context "when paused" do
100
+ before { transaction.pause! }
95
101
 
96
- it "should return the pasue state" do
97
- expect( transaction.paused? ).to be_true
102
+ it "should return the pause state" do
103
+ expect( transaction.paused? ).to be_true
104
+ end
98
105
  end
99
106
  end
100
107
  end
101
108
 
102
109
  context "with transaction instance" do
103
- let(:env) do
104
- {
105
- 'HTTP_USER_AGENT' => 'IE6',
106
- 'SERVER_NAME' => 'localhost',
107
- 'action_dispatch.routes' => 'not_available',
108
- 'HTTP_X_REQUEST_START' => '1000000'
109
- }
110
- end
111
- let(:transaction) { Appsignal::Transaction.create('3', env) }
112
-
113
110
  context "initialization" do
114
111
  subject { transaction }
115
112
 
116
- its(:request_id) { should == '3' }
113
+ its(:transaction_id) { should == '1' }
114
+ its(:namespace) { should == 'http_request' }
117
115
  its(:transaction_index) { should be_a Integer }
118
- its(:root_event_payload) { should be_nil }
119
- its(:exception) { should be_nil }
120
- its(:env) { should == env }
116
+ its(:request) { should_not be_nil }
117
+ its(:paused) { should be_false }
121
118
  its(:tags) { should == {} }
122
- its(:queue_start) { should == -1 }
123
- end
119
+ its(:transaction_index) { should be_a Integer }
120
+
121
+ context "options" do
122
+ subject { transaction.options }
124
123
 
125
- describe '#request' do
126
- subject { transaction.request }
124
+ its([:params_method]) { should == :params }
127
125
 
128
- it { should be_a ::Rack::Request }
126
+ context "with overridden options" do
127
+ let(:options) { {:params_method => :filtered_params} }
128
+
129
+ its([:params_method]) { should == :filtered_params }
130
+ end
131
+ end
129
132
  end
130
133
 
131
134
  describe "#set_tags" do
@@ -136,62 +139,94 @@ describe Appsignal::Transaction do
136
139
  end
137
140
  end
138
141
 
139
- describe '#set_root_event' do
140
- context "for a process_action event" do
141
- let(:name) { 'process_action.action_controller' }
142
- let(:payload) { create_payload }
143
-
144
- it "should set the meta data in the transaction and native" do
145
- Appsignal::Extension.should_receive(:set_transaction_base_data).with(
142
+ describe "set_action" do
143
+ it "should set the action in extension" do
144
+ Appsignal::Extension.should_receive(:set_transaction_action).with(
146
145
  kind_of(Integer),
147
- 'http_request',
148
- 'BlogPostsController#show',
149
- kind_of(Integer)
150
- )
146
+ 'PagesController#show'
147
+ ).once
151
148
 
152
- metadata = {
153
- 'path' => '/blog',
154
- 'request_format' => 'html',
155
- 'request_method' => 'GET',
156
- 'status' => '200'
157
- }
158
- metadata.each do |key, value|
159
- transaction.should_receive(:set_metadata).with(key, value).once
160
- end
149
+ transaction.set_action('PagesController#show')
150
+ end
151
+
152
+ it "should not set the action in extension when value is nil" do
153
+ Appsignal::Extension.should_not_receive(:set_transaction_action)
154
+
155
+ transaction.set_action(nil)
156
+ end
157
+ end
161
158
 
162
- transaction.set_root_event(name, payload)
159
+ describe "#set_http_or_background_action" do
160
+ context "for a hash with controller and action" do
161
+ let(:from) { {:controller => 'HomeController', :action => 'show'} }
163
162
 
164
- transaction.root_event_payload.should == payload
165
- transaction.action.should == 'BlogPostsController#show'
166
- transaction.kind.should == 'http_request'
167
- transaction.queue_start.should be_kind_of(Integer)
163
+ it "should set the action" do
164
+ transaction.should_receive(:set_action).with('HomeController#show')
168
165
  end
169
166
  end
170
167
 
171
- context "for a perform_job event" do
172
- let(:name) { 'perform_job.delayed_job' }
173
- let(:payload) { create_background_payload }
168
+ context "for a hash with just action" do
169
+ let(:from) { {:action => 'show'} }
174
170
 
175
- it "should set the meta data in the transaction and native" do
176
- Appsignal::Extension.should_receive(:set_transaction_base_data).with(
177
- kind_of(Integer),
178
- 'background_job',
179
- 'BackgroundJob#perform',
180
- kind_of(Integer)
181
- )
171
+ it "should set the action" do
172
+ transaction.should_receive(:set_action).with('show')
173
+ end
174
+ end
182
175
 
183
- transaction.set_root_event(name, payload)
176
+ context "for a hash with class and method" do
177
+ let(:from) { {:class => 'Worker', :method => 'perform'} }
184
178
 
185
- transaction.root_event_payload.should == payload
186
- transaction.action.should == 'BackgroundJob#perform'
187
- transaction.kind.should == 'background_job'
188
- transaction.queue_start.should be_kind_of(Integer)
179
+ it "should set the action" do
180
+ transaction.should_receive(:set_action).with('Worker#perform')
181
+ end
182
+ end
183
+
184
+ after { transaction.set_http_or_background_action(from) }
185
+ end
186
+
187
+ describe "set_queue_start" do
188
+ it "should set the queue start in extension" do
189
+ Appsignal::Extension.should_receive(:set_transaction_queue_start).with(
190
+ kind_of(Integer),
191
+ 10.0
192
+ ).once
193
+
194
+ transaction.set_queue_start(10.0)
195
+ end
196
+
197
+ it "should not set the queue start in extension when value is nil" do
198
+ Appsignal::Extension.should_not_receive(:set_transaction_queue_start)
199
+
200
+ transaction.set_queue_start(nil)
201
+ end
202
+ end
203
+
204
+ describe "#set_http_or_background_queue_start" do
205
+ context "for a http transaction" do
206
+ let(:namespace) { Appsignal::Transaction::HTTP_REQUEST }
207
+ let(:env) { {'HTTP_X_REQUEST_START' => (fixed_time * 1000).to_s} }
208
+
209
+ it "should set the queue start on the transaction" do
210
+ transaction.should_receive(:set_queue_start).with(13897836000)
211
+
212
+ transaction.set_http_or_background_queue_start
213
+ end
214
+ end
215
+
216
+ context "for a background transaction" do
217
+ let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
218
+ let(:env) { {:queue_start => fixed_time} }
219
+
220
+ it "should set the queue start on the transaction" do
221
+ transaction.should_receive(:set_queue_start).with(1389783600000)
222
+
223
+ transaction.set_http_or_background_queue_start
189
224
  end
190
225
  end
191
226
  end
192
227
 
193
228
  describe "#set_metadata" do
194
- it "should set the metdata in native" do
229
+ it "should set the metdata in extension" do
195
230
  Appsignal::Extension.should_receive(:set_transaction_metadata).with(
196
231
  kind_of(Integer),
197
232
  'request_method',
@@ -201,7 +236,7 @@ describe Appsignal::Transaction do
201
236
  transaction.set_metadata('request_method', 'GET')
202
237
  end
203
238
 
204
- it "should set the metdata in native when value is nil" do
239
+ it "should not set the metdata in extension when value is nil" do
205
240
  Appsignal::Extension.should_not_receive(:set_transaction_metadata)
206
241
 
207
242
  transaction.set_metadata('request_method', nil)
@@ -209,48 +244,42 @@ describe Appsignal::Transaction do
209
244
  end
210
245
 
211
246
  describe '#set_error' do
247
+ let(:env) { http_request_env_with_data }
212
248
  let(:error) { double(:error, :message => 'test message', :backtrace => ['line 1']) }
213
249
 
214
250
  it "should also respond to add_exception for backwords compatibility" do
215
251
  transaction.should respond_to(:add_exception)
216
252
  end
217
253
 
218
- it "should set an error and it's data in native" do
219
- Appsignal::Extension.should_receive(:set_transaction_error).with(
220
- kind_of(Integer),
221
- 'RSpec::Mocks::Mock',
222
- 'test message'
223
- )
224
- Appsignal::Extension.should_receive(:set_transaction_error_data).with(
225
- kind_of(Integer),
226
- 'environment',
227
- "{\"SERVER_NAME\":\"localhost\",\"HTTP_X_REQUEST_START\":\"1000000\",\"HTTP_USER_AGENT\":\"IE6\"}"
228
- ).once
229
- Appsignal::Extension.should_receive(:set_transaction_error_data).with(
230
- kind_of(Integer),
231
- 'session_data',
232
- "{}"
233
- ).once
234
- Appsignal::Extension.should_receive(:set_transaction_error_data).with(
235
- kind_of(Integer),
236
- 'backtrace',
237
- "[\"line 1\"]"
238
- ).once
239
- Appsignal::Extension.should_receive(:set_transaction_error_data).with(
240
- kind_of(Integer),
241
- 'tags',
242
- "{}"
243
- ).once
254
+ it "should not add the error if it's in the ignored list" do
255
+ Appsignal.stub(:is_ignored_error? => true)
256
+ Appsignal::Extension.should_not_receive(:set_transaction_error)
244
257
 
245
258
  transaction.set_error(error)
246
259
  end
247
260
 
248
- context "with root event payload" do
249
- before do
250
- transaction.set_root_event('process_action.action_controller', create_payload)
251
- end
252
-
253
- it "should also set params" do
261
+ context "for a http request" do
262
+ it "should set an error and it's data in native" do
263
+ Appsignal::Extension.should_receive(:set_transaction_error).with(
264
+ kind_of(Integer),
265
+ 'RSpec::Mocks::Mock',
266
+ 'test message'
267
+ )
268
+ Appsignal::Extension.should_receive(:set_transaction_error_data).with(
269
+ kind_of(Integer),
270
+ 'environment',
271
+ "{\"CONTENT_LENGTH\":\"0\",\"REQUEST_METHOD\":\"GET\",\"SERVER_NAME\":\"example.org\",\"SERVER_PORT\":\"80\",\"PATH_INFO\":\"/blog\"}"
272
+ ).once
273
+ Appsignal::Extension.should_receive(:set_transaction_error_data).with(
274
+ kind_of(Integer),
275
+ 'session_data',
276
+ "{}"
277
+ ).once
278
+ Appsignal::Extension.should_receive(:set_transaction_error_data).with(
279
+ kind_of(Integer),
280
+ 'backtrace',
281
+ "[\"line 1\"]"
282
+ ).once
254
283
  Appsignal::Extension.should_receive(:set_transaction_error_data).with(
255
284
  kind_of(Integer),
256
285
  'params',
@@ -258,9 +287,9 @@ describe Appsignal::Transaction do
258
287
  ).once
259
288
  Appsignal::Extension.should_receive(:set_transaction_error_data).with(
260
289
  kind_of(Integer),
261
- kind_of(String),
262
- kind_of(String)
263
- ).exactly(4).times
290
+ 'tags',
291
+ "{}"
292
+ ).once
264
293
 
265
294
  transaction.set_error(error)
266
295
  end
@@ -288,55 +317,157 @@ describe Appsignal::Transaction do
288
317
  end
289
318
  end
290
319
 
291
- # protected
320
+ context "generic request" do
321
+ let(:env) { {} }
322
+ subject { Appsignal::Transaction::GenericRequest.new(env) }
292
323
 
293
- describe "#set_background_queue_start" do
294
- before do
295
- transaction.stub(:root_event_payload => payload)
296
- transaction.send(:set_background_queue_start)
324
+ it "should initialize with an empty env" do
325
+ subject.env.should be_empty
297
326
  end
298
327
 
299
- subject { transaction.queue_start }
328
+ context "with a filled env" do
329
+ let(:env) do
330
+ {
331
+ :params => {:id => 1},
332
+ :queue_start => 10
333
+ }
334
+ end
300
335
 
301
- context "when queue start is nil" do
302
- let(:payload) { create_background_payload(:queue_start => nil) }
336
+ its(:env) { should == env }
337
+ its(:params) { should == {:id => 1} }
338
+ end
339
+ end
340
+
341
+ # protected
303
342
 
304
- it { should == -1 }
343
+ describe "#background_queue_start" do
344
+ subject { transaction.send(:background_queue_start) }
345
+
346
+ context "when queue start is nil" do
347
+ it { should == nil }
305
348
  end
306
349
 
307
350
  context "when queue start is set" do
308
- let(:payload) { create_background_payload }
351
+ let(:env) { background_env_with_data }
309
352
 
310
353
  it { should == 1389783590000 }
311
354
  end
312
355
  end
313
356
 
357
+ describe "#http_queue_start" do
358
+ let(:slightly_earlier_time) { fixed_time - 0.4 }
359
+ let(:slightly_earlier_time_value) { (slightly_earlier_time * factor).to_i }
360
+ subject { transaction.send(:http_queue_start) }
361
+
362
+ shared_examples "http queue start" do
363
+ context "when env is nil" do
364
+ before { transaction.request.stub(:env => nil) }
365
+
366
+ it { should be_nil }
367
+ end
368
+
369
+ context "with no relevant header set" do
370
+ let(:env) { {} }
371
+
372
+ it { should be_nil }
373
+ end
374
+
375
+ context "with the HTTP_X_REQUEST_START header set" do
376
+ let(:env) { {'HTTP_X_REQUEST_START' => "t=#{slightly_earlier_time_value}"} }
377
+
378
+ it { should == 1389783599 }
379
+
380
+ context "with unparsable content" do
381
+ let(:env) { {'HTTP_X_REQUEST_START' => 'something'} }
382
+
383
+ it { should be_nil }
384
+ end
385
+
386
+ context "with some cruft" do
387
+ let(:env) { {'HTTP_X_REQUEST_START' => "t=#{slightly_earlier_time_value}aaaa"} }
388
+
389
+ it { should == 1389783599 }
390
+ end
391
+
392
+ context "with a really low number" do
393
+ let(:env) { {'HTTP_X_REQUEST_START' => "t=100"} }
394
+
395
+ it { should be_nil }
396
+ end
397
+
398
+ context "with the alternate HTTP_X_QUEUE_START header set" do
399
+ let(:env) { {'HTTP_X_QUEUE_START' => "t=#{slightly_earlier_time_value}"} }
400
+
401
+ it { should == 1389783599 }
402
+ end
403
+ end
404
+ end
405
+
406
+ context "time in miliseconds" do
407
+ let(:factor) { 1_000 }
408
+
409
+ it_should_behave_like "http queue start"
410
+ end
411
+
412
+ context "time in microseconds" do
413
+ let(:factor) { 1_000_000 }
414
+
415
+ it_should_behave_like "http queue start"
416
+ end
417
+ end
418
+
314
419
  describe "#sanitized_params" do
315
420
  subject { transaction.send(:sanitized_params) }
316
421
 
317
- context "without a root event payload" do
422
+ context "without params" do
423
+ before { transaction.request.stub(:params => nil) }
424
+
318
425
  it { should be_nil }
319
426
  end
320
427
 
321
- context "with a root event payload" do
322
- before { transaction.stub(:root_event_payload => create_payload) }
428
+ context "when not sending params" do
429
+ before { Appsignal.config.config_hash[:send_params] = false }
430
+ after { Appsignal.config.config_hash[:send_params] = true }
323
431
 
324
- it "should call the params sanitizer" do
325
- Appsignal::ParamsSanitizer.should_receive(:sanitize).with(kind_of(Hash)).and_return({:id => 1})
432
+ it { should be_nil }
433
+ end
326
434
 
327
- subject.should == {:id => 1}
435
+ context "when params method does not exist" do
436
+ let(:options) { {:params_method => :nonsense} }
437
+
438
+ it { should be_nil }
439
+ end
440
+
441
+ context "with an array" do
442
+ let(:request) { Appsignal::Transaction::GenericRequest.new(background_env_with_data(:params => ['arg1', 'arg2'])) }
443
+
444
+ it { should == ['arg1', 'arg2'] }
445
+ end
446
+
447
+ context "with env" do
448
+ it "should call the params sanitizer" do
449
+ Appsignal::ParamsSanitizer.should_receive(:sanitize).with(kind_of(Hash)).and_return({
450
+ 'controller' => 'blog_posts',
451
+ 'action' => 'show',
452
+ 'id' => '1'
453
+ })
454
+
455
+ subject.should == {
456
+ 'controller' => 'blog_posts',
457
+ 'action' => 'show',
458
+ 'id' => '1'
459
+ }
328
460
  end
329
461
  end
330
462
  end
331
463
 
332
464
  describe "#sanitized_environment" do
333
465
  let(:whitelisted_keys) { Appsignal::Transaction::ENV_METHODS }
334
- let(:transaction) { Appsignal::Transaction.create('1', env) }
335
466
 
336
467
  subject { transaction.send(:sanitized_environment) }
337
468
 
338
469
  context "when env is nil" do
339
- let(:env) { nil }
470
+ before { transaction.request.stub(:env => nil) }
340
471
 
341
472
  it { should be_nil }
342
473
  end
@@ -358,19 +489,24 @@ describe Appsignal::Transaction do
358
489
  subject { transaction.send(:sanitized_session_data) }
359
490
 
360
491
  context "when env is nil" do
361
- let(:transaction) { Appsignal::Transaction.create('1', nil) }
492
+ before { transaction.request.stub(:session => nil) }
362
493
 
363
494
  it { should be_nil }
364
495
  end
365
496
 
366
497
  context "when env is empty" do
367
- let(:transaction) { Appsignal::Transaction.create('1', {}) }
498
+ before { transaction.request.stub(:session => {}) }
368
499
 
369
500
  it { should == {} }
370
501
  end
371
502
 
503
+ context "when request class does not have a session method" do
504
+ let(:request) { Appsignal::Transaction::GenericRequest.new({}) }
505
+
506
+ it { should be_nil }
507
+ end
508
+
372
509
  context "when there is a session" do
373
- let(:transaction) { Appsignal::Transaction.create('1', {}) }
374
510
  before do
375
511
  transaction.should respond_to(:request)
376
512
  transaction.stub_chain(:request, :session => {:foo => :bar})
@@ -406,22 +542,21 @@ describe Appsignal::Transaction do
406
542
  end
407
543
  end
408
544
  end
409
- end
410
545
 
411
- context "when skipping session data" do
412
- before do
413
- Appsignal.config = {:skip_session_data => true}
414
- end
546
+ context "when skipping session data" do
547
+ before do
548
+ Appsignal.config = {:skip_session_data => true}
549
+ end
415
550
 
416
- it "does not pass the session data into the params sanitizer" do
417
- Appsignal::ParamsSanitizer.should_not_receive(:sanitize)
418
- subject.should be_nil
551
+ it "does not pass the session data into the params sanitizer" do
552
+ Appsignal::ParamsSanitizer.should_not_receive(:sanitize)
553
+ subject.should be_nil
554
+ end
419
555
  end
420
556
  end
421
557
  end
422
558
 
423
559
  describe '#sanitized_tags' do
424
- let(:transaction) { Appsignal::Transaction.create('1', {}) }
425
560
  before do
426
561
  transaction.set_tags(
427
562
  {