appsignal 2.1.2 → 2.2.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +14 -0
  3. data/.rubocop_todo.yml +16 -171
  4. data/.travis.yml +14 -1
  5. data/.yardopts +8 -0
  6. data/CHANGELOG.md +21 -3
  7. data/README.md +20 -2
  8. data/Rakefile +60 -62
  9. data/appsignal.gemspec +24 -23
  10. data/ext/agent.yml +11 -11
  11. data/ext/appsignal_extension.c +43 -12
  12. data/ext/extconf.rb +9 -9
  13. data/gemfiles/padrino.gemfile +1 -1
  14. data/lib/appsignal.rb +403 -26
  15. data/lib/appsignal/auth_check.rb +28 -1
  16. data/lib/appsignal/cli.rb +1 -0
  17. data/lib/appsignal/cli/demo.rb +40 -0
  18. data/lib/appsignal/cli/diagnose.rb +345 -89
  19. data/lib/appsignal/cli/helpers.rb +9 -4
  20. data/lib/appsignal/cli/install.rb +6 -6
  21. data/lib/appsignal/cli/notify_of_deploy.rb +58 -0
  22. data/lib/appsignal/config.rb +40 -38
  23. data/lib/appsignal/demo.rb +20 -0
  24. data/lib/appsignal/event_formatter.rb +4 -0
  25. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +1 -0
  26. data/lib/appsignal/event_formatter/active_record/instantiation_formatter.rb +1 -0
  27. data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +1 -0
  28. data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +1 -0
  29. data/lib/appsignal/event_formatter/faraday/request_formatter.rb +1 -0
  30. data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +2 -1
  31. data/lib/appsignal/event_formatter/moped/query_formatter.rb +1 -0
  32. data/lib/appsignal/extension.rb +1 -0
  33. data/lib/appsignal/garbage_collection_profiler.rb +20 -19
  34. data/lib/appsignal/hooks.rb +1 -0
  35. data/lib/appsignal/hooks/active_support_notifications.rb +1 -0
  36. data/lib/appsignal/hooks/celluloid.rb +1 -0
  37. data/lib/appsignal/hooks/data_mapper.rb +1 -0
  38. data/lib/appsignal/hooks/delayed_job.rb +1 -0
  39. data/lib/appsignal/hooks/mongo_ruby_driver.rb +1 -0
  40. data/lib/appsignal/hooks/net_http.rb +1 -0
  41. data/lib/appsignal/hooks/passenger.rb +1 -0
  42. data/lib/appsignal/hooks/puma.rb +1 -0
  43. data/lib/appsignal/hooks/rake.rb +1 -0
  44. data/lib/appsignal/hooks/redis.rb +1 -0
  45. data/lib/appsignal/hooks/sequel.rb +1 -0
  46. data/lib/appsignal/hooks/shoryuken.rb +1 -0
  47. data/lib/appsignal/hooks/sidekiq.rb +1 -0
  48. data/lib/appsignal/hooks/unicorn.rb +1 -0
  49. data/lib/appsignal/hooks/webmachine.rb +1 -0
  50. data/lib/appsignal/integrations/capistrano/appsignal.cap +4 -4
  51. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +2 -0
  52. data/lib/appsignal/integrations/data_mapper.rb +1 -1
  53. data/lib/appsignal/integrations/delayed_job_plugin.rb +1 -0
  54. data/lib/appsignal/integrations/grape.rb +3 -1
  55. data/lib/appsignal/integrations/mongo_ruby_driver.rb +1 -0
  56. data/lib/appsignal/integrations/padrino.rb +36 -21
  57. data/lib/appsignal/integrations/railtie.rb +2 -2
  58. data/lib/appsignal/integrations/resque.rb +1 -0
  59. data/lib/appsignal/integrations/resque_active_job.rb +1 -0
  60. data/lib/appsignal/integrations/webmachine.rb +27 -24
  61. data/lib/appsignal/js_exception_transaction.rb +8 -8
  62. data/lib/appsignal/marker.rb +41 -4
  63. data/lib/appsignal/minutely.rb +1 -0
  64. data/lib/appsignal/rack/generic_instrumentation.rb +3 -2
  65. data/lib/appsignal/rack/js_exception_catcher.rb +55 -15
  66. data/lib/appsignal/rack/rails_instrumentation.rb +2 -1
  67. data/lib/appsignal/rack/sinatra_instrumentation.rb +4 -2
  68. data/lib/appsignal/rack/streaming_listener.rb +4 -2
  69. data/lib/appsignal/system.rb +5 -31
  70. data/lib/appsignal/transaction.rb +71 -6
  71. data/lib/appsignal/transmitter.rb +24 -13
  72. data/lib/appsignal/utils.rb +18 -11
  73. data/lib/appsignal/utils/params_sanitizer.rb +2 -1
  74. data/lib/appsignal/utils/query_params_sanitizer.rb +1 -0
  75. data/lib/appsignal/version.rb +1 -1
  76. data/resources/appsignal.yml.erb +1 -1
  77. data/spec/lib/appsignal/auth_check_spec.rb +64 -22
  78. data/spec/lib/appsignal/cli/diagnose_spec.rb +539 -81
  79. data/spec/lib/appsignal/cli/helpers_spec.rb +74 -2
  80. data/spec/lib/appsignal/cli/install_spec.rb +3 -3
  81. data/spec/lib/appsignal/config_spec.rb +38 -72
  82. data/spec/lib/appsignal/demo_spec.rb +2 -2
  83. data/spec/lib/appsignal/event_formatter_spec.rb +2 -2
  84. data/spec/lib/appsignal/extension_spec.rb +4 -0
  85. data/spec/lib/appsignal/garbage_collection_profiler_spec.rb +18 -21
  86. data/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb +1 -1
  87. data/spec/lib/appsignal/hooks/redis_spec.rb +34 -44
  88. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +2 -2
  89. data/spec/lib/appsignal/integrations/grape_spec.rb +6 -6
  90. data/spec/lib/appsignal/integrations/padrino_spec.rb +241 -122
  91. data/spec/lib/appsignal/integrations/railtie_spec.rb +34 -16
  92. data/spec/lib/appsignal/integrations/resque_spec.rb +1 -1
  93. data/spec/lib/appsignal/integrations/sinatra_spec.rb +38 -10
  94. data/spec/lib/appsignal/integrations/webmachine_spec.rb +2 -2
  95. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +7 -6
  96. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +95 -58
  97. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +9 -6
  98. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +11 -10
  99. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +20 -13
  100. data/spec/lib/appsignal/system_spec.rb +2 -32
  101. data/spec/lib/appsignal/transaction_spec.rb +48 -7
  102. data/spec/lib/appsignal/transmitter_spec.rb +52 -33
  103. data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +3 -1
  104. data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +3 -3
  105. data/spec/lib/appsignal/utils_spec.rb +49 -6
  106. data/spec/lib/appsignal_spec.rb +60 -5
  107. data/spec/spec_helper.rb +4 -3
  108. data/spec/support/helpers/api_request_helper.rb +1 -4
  109. data/spec/support/helpers/dependency_helper.rb +4 -0
  110. data/spec/support/helpers/system_helpers.rb +1 -17
  111. data/spec/support/helpers/time_helpers.rb +1 -1
  112. metadata +19 -8
  113. data/spec/lib/appsignal/system/container_spec.rb +0 -67
  114. data/spec/lib/appsignal/utils/gzip_spec.rb +0 -10
