appsignal 3.9.3 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +22 -19
  3. data/.rubocop.yml +1 -1
  4. data/CHANGELOG.md +180 -0
  5. data/Gemfile +1 -0
  6. data/README.md +0 -1
  7. data/Rakefile +1 -1
  8. data/benchmark.rake +99 -42
  9. data/build_matrix.yml +10 -12
  10. data/gemfiles/webmachine1.gemfile +5 -4
  11. data/lib/appsignal/cli/demo.rb +0 -1
  12. data/lib/appsignal/config.rb +57 -97
  13. data/lib/appsignal/demo.rb +15 -20
  14. data/lib/appsignal/environment.rb +6 -1
  15. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
  16. data/lib/appsignal/event_formatter.rb +3 -2
  17. data/lib/appsignal/helpers/instrumentation.rb +490 -16
  18. data/lib/appsignal/hooks/action_cable.rb +21 -16
  19. data/lib/appsignal/hooks/active_job.rb +15 -14
  20. data/lib/appsignal/hooks/delayed_job.rb +1 -1
  21. data/lib/appsignal/hooks/shoryuken.rb +3 -63
  22. data/lib/appsignal/integrations/action_cable.rb +5 -7
  23. data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
  24. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
  25. data/lib/appsignal/integrations/data_mapper.rb +1 -0
  26. data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
  27. data/lib/appsignal/integrations/dry_monitor.rb +1 -0
  28. data/lib/appsignal/integrations/excon.rb +1 -0
  29. data/lib/appsignal/integrations/http.rb +1 -0
  30. data/lib/appsignal/integrations/net_http.rb +1 -0
  31. data/lib/appsignal/integrations/object.rb +6 -0
  32. data/lib/appsignal/integrations/padrino.rb +21 -25
  33. data/lib/appsignal/integrations/que.rb +13 -20
  34. data/lib/appsignal/integrations/railtie.rb +1 -1
  35. data/lib/appsignal/integrations/rake.rb +45 -15
  36. data/lib/appsignal/integrations/redis.rb +1 -0
  37. data/lib/appsignal/integrations/redis_client.rb +1 -0
  38. data/lib/appsignal/integrations/resque.rb +2 -5
  39. data/lib/appsignal/integrations/shoryuken.rb +75 -0
  40. data/lib/appsignal/integrations/sidekiq.rb +7 -25
  41. data/lib/appsignal/integrations/unicorn.rb +1 -0
  42. data/lib/appsignal/integrations/webmachine.rb +12 -9
  43. data/lib/appsignal/logger.rb +7 -3
  44. data/lib/appsignal/probes/helpers.rb +1 -0
  45. data/lib/appsignal/probes/mri.rb +1 -0
  46. data/lib/appsignal/probes/sidekiq.rb +1 -0
  47. data/lib/appsignal/probes.rb +3 -0
  48. data/lib/appsignal/rack/abstract_middleware.rb +67 -24
  49. data/lib/appsignal/rack/body_wrapper.rb +143 -0
  50. data/lib/appsignal/rack/event_handler.rb +39 -8
  51. data/lib/appsignal/rack/generic_instrumentation.rb +6 -4
  52. data/lib/appsignal/rack/grape_middleware.rb +3 -2
  53. data/lib/appsignal/rack/hanami_middleware.rb +1 -1
  54. data/lib/appsignal/rack/instrumentation_middleware.rb +62 -0
  55. data/lib/appsignal/rack/rails_instrumentation.rb +1 -3
  56. data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -3
  57. data/lib/appsignal/rack/streaming_listener.rb +14 -59
  58. data/lib/appsignal/rack.rb +60 -0
  59. data/lib/appsignal/span.rb +1 -0
  60. data/lib/appsignal/transaction.rb +353 -104
  61. data/lib/appsignal/utils/data.rb +0 -1
  62. data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
  63. data/lib/appsignal/utils/integration_logger.rb +0 -13
  64. data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
  65. data/lib/appsignal/utils/json.rb +0 -1
  66. data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
  67. data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
  68. data/lib/appsignal/utils.rb +6 -0
  69. data/lib/appsignal/version.rb +1 -1
  70. data/lib/appsignal.rb +9 -6
  71. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  72. data/spec/lib/appsignal/config_spec.rb +139 -43
  73. data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
  74. data/spec/lib/appsignal/hooks/activejob_spec.rb +9 -0
  75. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
  76. data/spec/lib/appsignal/hooks/rake_spec.rb +100 -17
  77. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
  78. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
  79. data/spec/lib/appsignal/integrations/padrino_spec.rb +181 -131
  80. data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
  81. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
  82. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +4 -4
  83. data/spec/lib/appsignal/integrations/sinatra_spec.rb +10 -2
  84. data/spec/lib/appsignal/integrations/webmachine_spec.rb +77 -17
  85. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +144 -11
  86. data/spec/lib/appsignal/rack/body_wrapper_spec.rb +263 -0
  87. data/spec/lib/appsignal/rack/event_handler_spec.rb +81 -10
  88. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +70 -17
  89. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +1 -1
  90. data/spec/lib/appsignal/rack/instrumentation_middleware_spec.rb +38 -0
  91. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
  92. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +43 -120
  93. data/spec/lib/appsignal/rack_spec.rb +63 -0
  94. data/spec/lib/appsignal/transaction_spec.rb +1675 -953
  95. data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
  96. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
  97. data/spec/lib/appsignal_spec.rb +517 -13
  98. data/spec/support/helpers/transaction_helpers.rb +44 -20
  99. data/spec/support/matchers/transaction.rb +15 -1
  100. data/spec/support/mocks/dummy_app.rb +1 -1
  101. data/spec/support/testing.rb +1 -1
  102. metadata +12 -4
  103. data/support/check_versions +0 -22
