appsignal 2.5.0.alpha.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.rspec +4 -0
  4. data/.rubocop.yml +66 -0
  5. data/.rubocop_todo.yml +124 -0
  6. data/.travis.yml +72 -0
  7. data/.yardopts +8 -0
  8. data/CHANGELOG.md +639 -0
  9. data/Gemfile +3 -0
  10. data/LICENSE +20 -0
  11. data/README.md +264 -0
  12. data/Rakefile +214 -0
  13. data/appsignal.gemspec +42 -0
  14. data/benchmark.rake +77 -0
  15. data/bin/appsignal +13 -0
  16. data/ext/Rakefile +27 -0
  17. data/ext/agent.yml +64 -0
  18. data/ext/appsignal_extension.c +692 -0
  19. data/ext/base.rb +79 -0
  20. data/ext/extconf.rb +35 -0
  21. data/gemfiles/capistrano2.gemfile +7 -0
  22. data/gemfiles/capistrano3.gemfile +7 -0
  23. data/gemfiles/grape.gemfile +7 -0
  24. data/gemfiles/no_dependencies.gemfile +5 -0
  25. data/gemfiles/padrino.gemfile +7 -0
  26. data/gemfiles/que.gemfile +5 -0
  27. data/gemfiles/rails-3.2.gemfile +6 -0
  28. data/gemfiles/rails-4.0.gemfile +6 -0
  29. data/gemfiles/rails-4.1.gemfile +6 -0
  30. data/gemfiles/rails-4.2.gemfile +10 -0
  31. data/gemfiles/rails-5.0.gemfile +5 -0
  32. data/gemfiles/rails-5.1.gemfile +5 -0
  33. data/gemfiles/resque.gemfile +12 -0
  34. data/gemfiles/sequel-435.gemfile +11 -0
  35. data/gemfiles/sequel.gemfile +11 -0
  36. data/gemfiles/sinatra.gemfile +6 -0
  37. data/gemfiles/webmachine.gemfile +5 -0
  38. data/lib/appsignal.rb +804 -0
  39. data/lib/appsignal/auth_check.rb +65 -0
  40. data/lib/appsignal/capistrano.rb +10 -0
  41. data/lib/appsignal/cli.rb +108 -0
  42. data/lib/appsignal/cli/demo.rb +63 -0
  43. data/lib/appsignal/cli/diagnose.rb +500 -0
  44. data/lib/appsignal/cli/helpers.rb +72 -0
  45. data/lib/appsignal/cli/install.rb +277 -0
  46. data/lib/appsignal/cli/notify_of_deploy.rb +113 -0
  47. data/lib/appsignal/config.rb +287 -0
  48. data/lib/appsignal/demo.rb +107 -0
  49. data/lib/appsignal/event_formatter.rb +74 -0
  50. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +24 -0
  51. data/lib/appsignal/event_formatter/active_record/instantiation_formatter.rb +14 -0
  52. data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +14 -0
  53. data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +32 -0
  54. data/lib/appsignal/event_formatter/faraday/request_formatter.rb +19 -0
  55. data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +89 -0
  56. data/lib/appsignal/event_formatter/moped/query_formatter.rb +80 -0
  57. data/lib/appsignal/extension.rb +63 -0
  58. data/lib/appsignal/extension/jruby.rb +460 -0
  59. data/lib/appsignal/garbage_collection_profiler.rb +48 -0
  60. data/lib/appsignal/hooks.rb +105 -0
  61. data/lib/appsignal/hooks/action_cable.rb +113 -0
  62. data/lib/appsignal/hooks/active_support_notifications.rb +52 -0
  63. data/lib/appsignal/hooks/celluloid.rb +30 -0
  64. data/lib/appsignal/hooks/data_mapper.rb +18 -0
  65. data/lib/appsignal/hooks/delayed_job.rb +19 -0
  66. data/lib/appsignal/hooks/mongo_ruby_driver.rb +21 -0
  67. data/lib/appsignal/hooks/net_http.rb +29 -0
  68. data/lib/appsignal/hooks/passenger.rb +22 -0
  69. data/lib/appsignal/hooks/puma.rb +35 -0
  70. data/lib/appsignal/hooks/que.rb +21 -0
  71. data/lib/appsignal/hooks/rake.rb +39 -0
  72. data/lib/appsignal/hooks/redis.rb +30 -0
  73. data/lib/appsignal/hooks/sequel.rb +60 -0
  74. data/lib/appsignal/hooks/shoryuken.rb +43 -0
  75. data/lib/appsignal/hooks/sidekiq.rb +144 -0
  76. data/lib/appsignal/hooks/unicorn.rb +40 -0
  77. data/lib/appsignal/hooks/webmachine.rb +23 -0
  78. data/lib/appsignal/integrations/capistrano/appsignal.cap +39 -0
  79. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +52 -0
  80. data/lib/appsignal/integrations/data_mapper.rb +33 -0
  81. data/lib/appsignal/integrations/delayed_job_plugin.rb +54 -0
  82. data/lib/appsignal/integrations/grape.rb +53 -0
  83. data/lib/appsignal/integrations/mongo_ruby_driver.rb +55 -0
  84. data/lib/appsignal/integrations/object.rb +35 -0
  85. data/lib/appsignal/integrations/padrino.rb +84 -0
  86. data/lib/appsignal/integrations/que.rb +43 -0
  87. data/lib/appsignal/integrations/railtie.rb +41 -0
  88. data/lib/appsignal/integrations/rake.rb +2 -0
  89. data/lib/appsignal/integrations/resque.rb +20 -0
  90. data/lib/appsignal/integrations/resque_active_job.rb +30 -0
  91. data/lib/appsignal/integrations/sinatra.rb +17 -0
  92. data/lib/appsignal/integrations/webmachine.rb +38 -0
  93. data/lib/appsignal/js_exception_transaction.rb +54 -0
  94. data/lib/appsignal/marker.rb +63 -0
  95. data/lib/appsignal/minutely.rb +42 -0
  96. data/lib/appsignal/rack/generic_instrumentation.rb +49 -0
  97. data/lib/appsignal/rack/js_exception_catcher.rb +70 -0
  98. data/lib/appsignal/rack/rails_instrumentation.rb +51 -0
  99. data/lib/appsignal/rack/sinatra_instrumentation.rb +99 -0
  100. data/lib/appsignal/rack/streaming_listener.rb +73 -0
  101. data/lib/appsignal/system.rb +81 -0
  102. data/lib/appsignal/transaction.rb +498 -0
  103. data/lib/appsignal/transmitter.rb +107 -0
  104. data/lib/appsignal/utils.rb +127 -0
  105. data/lib/appsignal/utils/params_sanitizer.rb +59 -0
  106. data/lib/appsignal/utils/query_params_sanitizer.rb +55 -0
  107. data/lib/appsignal/version.rb +3 -0
  108. data/lib/sequel/extensions/appsignal_integration.rb +3 -0
  109. data/resources/appsignal.yml.erb +39 -0
  110. data/resources/cacert.pem +3866 -0
  111. data/spec/.rubocop.yml +7 -0
  112. data/spec/lib/appsignal/auth_check_spec.rb +80 -0
  113. data/spec/lib/appsignal/capistrano2_spec.rb +224 -0
  114. data/spec/lib/appsignal/capistrano3_spec.rb +237 -0
  115. data/spec/lib/appsignal/cli/demo_spec.rb +67 -0
  116. data/spec/lib/appsignal/cli/diagnose_spec.rb +988 -0
  117. data/spec/lib/appsignal/cli/helpers_spec.rb +171 -0
  118. data/spec/lib/appsignal/cli/install_spec.rb +632 -0
  119. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +168 -0
  120. data/spec/lib/appsignal/cli_spec.rb +56 -0
  121. data/spec/lib/appsignal/config_spec.rb +637 -0
  122. data/spec/lib/appsignal/demo_spec.rb +87 -0
  123. data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +44 -0
  124. data/spec/lib/appsignal/event_formatter/active_record/instantiation_formatter_spec.rb +21 -0
  125. data/spec/lib/appsignal/event_formatter/active_record/sql_formatter_spec.rb +21 -0
  126. data/spec/lib/appsignal/event_formatter/elastic_search/search_formatter_spec.rb +52 -0
  127. data/spec/lib/appsignal/event_formatter/faraday/request_formatter_spec.rb +21 -0
  128. data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +113 -0
  129. data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +112 -0
  130. data/spec/lib/appsignal/event_formatter_spec.rb +100 -0
  131. data/spec/lib/appsignal/extension/jruby_spec.rb +43 -0
  132. data/spec/lib/appsignal/extension_spec.rb +137 -0
  133. data/spec/lib/appsignal/garbage_collection_profiler_spec.rb +66 -0
  134. data/spec/lib/appsignal/hooks/action_cable_spec.rb +370 -0
  135. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +92 -0
  136. data/spec/lib/appsignal/hooks/celluloid_spec.rb +35 -0
  137. data/spec/lib/appsignal/hooks/data_mapper_spec.rb +39 -0
  138. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +358 -0
  139. data/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb +44 -0
  140. data/spec/lib/appsignal/hooks/net_http_spec.rb +53 -0
  141. data/spec/lib/appsignal/hooks/passenger_spec.rb +30 -0
  142. data/spec/lib/appsignal/hooks/puma_spec.rb +80 -0
  143. data/spec/lib/appsignal/hooks/que_spec.rb +19 -0
  144. data/spec/lib/appsignal/hooks/rake_spec.rb +73 -0
  145. data/spec/lib/appsignal/hooks/redis_spec.rb +55 -0
  146. data/spec/lib/appsignal/hooks/sequel_spec.rb +46 -0
  147. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +192 -0
  148. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +419 -0
  149. data/spec/lib/appsignal/hooks/unicorn_spec.rb +52 -0
  150. data/spec/lib/appsignal/hooks/webmachine_spec.rb +35 -0
  151. data/spec/lib/appsignal/hooks_spec.rb +195 -0
  152. data/spec/lib/appsignal/integrations/data_mapper_spec.rb +65 -0
  153. data/spec/lib/appsignal/integrations/grape_spec.rb +225 -0
  154. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +127 -0
  155. data/spec/lib/appsignal/integrations/object_spec.rb +249 -0
  156. data/spec/lib/appsignal/integrations/padrino_spec.rb +323 -0
  157. data/spec/lib/appsignal/integrations/que_spec.rb +174 -0
  158. data/spec/lib/appsignal/integrations/railtie_spec.rb +129 -0
  159. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +83 -0
  160. data/spec/lib/appsignal/integrations/resque_spec.rb +92 -0
  161. data/spec/lib/appsignal/integrations/sinatra_spec.rb +73 -0
  162. data/spec/lib/appsignal/integrations/webmachine_spec.rb +69 -0
  163. data/spec/lib/appsignal/js_exception_transaction_spec.rb +128 -0
  164. data/spec/lib/appsignal/marker_spec.rb +51 -0
  165. data/spec/lib/appsignal/minutely_spec.rb +50 -0
  166. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +90 -0
  167. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +147 -0
  168. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +117 -0
  169. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +213 -0
  170. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +161 -0
  171. data/spec/lib/appsignal/system_spec.rb +131 -0
  172. data/spec/lib/appsignal/transaction_spec.rb +1146 -0
  173. data/spec/lib/appsignal/transmitter_spec.rb +152 -0
  174. data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +136 -0
  175. data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +192 -0
  176. data/spec/lib/appsignal/utils_spec.rb +150 -0
  177. data/spec/lib/appsignal_spec.rb +1049 -0
  178. data/spec/spec_helper.rb +116 -0
  179. data/spec/support/fixtures/containers/cgroups/docker +14 -0
  180. data/spec/support/fixtures/containers/cgroups/docker_systemd +8 -0
  181. data/spec/support/fixtures/containers/cgroups/lxc +10 -0
  182. data/spec/support/fixtures/containers/cgroups/no_permission +0 -0
  183. data/spec/support/fixtures/containers/cgroups/none +1 -0
  184. data/spec/support/fixtures/generated_config.yml +24 -0
  185. data/spec/support/fixtures/uploaded_file.txt +0 -0
  186. data/spec/support/helpers/api_request_helper.rb +19 -0
  187. data/spec/support/helpers/cli_helpers.rb +26 -0
  188. data/spec/support/helpers/config_helpers.rb +21 -0
  189. data/spec/support/helpers/dependency_helper.rb +73 -0
  190. data/spec/support/helpers/directory_helper.rb +27 -0
  191. data/spec/support/helpers/env_helpers.rb +33 -0
  192. data/spec/support/helpers/example_exception.rb +13 -0
  193. data/spec/support/helpers/example_standard_error.rb +13 -0
  194. data/spec/support/helpers/log_helpers.rb +22 -0
  195. data/spec/support/helpers/std_streams_helper.rb +66 -0
  196. data/spec/support/helpers/system_helpers.rb +8 -0
  197. data/spec/support/helpers/time_helpers.rb +11 -0
  198. data/spec/support/helpers/transaction_helpers.rb +37 -0
  199. data/spec/support/matchers/contains_log.rb +7 -0
  200. data/spec/support/mocks/fake_gc_profiler.rb +19 -0
  201. data/spec/support/mocks/mock_extension.rb +6 -0
  202. data/spec/support/project_fixture/config/application.rb +0 -0
  203. data/spec/support/project_fixture/config/appsignal.yml +32 -0
  204. data/spec/support/project_fixture/config/environments/development.rb +0 -0
  205. data/spec/support/project_fixture/config/environments/production.rb +0 -0
  206. data/spec/support/project_fixture/config/environments/test.rb +0 -0
  207. data/spec/support/project_fixture/log/.gitkeep +0 -0
  208. data/spec/support/rails/my_app.rb +6 -0
  209. data/spec/support/shared_examples/instrument.rb +43 -0
  210. data/spec/support/stubs/delayed_job.rb +0 -0
  211. metadata +483 -0