@@ -8,7 +8,7 @@ describe Appsignal::Hooks::MongoRubyDriverHook do
8
8
  before(:context) do
9
9
  module Mongo
10
10
  module Monitoring
11
- COMMAND = "command"
11
+ COMMAND = "command".freeze
12
12
 
13
13
  class Global
14
14
  def subscribe
@@ -1,52 +1,50 @@
1
1
  describe Appsignal::Hooks::RedisHook do
2
- before :context do
3
- start_agent
2
+ before do
3
+ Appsignal.config = project_fixture_config
4
+ Appsignal::Hooks.load_hooks
4
5
  end
5
6
 
6
- context "with redis" do
7
- before :context do
8
- class Redis
9
- class Client
10
- def process(_commands)
11
- 1
12
- end
7
+ if DependencyHelper.redis_present?
8
+ context "with redis" do
9
+ context "with instrumentation enabled" do
10
+ before do
11
+ Appsignal.config.config_hash[:instrument_redis] = true
12
+ allow_any_instance_of(Redis::Client).to receive(:process_without_appsignal).and_return(1)
13
13
  end
14
- VERSION = "1.0"
15
- end
16
- end
17
14
 
18
- context "with instrumentation enabled" do
19
- before :context do
20
- Appsignal.config.config_hash[:instrument_redis] = true
21
- Appsignal::Hooks::RedisHook.new.install
22
- end
23
- after(:context) { Object.send(:remove_const, :Redis) }
15
+ describe "#dependencies_present?" do
16
+ subject { described_class.new.dependencies_present? }
24
17
 