@@ -3,8 +3,11 @@ describe Appsignal::Rack::EventHandler do
3
3
  let(:env) do
4
4
  {
5
5
  "HTTP_X_REQUEST_START" => "t=#{queue_start_time.to_i}", # in milliseconds
6
- "REQUEST_METHOD" => "GET",
7
- "PATH_INFO" => "/path"
6
+ "REQUEST_METHOD" => "POST",
7
+ "PATH_INFO" => "/path",
8
+ "QUERY_STRING" => "query_param1=value1&query_param2=value2",
9
+ "rack.session" => { "session1" => "value1", "session2" => "value2" },
10
+ "rack.input" => StringIO.new("post_param1=value1&post_param2=value2")
8
11
  }
9
12
  end
10
13
  let(:request) { Rack::Request.new(env) }
@@ -172,20 +175,93 @@ describe Appsignal::Rack::EventHandler do
172
175
  expect(last_transaction).to_not be_completed
173
176
  end
174
177
 
175
- it "completes the transaction" do
178
+ it "sets params on the transaction" do
179
+ on_start
180
+ on_finish
181
+
182
+ expect(last_transaction).to include_params(
183
+ "query_param1" => "value1",
184
+ "query_param2" => "value2",
185
+ "post_param1" => "value1",
186
+ "post_param2" => "value2"
187
+ )
188
+ end
189
+
190
+ it "sets headers on the transaction" do
176
191
  on_start
177
192
  on_finish
178
193
 
179
- expect(last_transaction).to_not have_action
180
194
  expect(last_transaction).to include_environment(
181
- "REQUEST_METHOD" => "GET",
195
+ "REQUEST_METHOD" => "POST",
182
196
  "PATH_INFO" => "/path"
183
197
  )
198
+ end
199
+
200
+ it "sets session data on the transaction" do
201
+ on_start
202
+ on_finish
203
+
204
+ expect(last_transaction).to include_session_data(
205
+ "session1" => "value1",
206
+ "session2" => "value2"
207
+ )
208
+ end
209
+
210
+ it "sets the queue start time on the transaction" do
211
+ on_start
212
+ on_finish
213
+
184
214
  expect(last_transaction).to have_queue_start(queue_start_time)
215
+ end
216
+
217
+ it "completes the transaction" do
218
+ on_start
219
+ on_finish
220
+
221
+ expect(last_transaction).to_not have_action
185
222
  expect(last_transaction).to be_completed
186
223
  end
187
224
 
188
225
  context "without a response" do