@@ -0,0 +1,127 @@
1
+ require "appsignal/integrations/mongo_ruby_driver"
2
+ describe Appsignal::Hooks::MongoMonitorSubscriber do
3
+ let(:subscriber) { Appsignal::Hooks::MongoMonitorSubscriber.new }
4
+
5
+ context "with transaction" do
6
+ let!(:transaction) do
7
+ Appsignal::Transaction.create("1", "http_request", {}, {})
8
+ end
9
+
10
+ describe "#started" do
11
+ let(:event) do
12
+ double(
13
+ :request_id => 1,
14
+ :command_name => "find",
15
+ :command => { "foo" => "bar" }
16
+ )
17
+ end
18
+
19
+ it "should sanitize command" do
20
+ expect(Appsignal::EventFormatter::MongoRubyDriver::QueryFormatter)
21
+ .to receive(:format).with("find", "foo" => "bar")
22
+
23
+ subscriber.started(event)
24
+ end
25
+
26
+ it "should store command on the transaction" do
27
+ subscriber.started(event)
28
+
29
+ expect(transaction.store("mongo_driver")).to eq(1 => { "foo" => "?" })
30
+ end
31
+
32
+ it "should start an event in the extension" do
33
+ expect(transaction).to receive(:start_event)
34
+
35
+ subscriber.started(event)
36
+ end
37
+ end
38
+
39
+ describe "#succeeded" do
40
+ let(:event) { double }
41
+
42
+ it "should finish the event" do
43
+ expect(subscriber).to receive(:finish).with("SUCCEEDED", event)
44
+
45
+ subscriber.succeeded(event)
46
+ end
47
+ end
48
+
49
+ describe "#failed" do
50
+ let(:event) { double }
51
+
52
+ it "should finish the event" do
53
+ expect(subscriber).to receive(:finish).with("FAILED", event)
54
+
55
+ subscriber.failed(event)
56
+ end
57
+ end
58
+
59
+ describe "#finish" do
60
+ let(:command) { { "foo" => "?" } }
61
+ let(:event) do
62
+ double(
63
+ :request_id => 2,
64
+ :command_name => :find,
65
+ :database_name => "test"
66
+ )
67
+ end
68
+
69
+ before do
70
+ store = transaction.store("mongo_driver")
71
+ store[2] = command
72
+ end
73
+
74
+ it "should get the query from the store" do
75
+ expect(transaction).to receive(:store).with("mongo_driver").and_return(command)
76
+
77
+ subscriber.finish("SUCCEEDED", event)
78
+ end
79
+
80
+ it "should finish the transaction in the extension" do
81
+ expect(transaction).to receive(:finish_event).with(
82
+ "query.mongodb",
83
+ "find | test | SUCCEEDED",
84
+ Appsignal::Utils.data_generate("foo" => "?"),
85
+ 0
86
+ )
87
+
88
+ subscriber.finish("SUCCEEDED", event)
89
+ end
90
+ end
91
+ end
92
+
93
+ context "without transaction" do
94
+ before do
95
+ allow(Appsignal::Transaction).to receive(:current).and_return(Appsignal::Transaction::NilTransaction.new)
96
+ end
97
+
98
+ it "should not attempt to start an event" do
99
+ expect(Appsignal::Extension).to_not receive(:start_event)
100
+
101
+ subscriber.started(double)
102
+ end
103
+
104
+ it "should not attempt to finish an event" do
105
+ expect(Appsignal::Extension).to_not receive(:finish_event)
106
+
107
+ subscriber.finish("SUCCEEDED", double)
108
+ end
109
+ end
110
+
111
+ context "when appsignal is paused" do
112
+ let(:transaction) { double(:paused? => true, :nil_transaction? => false) }
113
+ before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
114
+
115
+ it "should not attempt to start an event" do
116
+ expect(Appsignal::Extension).to_not receive(:start_event)
117
+
118
+ subscriber.started(double)
119
+ end
120
+
121
+ it "should not attempt to finish an event" do
122
+ expect(Appsignal::Extension).to_not receive(:finish_event)
123
+
124
+ subscriber.finish("SUCCEEDED", double)
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,249 @@
1
+ require "appsignal/integrations/object"
2
+
3
+ describe Object do
4
+ describe "#instrument_method" do
5
+ context "with instance method" do
6
+ let(:klass) do
7
+ Class.new do
8
+ def foo
9
+ 1
10
+ end
11
+ appsignal_instrument_method :foo
12
+ end
13
+ end
14
+ let(:instance) { klass.new }
15
+
16
+ context "when active" do
17
+ let(:transaction) { http_request_transaction }
18
+ before do
19
+ expect(Appsignal::Transaction).to receive(:current).at_least(:once).and_return(transaction)
20
+ Appsignal.config = project_fixture_config
21
+ end
22
+ after { Appsignal.config = nil }
23
+
24
+ context "with anonymous class" do
25
+ it "instruments the method and calls it" do
26
+ expect(Appsignal.active?).to be_truthy
27
+ expect(transaction).to receive(:start_event)
28
+ expect(transaction).to receive(:finish_event).with \
29
+ "foo.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
30
+ expect(instance.foo).to eq(1)
31
+ end
32
+ end
33
+
34
+ context "with named class" do
35
+ before do
36
+ class NamedClass
37
+ def foo
38
+ 1
39
+ end
40
+ appsignal_instrument_method :foo
41
+ end
42
+ end
43
+ after { Object.send(:remove_const, :NamedClass) }
44
+ let(:klass) { NamedClass }
45
+
46
+ it "instruments the method and calls it" do
47
+ expect(Appsignal.active?).to be_truthy
48
+ expect(transaction).to receive(:start_event)
49
+ expect(transaction).to receive(:finish_event).with \
50
+ "foo.NamedClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
51
+ expect(instance.foo).to eq(1)
52
+ end
53
+ end
54
+
55
+ context "with nested named class" do
56
+ before do
57
+ module MyModule
58
+ module NestedModule
59
+ class NamedClass
60
+ def bar
61
+ 2
62
+ end
63
+ appsignal_instrument_method :bar
64
+ end
65
+ end
66
+ end
67
+ end
68
+ after { Object.send(:remove_const, :MyModule) }
69
+ let(:klass) { MyModule::NestedModule::NamedClass }
70
+
71
+ it "instruments the method and calls it" do
72
+ expect(Appsignal.active?).to be_truthy
73
+ expect(transaction).to receive(:start_event)
74
+ expect(transaction).to receive(:finish_event).with \
75
+ "bar.NamedClass.NestedModule.MyModule.other", nil, nil,
76
+ Appsignal::EventFormatter::DEFAULT
77
+ expect(instance.bar).to eq(2)
78
+ end
79
+ end
80
+
81
+ context "with custom name" do
82
+ let(:klass) do
83
+ Class.new do
84
+ def foo
85
+ 1
86
+ end
87
+ appsignal_instrument_method :foo, :name => "my_method.group"
88
+ end
89
+ end
90
+
91
+ it "instruments with custom name" do
92
+ expect(Appsignal.active?).to be_truthy
93
+ expect(transaction).to receive(:start_event)
94
+ expect(transaction).to receive(:finish_event).with \
95
+ "my_method.group", nil, nil, Appsignal::EventFormatter::DEFAULT
96
+ expect(instance.foo).to eq(1)
97
+ end
98
+ end
99
+
100
+ context "with a method given a block" do
101
+ let(:klass) do
102
+ Class.new do
103
+ def foo
104
+ yield
105
+ end
106
+ appsignal_instrument_method :foo
107
+ end
108
+ end
109
+
110
+ it "should yield the block" do
111
+ expect(instance.foo { 42 }).to eq(42)
112
+ end
113
+ end
114
+ end
115
+
116
+ context "when not active" do
117
+ let(:transaction) { Appsignal::Transaction.current }
118
+
119
+ it "should not instrument, but still call the method" do
120
+ expect(Appsignal.active?).to be_falsy
121
+ expect(transaction).to_not receive(:start_event)
122
+ expect(instance.foo).to eq(1)
123
+ end
124
+ end
125
+ end
126
+
127
+ context "with class method" do
128
+ let(:klass) do
129
+ Class.new do
130
+ def self.bar
131
+ 2
132
+ end
133
+ appsignal_instrument_class_method :bar
134
+ end
135
+ end
136
+
137
+ context "when active" do
138
+ let(:transaction) { http_request_transaction }
139
+ before do
140
+ expect(Appsignal::Transaction).to receive(:current).at_least(:once)
141
+ .and_return(transaction)
142
+ Appsignal.config = project_fixture_config
143
+ end
144
+ after { Appsignal.config = nil }
145
+
146
+ context "with anonymous class" do
147
+ it "instruments the method and calls it" do
148
+ expect(Appsignal.active?).to be_truthy
149
+ expect(transaction).to receive(:start_event)
150
+ expect(transaction).to receive(:finish_event).with \
151
+ "bar.class_method.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
152
+ expect(klass.bar).to eq(2)
153
+ end
154
+ end
155
+
156
+ context "with named class" do
157
+ before do
158
+ class NamedClass
159
+ def self.bar
160
+ 2
161
+ end
162
+ appsignal_instrument_class_method :bar
163
+ end
164
+ end
165
+ after { Object.send(:remove_const, :NamedClass) }
166
+ let(:klass) { NamedClass }
167
+
168
+ it "instruments the method and calls it" do
169
+ expect(Appsignal.active?).to be_truthy
170
+ expect(transaction).to receive(:start_event)
171
+ expect(transaction).to receive(:finish_event).with \
172
+ "bar.class_method.NamedClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
173
+ expect(klass.bar).to eq(2)
174
+ end
175
+
176
+ context "with nested named class" do
177
+ before do
178
+ module MyModule
179
+ module NestedModule
180
+ class NamedClass
181
+ def self.bar
182
+ 2
183
+ end
184
+ appsignal_instrument_class_method :bar
185
+ end
186
+ end
187
+ end
188
+ end
189
+ after { Object.send(:remove_const, :MyModule) }
190
+ let(:klass) { MyModule::NestedModule::NamedClass }
191
+
192
+ it "instruments the method and calls it" do
193
+ expect(Appsignal.active?).to be_truthy
194
+ expect(transaction).to receive(:start_event)
195
+ expect(transaction).to receive(:finish_event).with \
196
+ "bar.class_method.NamedClass.NestedModule.MyModule.other", nil, nil,
197
+ Appsignal::EventFormatter::DEFAULT
198
+ expect(klass.bar).to eq(2)
199
+ end
200
+ end
201
+ end
202
+
203
+ context "with custom name" do
204
+ let(:klass) do
205
+ Class.new do
206
+ def self.bar
207
+ 2
208
+ end
209
+ appsignal_instrument_class_method :bar, :name => "my_method.group"
210
+ end
211
+ end
212
+
213
+ it "instruments with custom name" do
214
+ expect(Appsignal.active?).to be_truthy
215
+ expect(transaction).to receive(:start_event)
216
+ expect(transaction).to receive(:finish_event).with \
217
+ "my_method.group", nil, nil, Appsignal::EventFormatter::DEFAULT
218
+ expect(klass.bar).to eq(2)
219
+ end
220
+ end
221
+
222
+ context "with a method given a block" do
223
+ let(:klass) do
224
+ Class.new do
225
+ def self.bar
226
+ yield
227
+ end
228
+ appsignal_instrument_class_method :bar
229
+ end
230
+ end
231
+
232
+ it "should yield the block" do
233
+ expect(klass.bar { 42 }).to eq(42)
234
+ end
235
+ end
236
+ end
237
+
238
+ context "when not active" do
239
+ let(:transaction) { Appsignal::Transaction.current }
240
+
241
+ it "should not instrument, but still call the method" do
242
+ expect(Appsignal.active?).to be_falsy
243
+ expect(transaction).to_not receive(:start_event)
244
+ expect(klass.bar).to eq(2)
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,323 @@
1
+ if DependencyHelper.padrino_present?
2
+ describe "Padrino integration" do
3
+ require "appsignal/integrations/padrino"
4
+
5
+ before do
6
+ allow(Appsignal).to receive(:active?).and_return(true)
7
+ allow(Appsignal).to receive(:start).and_return(true)
8
+ allow(Appsignal).to receive(:start_logger).and_return(true)
9
+ end
10
+
11
+ describe Appsignal::Integrations::PadrinoPlugin do
12
+ it "starts AppSignal on init" do
13
+ expect(Appsignal).to receive(:start)
14
+ end
15
+
16
+ it "starts the logger on init" do
17
+ expect(Appsignal).to receive(:start_logger)
18
+ end
19
+
20
+ context "when not active" do
21
+ before { allow(Appsignal).to receive(:active?).and_return(false) }
22
+
23
+ it "does not add the listener middleware to the stack" do
24
+ expect(Padrino).to_not receive(:use)
25
+ end
26
+ end
27
+
28
+ context "when APPSIGNAL_APP_ENV ENV var is provided" do
29
+ it "uses this as the environment" do
30
+ ENV["APPSIGNAL_APP_ENV"] = "custom"
31
+
32
+ # Reset the plugin to pull down the latest data
33
+ Appsignal::Integrations::PadrinoPlugin.init
34
+
35
+ expect(Appsignal.config.env).to eq("custom")
36
+ end
37
+ end
38
+
39
+ context "when APPSIGNAL_APP_ENV ENV var is not provided" do
40
+ it "uses the Padrino environment" do
41
+ # Reset the plugin to pull down the latest data
42
+ Appsignal::Integrations::PadrinoPlugin.init
43
+
44
+ expect(Padrino.env.to_s).to eq("test")
45
+ expect(Appsignal.config.env).to eq(Padrino.env.to_s)
46
+ end
47
+ end
48
+
49
+ after { Appsignal::Integrations::PadrinoPlugin.init }
50
+ end
51
+
52
+ describe Padrino::Routing::InstanceMethods do
53
+ class PadrinoClassWithRouter
54
+ include Padrino::Routing
55
+ end
56
+
57
+ let(:base) { double }
58
+ let(:router) { PadrinoClassWithRouter.new }
59
+ let(:env) { {} }
60
+ # TODO: use an instance double
61
+ let(:settings) { double(:name => "TestApp") }
62
+
63
+ describe "routes" do
64
+ let(:transaction) do
65
+ instance_double "Appsignal::Transaction",
66
+ :set_http_or_background_action => nil,
67
+ :set_http_or_background_queue_start => nil,
68
+ :set_metadata => nil,
69
+ :set_action => nil,
70
+ :set_action_if_nil => nil,
71
+ :set_error => nil,
72
+ :start_event => nil,
73
+ :finish_event => nil,
74
+ :complete => nil
75
+ end
76
+ let(:request_kind) { kind_of(Sinatra::Request) }
77
+ let(:env) do
78
+ {
79
+ "REQUEST_METHOD" => "GET",
80
+ "PATH_INFO" => path,
81
+ "REQUEST_PATH" => path,
82
+ "rack.input" => StringIO.new
83
+ }
84
+ end
85
+ let(:app) do
86
+ Class.new(Padrino::Application) do
87
+ def self.name
88
+ "PadrinoTestApp"
89
+ end
90
+ end
91
+ end
92
+ let(:response) { app.call(env) }
93
+ before do
94
+ allow(Appsignal::Transaction).to receive(:create).and_return(transaction)
95
+ allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
96
+ end
97
+
98
+ RSpec::Matchers.define :match_response do |expected_status, expected_content|
99
+ match do |response|
100
+ status, _headers, content = response
101
+ status == expected_status && content == [expected_content].compact
102
+ end
103
+ end
104
+
105
+ def expect_a_transaction_to_be_created
106
+ expect(Appsignal::Transaction).to receive(:create).with(
107
+ kind_of(String),
108
+ Appsignal::Transaction::HTTP_REQUEST,
109
+ request_kind
110
+ ).and_return(transaction)
111
+
112
+ expect(Appsignal).to receive(:instrument)
113
+ .at_least(:once)
114
+ .with("process_action.padrino")
115
+ .and_call_original
116
+ expect(transaction).to receive(:set_metadata).with("path", path)
117
+ expect(transaction).to receive(:set_metadata).with("method", "GET")
118
+ expect(transaction).to receive(:complete)
119
+ end
120
+
121
+ def expect_no_transaction_to_be_created
122
+ expect(Appsignal::Transaction).to_not receive(:create)
123
+ expect(Appsignal).to_not receive(:instrument)
124
+ end
125
+
126
+ context "when AppSignal is not active" do
127
+ before { allow(Appsignal).to receive(:active?).and_return(false) }
128
+ let(:path) { "/foo" }
129
+ before { app.controllers { get(:foo) { "content" } } }
130
+ after { expect(response).to match_response(200, "content") }
131
+
132
+ it "does not instrument the request" do
133
+ expect_no_transaction_to_be_created
134
+ end
135
+ end
136
+
137
+ context "when AppSignal is active" do
138
+ context "with not existing route" do
139
+ let(:path) { "/404" }
140
+
141
+ it "instruments the request" do
142
+ expect_a_transaction_to_be_created
143
+ # Uses path for action name
144
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#/404")
145
+ expect(response).to match_response(404, "<h1>Not Found</h1>")
146
+ end
147
+ end
148
+
149
+ context "when Sinatra tells us it's a static file" do
150
+ let(:path) { "/static" }
151
+ before do
152
+ env["sinatra.static_file"] = true
153
+ expect_any_instance_of(app)
154
+ .to receive(:route_without_appsignal).and_return([200, {}, ["foo"]])
155
+ end
156
+ after { expect(response).to match_response(200, "foo") }
157
+
158
+ it "does not instrument the request" do
159
+ expect_no_transaction_to_be_created
160
+ end
161
+ end
162
+
163
+ # Older Padrino versions don't support `action` (v11.0+)
164
+ context "without #action on Sinatra::Request" do
165
+ let(:path) { "/my_original_path" }
166
+ let(:request_kind) do
167
+ double(
168
+ :params => {},
169
+ :path => path,
170
+ :path_info => path,
171
+ :request_method => "GET",
172
+ :head? => false,
173
+ :route_obj => double(:original_path => "original_path method"),
174
+ :route_obj= => nil # Make sure route_obj doesn't get overwritten
175
+ )
176
+ end
177
+ before do
178
+ expect(Sinatra::Request).to receive(:new).and_return(request_kind)
179
+ app.controllers { get(:my_original_path) { "content" } }
180
+ end
181
+ after { expect(response).to match_response(200, "content") }
182
+
183
+ it "falls back on Sinatra::Request#route_obj.original_path" do
184
+ expect_a_transaction_to_be_created
185
+ expect(transaction)
186
+ .to receive(:set_action_if_nil).with("PadrinoTestApp:original_path method")
187
+ end
188
+ end
189
+
190
+ context "without Sinatra::Request#route_obj.original_path" do
191
+ let(:path) { "/my_original_path" }
192
+ let(:request_kind) do
193
+ double(
194
+ :params => {},
195
+ :path => path,
196
+ :path_info => path,
197
+ :request_method => "GET",
198
+ :head? => false,
199
+ :route_obj= => nil # Make sure route_obj doesn't get overwritten
200
+ )
201
+ end
202
+ before do
203
+ expect(Sinatra::Request).to receive(:new).and_return(request_kind)
204
+ app.controllers { get(:my_original_path) { "content" } }
205
+ end
206
+ after { expect(response).to match_response(200, "content") }
207
+
208
+ it "falls back on app name" do
209
+ expect_a_transaction_to_be_created
210
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp")
211
+ end
212
+ end
213
+
214
+ context "with existing route" do
215
+ context "with an exception in the controller" do
216
+ let(:path) { "/exception" }
217
+ before do
218
+ app.controllers { get(:exception) { raise ExampleException } }
219
+ expect_a_transaction_to_be_created
220
+ end
221
+ after do
222
+ expect { response }.to raise_error(ExampleException)
223
+ end
224
+
225
+ it "sets the action name based on the app name and action name" do
226
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#exception")
227
+ end
228
+
229
+ it "sets the error on the transaction" do
230
+ expect(transaction).to receive(:set_error).with(ExampleException)
231
+ end
232
+ end
233
+
234
+ context "without an exception in the controller" do
235
+ let(:path) { "/" }
236
+ after { expect(response).to match_response(200, "content") }
237
+
238
+ context "with action name as symbol" do
239
+ context "with :index helper" do
240
+ before do
241
+ # :index == "/"
242
+ app.controllers { get(:index) { "content" } }
243
+ end
244
+
245
+ it "sets the action with the app name and action name" do
246
+ expect_a_transaction_to_be_created
247
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#index")
248
+ end
249
+ end
250
+
251
+ context "with custom action name" do
252
+ let(:path) { "/foo" }
253
+ before do
254
+ app.controllers { get(:foo) { "content" } }
255
+ end
256
+
257
+ it "sets the action with the app name and action name" do
258
+ expect_a_transaction_to_be_created
259
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#foo")
260
+ end
261
+ end
262
+ end
263
+
264
+ context "with an action defined with a path" do
265
+ context "with root path" do
266
+ before do
267
+ # :index == "/"
268
+ app.controllers { get("/") { "content" } }
269
+ end
270
+
271
+ it "sets the action with the app name and action path" do
272
+ expect_a_transaction_to_be_created
273
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#/")
274
+ end
275
+ end
276
+
277
+ context "with custom path" do
278
+ let(:path) { "/foo" }
279
+ before do
280
+ app.controllers { get("/foo") { "content" } }
281
+ end
282
+
283
+ it "sets the action with the app name and action path" do
284
+ expect_a_transaction_to_be_created
285
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:#/foo")
286
+ end
287
+ end
288
+ end
289
+
290
+ context "with controller" do
291
+ let(:path) { "/my_controller" }
292
+
293
+ context "with controller as name" do
294
+ before do
295
+ # :index == "/"
296
+ app.controllers(:my_controller) { get(:index) { "content" } }
297
+ end
298
+
299
+ it "sets the action with the app name, controller name and action name" do
300
+ expect_a_transaction_to_be_created
301
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:my_controller#index")
302
+ end
303
+ end
304
+
305
+ context "with controller as path" do
306
+ before do
307
+ # :index == "/"
308
+ app.controllers("/my_controller") { get(:index) { "content" } }
309
+ end
310
+
311
+ it "sets the action with the app name, controller name and action path" do
312
+ expect_a_transaction_to_be_created
313
+ expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp:/my_controller#index")
314
+ end
315
+ end
316
+ end
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end
322
+ end
323
+ end