25
- describe "#dependencies_present?" do
26
- subject { described_class.new.dependencies_present? }
18
+ it { is_expected.to be_truthy }
19
+ end
20
+
21
+ it "should instrument a redis call" do
22
+ Appsignal::Transaction.create("uuid", Appsignal::Transaction::HTTP_REQUEST, "test")
23
+ expect(Appsignal::Transaction.current).to receive(:start_event)
24
+ .at_least(:once)
25
+ expect(Appsignal::Transaction.current).to receive(:finish_event)
26
+ .at_least(:once)
27
+ .with("query.redis", nil, nil, 0)
27
28
 
28
- it { is_expected.to be_truthy }
29
+ client = Redis::Client.new
30
+ expect(client.process([])).to eq 1
31
+ end
29
32
  end
30
33
 
31
- it "should instrument a redis call" do
32
- Appsignal::Transaction.create("uuid", Appsignal::Transaction::HTTP_REQUEST, "test")
33
- expect(Appsignal::Transaction.current).to receive(:start_event)
34
- .at_least(:once)
35
- expect(Appsignal::Transaction.current).to receive(:finish_event)
36
- .at_least(:once)
37
- .with("query.redis", nil, nil, 0)
34
+ context "with instrumentation disabled" do
35
+ before do
36
+ Appsignal.config.config_hash[:instrument_redis] = false
37
+ end
38
38
 
39
- client = Redis::Client.new
39
+ describe "#dependencies_present?" do
40
+ subject { described_class.new.dependencies_present? }
40
41
 
41
- expect(client.process([])).to eq 1
42
+ it { is_expected.to be_falsy }
43
+ end
42
44
  end
43
45
  end
44
-
45
- context "with instrumentation disabled" do
46
- before :context do
47
- Appsignal.config.config_hash[:instrument_net_http] = false
48
- end
49
-
46
+ else
47
+ context "without redis" do
50
48
  describe "#dependencies_present?" do
51
49
  subject { described_class.new.dependencies_present? }
52
50
 
@@ -54,12 +52,4 @@ describe Appsignal::Hooks::RedisHook do
54
52
  end
55
53
  end
56
54
  end
57
-
58
- context "without redis" do
59
- describe "#dependencies_present?" do
60
- subject { described_class.new.dependencies_present? }
61
-
62
- it { is_expected.to be_falsy }
63
- end
64
- end
65
55
  end
@@ -30,7 +30,7 @@ describe Appsignal::Hooks::SidekiqPlugin do
30
30
  "queue" => "default",
31
31
  "extra" => "data"
32
32
  },
33
- :params => ["Model", "1"],
33
+ :params => %w(Model 1),
34
34
  :queue_start => Time.parse("01-01-2001 10:00:00UTC"),
35
35
  :queue_time => 60_000.to_f
36
36
  )
@@ -63,7 +63,7 @@ describe Appsignal::Hooks::SidekiqPlugin do
63
63
  :metadata => {
64
64
  "queue" => "default"
65
65
  },
