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.
- checksums.yaml +7 -0
- data/.gitignore +33 -0
- data/.rspec +4 -0
- data/.rubocop.yml +66 -0
- data/.rubocop_todo.yml +124 -0
- data/.travis.yml +72 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +639 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +264 -0
- data/Rakefile +214 -0
- data/appsignal.gemspec +42 -0
- data/benchmark.rake +77 -0
- data/bin/appsignal +13 -0
- data/ext/Rakefile +27 -0
- data/ext/agent.yml +64 -0
- data/ext/appsignal_extension.c +692 -0
- data/ext/base.rb +79 -0
- data/ext/extconf.rb +35 -0
- data/gemfiles/capistrano2.gemfile +7 -0
- data/gemfiles/capistrano3.gemfile +7 -0
- data/gemfiles/grape.gemfile +7 -0
- data/gemfiles/no_dependencies.gemfile +5 -0
- data/gemfiles/padrino.gemfile +7 -0
- data/gemfiles/que.gemfile +5 -0
- data/gemfiles/rails-3.2.gemfile +6 -0
- data/gemfiles/rails-4.0.gemfile +6 -0
- data/gemfiles/rails-4.1.gemfile +6 -0
- data/gemfiles/rails-4.2.gemfile +10 -0
- data/gemfiles/rails-5.0.gemfile +5 -0
- data/gemfiles/rails-5.1.gemfile +5 -0
- data/gemfiles/resque.gemfile +12 -0
- data/gemfiles/sequel-435.gemfile +11 -0
- data/gemfiles/sequel.gemfile +11 -0
- data/gemfiles/sinatra.gemfile +6 -0
- data/gemfiles/webmachine.gemfile +5 -0
- data/lib/appsignal.rb +804 -0
- data/lib/appsignal/auth_check.rb +65 -0
- data/lib/appsignal/capistrano.rb +10 -0
- data/lib/appsignal/cli.rb +108 -0
- data/lib/appsignal/cli/demo.rb +63 -0
- data/lib/appsignal/cli/diagnose.rb +500 -0
- data/lib/appsignal/cli/helpers.rb +72 -0
- data/lib/appsignal/cli/install.rb +277 -0
- data/lib/appsignal/cli/notify_of_deploy.rb +113 -0
- data/lib/appsignal/config.rb +287 -0
- data/lib/appsignal/demo.rb +107 -0
- data/lib/appsignal/event_formatter.rb +74 -0
- data/lib/appsignal/event_formatter/action_view/render_formatter.rb +24 -0
- data/lib/appsignal/event_formatter/active_record/instantiation_formatter.rb +14 -0
- data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +14 -0
- data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +32 -0
- data/lib/appsignal/event_formatter/faraday/request_formatter.rb +19 -0
- data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +89 -0
- data/lib/appsignal/event_formatter/moped/query_formatter.rb +80 -0
- data/lib/appsignal/extension.rb +63 -0
- data/lib/appsignal/extension/jruby.rb +460 -0
- data/lib/appsignal/garbage_collection_profiler.rb +48 -0
- data/lib/appsignal/hooks.rb +105 -0
- data/lib/appsignal/hooks/action_cable.rb +113 -0
- data/lib/appsignal/hooks/active_support_notifications.rb +52 -0
- data/lib/appsignal/hooks/celluloid.rb +30 -0
- data/lib/appsignal/hooks/data_mapper.rb +18 -0
- data/lib/appsignal/hooks/delayed_job.rb +19 -0
- data/lib/appsignal/hooks/mongo_ruby_driver.rb +21 -0
- data/lib/appsignal/hooks/net_http.rb +29 -0
- data/lib/appsignal/hooks/passenger.rb +22 -0
- data/lib/appsignal/hooks/puma.rb +35 -0
- data/lib/appsignal/hooks/que.rb +21 -0
- data/lib/appsignal/hooks/rake.rb +39 -0
- data/lib/appsignal/hooks/redis.rb +30 -0
- data/lib/appsignal/hooks/sequel.rb +60 -0
- data/lib/appsignal/hooks/shoryuken.rb +43 -0
- data/lib/appsignal/hooks/sidekiq.rb +144 -0
- data/lib/appsignal/hooks/unicorn.rb +40 -0
- data/lib/appsignal/hooks/webmachine.rb +23 -0
- data/lib/appsignal/integrations/capistrano/appsignal.cap +39 -0
- data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +52 -0
- data/lib/appsignal/integrations/data_mapper.rb +33 -0
- data/lib/appsignal/integrations/delayed_job_plugin.rb +54 -0
- data/lib/appsignal/integrations/grape.rb +53 -0
- data/lib/appsignal/integrations/mongo_ruby_driver.rb +55 -0
- data/lib/appsignal/integrations/object.rb +35 -0
- data/lib/appsignal/integrations/padrino.rb +84 -0
- data/lib/appsignal/integrations/que.rb +43 -0
- data/lib/appsignal/integrations/railtie.rb +41 -0
- data/lib/appsignal/integrations/rake.rb +2 -0
- data/lib/appsignal/integrations/resque.rb +20 -0
- data/lib/appsignal/integrations/resque_active_job.rb +30 -0
- data/lib/appsignal/integrations/sinatra.rb +17 -0
- data/lib/appsignal/integrations/webmachine.rb +38 -0
- data/lib/appsignal/js_exception_transaction.rb +54 -0
- data/lib/appsignal/marker.rb +63 -0
- data/lib/appsignal/minutely.rb +42 -0
- data/lib/appsignal/rack/generic_instrumentation.rb +49 -0
- data/lib/appsignal/rack/js_exception_catcher.rb +70 -0
- data/lib/appsignal/rack/rails_instrumentation.rb +51 -0
- data/lib/appsignal/rack/sinatra_instrumentation.rb +99 -0
- data/lib/appsignal/rack/streaming_listener.rb +73 -0
- data/lib/appsignal/system.rb +81 -0
- data/lib/appsignal/transaction.rb +498 -0
- data/lib/appsignal/transmitter.rb +107 -0
- data/lib/appsignal/utils.rb +127 -0
- data/lib/appsignal/utils/params_sanitizer.rb +59 -0
- data/lib/appsignal/utils/query_params_sanitizer.rb +55 -0
- data/lib/appsignal/version.rb +3 -0
- data/lib/sequel/extensions/appsignal_integration.rb +3 -0
- data/resources/appsignal.yml.erb +39 -0
- data/resources/cacert.pem +3866 -0
- data/spec/.rubocop.yml +7 -0
- data/spec/lib/appsignal/auth_check_spec.rb +80 -0
- data/spec/lib/appsignal/capistrano2_spec.rb +224 -0
- data/spec/lib/appsignal/capistrano3_spec.rb +237 -0
- data/spec/lib/appsignal/cli/demo_spec.rb +67 -0
- data/spec/lib/appsignal/cli/diagnose_spec.rb +988 -0
- data/spec/lib/appsignal/cli/helpers_spec.rb +171 -0
- data/spec/lib/appsignal/cli/install_spec.rb +632 -0
- data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +168 -0
- data/spec/lib/appsignal/cli_spec.rb +56 -0
- data/spec/lib/appsignal/config_spec.rb +637 -0
- data/spec/lib/appsignal/demo_spec.rb +87 -0
- data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +44 -0
- data/spec/lib/appsignal/event_formatter/active_record/instantiation_formatter_spec.rb +21 -0
- data/spec/lib/appsignal/event_formatter/active_record/sql_formatter_spec.rb +21 -0
- data/spec/lib/appsignal/event_formatter/elastic_search/search_formatter_spec.rb +52 -0
- data/spec/lib/appsignal/event_formatter/faraday/request_formatter_spec.rb +21 -0
- data/spec/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter_spec.rb +113 -0
- data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +112 -0
- data/spec/lib/appsignal/event_formatter_spec.rb +100 -0
- data/spec/lib/appsignal/extension/jruby_spec.rb +43 -0
- data/spec/lib/appsignal/extension_spec.rb +137 -0
- data/spec/lib/appsignal/garbage_collection_profiler_spec.rb +66 -0
- data/spec/lib/appsignal/hooks/action_cable_spec.rb +370 -0
- data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +92 -0
- data/spec/lib/appsignal/hooks/celluloid_spec.rb +35 -0
- data/spec/lib/appsignal/hooks/data_mapper_spec.rb +39 -0
- data/spec/lib/appsignal/hooks/delayed_job_spec.rb +358 -0
- data/spec/lib/appsignal/hooks/mongo_ruby_driver_spec.rb +44 -0
- data/spec/lib/appsignal/hooks/net_http_spec.rb +53 -0
- data/spec/lib/appsignal/hooks/passenger_spec.rb +30 -0
- data/spec/lib/appsignal/hooks/puma_spec.rb +80 -0
- data/spec/lib/appsignal/hooks/que_spec.rb +19 -0
- data/spec/lib/appsignal/hooks/rake_spec.rb +73 -0
- data/spec/lib/appsignal/hooks/redis_spec.rb +55 -0
- data/spec/lib/appsignal/hooks/sequel_spec.rb +46 -0
- data/spec/lib/appsignal/hooks/shoryuken_spec.rb +192 -0
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +419 -0
- data/spec/lib/appsignal/hooks/unicorn_spec.rb +52 -0
- data/spec/lib/appsignal/hooks/webmachine_spec.rb +35 -0
- data/spec/lib/appsignal/hooks_spec.rb +195 -0
- data/spec/lib/appsignal/integrations/data_mapper_spec.rb +65 -0
- data/spec/lib/appsignal/integrations/grape_spec.rb +225 -0
- data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +127 -0
- data/spec/lib/appsignal/integrations/object_spec.rb +249 -0
- data/spec/lib/appsignal/integrations/padrino_spec.rb +323 -0
- data/spec/lib/appsignal/integrations/que_spec.rb +174 -0
- data/spec/lib/appsignal/integrations/railtie_spec.rb +129 -0
- data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +83 -0
- data/spec/lib/appsignal/integrations/resque_spec.rb +92 -0
- data/spec/lib/appsignal/integrations/sinatra_spec.rb +73 -0
- data/spec/lib/appsignal/integrations/webmachine_spec.rb +69 -0
- data/spec/lib/appsignal/js_exception_transaction_spec.rb +128 -0
- data/spec/lib/appsignal/marker_spec.rb +51 -0
- data/spec/lib/appsignal/minutely_spec.rb +50 -0
- data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +90 -0
- data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +147 -0
- data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +117 -0
- data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +213 -0
- data/spec/lib/appsignal/rack/streaming_listener_spec.rb +161 -0
- data/spec/lib/appsignal/system_spec.rb +131 -0
- data/spec/lib/appsignal/transaction_spec.rb +1146 -0
- data/spec/lib/appsignal/transmitter_spec.rb +152 -0
- data/spec/lib/appsignal/utils/params_sanitizer_spec.rb +136 -0
- data/spec/lib/appsignal/utils/query_params_sanitizer_spec.rb +192 -0
- data/spec/lib/appsignal/utils_spec.rb +150 -0
- data/spec/lib/appsignal_spec.rb +1049 -0
- data/spec/spec_helper.rb +116 -0
- data/spec/support/fixtures/containers/cgroups/docker +14 -0
- data/spec/support/fixtures/containers/cgroups/docker_systemd +8 -0
- data/spec/support/fixtures/containers/cgroups/lxc +10 -0
- data/spec/support/fixtures/containers/cgroups/no_permission +0 -0
- data/spec/support/fixtures/containers/cgroups/none +1 -0
- data/spec/support/fixtures/generated_config.yml +24 -0
- data/spec/support/fixtures/uploaded_file.txt +0 -0
- data/spec/support/helpers/api_request_helper.rb +19 -0
- data/spec/support/helpers/cli_helpers.rb +26 -0
- data/spec/support/helpers/config_helpers.rb +21 -0
- data/spec/support/helpers/dependency_helper.rb +73 -0
- data/spec/support/helpers/directory_helper.rb +27 -0
- data/spec/support/helpers/env_helpers.rb +33 -0
- data/spec/support/helpers/example_exception.rb +13 -0
- data/spec/support/helpers/example_standard_error.rb +13 -0
- data/spec/support/helpers/log_helpers.rb +22 -0
- data/spec/support/helpers/std_streams_helper.rb +66 -0
- data/spec/support/helpers/system_helpers.rb +8 -0
- data/spec/support/helpers/time_helpers.rb +11 -0
- data/spec/support/helpers/transaction_helpers.rb +37 -0
- data/spec/support/matchers/contains_log.rb +7 -0
- data/spec/support/mocks/fake_gc_profiler.rb +19 -0
- data/spec/support/mocks/mock_extension.rb +6 -0
- data/spec/support/project_fixture/config/application.rb +0 -0
- data/spec/support/project_fixture/config/appsignal.yml +32 -0
- data/spec/support/project_fixture/config/environments/development.rb +0 -0
- data/spec/support/project_fixture/config/environments/production.rb +0 -0
- data/spec/support/project_fixture/config/environments/test.rb +0 -0
- data/spec/support/project_fixture/log/.gitkeep +0 -0
- data/spec/support/rails/my_app.rb +6 -0
- data/spec/support/shared_examples/instrument.rb +43 -0
- data/spec/support/stubs/delayed_job.rb +0 -0
- metadata +483 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
require "appsignal/rack/streaming_listener"
|
|
2
|
+
|
|
3
|
+
describe Appsignal::Rack::StreamingListener do
|
|
4
|
+
let(:headers) { {} }
|
|
5
|
+
let(:env) do
|
|
6
|
+
{
|
|
7
|
+
"rack.input" => StringIO.new,
|
|
8
|
+
"REQUEST_METHOD" => "GET",
|
|
9
|
+
"PATH_INFO" => "/homepage",
|
|
10
|
+
"QUERY_STRING" => "param=something"
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
let(:app) { double(:call => [200, headers, "body"]) }
|
|
14
|
+
let(:listener) { Appsignal::Rack::StreamingListener.new(app, {}) }
|
|
15
|
+
|
|
16
|
+
describe "#call" do
|
|
17
|
+
context "when Appsignal is active" do
|
|
18
|
+
before { allow(Appsignal).to receive(:active?).and_return(true) }
|
|
19
|
+
|
|
20
|
+
it "should call `call_with_appsignal_monitoring`" do
|
|
21
|
+
expect(listener).to receive(:call_with_appsignal_monitoring)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context "when Appsignal is not active" do
|
|
26
|
+
before { allow(Appsignal).to receive(:active?).and_return(false) }
|
|
27
|
+
|
|
28
|
+
it "should not call `call_with_appsignal_monitoring`" do
|
|
29
|
+
expect(listener).to_not receive(:call_with_appsignal_monitoring)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
after { listener.call(env) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe "#call_with_appsignal_monitoring" do
|
|
37
|
+
let!(:transaction) do
|
|
38
|
+
Appsignal::Transaction.create(
|
|
39
|
+
SecureRandom.uuid,
|
|
40
|
+
Appsignal::Transaction::HTTP_REQUEST,
|
|
41
|
+
::Rack::Request.new(env)
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
let(:wrapper) { Appsignal::StreamWrapper.new("body", transaction) }
|
|
45
|
+
let(:raw_payload) { { :foo => :bar } }
|
|
46
|
+
|
|
47
|
+
before do
|
|
48
|
+
allow(SecureRandom).to receive(:uuid).and_return("123")
|
|
49
|
+
allow(listener).to receive(:raw_payload).and_return(raw_payload)
|
|
50
|
+
allow(Appsignal::Transaction).to receive(:create).and_return(transaction)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should create a transaction" do
|
|
54
|
+
expect(Appsignal::Transaction).to receive(:create)
|
|
55
|
+
.with("123", Appsignal::Transaction::HTTP_REQUEST, instance_of(Rack::Request))
|
|
56
|
+
.and_return(transaction)
|
|
57
|
+
|
|
58
|
+
listener.call_with_appsignal_monitoring(env)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "should instrument the call" do
|
|
62
|
+
expect(Appsignal).to receive(:instrument)
|
|
63
|
+
.with("process_action.rack")
|
|
64
|
+
.and_yield
|
|
65
|
+
|
|
66
|
+
listener.call_with_appsignal_monitoring(env)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "should add `appsignal.action` to the transaction" do
|
|
70
|
+
allow(Appsignal).to receive(:instrument).and_yield
|
|
71
|
+
|
|
72
|
+
env["appsignal.action"] = "Action"
|
|
73
|
+
|
|
74
|
+
expect(transaction).to receive(:set_action_if_nil).with("Action")
|
|
75
|
+
|
|
76
|
+
listener.call_with_appsignal_monitoring(env)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "should add the path, method and queue start to the transaction" do
|
|
80
|
+
allow(Appsignal).to receive(:instrument).and_yield
|
|
81
|
+
|
|
82
|
+
expect(transaction).to receive(:set_metadata).with("path", "/homepage")
|
|
83
|
+
expect(transaction).to receive(:set_metadata).with("method", "GET")
|
|
84
|
+
expect(transaction).to receive(:set_http_or_background_queue_start)
|
|
85
|
+
|
|
86
|
+
listener.call_with_appsignal_monitoring(env)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
context "with an exception in the instrumentation call" do
|
|
90
|
+
let(:error) { ExampleException }
|
|
91
|
+
|
|
92
|
+
it "should add the exception to the transaction" do
|
|
93
|
+
allow(app).to receive(:call).and_raise(error)
|
|
94
|
+
|
|
95
|
+
expect(transaction).to receive(:set_error).with(error)
|
|
96
|
+
|
|
97
|
+
expect do
|
|
98
|
+
listener.call_with_appsignal_monitoring(env)
|
|
99
|
+
end.to raise_error(error)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "should wrap the body in a wrapper" do
|
|
104
|
+
expect(Appsignal::StreamWrapper).to receive(:new)
|
|
105
|
+
.with("body", transaction)
|
|
106
|
+
.and_return(wrapper)
|
|
107
|
+
|
|
108
|
+
body = listener.call_with_appsignal_monitoring(env)[2]
|
|
109
|
+
|
|
110
|
+
expect(body).to be_a(Appsignal::StreamWrapper)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
describe Appsignal::StreamWrapper do
|
|
116
|
+
let(:stream) { double }
|
|
117
|
+
let(:transaction) { Appsignal::Transaction.create(SecureRandom.uuid, Appsignal::Transaction::HTTP_REQUEST, {}) }
|
|
118
|
+
let(:wrapper) { Appsignal::StreamWrapper.new(stream, transaction) }
|
|
119
|
+
|
|
120
|
+
describe "#each" do
|
|
121
|
+
it "calls the original stream" do
|
|
122
|
+
expect(stream).to receive(:each)
|
|
123
|
+
|
|
124
|
+
wrapper.each
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
context "when #each raises an error" do
|
|
128
|
+
let(:error) { ExampleException }
|
|
129
|
+
|
|
130
|
+
it "records the exception" do
|
|
131
|
+
allow(stream).to receive(:each).and_raise(error)
|
|
132
|
+
|
|
133
|
+
expect(transaction).to receive(:set_error).with(error)
|
|
134
|
+
|
|
135
|
+
expect { wrapper.send(:each) }.to raise_error(error)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
describe "#close" do
|
|
141
|
+
it "closes the original stream and completes the transaction" do
|
|
142
|
+
expect(stream).to receive(:close)
|
|
143
|
+
expect(Appsignal::Transaction).to receive(:complete_current!)
|
|
144
|
+
|
|
145
|
+
wrapper.close
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
context "when #close raises an error" do
|
|
149
|
+
let(:error) { ExampleException }
|
|
150
|
+
|
|
151
|
+
it "records the exception and completes the transaction" do
|
|
152
|
+
allow(stream).to receive(:close).and_raise(error)
|
|
153
|
+
|
|
154
|
+
expect(transaction).to receive(:set_error).with(error)
|
|
155
|
+
expect(transaction).to receive(:complete)
|
|
156
|
+
|
|
157
|
+
expect { wrapper.send(:close) }.to raise_error(error)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
require "appsignal/system"
|
|
2
|
+
|
|
3
|
+
describe Appsignal::System do
|
|
4
|
+
describe ".heroku?" do
|
|
5
|
+
subject { described_class.heroku? }
|
|
6
|
+
|
|
7
|
+
context "when on Heroku" do
|
|
8
|
+
around { |example| recognize_as_heroku { example.run } }
|
|
9
|
+
|
|
10
|
+
it "returns true" do
|
|
11
|
+
is_expected.to eq(true)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context "when not on Heroku" do
|
|
16
|
+
it "returns false" do
|
|
17
|
+
is_expected.to eq(false)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe ".installed_agent_architecture" do
|
|
23
|
+
let(:const_name) { "GEM_EXT_PATH".freeze }
|
|
24
|
+
let(:tmp_ext_dir) { File.join(tmp_dir, "ext") }
|
|
25
|
+
let(:architecture_file) { File.join(Appsignal::System::GEM_EXT_PATH, "appsignal.architecture") }
|
|
26
|
+
around do |example|
|
|
27
|
+
original_gem_ext_path = Appsignal::System.const_get(const_name)
|
|
28
|
+
Appsignal::System.send(:remove_const, const_name)
|
|
29
|
+
Appsignal::System.const_set(const_name, tmp_ext_dir)
|
|
30
|
+
example.run
|
|
31
|
+
Appsignal::System.send(:remove_const, const_name)
|
|
32
|
+
Appsignal::System.const_set(const_name, original_gem_ext_path)
|
|
33
|
+
end
|
|
34
|
+
after { FileUtils.rm_rf(tmp_ext_dir) }
|
|
35
|
+
subject { described_class.installed_agent_architecture }
|
|
36
|
+
|
|
37
|
+
context "with an ext/appsignal.architecture file" do
|
|
38
|
+
before do
|
|
39
|
+
FileUtils.mkdir_p(Appsignal::System::GEM_EXT_PATH)
|
|
40
|
+
File.open(architecture_file, "w") do |file|
|
|
41
|
+
file.write "foo"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "returns the contents of the file" do
|
|
46
|
+
expect(subject).to eq("foo")
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
context "without an ext/appsignal.architecture file" do
|
|
51
|
+
it "returns nil" do
|
|
52
|
+
expect(subject).to be_nil
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe ".agent_platform" do
|
|
58
|
+
let(:os) { "linux-gnu" }
|
|
59
|
+
let(:ldd_output) { "" }
|
|
60
|
+
before do
|
|
61
|
+
allow(described_class).to receive(:ldd_version_output).and_return(ldd_output)
|
|
62
|
+
allow(RbConfig::CONFIG).to receive(:[])
|
|
63
|
+
allow(RbConfig::CONFIG).to receive(:[]).with("host_os").and_return(os)
|
|
64
|
+
end
|
|
65
|
+
subject { described_class.agent_platform }
|
|
66
|
+
|
|
67
|
+
context "when the system detection doesn't work" do
|
|
68
|
+
it "returns the libc build" do
|
|
69
|
+
is_expected.to eq("linux")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context "when using the APPSIGNAL_BUILD_FOR_MUSL env var" do
|
|
74
|
+
it "returns the musl build" do
|
|
75
|
+
ENV["APPSIGNAL_BUILD_FOR_MUSL"] = "1"
|
|
76
|
+
is_expected.to eq("linux-musl")
|
|
77
|
+
ENV.delete("APPSIGNAL_BUILD_FOR_MUSL")
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
context "when on a musl system" do
|
|
82
|
+
let(:ldd_output) { "musl libc (x86_64)\nVersion 1.1.16" }
|
|
83
|
+
|
|
84
|
+
it "returns the musl build" do
|
|
85
|
+
is_expected.to eq("linux-musl")
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
context "when on a libc system" do
|
|
90
|
+
let(:ldd_output) { "ldd (Debian GLIBC 2.15-18+deb8u7) 2.15" }
|
|
91
|
+
|
|
92
|
+
it "returns the libc build" do
|
|
93
|
+
is_expected.to eq("linux")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
context "when on an old libc system" do
|
|
97
|
+
let(:ldd_output) { "ldd (Debian GLIBC 2.14-18+deb8u7) 2.14" }
|
|
98
|
+
|
|
99
|
+
it "returns the musl build" do
|
|
100
|
+
is_expected.to eq("linux-musl")
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
context "when on a very old libc system" do
|
|
105
|
+
let(:ldd_output) { "ldd (Debian GLIBC 2.5-18+deb8u7) 2.5" }
|
|
106
|
+
|
|
107
|
+
it "returns the musl build" do
|
|
108
|
+
is_expected.to eq("linux-musl")
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
context "when on macOS" do
|
|
114
|
+
let(:os) { "darwin16.7.0" }
|
|
115
|
+
let(:ldd_output) { "ldd: command not found" }
|
|
116
|
+
|
|
117
|
+
it "returns the darwin build" do
|
|
118
|
+
is_expected.to eq("darwin")
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
context "when on FreeBSD" do
|
|
123
|
+
let(:os) { "freebsd11" }
|
|
124
|
+
let(:ldd_output) { "ldd: illegal option -- -" }
|
|
125
|
+
|
|
126
|
+
it "returns the darwin build" do
|
|
127
|
+
is_expected.to eq("freebsd")
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,1146 @@
|
|
|
1
|
+
describe Appsignal::Transaction do
|
|
2
|
+
before :context do
|
|
3
|
+
start_agent
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
let(:transaction_id) { "1" }
|
|
7
|
+
let(:time) { Time.at(fixed_time) }
|
|
8
|
+
let(:namespace) { Appsignal::Transaction::HTTP_REQUEST }
|
|
9
|
+
let(:env) { {} }
|
|
10
|
+
let(:merged_env) { http_request_env_with_data(env) }
|
|
11
|
+
let(:options) { {} }
|
|
12
|
+
let(:request) { Rack::Request.new(merged_env) }
|
|
13
|
+
let(:transaction) { Appsignal::Transaction.new(transaction_id, namespace, request, options) }
|
|
14
|
+
let(:log) { StringIO.new }
|
|
15
|
+
|
|
16
|
+
before { Timecop.freeze(time) }
|
|
17
|
+
after { Timecop.return }
|
|
18
|
+
around do |example|
|
|
19
|
+
use_logger_with log do
|
|
20
|
+
example.run
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe "class methods" do
|
|
25
|
+
def current_transaction
|
|
26
|
+
Appsignal::Transaction.current
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe ".create" do
|
|
30
|
+
def create_transaction(id = transaction_id)
|
|
31
|
+
Appsignal::Transaction.create(id, namespace, request, options)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context "when no transaction is running" do
|
|
35
|
+
let!(:transaction) { create_transaction }
|
|
36
|
+
|
|
37
|
+
it "returns the created transaction" do
|
|
38
|
+
expect(transaction).to be_a Appsignal::Transaction
|
|
39
|
+
expect(transaction.transaction_id).to eq transaction_id
|
|
40
|
+
expect(transaction.namespace).to eq namespace
|
|
41
|
+
expect(transaction.request).to eq request
|
|
42
|
+
|
|
43
|
+
expect(transaction.to_h).to include(
|
|
44
|
+
"id" => transaction_id,
|
|
45
|
+
"namespace" => namespace
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "assigns the transaction to current" do
|
|
50
|
+
expect(transaction).to eq current_transaction
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context "when a transaction is already running" do
|
|
55
|
+
before { create_transaction }
|
|
56
|
+
|
|
57
|
+
it "does not create a new transaction, but returns the current transaction" do
|
|
58
|
+
expect do
|
|
59
|
+
new_transaction = create_transaction("2")
|
|
60
|
+
expect(new_transaction).to eq(current_transaction)
|
|
61
|
+
expect(new_transaction.transaction_id).to eq(transaction_id)
|
|
62
|
+
end.to_not(change { current_transaction })
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "logs a debug message" do
|
|
66
|
+
create_transaction("2")
|
|
67
|
+
expect(log_contents(log)).to contains_log :debug,
|
|
68
|
+
"Trying to start new transaction with id '2', but a " \
|
|
69
|
+
"transaction with id '#{transaction_id}' is already " \
|
|
70
|
+
"running. Using transaction '#{transaction_id}'."
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context "with option :force => true" do
|
|
74
|
+
it "returns the newly created (and current) transaction" do
|
|
75
|
+
original_transaction = current_transaction
|
|
76
|
+
expect(original_transaction).to_not be_nil
|
|
77
|
+
expect(current_transaction.transaction_id).to eq transaction_id
|
|
78
|
+
|
|
79
|
+
options[:force] = true
|
|
80
|
+
expect(create_transaction("2")).to_not eq original_transaction
|
|
81
|
+
expect(current_transaction.transaction_id).to eq "2"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
describe ".current" do
|
|
88
|
+
subject { Appsignal::Transaction.current }
|
|
89
|
+
|
|
90
|
+
context "when there is a current transaction" do
|
|
91
|
+
let!(:transaction) do
|
|
92
|
+
Appsignal::Transaction.create(transaction_id, namespace, request, options)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "reads :appsignal_transaction from the current Thread" do
|
|
96
|
+
expect(subject).to eq Thread.current[:appsignal_transaction]
|
|
97
|
+
expect(subject).to eq transaction
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "is not a NilTransaction" do
|
|
101
|
+
expect(subject.nil_transaction?).to eq false
|
|
102
|
+
expect(subject).to be_a Appsignal::Transaction
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
context "when there is no current transaction" do
|
|
107
|
+
it "has no :appsignal_transaction registered on the current Thread" do
|
|
108
|
+
expect(Thread.current[:appsignal_transaction]).to be_nil
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "returns a NilTransaction stub" do
|
|
112
|
+
expect(subject.nil_transaction?).to eq true
|
|
113
|
+
expect(subject).to be_a Appsignal::Transaction::NilTransaction
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe ".complete_current!" do
|
|
119
|
+
let!(:transaction) { Appsignal::Transaction.create(transaction_id, namespace, options) }
|
|
120
|
+
|
|
121
|
+
it "completes the current transaction" do
|
|
122
|
+
expect(transaction).to eq current_transaction
|
|
123
|
+
expect(transaction).to receive(:complete).and_call_original
|
|
124
|
+
|
|
125
|
+
Appsignal::Transaction.complete_current!
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it "unsets the current transaction on the current Thread" do
|
|
129
|
+
expect do
|
|
130
|
+
Appsignal::Transaction.complete_current!
|
|
131
|
+
end.to change { Thread.current[:appsignal_transaction] }.from(transaction).to(nil)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
context "when encountering an error while completing" do
|
|
135
|
+
before do
|
|
136
|
+
expect(transaction).to receive(:complete).and_raise ExampleStandardError
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "logs an error message" do
|
|
140
|
+
Appsignal::Transaction.complete_current!
|
|
141
|
+
expect(log_contents(log)).to contains_log :error,
|
|
142
|
+
"Failed to complete transaction ##{transaction.transaction_id}. ExampleStandardError"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "clears the current transaction" do
|
|
146
|
+
expect do
|
|
147
|
+
Appsignal::Transaction.complete_current!
|
|
148
|
+
end.to change { Thread.current[:appsignal_transaction] }.from(transaction).to(nil)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
describe "#complete" do
|
|
155
|
+
context "when transaction is being sampled" do
|
|
156
|
+
it "samples data" do
|
|
157
|
+
expect(transaction.ext).to receive(:finish).and_return(true)
|
|
158
|
+
# Stub call to extension, because that would remove the transaction
|
|
159
|
+
# from the extension.
|
|
160
|
+
expect(transaction.ext).to receive(:complete)
|
|
161
|
+
|
|
162
|
+
transaction.set_tags(:foo => "bar")
|
|
163
|
+
transaction.complete
|
|
164
|
+
expect(transaction.to_h["sample_data"]).to include(
|
|
165
|
+
"tags" => { "foo" => "bar" }
|
|
166
|
+
)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
context "when transaction is not being sampled" do
|
|
171
|
+
it "does not sample data" do
|
|
172
|
+
expect(transaction).to_not receive(:sample_data)
|
|
173
|
+
expect(transaction.ext).to receive(:finish).and_return(false)
|
|
174
|
+
expect(transaction.ext).to receive(:complete).and_call_original
|
|
175
|
+
|
|
176
|
+
transaction.complete
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
context "when a transaction is marked as discarded" do
|
|
181
|
+
it "does not complete the transaction" do
|
|
182
|
+
expect(transaction.ext).to_not receive(:complete)
|
|
183
|
+
|
|
184
|
+
expect do
|
|
185
|
+
transaction.discard!
|
|
186
|
+
end.to change { transaction.discarded? }.from(false).to(true)
|
|
187
|
+
|
|
188
|
+
transaction.complete
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it "logs a debug message" do
|
|
192
|
+
transaction.discard!
|
|
193
|
+
transaction.complete
|
|
194
|
+
|
|
195
|
+
expect(log_contents(log)).to contains_log :debug,
|
|
196
|
+
"Skipping transaction '#{transaction_id}' because it was manually discarded."
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
context "when a discarded transaction is restored" do
|
|
200
|
+
before { transaction.discard! }
|
|
201
|
+
|
|
202
|
+
it "completes the transaction" do
|
|
203
|
+
expect(transaction.ext).to receive(:complete).and_call_original
|
|
204
|
+
|
|
205
|
+
expect do
|
|
206
|
+
transaction.restore!
|
|
207
|
+
end.to change { transaction.discarded? }.from(true).to(false)
|
|
208
|
+
|
|
209
|
+
transaction.complete
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
context "pausing" do
|
|
216
|
+
describe "#pause!" do
|
|
217
|
+
it "should change the pause flag to true" do
|
|
218
|
+
expect do
|
|
219
|
+
transaction.pause!
|
|
220
|
+
end.to change(transaction, :paused).from(false).to(true)
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
describe "#resume!" do
|
|
225
|
+
before { transaction.pause! }
|
|
226
|
+
|
|
227
|
+
it "should change the pause flag to false" do
|
|
228
|
+
expect do
|
|
229
|
+
transaction.resume!
|
|
230
|
+
end.to change(transaction, :paused).from(true).to(false)
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
describe "#paused?" do
|
|
235
|
+
it "should return the pause state" do
|
|
236
|
+
expect(transaction.paused?).to be_falsy
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
context "when paused" do
|
|
240
|
+
before { transaction.pause! }
|
|
241
|
+
|
|
242
|
+
it "should return the pause state" do
|
|
243
|
+
expect(transaction.paused?).to be_truthy
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
context "with transaction instance" do
|
|
250
|
+
context "initialization" do
|
|
251
|
+
it "loads the AppSignal extension" do
|
|
252
|
+
expect(transaction.ext).to_not be_nil
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
it "sets the transaction id" do
|
|
256
|
+
expect(transaction.transaction_id).to eq "1"
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
it "sets the namespace to http_request" do
|
|
260
|
+
expect(transaction.namespace).to eq "http_request"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
it "sets the request" do
|
|
264
|
+
expect(transaction.request).to_not be_nil
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
it "sets the request not to paused" do
|
|
268
|
+
expect(transaction.paused).to be_falsy
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
it "sets no tags by default" do
|
|
272
|
+
expect(transaction.tags).to eq({})
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
describe "#options" do
|
|
276
|
+
subject { transaction.options }
|
|
277
|
+
|
|
278
|
+
it "sets the default :params_method" do
|
|
279
|
+
expect(subject[:params_method]).to eq :params
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
context "with overridden options" do
|
|
283
|
+
let(:options) { { :params_method => :filtered_params } }
|
|
284
|
+
|
|
285
|
+
it "sets the overriden :params_method" do
|
|
286
|
+
expect(subject[:params_method]).to eq :filtered_params
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
describe "#store" do
|
|
293
|
+
it "should return an empty store when it's not already present" do
|
|
294
|
+
expect(transaction.store("test")).to eql({})
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
it "should store changes to the store" do
|
|
298
|
+
transaction_store = transaction.store("test")
|
|
299
|
+
transaction_store["transaction"] = "value"
|
|
300
|
+
|
|
301
|
+
expect(transaction.store("test")).to eql("transaction" => "value")
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
describe "#params" do
|
|
306
|
+
subject { transaction.params }
|
|
307
|
+
|
|
308
|
+
context "with custom params set on transaction" do
|
|
309
|
+
before do
|
|
310
|
+
transaction.params = { :foo => "bar" }
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
it "returns custom parameters" do
|
|
314
|
+
expect(subject).to eq(:foo => "bar")
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
context "without custom params set on transaction" do
|
|
319
|
+
it "returns parameters from request" do
|
|
320
|
+
expect(subject).to eq(
|
|
321
|
+
"action" => "show",
|
|
322
|
+
"controller" => "blog_posts",
|
|
323
|
+
"id" => "1"
|
|
324
|
+
)
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
describe "#params=" do
|
|
330
|
+
it "sets params on the transaction" do
|
|
331
|
+
transaction.params = { :foo => "bar" }
|
|
332
|
+
expect(transaction.params).to eq(:foo => "bar")
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
describe "#set_tags" do
|
|
337
|
+
it "should add tags to transaction" do
|
|
338
|
+
expect do
|
|
339
|
+
transaction.set_tags("a" => "b")
|
|
340
|
+
end.to change(transaction, :tags).to("a" => "b")
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
describe "set_action" do
|
|
345
|
+
it "should set the action in extension" do
|
|
346
|
+
expect(transaction.ext).to receive(:set_action).with(
|
|
347
|
+
"PagesController#show"
|
|
348
|
+
).once
|
|
349
|
+
|
|
350
|
+
transaction.set_action("PagesController#show")
|
|
351
|
+
|
|
352
|
+
expect(transaction.action).to eq "PagesController#show"
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
it "should not set the action in extension when value is nil" do
|
|
356
|
+
expect(Appsignal::Extension).to_not receive(:set_action)
|
|
357
|
+
|
|
358
|
+
transaction.set_action(nil)
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
describe "set_action_if_nil" do
|
|
363
|
+
context "if action is currently nil" do
|
|
364
|
+
it "should set the action" do
|
|
365
|
+
expect(transaction.ext).to receive(:set_action).with(
|
|
366
|
+
"PagesController#show"
|
|
367
|
+
).once
|
|
368
|
+
|
|
369
|
+
transaction.set_action_if_nil("PagesController#show")
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
context "if action is currently set" do
|
|
374
|
+
it "should not set the action" do
|
|
375
|
+
transaction.set_action("something")
|
|
376
|
+
|
|
377
|
+
expect(transaction.ext).not_to receive(:set_action)
|
|
378
|
+
|
|
379
|
+
transaction.set_action_if_nil("PagesController#show")
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
describe "set_namespace" do
|
|
385
|
+
it "should set the action in extension" do
|
|
386
|
+
expect(transaction.ext).to receive(:set_namespace).with(
|
|
387
|
+
"custom"
|
|
388
|
+
).once
|
|
389
|
+
|
|
390
|
+
transaction.set_namespace("custom")
|
|
391
|
+
|
|
392
|
+
expect(transaction.namespace).to eq "custom"
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
it "should not set the action in extension when value is nil" do
|
|
396
|
+
expect(Appsignal::Extension).to_not receive(:set_namespace)
|
|
397
|
+
|
|
398
|
+
transaction.set_action(nil)
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
describe "#set_http_or_background_action" do
|
|
403
|
+
context "for a hash with controller and action" do
|
|
404
|
+
let(:from) { { :controller => "HomeController", :action => "show" } }
|
|
405
|
+
|
|
406
|
+
it "should set the action" do
|
|
407
|
+
expect(transaction).to receive(:set_action).with("HomeController#show")
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
context "for a hash with just action" do
|
|
412
|
+
let(:from) { { :action => "show" } }
|
|
413
|
+
|
|
414
|
+
it "should set the action" do
|
|
415
|
+
expect(transaction).to receive(:set_action).with("show")
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
context "for a hash with class and method" do
|
|
420
|
+
let(:from) { { :class => "Worker", :method => "perform" } }
|
|
421
|
+
|
|
422
|
+
it "should set the action" do
|
|
423
|
+
expect(transaction).to receive(:set_action).with("Worker#perform")
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
after { transaction.set_http_or_background_action(from) }
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
describe "set_queue_start" do
|
|
431
|
+
it "should set the queue start in extension" do
|
|
432
|
+
expect(transaction.ext).to receive(:set_queue_start).with(
|
|
433
|
+
10.0
|
|
434
|
+
).once
|
|
435
|
+
|
|
436
|
+
transaction.set_queue_start(10.0)
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
it "should not set the queue start in extension when value is nil" do
|
|
440
|
+
expect(transaction.ext).to_not receive(:set_queue_start)
|
|
441
|
+
|
|
442
|
+
transaction.set_queue_start(nil)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
it "should not raise an error when the queue start is too big" do
|
|
446
|
+
expect(transaction.ext).to receive(:set_queue_start).and_raise(RangeError)
|
|
447
|
+
|
|
448
|
+
expect(Appsignal.logger).to receive(:warn).with("Queue start value 10 is too big")
|
|
449
|
+
|
|
450
|
+
expect do
|
|
451
|
+
transaction.set_queue_start(10)
|
|
452
|
+
end.to_not raise_error
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
describe "#set_http_or_background_queue_start" do
|
|
457
|
+
context "for a http transaction" do
|
|
458
|
+
let(:namespace) { Appsignal::Transaction::HTTP_REQUEST }
|
|
459
|
+
let(:env) { { "HTTP_X_REQUEST_START" => (fixed_time * 1000).to_s } }
|
|
460
|
+
|
|
461
|
+
it "should set the queue start on the transaction" do
|
|
462
|
+
expect(transaction).to receive(:set_queue_start).with(13_897_836_000)
|
|
463
|
+
|
|
464
|
+
transaction.set_http_or_background_queue_start
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
context "for a background transaction" do
|
|
469
|
+
let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
|
|
470
|
+
let(:env) { { :queue_start => fixed_time } }
|
|
471
|
+
|
|
472
|
+
it "should set the queue start on the transaction" do
|
|
473
|
+
expect(transaction).to receive(:set_queue_start).with(1_389_783_600_000)
|
|
474
|
+
|
|
475
|
+
transaction.set_http_or_background_queue_start
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
describe "#set_metadata" do
|
|
481
|
+
it "should set the metdata in extension" do
|
|
482
|
+
expect(transaction.ext).to receive(:set_metadata).with(
|
|
483
|
+
"request_method",
|
|
484
|
+
"GET"
|
|
485
|
+
).once
|
|
486
|
+
|
|
487
|
+
transaction.set_metadata("request_method", "GET")
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
it "should not set the metdata in extension when value is nil" do
|
|
491
|
+
expect(transaction.ext).to_not receive(:set_metadata)
|
|
492
|
+
|
|
493
|
+
transaction.set_metadata("request_method", nil)
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
describe "set_sample_data" do
|
|
498
|
+
it "should set the data" do
|
|
499
|
+
expect(transaction.ext).to receive(:set_sample_data).with(
|
|
500
|
+
"params",
|
|
501
|
+
Appsignal::Utils.data_generate("controller" => "blog_posts", "action" => "show", "id" => "1")
|
|
502
|
+
).once
|
|
503
|
+
|
|
504
|
+
transaction.set_sample_data(
|
|
505
|
+
"params",
|
|
506
|
+
:controller => "blog_posts",
|
|
507
|
+
:action => "show",
|
|
508
|
+
:id => "1"
|
|
509
|
+
)
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
it "should do nothing if the data cannot be converted to json" do
|
|
513
|
+
expect(transaction.ext).to_not receive(:set_sample_data).with(
|
|
514
|
+
"params",
|
|
515
|
+
kind_of(String)
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
transaction.set_sample_data("params", "string")
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
describe "#sample_data" do
|
|
523
|
+
it "should sample data" do
|
|
524
|
+
expect(transaction.ext).to receive(:set_sample_data).with(
|
|
525
|
+
"environment",
|
|
526
|
+
Appsignal::Utils.data_generate(
|
|
527
|
+
"CONTENT_LENGTH" => "0",
|
|
528
|
+
"REQUEST_METHOD" => "GET",
|
|
529
|
+
"SERVER_NAME" => "example.org",
|
|
530
|
+
"SERVER_PORT" => "80",
|
|
531
|
+
"PATH_INFO" => "/blog"
|
|
532
|
+
)
|
|
533
|
+
).once
|
|
534
|
+
expect(transaction.ext).to receive(:set_sample_data).with(
|
|
535
|
+
"session_data",
|
|
536
|
+
Appsignal::Utils.data_generate({})
|
|
537
|
+
).once
|
|
538
|
+
expect(transaction.ext).to receive(:set_sample_data).with(
|
|
539
|
+
"params",
|
|
540
|
+
Appsignal::Utils.data_generate("controller" => "blog_posts", "action" => "show", "id" => "1")
|
|
541
|
+
).once
|
|
542
|
+
expect(transaction.ext).to receive(:set_sample_data).with(
|
|
543
|
+
"metadata",
|
|
544
|
+
Appsignal::Utils.data_generate("key" => "value")
|
|
545
|
+
).once
|
|
546
|
+
expect(transaction.ext).to receive(:set_sample_data).with(
|
|
547
|
+
"tags",
|
|
548
|
+
Appsignal::Utils.data_generate({})
|
|
549
|
+
).once
|
|
550
|
+
|
|
551
|
+
transaction.sample_data
|
|
552
|
+
end
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
describe "#set_error" do
|
|
556
|
+
let(:env) { http_request_env_with_data }
|
|
557
|
+
let(:error) { double(:error, :message => "test message", :backtrace => ["line 1"]) }
|
|
558
|
+
|
|
559
|
+
it "should also respond to add_exception for backwords compatibility" do
|
|
560
|
+
expect(transaction).to respond_to(:add_exception)
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
it "should not add the error if appsignal is not active" do
|
|
564
|
+
allow(Appsignal).to receive(:active?).and_return(false)
|
|
565
|
+
expect(transaction.ext).to_not receive(:set_error)
|
|
566
|
+
|
|
567
|
+
transaction.set_error(error)
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
context "for a http request" do
|
|
571
|
+
it "should set an error in the extension" do
|
|
572
|
+
expect(transaction.ext).to receive(:set_error).with(
|
|
573
|
+
"RSpec::Mocks::Double",
|
|
574
|
+
"test message",
|
|
575
|
+
Appsignal::Utils.data_generate(["line 1"])
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
transaction.set_error(error)
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
context "when error message is nil" do
|
|
583
|
+
let(:error) { double(:error, :message => nil, :backtrace => ["line 1"]) }
|
|
584
|
+
|
|
585
|
+
it "should not raise an error" do
|
|
586
|
+
expect { transaction.set_error(error) }.to_not raise_error
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
it "should set an error in the extension" do
|
|
590
|
+
expect(transaction.ext).to receive(:set_error).with(
|
|
591
|
+
"RSpec::Mocks::Double",
|
|
592
|
+
"",
|
|
593
|
+
Appsignal::Utils.data_generate(["line 1"])
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
transaction.set_error(error)
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
describe "#start_event" do
|
|
602
|
+
it "should start the event in the extension" do
|
|
603
|
+
expect(transaction.ext).to receive(:start_event).with(0).and_call_original
|
|
604
|
+
|
|
605
|
+
transaction.start_event
|
|
606
|
+
end
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
describe "#finish_event" do
|
|
610
|
+
let(:fake_gc_time) { 123 }
|
|
611
|
+
before do
|
|
612
|
+
expect(described_class.garbage_collection_profiler)
|
|
613
|
+
.to receive(:total_time).at_least(:once).and_return(fake_gc_time)
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
it "should finish the event in the extension" do
|
|
617
|
+
expect(transaction.ext).to receive(:finish_event).with(
|
|
618
|
+
"name",
|
|
619
|
+
"title",
|
|
620
|
+
"body",
|
|
621
|
+
1,
|
|
622
|
+
fake_gc_time
|
|
623
|
+
).and_call_original
|
|
624
|
+
|
|
625
|
+
transaction.finish_event(
|
|
626
|
+
"name",
|
|
627
|
+
"title",
|
|
628
|
+
"body",
|
|
629
|
+
1
|
|
630
|
+
)
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
it "should finish the event in the extension with nil arguments" do
|
|
634
|
+
expect(transaction.ext).to receive(:finish_event).with(
|
|
635
|
+
"name",
|
|
636
|
+
"",
|
|
637
|
+
"",
|
|
638
|
+
0,
|
|
639
|
+
fake_gc_time
|
|
640
|
+
).and_call_original
|
|
641
|
+
|
|
642
|
+
transaction.finish_event(
|
|
643
|
+
"name",
|
|
644
|
+
nil,
|
|
645
|
+
nil,
|
|
646
|
+
nil
|
|
647
|
+
)
|
|
648
|
+
end
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
describe "#record_event" do
|
|
652
|
+
let(:fake_gc_time) { 123 }
|
|
653
|
+
before do
|
|
654
|
+
expect(described_class.garbage_collection_profiler)
|
|
655
|
+
.to receive(:total_time).at_least(:once).and_return(fake_gc_time)
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
it "should record the event in the extension" do
|
|
659
|
+
expect(transaction.ext).to receive(:record_event).with(
|
|
660
|
+
"name",
|
|
661
|
+
"title",
|
|
662
|
+
"body",
|
|
663
|
+
1,
|
|
664
|
+
1000,
|
|
665
|
+
fake_gc_time
|
|
666
|
+
).and_call_original
|
|
667
|
+
|
|
668
|
+
transaction.record_event(
|
|
669
|
+
"name",
|
|
670
|
+
"title",
|
|
671
|
+
"body",
|
|
672
|
+
1000,
|
|
673
|
+
1
|
|
674
|
+
)
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
it "should finish the event in the extension with nil arguments" do
|
|
678
|
+
expect(transaction.ext).to receive(:record_event).with(
|
|
679
|
+
"name",
|
|
680
|
+
"",
|
|
681
|
+
"",
|
|
682
|
+
0,
|
|
683
|
+
1000,
|
|
684
|
+
fake_gc_time
|
|
685
|
+
).and_call_original
|
|
686
|
+
|
|
687
|
+
transaction.record_event(
|
|
688
|
+
"name",
|
|
689
|
+
nil,
|
|
690
|
+
nil,
|
|
691
|
+
1000,
|
|
692
|
+
nil
|
|
693
|
+
)
|
|
694
|
+
end
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
describe "#instrument" do
|
|
698
|
+
it_behaves_like "instrument helper" do
|
|
699
|
+
let(:instrumenter) { transaction }
|
|
700
|
+
end
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
context "generic request" do
|
|
704
|
+
let(:env) { {} }
|
|
705
|
+
subject { Appsignal::Transaction::GenericRequest.new(env) }
|
|
706
|
+
|
|
707
|
+
it "initializes with an empty env" do
|
|
708
|
+
expect(subject.env).to be_empty
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
context "when given an env" do
|
|
712
|
+
let(:env) do
|
|
713
|
+
{
|
|
714
|
+
:params => { :id => 1 },
|
|
715
|
+
:queue_start => 10
|
|
716
|
+
}
|
|
717
|
+
end
|
|
718
|
+
|
|
719
|
+
it "sets the given env" do
|
|
720
|
+
expect(subject.env).to eq env
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
it "sets the params present in the env" do
|
|
724
|
+
expect(subject.params).to eq(:id => 1)
|
|
725
|
+
end
|
|
726
|
+
end
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
# private
|
|
730
|
+
|
|
731
|
+
describe "#background_queue_start" do
|
|
732
|
+
subject { transaction.send(:background_queue_start) }
|
|
733
|
+
|
|
734
|
+
context "when request is nil" do
|
|
735
|
+
let(:request) { nil }
|
|
736
|
+
|
|
737
|
+
it { is_expected.to eq nil }
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
context "when env is nil" do
|
|
741
|
+
before { expect(transaction.request).to receive(:env).and_return(nil) }
|
|
742
|
+
|
|
743
|
+
it { is_expected.to eq nil }
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
context "when queue start is nil" do
|
|
747
|
+
it { is_expected.to eq nil }
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
context "when queue start is set" do
|
|
751
|
+
let(:env) { background_env_with_data }
|
|
752
|
+
|
|
753
|
+
it { is_expected.to eq 1_389_783_590_000 }
|
|
754
|
+
end
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
describe "#http_queue_start" do
|
|
758
|
+
let(:slightly_earlier_time) { fixed_time - 0.4 }
|
|
759
|
+
let(:slightly_earlier_time_value) { (slightly_earlier_time * factor).to_i }
|
|
760
|
+
subject { transaction.send(:http_queue_start) }
|
|
761
|
+
|
|
762
|
+
shared_examples "http queue start" do
|
|
763
|
+
context "when request is nil" do
|
|
764
|
+
let(:request) { nil }
|
|
765
|
+
|
|
766
|
+
it { is_expected.to be_nil }
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
context "when env is nil" do
|
|
770
|
+
before { expect(transaction.request).to receive(:env).and_return(nil) }
|
|
771
|
+
|
|
772
|
+
it { is_expected.to be_nil }
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
context "with no relevant header set" do
|
|
776
|
+
let(:env) { {} }
|
|
777
|
+
|
|
778
|
+
it { is_expected.to be_nil }
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
context "with the HTTP_X_REQUEST_START header set" do
|
|
782
|
+
let(:env) { { "HTTP_X_REQUEST_START" => "t=#{slightly_earlier_time_value}" } }
|
|
783
|
+
|
|
784
|
+
it { is_expected.to eq 1_389_783_599_600 }
|
|
785
|
+
|
|
786
|
+
context "with unparsable content" do
|
|
787
|
+
let(:env) { { "HTTP_X_REQUEST_START" => "something" } }
|
|
788
|
+
|
|
789
|
+
it { is_expected.to be_nil }
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
context "with some cruft" do
|
|
793
|
+
let(:env) { { "HTTP_X_REQUEST_START" => "t=#{slightly_earlier_time_value}aaaa" } }
|
|
794
|
+
|
|
795
|
+
it { is_expected.to eq 1_389_783_599_600 }
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
context "with a really low number" do
|
|
799
|
+
let(:env) { { "HTTP_X_REQUEST_START" => "t=100" } }
|
|
800
|
+
|
|
801
|
+
it { is_expected.to be_nil }
|
|
802
|
+
end
|
|
803
|
+
|
|
804
|
+
context "with the alternate HTTP_X_QUEUE_START header set" do
|
|
805
|
+
let(:env) { { "HTTP_X_QUEUE_START" => "t=#{slightly_earlier_time_value}" } }
|
|
806
|
+
|
|
807
|
+
it { is_expected.to eq 1_389_783_599_600 }
|
|
808
|
+
end
|
|
809
|
+
end
|
|
810
|
+
end
|
|
811
|
+
|
|
812
|
+
context "time in miliseconds" do
|
|
813
|
+
let(:factor) { 1_000 }
|
|
814
|
+
|
|
815
|
+
it_should_behave_like "http queue start"
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
context "time in microseconds" do
|
|
819
|
+
let(:factor) { 1_000_000 }
|
|
820
|
+
|
|
821
|
+
it_should_behave_like "http queue start"
|
|
822
|
+
end
|
|
823
|
+
end
|
|
824
|
+
|
|
825
|
+
describe "#sanitized_params" do
|
|
826
|
+
subject { transaction.send(:sanitized_params) }
|
|
827
|
+
|
|
828
|
+
context "with custom params" do
|
|
829
|
+
before do
|
|
830
|
+
transaction.params = { :foo => "bar", :baz => :bat }
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
it "returns custom params" do
|
|
834
|
+
is_expected.to eq(:foo => "bar", :baz => :bat)
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
context "with AppSignal filtering" do
|
|
838
|
+
before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
|
|
839
|
+
after { Appsignal.config.config_hash[:filter_parameters] = [] }
|
|
840
|
+
|
|
841
|
+
it "returns sanitized custom params" do
|
|
842
|
+
expect(subject).to eq(:foo => "[FILTERED]", :baz => :bat)
|
|
843
|
+
end
|
|
844
|
+
end
|
|
845
|
+
end
|
|
846
|
+
|
|
847
|
+
context "without request params" do
|
|
848
|
+
before { allow(transaction.request).to receive(:params).and_return(nil) }
|
|
849
|
+
|
|
850
|
+
it { is_expected.to be_nil }
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
context "when request params crashes" do
|
|
854
|
+
before { allow(transaction.request).to receive(:params).and_raise(NoMethodError) }
|
|
855
|
+
|
|
856
|
+
it { is_expected.to be_nil }
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
context "when request params method does not exist" do
|
|
860
|
+
let(:options) { { :params_method => :nonsense } }
|
|
861
|
+
|
|
862
|
+
it { is_expected.to be_nil }
|
|
863
|
+
end
|
|
864
|
+
|
|
865
|
+
context "when not sending params" do
|
|
866
|
+
before { Appsignal.config.config_hash[:send_params] = false }
|
|
867
|
+
after { Appsignal.config.config_hash[:send_params] = true }
|
|
868
|
+
|
|
869
|
+
it { is_expected.to be_nil }
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
context "with an array" do
|
|
873
|
+
let(:request) do
|
|
874
|
+
Appsignal::Transaction::GenericRequest.new(background_env_with_data(:params => %w[arg1 arg2]))
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
it { is_expected.to eq %w[arg1 arg2] }
|
|
878
|
+
|
|
879
|
+
context "with AppSignal filtering" do
|
|
880
|
+
before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
|
|
881
|
+
after { Appsignal.config.config_hash[:filter_parameters] = [] }
|
|
882
|
+
|
|
883
|
+
it { is_expected.to eq %w[arg1 arg2] }
|
|
884
|
+
end
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
context "with env" do
|
|
888
|
+
context "with sanitization" do
|
|
889
|
+
let(:request) do
|
|
890
|
+
Appsignal::Transaction::GenericRequest.new \
|
|
891
|
+
http_request_env_with_data(:params => { :foo => :bar })
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
it "should call the params sanitizer" do
|
|
895
|
+
expect(subject).to eq(:foo => :bar)
|
|
896
|
+
end
|
|
897
|
+
end
|
|
898
|
+
|
|
899
|
+
context "with AppSignal filtering" do
|
|
900
|
+
let(:request) do
|
|
901
|
+
Appsignal::Transaction::GenericRequest.new \
|
|
902
|
+
http_request_env_with_data(:params => { :foo => :bar, :baz => :bat })
|
|
903
|
+
end
|
|
904
|
+
before { Appsignal.config.config_hash[:filter_parameters] = %w[foo] }
|
|
905
|
+
after { Appsignal.config.config_hash[:filter_parameters] = [] }
|
|
906
|
+
|
|
907
|
+
it "should call the params sanitizer with filtering" do
|
|
908
|
+
expect(subject).to eq(:foo => "[FILTERED]", :baz => :bat)
|
|
909
|
+
end
|
|
910
|
+
end
|
|
911
|
+
end
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
describe "#sanitized_environment" do
|
|
915
|
+
let(:whitelisted_keys) { Appsignal::Transaction::ENV_METHODS }
|
|
916
|
+
|
|
917
|
+
subject { transaction.send(:sanitized_environment) }
|
|
918
|
+
|
|
919
|
+
context "when request is nil" do
|
|
920
|
+
let(:request) { nil }
|
|
921
|
+
|
|
922
|
+
it { is_expected.to be_nil }
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
context "when env is nil" do
|
|
926
|
+
before { expect(transaction.request).to receive(:env).and_return(nil) }
|
|
927
|
+
|
|
928
|
+
it { is_expected.to be_nil }
|
|
929
|
+
end
|
|
930
|
+
|
|
931
|
+
context "when env is present" do
|
|
932
|
+
let(:env) do
|
|
933
|
+
{}.tap do |hash|
|
|
934
|
+
whitelisted_keys.each { |o| hash[o] = 1 } # use all whitelisted keys
|
|
935
|
+
hash[whitelisted_keys] = nil # don't add if nil
|
|
936
|
+
hash[:not_whitelisted] = "I will be sanitized"
|
|
937
|
+
end
|
|
938
|
+
end
|
|
939
|
+
|
|
940
|
+
it "only sets whitelisted keys" do
|
|
941
|
+
expect(subject.keys).to match_array(whitelisted_keys)
|
|
942
|
+
end
|
|
943
|
+
end
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
describe "#sanitized_session_data" do
|
|
947
|
+
subject { transaction.send(:sanitized_session_data) }
|
|
948
|
+
|
|
949
|
+
context "when request is nil" do
|
|
950
|
+
let(:request) { nil }
|
|
951
|
+
|
|
952
|
+
it { is_expected.to be_nil }
|
|
953
|
+
end
|
|
954
|
+
|
|
955
|
+
context "when session is nil" do
|
|
956
|
+
before { expect(transaction.request).to receive(:session).and_return(nil) }
|
|
957
|
+
|
|
958
|
+
it { is_expected.to be_nil }
|
|
959
|
+
end
|
|
960
|
+
|
|
961
|
+
context "when session is empty" do
|
|
962
|
+
before { expect(transaction.request).to receive(:session).and_return({}) }
|
|
963
|
+
|
|
964
|
+
it { is_expected.to eq({}) }
|
|
965
|
+
end
|
|
966
|
+
|
|
967
|
+
context "when request class does not have a session method" do
|
|
968
|
+
let(:request) { Appsignal::Transaction::GenericRequest.new({}) }
|
|
969
|
+
|
|
970
|
+
it { is_expected.to be_nil }
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
context "when there is a session" do
|
|
974
|
+
before do
|
|
975
|
+
expect(transaction).to respond_to(:request)
|
|
976
|
+
allow(transaction).to receive_message_chain(:request, :session => { :foo => :bar })
|
|
977
|
+
allow(transaction).to receive_message_chain(:request, :fullpath => :bar)
|
|
978
|
+
end
|
|
979
|
+
|
|
980
|
+
it "passes the session data into the params sanitizer" do
|
|
981
|
+
expect(Appsignal::Utils::ParamsSanitizer).to receive(:sanitize).with(:foo => :bar)
|
|
982
|
+
.and_return(:sanitized_foo)
|
|
983
|
+
expect(subject).to eq :sanitized_foo
|
|
984
|
+
end
|
|
985
|
+
|
|
986
|
+
if defined? ActionDispatch::Request::Session
|
|
987
|
+
context "with ActionDispatch::Request::Session" do
|
|
988
|
+
before do
|
|
989
|
+
expect(transaction).to respond_to(:request)
|
|
990
|
+
allow(transaction).to receive_message_chain(:request, :session => action_dispatch_session)
|
|
991
|
+
allow(transaction).to receive_message_chain(:request, :fullpath => :bar)
|
|
992
|
+
end
|
|
993
|
+
|
|
994
|
+
it "should return an session hash" do
|
|
995
|
+
expect(Appsignal::Utils::ParamsSanitizer).to receive(:sanitize).with("foo" => :bar)
|
|
996
|
+
.and_return(:sanitized_foo)
|
|
997
|
+
subject
|
|
998
|
+
end
|
|
999
|
+
|
|
1000
|
+
def action_dispatch_session
|
|
1001
|
+
store = Class.new do
|
|
1002
|
+
def load_session(_env)
|
|
1003
|
+
[1, { :foo => :bar }]
|
|
1004
|
+
end
|
|
1005
|
+
|
|
1006
|
+
def session_exists?(_env)
|
|
1007
|
+
true
|
|
1008
|
+
end
|
|
1009
|
+
end.new
|
|
1010
|
+
ActionDispatch::Request::Session.create(store, ActionDispatch::Request.new("rack.input" => StringIO.new), {})
|
|
1011
|
+
end
|
|
1012
|
+
end
|
|
1013
|
+
end
|
|
1014
|
+
|
|
1015
|
+
context "when skipping session data" do
|
|
1016
|
+
before do
|
|
1017
|
+
Appsignal.config = { :skip_session_data => true }
|
|
1018
|
+
end
|
|
1019
|
+
|
|
1020
|
+
it "does not pass the session data into the params sanitizer" do
|
|
1021
|
+
expect(Appsignal::Utils::ParamsSanitizer).to_not receive(:sanitize)
|
|
1022
|
+
expect(subject).to be_nil
|
|
1023
|
+
end
|
|
1024
|
+
end
|
|
1025
|
+
end
|
|
1026
|
+
end
|
|
1027
|
+
|
|
1028
|
+
describe "#metadata" do
|
|
1029
|
+
subject { transaction.send(:metadata) }
|
|
1030
|
+
|
|
1031
|
+
context "when request is nil" do
|
|
1032
|
+
let(:request) { nil }
|
|
1033
|
+
|
|
1034
|
+
it { is_expected.to be_nil }
|
|
1035
|
+
end
|
|
1036
|
+
|
|
1037
|
+
context "when env is nil" do
|
|
1038
|
+
before { expect(transaction.request).to receive(:env).and_return(nil) }
|
|
1039
|
+
|
|
1040
|
+
it { is_expected.to be_nil }
|
|
1041
|
+
end
|
|
1042
|
+
|
|
1043
|
+
context "when env is present" do
|
|
1044
|
+
let(:env) { { :metadata => { :key => "value" } } }
|
|
1045
|
+
|
|
1046
|
+
it { is_expected.to eq env[:metadata] }
|
|
1047
|
+
end
|
|
1048
|
+
end
|
|
1049
|
+
|
|
1050
|
+
describe "#sanitized_tags" do
|
|
1051
|
+
before do
|
|
1052
|
+
transaction.set_tags(
|
|
1053
|
+
:valid_key => "valid_value",
|
|
1054
|
+
"valid_string_key" => "valid_value",
|
|
1055
|
+
:both_symbols => :valid_value,
|
|
1056
|
+
:integer_value => 1,
|
|
1057
|
+
:hash_value => { "invalid" => "hash" },
|
|
1058
|
+
:array_value => %w[invalid array],
|
|
1059
|
+
:to_long_value => SecureRandom.urlsafe_base64(101),
|
|
1060
|
+
:object => Object.new,
|
|
1061
|
+
SecureRandom.urlsafe_base64(101) => "to_long_key"
|
|
1062
|
+
)
|
|
1063
|
+
end
|
|
1064
|
+
subject { transaction.send(:sanitized_tags).keys }
|
|
1065
|
+
|
|
1066
|
+
it "should only return whitelisted data" do
|
|
1067
|
+
is_expected.to match_array([
|
|
1068
|
+
:valid_key,
|
|
1069
|
+
"valid_string_key",
|
|
1070
|
+
:both_symbols,
|
|
1071
|
+
:integer_value
|
|
1072
|
+
])
|
|
1073
|
+
end
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
describe "#cleaned_backtrace" do
|
|
1077
|
+
subject { transaction.send(:cleaned_backtrace, ["line 1", "line 2"]) }
|
|
1078
|
+
|
|
1079
|
+
it "returns the backtrace" do
|
|
1080
|
+
expect(subject).to eq ["line 1", "line 2"]
|
|
1081
|
+
end
|
|
1082
|
+
|
|
1083
|
+
if rails_present?
|
|
1084
|
+
context "with rails" do
|
|
1085
|
+
it "cleans the backtrace with the Rails backtrace cleaner" do
|
|
1086
|
+
::Rails.backtrace_cleaner.add_filter do |line|
|
|
1087
|
+
line.tr("2", "?")
|
|
1088
|
+
end
|
|
1089
|
+
expect(subject).to eq ["line 1", "line ?"]
|
|
1090
|
+
end
|
|
1091
|
+
end
|
|
1092
|
+
end
|
|
1093
|
+
end
|
|
1094
|
+
end
|
|
1095
|
+
|
|
1096
|
+
describe ".to_hash / .to_h" do
|
|
1097
|
+
subject { transaction.to_hash }
|
|
1098
|
+
|
|
1099
|
+
context "when extension returns serialized JSON" do
|
|
1100
|
+
it "parses the result and returns a Hash" do
|
|
1101
|
+
expect(subject).to include(
|
|
1102
|
+
"action" => nil,
|
|
1103
|
+
"error" => nil,
|
|
1104
|
+
"events" => [],
|
|
1105
|
+
"id" => transaction_id,
|
|
1106
|
+
"metadata" => {},
|
|
1107
|
+
"namespace" => namespace,
|
|
1108
|
+
"sample_data" => {}
|
|
1109
|
+
)
|
|
1110
|
+
end
|
|
1111
|
+
end
|
|
1112
|
+
|
|
1113
|
+
context "when the extension returns invalid serialized JSON" do
|
|
1114
|
+
before do
|
|
1115
|
+
expect(transaction.ext).to receive(:to_json).and_return("foo")
|
|
1116
|
+
end
|
|
1117
|
+
|
|
1118
|
+
it "raises a JSON parse error" do
|
|
1119
|
+
expect { subject }.to raise_error(JSON::ParserError)
|
|
1120
|
+
end
|
|
1121
|
+
end
|
|
1122
|
+
end
|
|
1123
|
+
|
|
1124
|
+
describe Appsignal::Transaction::NilTransaction do
|
|
1125
|
+
subject { Appsignal::Transaction::NilTransaction.new }
|
|
1126
|
+
|
|
1127
|
+
it "should have method stubs" do
|
|
1128
|
+
expect do
|
|
1129
|
+
subject.complete
|
|
1130
|
+
subject.pause!
|
|
1131
|
+
subject.resume!
|
|
1132
|
+
subject.paused?
|
|
1133
|
+
subject.store(:key)
|
|
1134
|
+
subject.set_tags(:tag => 1)
|
|
1135
|
+
subject.set_action("action")
|
|
1136
|
+
subject.set_http_or_background_action
|
|
1137
|
+
subject.set_queue_start(1)
|
|
1138
|
+
subject.set_http_or_background_queue_start
|
|
1139
|
+
subject.set_metadata("key", "value")
|
|
1140
|
+
subject.set_sample_data("key", "data")
|
|
1141
|
+
subject.sample_data
|
|
1142
|
+
subject.set_error("a")
|
|
1143
|
+
end.to_not raise_error
|
|
1144
|
+
end
|
|
1145
|
+
end
|
|
1146
|
+
end
|