226
+ it "sets params on the transaction" do
227
+ on_start
228
+ on_finish
229
+
230
+ expect(last_transaction).to include_params(
231
+ "query_param1" => "value1",
232
+ "query_param2" => "value2",
233
+ "post_param1" => "value1",
234
+ "post_param2" => "value2"
235
+ )
236
+ end
237
+
238
+ it "sets headers on the transaction" do
239
+ on_start
240
+ on_finish
241
+
242
+ expect(last_transaction).to include_environment(
243
+ "REQUEST_METHOD" => "POST",
244
+ "PATH_INFO" => "/path"
245
+ )
246
+ end
247
+
248
+ it "sets session data on the transaction" do
249
+ on_start
250
+ on_finish
251
+
252
+ expect(last_transaction).to include_session_data(
253
+ "session1" => "value1",
254
+ "session2" => "value2"
255
+ )
256
+ end
257
+
258
+ it "sets the queue start time on the transaction" do
259
+ on_start
260
+ on_finish
261
+
262
+ expect(last_transaction).to have_queue_start(queue_start_time)
263
+ end
264
+
189
265
  it "completes the transaction" do
190
266
  on_start
191
267
  on_finish(request, nil)
@@ -193,11 +269,6 @@ describe Appsignal::Rack::EventHandler do
193
269
  # The action is not set on purpose, as we can't set a normalized route
194
270
  # It requires the app to set an action name
195
271
  expect(last_transaction).to_not have_action
196
- expect(last_transaction).to include_environment(
197
- "REQUEST_METHOD" => "GET",
198
- "PATH_INFO" => "/path"
199
- )
200
- expect(last_transaction).to have_queue_start(queue_start_time)
201
272
  expect(last_transaction).to be_completed
202
273
  end
203
274
 
@@ -1,28 +1,81 @@
1
- describe Appsignal::Rack::GenericInstrumentation do
2
- let(:app) { double(:call => true) }
3
- let(:env) { Rack::MockRequest.env_for("/some/path") }
4
- let(:middleware) { Appsignal::Rack::GenericInstrumentation.new(app, {}) }
1
+ describe "Appsignal::Rack::GenericInstrumentation" do
2
+ describe "Appsignal::Rack::GenericInstrumentation constant" do
3
+ let(:err_stream) { std_stream }
4
+ let(:stderr) { err_stream.read }
5
+ before do
6
+ if Appsignal::Rack.const_defined?(:GenericInstrumentation)
7
+ hide_const "Appsignal::Rack::GenericInstrumentation"
8
+ end
9
+ end
10
+
11
+ it "returns the Rack::GenericInstrumentation constant" do
12
+ silence do
13
+ expect(Appsignal::Rack::GenericInstrumentation)
14
+ .to be(Appsignal::Rack::GenericInstrumentationAlias)
15
+ end
16
+ end
5
17
 
6
- before(:context) { start_agent }
7
- around { |example| keep_transactions { example.run } }
18
+ it "prints a deprecation warning to STDERR" do
19
+ capture_std_streams(std_stream, err_stream) do
20
+ Appsignal::Rack::GenericInstrumentation
21
+ end
8
22
 
9
- def make_request(env)
10
- middleware.call(env)
11
- end
23
+ expect(stderr).to include(
24
+ "appsignal WARNING: The constant Appsignal::Rack::GenericInstrumentation " \
25
+ "has been deprecated."
26
+ )
27
+ end
12
28
 
13
- context "without an exception" do
14
- it "reports a process_action.generic event" do
15
- make_request(env)
29
+ it "logs a warning" do
30
+ logs =
31
+ capture_logs do
32
+ silence do
33
+ Appsignal::Rack::GenericInstrumentation
34
+ end
35
+ end
16
36
 
17
- expect(last_transaction).to include_event("name" => "process_action.generic")
37
+ expect(logs).to contains_log(
38
+ :warn,
39
+ "The constant Appsignal::Rack::GenericInstrumentation has been deprecated."
40
+ )
18
41
  end
19
42
  end
20
43
 