66
- :params => ["Model", "1"],
66
+ :params => %w(Model 1),
67
67
  :queue_start => Time.parse("01-01-2001 10:00:00UTC").to_f,
68
68
  :queue_time => 60_000.to_f
69
69
  )
@@ -64,7 +64,7 @@ if DependencyHelper.grape_present?
64
64
 
65
65
  it "sets metadata" do
66
66
  expect(transaction).to receive(:set_http_or_background_queue_start)
67
- expect(transaction).to receive(:set_action).with("POST::GrapeExample::Api#/ping")
67
+ expect(transaction).to receive(:set_action_if_nil).with("POST::GrapeExample::Api#/ping")
68
68
  expect(transaction).to receive(:set_metadata).with("path", "/ping")
69
69
  expect(transaction).to receive(:set_metadata).with("method", "POST")
70
70
  end
@@ -84,7 +84,7 @@ if DependencyHelper.grape_present?
84
84
 
85
85
  it "sets metadata" do
86
86
  expect(transaction).to receive(:set_http_or_background_queue_start)
87
- expect(transaction).to receive(:set_action).with("POST::GrapeExample::Api#/ping")
87
+ expect(transaction).to receive(:set_action_if_nil).with("POST::GrapeExample::Api#/ping")
88
88
  expect(transaction).to receive(:set_metadata).with("path", "/ping")
89
89
  expect(transaction).to receive(:set_metadata).with("method", "POST")
90
90
  end
@@ -143,7 +143,7 @@ if DependencyHelper.grape_present?
143
143
  end
144
144
 
145
145
  it "sets non-unique route_param path" do
146
- expect(transaction).to receive(:set_action).with("GET::GrapeExample::Api#/users/:id/")
146
+ expect(transaction).to receive(:set_action_if_nil).with("GET::GrapeExample::Api#/users/:id/")
147
147
  expect(transaction).to receive(:set_metadata).with("path", "/users/:id/")
148
148
  expect(transaction).to receive(:set_metadata).with("method", "GET")
149
149
  end
@@ -167,7 +167,7 @@ if DependencyHelper.grape_present?
167
167
  end
168
168
 
169
169
  it "sets namespaced path" do
170
- expect(transaction).to receive(:set_action).with("POST::GrapeExample::Api#/v1/beta/ping")
170
+ expect(transaction).to receive(:set_action_if_nil).with("POST::GrapeExample::Api#/v1/beta/ping")
171
171
  expect(transaction).to receive(:set_metadata).with("path", "/v1/beta/ping")
172
172
  expect(transaction).to receive(:set_metadata).with("method", "POST")
173
173
  end
@@ -189,7 +189,7 @@ if DependencyHelper.grape_present?
189
189
  end
190
190
 
191
191
  it "sets namespaced path" do
192
- expect(transaction).to receive(:set_action).with("POST::GrapeExample::Api#/v1/beta/ping")
192
+ expect(transaction).to receive(:set_action_if_nil).with("POST::GrapeExample::Api#/v1/beta/ping")
193
193
  expect(transaction).to receive(:set_metadata).with("path", "/v1/beta/ping")
194
194
  expect(transaction).to receive(:set_metadata).with("method", "POST")
195
195
  end
@@ -210,7 +210,7 @@ if DependencyHelper.grape_present?
210
210
  end
211
211
 
212
212
  it "sets namespaced path" do
213
- expect(transaction).to receive(:set_action).with("POST::GrapeExample::Api#/v1/beta/ping")
213
+ expect(transaction).to receive(:set_action_if_nil).with("POST::GrapeExample::Api#/v1/beta/ping")
214
214
  expect(transaction).to receive(:set_metadata).with("path", "/v1/beta/ping")
215
215
  expect(transaction).to receive(:set_metadata).with("method", "POST")
216
216
  end
@@ -1,10 +1,6 @@
1
1
  if DependencyHelper.padrino_present?
2
2
  describe "Padrino integration" do
3
- require File.expand_path("lib/appsignal/integrations/padrino.rb")
4
-
5
- class ClassWithRouter
6
- include Padrino::Routing
7
- end
3
+ require "appsignal/integrations/padrino"
8
4
 
9
5
  before do
10
6
  allow(Appsignal).to receive(:active?).and_return(true)
@@ -12,188 +8,311 @@ if DependencyHelper.padrino_present?
12
8
  allow(Appsignal).to receive(:start_logger).and_return(true)
13
9
  end
14
10
 
15
- describe "Appsignal::Integrations::PadrinoPlugin" do
16
- it "should start the logger on init" do
17
- expect(Appsignal).to receive(:start_logger)
11
+ describe Appsignal::Integrations::PadrinoPlugin do
12
+ it "starts AppSignal on init" do
13
+ expect(Appsignal).to receive(:start)
18
14
  end
19
15
 
20
- it "should start appsignal on init" do
21
- expect(Appsignal).to receive(:start)
16
+ it "starts the logger on init" do
17
+ expect(Appsignal).to receive(:start_logger)
22
18
  end
23
19
 
24
20
  context "when not active" do
25
21
  before { allow(Appsignal).to receive(:active?).and_return(false) }
26
22
 
27
- it "should not add the Listener middleware to the stack" do
23
+ it "does not add the listener middleware to the stack" do
28
24
  expect(Padrino).to_not receive(:use)
29
25
  end
30
26
  end
31
27
 
32
28
  context "when APPSIGNAL_APP_ENV ENV var is provided" do
33
- it "should use this as the environment" do
29
+ it "uses this as the environment" do
34
30
  ENV["APPSIGNAL_APP_ENV"] = "custom"
35
31
 
36
32
  # Reset the plugin to pull down the latest data
37
33
  Appsignal::Integrations::PadrinoPlugin.init
38
34
 
39
- expect(Appsignal.config.env).to eq "custom"
35
+ expect(Appsignal.config.env).to eq("custom")
40
36
  end
41
37
  end
42
38
 
43
39
  context "when APPSIGNAL_APP_ENV ENV var is not provided" do
44
- it "should use the Padrino environment" do
45
- ENV["APPSIGNAL_APP_ENV"] = nil
46
-
40
+ it "uses the Padrino environment" do
47
41
  # Reset the plugin to pull down the latest data
48
42
  Appsignal::Integrations::PadrinoPlugin.init
49
43
 
50
- expect(Appsignal.config.env).to eq Padrino.env.to_s
44
+ expect(Padrino.env.to_s).to eq("test")
45
+ expect(Appsignal.config.env).to eq(Padrino.env.to_s)
51
46
  end
52
47
  end
53
48
 
54
49
  after { Appsignal::Integrations::PadrinoPlugin.init }
55
50
  end
56
51
 
57
- describe "Padrino::Routing::InstanceMethods" do
52
+ describe Padrino::Routing::InstanceMethods do
53
+ class PadrinoClassWithRouter
54
+ include Padrino::Routing
55
+ end
56
+
58
57
  let(:base) { double }
59
- let(:router) { ClassWithRouter.new }
58
+ let(:router) { PadrinoClassWithRouter.new }
60
59
  let(:env) { {} }
61
60
  let(:settings) { double(:name => "TestApp") }
62
61
 
63
- describe "#route!" do
64
- let(:request) do
65
- double(
66
- :params => { "id" => 1 },
67
- :session => { "user_id" => 123 },
68
- :request_method => "GET",
69
- :path => "/users/1",
70
- :controller => "users",
71
- :action => "show",
72
- :env => {}
73
- )
62
+ describe "routes" do
63
+ let(:transaction) do
64
+ instance_double "Appsignal::Transaction",
65
+ :set_http_or_background_action => nil,
66
+ :set_http_or_background_queue_start => nil,
67
+ :set_metadata => nil,
68
+ :set_action => nil,
69
+ :set_action_if_nil => nil,
70
+ :set_error => nil,
71
+ :start_event => nil,
72
+ :finish_event => nil,
73
+ :complete => nil
74
74
  end