21
- context "without action name metadata" do
22
- it "reports 'unknown' as the action name" do
23
- make_request(env)
44
+ describe "middleware" do
45
+ let(:app) { double(:call => true) }
46
+ let(:env) { Rack::MockRequest.env_for("/some/path") }
47
+ let(:middleware) { Appsignal::Rack::GenericInstrumentation.new(app, {}) }
48
+
49
+ before(:context) { start_agent }
50
+ around { |example| keep_transactions { example.run } }
51
+
52
+ def make_request(env)
53
+ middleware.call(env)
54
+ end
55
+
56
+ context "without an exception" do
57
+ it "reports a process_action.generic event" do
58
+ make_request(env)
59
+
60
+ expect(last_transaction).to include_event("name" => "process_action.generic")
61
+ end
62
+ end
63
+
64
+ context "with action name env" do
65
+ it "reports the appsignal.action env as the action name" do
66
+ env["appsignal.action"] = "MyAction"
67
+ make_request(env)
68
+
69
+ expect(last_transaction).to have_action("MyAction")
70
+ end
71
+ end
72
+
73
+ context "without action name metadata" do
74
+ it "reports 'unknown' as the action name" do
75
+ make_request(env)
24
76
 
25
- expect(last_transaction).to have_action("unknown")
77
+ expect(last_transaction).to have_action("unknown")
78
+ end
26
79
  end
27
80
  end
28
81
  end
@@ -5,7 +5,7 @@ if DependencyHelper.grape_present?
5
5
  let(:err_stream) { std_stream }
6
6
  let(:stderr) { err_stream.read }
7
7
 
8
- it "returns the Probes constant calling the Minutely constant" do
8
+ it "returns the Rack::GrapeMiddleware constant calling the Grape::Middleware constant" do
9
9
  silence { expect(Appsignal::Grape::Middleware).to be(Appsignal::Rack::GrapeMiddleware) }
10
10
  end
11
11
 
@@ -0,0 +1,38 @@
1
+ describe Appsignal::Rack::InstrumentationMiddleware do
2
+ let(:app) { DummyApp.new }
3
+ let(:env) { Rack::MockRequest.env_for("/some/path") }
4
+ let(:middleware) { described_class.new(app, {}) }
5
+
6
+ before { start_agent }
7
+ around { |example| keep_transactions { example.run } }
8
+
9
+ def make_request(env)
10
+ middleware.call(env)
11
+ end
12
+
13
+ context "without an exception" do
14
+ it "reports a process_request_middleware.rack event" do
15
+ make_request(env)
16
+
17
+ expect(last_transaction).to include_event("name" => "process_request_middleware.rack")
18
+ end
19
+ end
20
+
21
+ context "with custom action name" do
22
+ let(:app) { DummyApp.new { |_env| Appsignal.set_action("MyAction") } }
23
+
24
+ it "reports the custom action name" do
25
+ make_request(env)
26
+
27
+ expect(last_transaction).to have_action("MyAction")
28
+ end
29
+ end
30
+
31
+ context "without action name metadata" do
32
+ it "reports no action name" do
33
+ make_request(env)
34
+
35
+ expect(last_transaction).to_not have_action
36
+ end
37
+ end
38
+ end
@@ -114,8 +114,10 @@ if DependencyHelper.rails_present?
114
114
  make_request
115
115
 
116
116
  expect(last_transaction).to_not include_metadata("method" => anything)
117
- expect(log_contents(log))
118
- .to contains_log(:error, "Unable to report HTTP request method: '")
117
+ expect(log_contents(log)).to contains_log(
118
+ :error,
119
+ "Exception while fetching the HTTP request method: "
120
+ )
119
121
  end
120
122
  end
121
123
 
@@ -1,146 +1,69 @@
1
- require "appsignal/rack/streaming_listener"
2
-
3
- describe Appsignal::Rack::StreamingListener do
4
- let(:env) do
5
- {
6
- "rack.input" => StringIO.new,
7
- "REQUEST_METHOD" => "GET",
8
- "PATH_INFO" => "/homepage",
9
- "QUERY_STRING" => "param=something"
10
- }
1
+ describe "Appsignal::Rack::StreamingListener" do
2
+ def load_middleware
3
+ load "lib/appsignal/rack/streaming_listener.rb"
11
4
  end