75
-
75
+ let(:request_kind) { kind_of(Sinatra::Request) }
76
+ let(:env) do
77
+ {
78
+ "REQUEST_METHOD" => "GET",
79
+ "PATH_INFO" => path,
80
+ "REQUEST_PATH" => path,
81
+ "rack.input" => StringIO.new
82
+ }
83
+ end
84
+ let(:app) do
85
+ Class.new(Padrino::Application) do
86
+ def self.name
87
+ "PadrinoTestApp"
88
+ end
89
+ end
90
+ end
91
+ let(:response) { app.call(env) }
76
92
  before do
77
- allow(router).to receive(:route_without_appsignal).and_return(true)
78
- allow(router).to receive(:request).and_return(request)
79
- allow(router).to receive(:env).and_return(env)
80
- allow(router).to receive(:settings).and_return(settings)
81
- allow(router).to receive(:get_payload_action).and_return("controller#action")
93
+ allow(Appsignal::Transaction).to receive(:create).and_return(transaction)
94
+ allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
82
95
  end
83
96
 
84
- context "when Sinatra tells us it's a static file" do
85
- let(:env) { { "sinatra.static_file" => true } }
86
-
87
- it "should call the original method" do
88
- expect(router).to receive(:route_without_appsignal)
97
+ RSpec::Matchers.define :match_response do |expected_status, expected_content|
98
+ match do |response|
99
+ status, _headers, content = response
100
+ status == expected_status && content == [expected_content].compact
89
101
  end
102
+ end
90
103
 
91
- it "should not instrument the request" do
92
- expect(Appsignal).to_not receive(:instrument)
93
- end
104
+ def expect_a_transaction_to_be_created
105
+ expect(Appsignal::Transaction).to receive(:create).with(
106
+ kind_of(String),
107
+ Appsignal::Transaction::HTTP_REQUEST,
108
+ request_kind
109
+ ).and_return(transaction)
110
+ expect(Appsignal).to receive(:instrument)
111
+ .at_least(:once)
112
+ .with("process_action.padrino")
113
+ .and_call_original
114
+ expect(transaction).to receive(:set_metadata).with("path", path)
115
+ expect(transaction).to receive(:set_metadata).with("method", "GET")
116
+ expect(transaction).to receive(:complete)
117
+ end
94
118
 
95
- after { router.route!(base) }
119
+ def expect_no_transaction_to_be_created
120
+ expect(Appsignal::Transaction).to_not receive(:create)
121
+ expect(Appsignal).to_not receive(:instrument)
96
122
  end
97
123
 
98
- context "when appsignal is not active" do
124
+ context "when AppSignal is not active" do
99
125
  before { allow(Appsignal).to receive(:active?).and_return(false) }
126
+ let(:path) { "/foo" }
127
+ before { app.controllers { get(:foo) { "content" } } }
128
+ after { expect(response).to match_response(200, "content") }
100
129
 
101
- it "should call the original method" do
102
- expect(router).to receive(:route_without_appsignal)
103
- end
104
-
105
- it "should not instrument the request" do
106
- expect(Appsignal).to_not receive(:instrument)
130
+ it "does not instrument the request" do
131
+ expect_no_transaction_to_be_created
107
132
  end
108
-
109
- after { router.route!(base) }
110
133
  end
111
134
 
112
- context "with a dynamic request" do
113
- let(:transaction) do
114
- double(
115
- :set_http_or_background_action => nil,
116
- :set_http_or_background_queue_start => nil,
117
- :set_metadata => nil,
118
- :set_action => nil,
119
- :set_error => nil
120
- )
121
- end
122
- before { allow(Appsignal::Transaction).to receive(:create).and_return(transaction) }
123
-
124
- context "without an error" do
125
- it "should create a transaction" do
126
- expect(Appsignal::Transaction).to receive(:create).with(
127
- kind_of(String),
128
- Appsignal::Transaction::HTTP_REQUEST,
129
- request
130
- ).and_return(transaction)
131
- end
132
-
133
- it "should call the original routing method" do
134
- expect(router).to receive(:route_without_appsignal)
135
- end
135
+ context "when AppSignal is active" do
136
+ context "with not existing route" do
137
+ let(:path) { "/404" }
136
138
 
137
- it "should instrument the action" do
138
- expect(Appsignal).to receive(:instrument).with("process_action.padrino")
139
+ it "instruments the request" do
140
+ expect_a_transaction_to_be_created
141
+ # Uses path for action name
142
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#/404")
143
+ expect(response).to match_response(404, "<h1>Not Found</h1>")
139
144
  end
145
+ end
140
146
 
141
- it "should set metadata" do
142
- expect(transaction).to receive(:set_metadata).twice
147
+ context "when Sinatra tells us it's a static file" do
148
+ let(:path) { "/static" }
149
+ before do
150
+ env["sinatra.static_file"] = true
151
+ expect_any_instance_of(app)
152
+ .to receive(:route_without_appsignal).and_return([200, {}, ["foo"]])
143
153
  end
154
+ after { expect(response).to match_response(200, "foo") }
144
155
 
145
- it "should set the action on the transaction" do
146
- expect(transaction).to receive(:set_action).with("controller#action")
156
+ it "does not instrument the request" do
157
+ expect_no_transaction_to_be_created
147
158
  end
148
-
149
- after { router.route!(base) }
150
159
  end
151
160
 
152
- context "with an error" do
153
- let(:error) { VerySpecificError.new }
154
- before { allow(router).to receive(:route_without_appsignal).and_raise(error) }
155
-
156
- it "should add the exception to the current transaction" do
157
- expect(transaction).to receive(:set_error).with(error)
158
-
159
- router.route!(base) rescue VerySpecificError
161
+ # Older Padrino versions don't support `action` (v11.0+)
162
+ context "without #action on Sinatra::Request" do
163
+ let(:path) { "/my_original_path" }
164
+ let(:request_kind) do
165
+ double(
166
+ :params => {},
167
+ :path => path,
168
+ :path_info => path,
169
+ :request_method => "GET",
170
+ :head? => false,
171
+ :route_obj => double(:original_path => "original_path method"),
172
+ :route_obj= => nil # Make sure route_obj doesn't get overwritten
173
+ )
160
174
  end
161
- end
162
- end
163
- end
164
-
165
- describe "#get_payload_action" do
166
- before { allow(router).to receive(:settings).and_return(settings) }
167
-
168
- context "when request is nil" do
169
- it "should return the site" do
170
- expect(router.get_payload_action(nil)).to eql("TestApp")
171
- end
172
- end
173
-
174
- context "when there's no route object" do
175
- let(:request) { double(:controller => "Controller", :action => "action") }
175
+ before do
176
+ expect(Sinatra::Request).to receive(:new).and_return(request_kind)
177
+ app.controllers { get(:my_original_path) { "content" } }
178
+ end
179
+ after { expect(response).to match_response(200, "content") }
176
180
 
177
- it "should return the site name, controller and action" do
178
- expect(router.get_payload_action(request)).to eql("TestApp:Controller#action")
181
+ it "falls back on Sinatra::Request#route_obj.original_path" do
182
+ expect_a_transaction_to_be_created
183
+ expect(transaction)
184
+ .to receive(:set_action_if_nil).with("PadrinoTestApp:original_path method")
185
+ end
179
186
  end
180
187
 
181
- context "when there's no action" do
182
- let(:request) { double(:controller => "Controller", :fullpath => "/action") }
188
+ context "without Sinatra::Request#route_obj.original_path" do
189
+ let(:path) { "/my_original_path" }
190
+ let(:request_kind) do
191
+ double(
192
+ :params => {},
193
+ :path => path,
194
+ :path_info => path,
195
+ :request_method => "GET",
196
+ :head? => false,
197
+ :route_obj= => nil # Make sure route_obj doesn't get overwritten
198
+ )
199
+ end
200
+ before do
201
+ expect(Sinatra::Request).to receive(:new).and_return(request_kind)
202
+ app.controllers { get(:my_original_path) { "content" } }
203
+ end
204
+ after { expect(response).to match_response(200, "content") }
183
205
 