12
- let(:app) { DummyApp.new }
13
- let(:listener) { Appsignal::Rack::StreamingListener.new(app, {}) }
14
- before(:context) { start_agent }
15
- around { |example| keep_transactions { example.run } }
16
5
 
17
- describe "#call" do
18
- context "when Appsignal is not active" do
19
- before { allow(Appsignal).to receive(:active?).and_return(false) }
6
+ describe "loading the streaming_listener integrations file" do
7
+ let(:err_stream) { std_stream }
8
+ let(:stderr) { err_stream.read }
9
+ after { Appsignal::Rack.send(:remove_const, :StreamingListener) }
20
10
 
21
- it "does not create a transaction" do
22
- expect do
23
- listener.call(env)
24
- end.to_not(change { created_transactions.count })
11
+ it "prints a deprecation warning to STDERR" do
12
+ capture_std_streams(std_stream, err_stream) do
13
+ load_middleware
25
14
  end
26
15
 
27
- it "calls the app" do
28
- listener.call(env)
29
-
30
- expect(app).to be_called
31
- end
16
+ expect(stderr).to include(
17
+ "appsignal WARNING: The constant Appsignal::Rack::StreamingListener " \
18
+ "has been deprecated."
19
+ )
32
20
  end
33
21
 
34
- context "when Appsignal is active" do
35
- before { allow(Appsignal).to receive(:active?).and_return(true) }
36
-
37
- let(:wrapper) { Appsignal::StreamWrapper.new("body", transaction) }
38
- let(:raw_payload) { { :foo => :bar } }
39
- before { allow(listener).to receive(:raw_payload).and_return(raw_payload) }
40
-
41
- it "creates a transaction" do
42
- expect do
43
- listener.call(env)
44
- end.to(change { created_transactions.count }.by(1))
45
- end
46
-
47
- it "instruments the call" do
48
- listener.call(env)
49
-
50
- expect(last_transaction).to include_event("name" => "process_action.rack")
51
- end
52
-
53
- it "set `appsignal.action` to the action name" do
54
- env["appsignal.action"] = "Action"
55
-
56
- listener.call(env)
57
-
58
- expect(last_transaction).to have_action("Action")
59
- end
60
-
61
- it "adds the path, method and queue start to the transaction" do
62
- listener.call(env)
63
-
64
- expect(last_transaction).to include_metadata(
65
- "path" => "/homepage",
66
- "method" => "GET"
67
- )
68
- expect(last_transaction).to have_queue_start
69
- end
70
-
71
- context "with an exception in the instrumentation call" do
72
- let(:error) { ExampleException.new("error message") }
73
- let(:app) { DummyApp.new { raise error } }
74
-
75
- it "adds the exception to the transaction" do
76
- expect do
77
- listener.call(env)
78
- end.to raise_error(error)
79
-
80
- expect(last_transaction).to have_error("ExampleException", "error message")
22
+ it "logs a warning" do
23
+ logs =
24
+ capture_logs do
25
+ silence do
26
+ load_middleware
27
+ end
81
28
  end
82
- end
83
29
 
84
- it "wraps the body in a wrapper" do
85
- _, _, body = listener.call(env)
86
-
87
- expect(body).to be_a(Appsignal::StreamWrapper)
88
- end
30
+ expect(logs).to contains_log(
31
+ :warn,
32
+ "The constant Appsignal::Rack::StreamingListener has been deprecated."
33
+ )
89
34
  end
90
35
  end
91
- end
92
-
93
- describe Appsignal::StreamWrapper do
94
- let(:stream) { double }
95
- let(:transaction) { http_request_transaction }
96
- let(:wrapper) { Appsignal::StreamWrapper.new(stream, transaction) }
97
- before do
98
- start_agent
99
- set_current_transaction(transaction)
100
- end
101
- around { |example| keep_transactions { example.run } }
102
36
 
103
- describe "#each" do
104
- it "calls the original stream" do
105
- expect(stream).to receive(:each)
37
+ describe "middleware" do
38
+ let(:env) { {} }
39
+ let(:app) { DummyApp.new }
40
+ let(:middleware) { Appsignal::Rack::StreamingListener.new(app, {}) }
41
+ around { |example| keep_transactions { example.run } }
42
+ before(:context) { load_middleware }
43
+ before { start_agent }
106
44
 