184
- it "should return the site name, controller and fullpath" do
185
- expect(router.get_payload_action(request)).to eql("TestApp:Controller#/action")
206
+ it "falls back on app name" do
207
+ expect_a_transaction_to_be_created
208
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp")
186
209
  end
187
210
  end
188
- end
189
211
 
190
- context "when request has a route object" do
191
- let(:request) { double }
192
- let(:route_object) { double(:original_path => "/accounts/edit/:id") }
193
- before { allow(request).to receive(:route_obj).and_return(route_object) }
212
+ context "with existing route" do
213
+ context "with an exception in the controller" do
214
+ let(:path) { "/exception" }
215
+ before do
216
+ app.controllers { get(:exception) { raise VerySpecificError } }
217
+ expect_a_transaction_to_be_created
218
+ end
219
+ after do
220
+ expect { response }.to raise_error(VerySpecificError)
221
+ end
222
+
223
+ it "sets the action name based on the app name and action name" do
224
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#exception")
225
+ end
226
+
227
+ it "sets the error on the transaction" do
228
+ expect(transaction).to receive(:set_error).with(VerySpecificError)
229
+ end
230
+ end
194
231
 
195
- it "should return the original path" do
196
- expect(router.get_payload_action(request)).to eql("TestApp:/accounts/edit/:id")
232
+ context "without an exception in the controller" do
233
+ let(:path) { "/" }
234
+ after { expect(response).to match_response(200, "content") }
235
+
236
+ context "with action name as symbol" do
237
+ context "with :index helper" do
238
+ before do
239
+ # :index == "/"
240
+ app.controllers { get(:index) { "content" } }
241
+ end
242
+
243
+ it "sets the action with the app name and action name" do
244
+ expect_a_transaction_to_be_created
245
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#index")
246
+ end
247
+ end
248
+
249
+ context "with custom action name" do
250
+ let(:path) { "/foo" }
251
+ before do
252
+ app.controllers { get(:foo) { "content" } }
253
+ end
254
+
255
+ it "sets the action with the app name and action name" do
256
+ expect_a_transaction_to_be_created
257
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#foo")
258
+ end
259
+ end
260
+ end
261
+
262
+ context "with an action defined with a path" do
263
+ context "with root path" do
264
+ before do
265
+ # :index == "/"
266
+ app.controllers { get("/") { "content" } }
267
+ end
268
+
269
+ it "sets the action with the app name and action path" do
270
+ expect_a_transaction_to_be_created
271
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#/")
272
+ end
273
+ end
274
+
275
+ context "with custom path" do
276
+ let(:path) { "/foo" }
277
+ before do
278
+ app.controllers { get("/foo") { "content" } }
279
+ end
280
+
281
+ it "sets the action with the app name and action path" do
282
+ expect_a_transaction_to_be_created
283
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#/foo")
284
+ end
285
+ end
286
+ end
287
+
288
+ context "with controller" do
289
+ let(:path) { "/my_controller" }
290
+
291
+ context "with controller as name" do
292
+ before do
293
+ # :index == "/"
294
+ app.controllers(:my_controller) { get(:index) { "content" } }
295
+ end
296
+
297
+ it "sets the action with the app name, controller name and action name" do
298
+ expect_a_transaction_to_be_created
299
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:my_controller#index")
300
+ end
301
+ end
302
+
303
+ context "with controller as path" do
304
+ before do
305
+ # :index == "/"
306
+ app.controllers("/my_controller") { get(:index) { "content" } }
307
+ end
308
+
309
+ it "sets the action with the app name, controller name and action path" do
310
+ expect_a_transaction_to_be_created
311
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:/my_controller#index")
312
+ end
313
+ end
314
+ end
315
+ end
197
316
  end
198
317
  end
199
318
  end