107
- wrapper.each
45
+ def make_request
46
+ middleware.call(env)
108
47
  end
109
48
 
110
- context "when #each raises an error" do
111
- let(:error) { ExampleException.new("error message") }
112
-
113
- it "records the exception" do
114
- allow(stream).to receive(:each).and_raise(error)
49
+ it "instruments the call" do
50
+ make_request
115
51
 
116
- expect { wrapper.send(:each) }.to raise_error(error)
117
-
118
- expect(transaction).to have_error("ExampleException", "error message")
119
- end
52
+ expect(last_transaction).to include_event("name" => "process_streaming_request.rack")
120
53
  end
121
- end
122
-
123
- describe "#close" do
124
- it "closes the original stream and completes the transaction" do
125
- expect(stream).to receive(:close)
126
54
 
127
- wrapper.close
55
+ it "set no action by default" do
56
+ make_request
128
57
 
129
- expect(current_transaction?).to be_falsy
130
- expect(transaction).to be_completed
58
+ expect(last_transaction).to_not have_action
131
59
  end
132
60
 
133
- context "when #close raises an error" do
134
- let(:error) { ExampleException.new("error message") }
61
+ it "set `appsignal.action` to the action name" do
62
+ env["appsignal.action"] = "Action"
135
63
 
136
- it "records the exception and completes the transaction" do
137
- allow(stream).to receive(:close).and_raise(error)
64
+ make_request
138
65
 
139
- expect { wrapper.send(:close) }.to raise_error(error)
140
-
141
- expect(transaction).to have_error("ExampleException", "error message")
142
- expect(transaction).to be_completed
143
- end
66
+ expect(last_transaction).to have_action("Action")
144
67
  end
145
68
  end
146
69
  end
@@ -0,0 +1,63 @@
1
+ describe Appsignal::Rack::Utils do
2
+ describe ".queue_start_from" do
3
+ let(:header_time) { fixed_time - 0.4 }
4
+ let(:header_time_value) { (header_time * factor).to_i }
5
+ subject { described_class.queue_start_from(env) }
6
+
7
+ shared_examples "HTTP queue start" do
8
+ context "when env is nil" do
9
+ let(:env) { nil }
10
+
11
+ it { is_expected.to be_nil }
12
+ end
13
+
14
+ context "with no relevant header set" do
15
+ let(:env) { {} }
16
+
17
+ it { is_expected.to be_nil }
18
+ end
19
+
20
+ context "with the HTTP_X_REQUEST_START header set" do
21
+ let(:env) { { "HTTP_X_REQUEST_START" => "t=#{header_time_value}" } }
22
+
23
+ it { is_expected.to eq 1_389_783_599_600 }
24
+
25
+ context "with unparsable content" do
26
+ let(:env) { { "HTTP_X_REQUEST_START" => "something" } }
27
+
28
+ it { is_expected.to be_nil }
29
+ end
30
+
31
+ context "with unparsable content at the end" do
32
+ let(:env) { { "HTTP_X_REQUEST_START" => "t=#{header_time_value}aaaa" } }
33
+
34
+ it { is_expected.to eq 1_389_783_599_600 }
35
+ end
36
+
37
+ context "with a really low number" do
38
+ let(:env) { { "HTTP_X_REQUEST_START" => "t=100" } }
39
+
40
+ it { is_expected.to be_nil }
41
+ end
42
+
43
+ context "with the alternate HTTP_X_QUEUE_START header set" do
44
+ let(:env) { { "HTTP_X_QUEUE_START" => "t=#{header_time_value}" } }
45
+
46
+ it { is_expected.to eq 1_389_783_599_600 }
47
+ end
48
+ end
49
+ end
50
+
51
+ context "time in milliseconds" do
52
+ let(:factor) { 1_000 }
53
+
54
+ it_should_behave_like "HTTP queue start"
55
+ end
56
+
57
+ context "time in microseconds" do
58
+ let(:factor) { 1_000_000 }
59
+
60
+ it_should_behave_like "HTTP queue start"
61
+ end
62
+ end
63
+ end