appsignal 2.11.1-java → 3.0.0.beta.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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -1
  3. data/.semaphore/semaphore.yml +197 -23
  4. data/CHANGELOG.md +25 -0
  5. data/README.md +9 -0
  6. data/Rakefile +9 -6
  7. data/build_matrix.yml +13 -4
  8. data/ext/agent.yml +17 -17
  9. data/ext/base.rb +12 -9
  10. data/gemfiles/no_dependencies.gemfile +7 -0
  11. data/gemfiles/resque-2.gemfile +0 -1
  12. data/gemfiles/webmachine.gemfile +1 -0
  13. data/lib/appsignal.rb +1 -27
  14. data/lib/appsignal/auth_check.rb +2 -8
  15. data/lib/appsignal/cli.rb +1 -23
  16. data/lib/appsignal/cli/diagnose/utils.rb +8 -11
  17. data/lib/appsignal/cli/install.rb +5 -8
  18. data/lib/appsignal/config.rb +0 -24
  19. data/lib/appsignal/event_formatter.rb +0 -25
  20. data/lib/appsignal/helpers/instrumentation.rb +32 -0
  21. data/lib/appsignal/hooks.rb +0 -23
  22. data/lib/appsignal/hooks/action_cable.rb +3 -34
  23. data/lib/appsignal/hooks/active_support_notifications.rb +7 -86
  24. data/lib/appsignal/hooks/celluloid.rb +5 -9
  25. data/lib/appsignal/hooks/net_http.rb +2 -12
  26. data/lib/appsignal/hooks/puma.rb +3 -5
  27. data/lib/appsignal/hooks/que.rb +1 -1
  28. data/lib/appsignal/hooks/rake.rb +2 -24
  29. data/lib/appsignal/hooks/redis.rb +2 -13
  30. data/lib/appsignal/hooks/resque.rb +2 -43
  31. data/lib/appsignal/hooks/shoryuken.rb +43 -4
  32. data/lib/appsignal/hooks/unicorn.rb +3 -24
  33. data/lib/appsignal/hooks/webmachine.rb +1 -7
  34. data/lib/appsignal/integrations/action_cable.rb +34 -0
  35. data/lib/appsignal/integrations/active_support_notifications.rb +77 -0
  36. data/lib/appsignal/integrations/net_http.rb +16 -0
  37. data/lib/appsignal/integrations/object.rb +44 -17
  38. data/lib/appsignal/integrations/padrino.rb +5 -7
  39. data/lib/appsignal/integrations/que.rb +26 -33
  40. data/lib/appsignal/integrations/railtie.rb +1 -4
  41. data/lib/appsignal/integrations/rake.rb +26 -2
  42. data/lib/appsignal/integrations/redis.rb +17 -0
  43. data/lib/appsignal/integrations/resque.rb +39 -10
  44. data/lib/appsignal/integrations/unicorn.rb +28 -0
  45. data/lib/appsignal/integrations/webmachine.rb +22 -24
  46. data/lib/appsignal/minutely.rb +0 -12
  47. data/lib/appsignal/system.rb +4 -0
  48. data/lib/appsignal/transaction.rb +30 -2
  49. data/lib/appsignal/version.rb +1 -1
  50. data/spec/lib/appsignal/auth_check_spec.rb +1 -24
  51. data/spec/lib/appsignal/cli_spec.rb +1 -1
  52. data/spec/lib/appsignal/config_spec.rb +0 -66
  53. data/spec/lib/appsignal/event_formatter_spec.rb +0 -37
  54. data/spec/lib/appsignal/hooks/celluloid_spec.rb +6 -1
  55. data/spec/lib/appsignal/hooks/rake_spec.rb +1 -2
  56. data/spec/lib/appsignal/hooks/redis_spec.rb +50 -15
  57. data/spec/lib/appsignal/hooks/resque_spec.rb +10 -2
  58. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +151 -104
  59. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +4 -2
  60. data/spec/lib/appsignal/hooks/unicorn_spec.rb +14 -3
  61. data/spec/lib/appsignal/hooks/webmachine_spec.rb +2 -13
  62. data/spec/lib/appsignal/hooks_spec.rb +0 -57
  63. data/spec/lib/appsignal/integrations/object_spec.rb +25 -10
  64. data/spec/lib/appsignal/integrations/padrino_spec.rb +2 -3
  65. data/spec/lib/appsignal/integrations/railtie_spec.rb +0 -45
  66. data/spec/lib/appsignal/integrations/webmachine_spec.rb +26 -8
  67. data/spec/lib/appsignal/minutely_spec.rb +0 -19
  68. data/spec/lib/appsignal/transaction_spec.rb +56 -14
  69. data/spec/lib/appsignal/transmitter_spec.rb +1 -1
  70. data/spec/lib/appsignal_spec.rb +30 -69
  71. data/spec/spec_helper.rb +1 -15
  72. metadata +13 -22
  73. data/lib/appsignal/cli/notify_of_deploy.rb +0 -131
  74. data/lib/appsignal/integrations/resque_active_job.rb +0 -19
  75. data/lib/appsignal/js_exception_transaction.rb +0 -56
  76. data/lib/appsignal/rack/js_exception_catcher.rb +0 -80
  77. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +0 -180
  78. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +0 -28
  79. data/spec/lib/appsignal/integrations/resque_spec.rb +0 -28
  80. data/spec/lib/appsignal/js_exception_transaction_spec.rb +0 -128
  81. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +0 -170
@@ -262,7 +262,8 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
262
262
  "sample_data" => {
263
263
  "environment" => {},
264
264
  "params" => expected_args,
265
- "tags" => {}
265
+ "tags" => {},
266
+ "breadcrumbs" => []
266
267
  }
267
268
  )
268
269
  expect_transaction_to_have_sidekiq_event(transaction_hash)
@@ -290,7 +291,8 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
290
291
  "sample_data" => {
291
292
  "environment" => {},
292
293
  "params" => expected_args,
293
- "tags" => {}
294
+ "tags" => {},
295
+ "breadcrumbs" => []
294
296
  }
295
297
  )
296
298
  # TODO: Not available in transaction.to_h yet.
@@ -3,12 +3,22 @@ describe Appsignal::Hooks::UnicornHook do
3
3
  before :context do
4
4
  module Unicorn
5
5
  class HttpServer
6
- def worker_loop(worker)
6
+ def worker_loop(_worker)
7
+ @worker_loop = true
8
+ end
9
+
10
+ def worker_loop?
11
+ @worker_loop == true
7
12
  end
8
13
  end
9
14
 
10
15
  class Worker
11
16
  def close
17
+ @close = true
18
+ end
19
+
20
+ def close?
21
+ @close == true
12
22
  end
13
23
  end
14
24
  end
@@ -27,18 +37,19 @@ describe Appsignal::Hooks::UnicornHook do
27
37
  worker = double
28
38
 
29
39
  expect(Appsignal).to receive(:forked)
30
- expect(server).to receive(:worker_loop_without_appsignal).with(worker)
31
40
 
32
41
  server.worker_loop(worker)
42
+
43
+ expect(server.worker_loop?).to be true
33
44
  end
34
45
 
35
46
  it "adds behavior to Unicorn::Worker#close" do
36
47
  worker = Unicorn::Worker.new
37
48
 
38
49
  expect(Appsignal).to receive(:stop)
39
- expect(worker).to receive(:close_without_appsignal)
40
50
 
41
51
  worker.close
52
+ expect(worker.close?).to be true
42
53
  end
43
54
  end
44
55
 
@@ -10,19 +10,8 @@ describe Appsignal::Hooks::WebmachineHook do
10
10
  it { is_expected.to be_truthy }
11
11
  end
12
12
 
13
- it "should include the run alias methods" do
14
- expect(fsm).to respond_to(:run_with_appsignal)
15
- expect(fsm).to respond_to(:run_without_appsignal)
16
- end
17
-
18
- it "should include the handle_exceptions alias methods" do
19
- expect(
20
- fsm.respond_to?(:handle_exceptions_with_appsignal, true)
21
- ).to be_truthy
22
-
23
- expect(
24
- fsm.respond_to?(:handle_exceptions_without_appsignal, true)
25
- ).to be_truthy
13
+ it "adds behavior to Webmachine::Decision::FSM" do
14
+ expect(fsm.class.ancestors.first).to eq(Appsignal::Integrations::WebmachineIntegration)
26
15
  end
27
16
  end
28
17
  else
@@ -78,63 +78,6 @@ describe Appsignal::Hooks do
78
78
  expect(Appsignal::Hooks.hooks[:mock_error_hook].installed?).to be_falsy
79
79
  Appsignal::Hooks.hooks.delete(:mock_error_hook)
80
80
  end
81
-
82
- describe "missing constants" do
83
- let(:err_stream) { std_stream }
84
- let(:stderr) { err_stream.read }
85
- let(:log_stream) { std_stream }
86
- let(:log) { log_contents(log_stream) }
87
- before do
88
- Appsignal.logger = test_logger(log_stream)
89
- end
90
-
91
- def call_constant(&block)
92
- capture_std_streams(std_stream, err_stream, &block)
93
- end
94
-
95
- describe "SidekiqProbe" do
96
- it "logs a deprecation message and returns the new constant" do
97
- constant = call_constant { Appsignal::Hooks::SidekiqProbe }
98
-
99
- expect(constant).to eql(Appsignal::Probes::SidekiqProbe)
100
- expect(constant.name).to eql("Appsignal::Probes::SidekiqProbe")
101
-
102
- deprecation_message =
103
- "The constant Appsignal::Hooks::SidekiqProbe has been deprecated. " \
104
- "Please update the constant name to Appsignal::Probes::SidekiqProbe " \
105
- "in the following file to remove this message.\n#{__FILE__}:"
106
- expect(stderr).to include "appsignal WARNING: #{deprecation_message}"
107
- expect(log).to contains_log :warn, deprecation_message
108
- end
109
- end
110
-
111
- describe "PumaProbe" do
112
- it "logs a deprecation message and returns the new constant" do
113
- constant = call_constant { Appsignal::Hooks::PumaProbe }
114
-
115
- expect(constant).to eql(Appsignal::Probes::PumaProbe)
116
- expect(constant.name).to eql("Appsignal::Probes::PumaProbe")
117
-
118
- deprecation_message =
119
- "The constant Appsignal::Hooks::PumaProbe has been deprecated. " \
120
- "Please update the constant name to Appsignal::Probes::PumaProbe " \
121
- "in the following file to remove this message.\n#{__FILE__}:"
122
- expect(stderr).to include "appsignal WARNING: #{deprecation_message}"
123
- expect(log).to contains_log :warn, deprecation_message
124
- end
125
- end
126
-
127
- describe "other constant" do
128
- it "raises a NameError like Ruby normally does" do
129
- expect do
130
- call_constant { Appsignal::Hooks::Unknown }
131
- end.to raise_error(NameError)
132
-
133
- expect(stderr).to be_empty
134
- expect(log).to be_empty
135
- end
136
- end
137
- end
138
81
  end
139
82
 
140
83
  describe Appsignal::Hooks::Helpers do
@@ -5,14 +5,22 @@ describe Object do
5
5
  context "with instance method" do
6
6
  let(:klass) do
7
7
  Class.new do
8
- def foo
9
- 1
8
+ def foo(param1, options = {}, keyword_param: 1)
9
+ [param1, options, keyword_param]
10
10
  end
11
11
  appsignal_instrument_method :foo
12
12
  end
13
13
  end
14
14
  let(:instance) { klass.new }
15
15
 
16
+ def call_with_arguments
17
+ instance.foo(
18
+ "abc",
19
+ { :foo => "bar" },
20
+ :keyword_param => 2
21
+ )
22
+ end
23
+
16
24
  context "when active" do
17
25
  let(:transaction) { http_request_transaction }
18
26
  before do
@@ -27,7 +35,7 @@ describe Object do
27
35
  expect(transaction).to receive(:start_event)
28
36
  expect(transaction).to receive(:finish_event).with \
29
37
  "foo.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
30
- expect(instance.foo).to eq(1)
38
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }, 2])
31
39
  end
32
40
  end
33
41
 
@@ -116,10 +124,10 @@ describe Object do
116
124
  context "when not active" do
117
125
  let(:transaction) { Appsignal::Transaction.current }
118
126
 
119
- it "should not instrument, but still call the method" do
127
+ it "does not instrument, but still calls the method" do
120
128
  expect(Appsignal.active?).to be_falsy
121
129
  expect(transaction).to_not receive(:start_event)
122
- expect(instance.foo).to eq(1)
130
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }, 2])
123
131
  end
124
132
  end
125
133
  end
@@ -127,12 +135,19 @@ describe Object do
127
135
  context "with class method" do
128
136
  let(:klass) do
129
137
  Class.new do
130
- def self.bar
131
- 2
138
+ def self.bar(param1, options = {}, keyword_param: 1)
139
+ [param1, options, keyword_param]
132
140
  end
133
141
  appsignal_instrument_class_method :bar
134
142
  end
135
143
  end
144
+ def call_with_arguments
145
+ klass.bar(
146
+ "abc",
147
+ { :foo => "bar" },
148
+ :keyword_param => 2
149
+ )
150
+ end
136
151
 
137
152
  context "when active" do
138
153
  let(:transaction) { http_request_transaction }
@@ -149,7 +164,7 @@ describe Object do
149
164
  expect(transaction).to receive(:start_event)
150
165
  expect(transaction).to receive(:finish_event).with \
151
166
  "bar.class_method.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
152
- expect(klass.bar).to eq(2)
167
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }, 2])
153
168
  end
154
169
  end
155
170
 
@@ -238,10 +253,10 @@ describe Object do
238
253
  context "when not active" do
239
254
  let(:transaction) { Appsignal::Transaction.current }
240
255
 
241
- it "should not instrument, but still call the method" do
256
+ it "does not instrument, but still call the method" do
242
257
  expect(Appsignal.active?).to be_falsy
243
258
  expect(transaction).to_not receive(:start_event)
244
- expect(klass.bar).to eq(2)
259
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }, 2])
245
260
  end
246
261
  end
247
262
  end
@@ -150,10 +150,9 @@ if DependencyHelper.padrino_present?
150
150
  let(:path) { "/static" }
151
151
  before do
152
152
  env["sinatra.static_file"] = true
153
- expect_any_instance_of(app)
154
- .to receive(:route_without_appsignal).and_return([200, {}, ["foo"]])
153
+ app.controllers { get(:static) { "Static!" } }
155
154
  end
156
- after { expect(response).to match_response(200, "foo") }
155
+ after { expect(response).to match_response(200, "Static!") }
157
156
 
158
157
  it "does not instrument the request" do
159
158
  expect_no_transaction_to_be_created
@@ -79,51 +79,6 @@ if DependencyHelper.rails_present?
79
79
 
80
80
  after { Appsignal::Integrations::Railtie.initialize_appsignal(app) }
81
81
  end
82
-
83
- describe "frontend_error_catching middleware" do
84
- let(:config) do
85
- Appsignal::Config.new(
86
- project_fixture_path,
87
- "test",
88
- :name => "MyApp",
89
- :enable_frontend_error_catching => enable_frontend_error_catching
90
- )
91
- end
92
- before { allow(Appsignal::Config).to receive(:new).and_return(config) }
93
- after { Appsignal::Integrations::Railtie.initialize_appsignal(app) }
94
-
95
- context "when enabled" do
96
- let(:enable_frontend_error_catching) { true }
97
-
98
- it "adds the Rails and JSExceptionCatcher middleware" do
99
- expect(app.middleware).to receive(:insert_after).with(
100
- ActionDispatch::DebugExceptions,
101
- Appsignal::Rack::RailsInstrumentation
102
- )
103
-
104
- expect(app.middleware).to receive(:insert_before).with(
105
- Appsignal::Rack::RailsInstrumentation,
106
- Appsignal::Rack::JSExceptionCatcher
107
- )
108
- end
109
- end
110
-
111
- context "when not enabled" do
112
- let(:enable_frontend_error_catching) { false }
113
-
114
- it "adds the Rails middleware, but not the JSExceptionCatcher middleware" do
115
- expect(app.middleware).to receive(:insert_after).with(
116
- ActionDispatch::DebugExceptions,
117
- Appsignal::Rack::RailsInstrumentation
118
- )
119
-
120
- expect(app.middleware).to_not receive(:insert_before).with(
121
- Appsignal::Rack::RailsInstrumentation,
122
- Appsignal::Rack::JSExceptionCatcher
123
- )
124
- end
125
- end
126
- end
127
82
  end
128
83
  end
129
84
  end
@@ -1,12 +1,24 @@
1
1
  if DependencyHelper.webmachine_present?
2
2
  require "appsignal/integrations/webmachine"
3
3
 
4
- describe Appsignal::Integrations::WebmachinePlugin::FSM do
4
+ class Response
5
+ attr_accessor :code
6
+
7
+ def body
8
+ ""
9
+ end
10
+
11
+ def headers
12
+ {}
13
+ end
14
+ end
15
+
16
+ describe Appsignal::Integrations::WebmachineIntegration do
5
17
  let(:request) do
6
18
  Webmachine::Request.new("GET", "http://google.com:80/foo", {}, nil)
7
19
  end
8
- let(:resource) { double(:trace? => false, :handle_exception => true) }
9
- let(:response) { double }
20
+ let(:resource) { double(:trace? => false, :handle_exception => true, :"code=" => nil) }
21
+ let(:response) { Response.new }
10
22
  let(:transaction) { double(:set_action_if_nil => true) }
11
23
  let(:fsm) { Webmachine::Decision::FSM.new(resource, request, response) }
12
24
  before(:context) { start_agent }
@@ -18,10 +30,8 @@ if DependencyHelper.webmachine_present?
18
30
  end
19
31
  end
20
32
 
21
- describe "#run_with_appsignal" do
33
+ describe "#run" do
22
34
  before do
23
- allow(fsm).to receive(:request).and_return(request)
24
- allow(fsm).to receive(:run_without_appsignal).and_return(true)
25
35
  allow(SecureRandom).to receive(:uuid).and_return("uuid")
26
36
  allow(Appsignal::Transaction).to receive(:create).and_return(transaction)
27
37
  end
@@ -40,7 +50,7 @@ if DependencyHelper.webmachine_present?
40
50
  end
41
51
 
42
52
  it "should call the original method" do
43
- expect(fsm).to receive(:run_without_appsignal)
53
+ expect(fsm).to receive(:run)
44
54
  end
45
55
 
46
56
  it "should instrument the original method" do
@@ -52,9 +62,17 @@ if DependencyHelper.webmachine_present?
52
62
  end
53
63
 
54
64
  after { fsm.run }
65
+
66
+ describe "concerning the response" do
67
+ it "sets a response code" do
68
+ expect(fsm.response.code).to be_nil
69
+ fsm.run
70
+ expect(fsm.response.code).not_to be_nil
71
+ end
72
+ end
55
73
  end
56
74
 
57
- describe "#handle_exceptions_with_appsignal" do
75
+ describe "#handle_exceptions" do
58
76
  let(:error) { ExampleException }
59
77
 
60
78
  it "should catch the error and send it to AppSignal" do
@@ -284,25 +284,6 @@ describe Appsignal::Minutely do
284
284
  end
285
285
  end
286
286
 
287
- describe "#<<" do
288
- let(:err_stream) { std_stream }
289
- let(:stderr) { err_stream.read }
290
- let(:log_stream) { std_stream }
291
- let(:log) { log_contents(log_stream) }
292
- before { Appsignal.logger = test_logger(log_stream) }
293
-
294
- it "adds the probe, but prints a deprecation warning" do
295
- probe = lambda {}
296
- capture_std_streams(std_stream, err_stream) { collection << probe }
297
- deprecation_message = "Deprecated " \
298
- "`Appsignal::Minute.probes <<` call. Please use " \
299
- "`Appsignal::Minutely.probes.register` instead."
300
- expect(stderr).to include "appsignal WARNING: #{deprecation_message}"
301
- expect(log).to contains_log :warn, deprecation_message
302
- expect(collection[probe.object_id]).to eql(probe)
303
- end
304
- end
305
-
306
287
  describe "#register" do
307
288
  let(:log_stream) { std_stream }
308
289
  let(:log) { log_contents(log_stream) }
@@ -356,6 +356,57 @@ describe Appsignal::Transaction do
356
356
  end
357
357
  end
358
358
 
359
+ describe "#add_breadcrumb" do
360
+ context "when over the limit" do
361
+ before do
362
+ 22.times do |i|
363
+ transaction.add_breadcrumb(
364
+ "network",
365
+ "GET http://localhost",
366
+ "User made external network request",
367
+ { :code => i + 1 },
368
+ Time.parse("10-10-2010 10:00:00 UTC")
369
+ )
370
+ end
371
+ transaction.sample_data
372
+ end
373
+
374
+ it "stores last <LIMIT> breadcrumbs on the transaction" do
375
+ expect(transaction.to_h["sample_data"]["breadcrumbs"].length).to eql(20)
376
+ expect(transaction.to_h["sample_data"]["breadcrumbs"][0]).to eq(
377
+ "action" => "GET http://localhost",
378
+ "category" => "network",
379
+ "message" => "User made external network request",
380
+ "metadata" => { "code" => 3 },
381
+ "time" => 1286704800 # rubocop:disable Style/NumericLiterals
382
+ )
383
+ expect(transaction.to_h["sample_data"]["breadcrumbs"][19]).to eq(
384
+ "action" => "GET http://localhost",
385
+ "category" => "network",
386
+ "message" => "User made external network request",
387
+ "metadata" => { "code" => 22 },
388
+ "time" => 1286704800 # rubocop:disable Style/NumericLiterals
389
+ )
390
+ end
391
+ end
392
+
393
+ context "with defaults" do
394
+ it "stores breadcrumb with defaults on transaction" do
395
+ timeframe_start = Time.now.utc.to_i
396
+ transaction.add_breadcrumb("user_action", "clicked HOME")
397
+ transaction.sample_data
398
+ timeframe_end = Time.now.utc.to_i
399
+
400
+ breadcrumb = transaction.to_h["sample_data"]["breadcrumbs"][0]
401
+ expect(breadcrumb["category"]).to eq("user_action")
402
+ expect(breadcrumb["action"]).to eq("clicked HOME")
403
+ expect(breadcrumb["message"]).to eq("")
404
+ expect(breadcrumb["time"]).to be_between(timeframe_start, timeframe_end)
405
+ expect(breadcrumb["metadata"]).to eq({})
406
+ end
407
+ end
408
+ end
409
+
359
410
  describe "#set_action" do
360
411
  context "when the action is set" do
361
412
  it "updates the action name on the transaction" do
@@ -589,7 +640,7 @@ describe Appsignal::Transaction do
589
640
  end
590
641
 
591
642
  context "when the data cannot be converted to JSON" do
592
- it "does not update the sample data on the transaction", :not_ruby19 do
643
+ it "does not update the sample data on the transaction" do
593
644
  klass = Class.new do
594
645
  def to_s
595
646
  raise "foo" # Cause a deliberate error
@@ -601,19 +652,6 @@ describe Appsignal::Transaction do
601
652
  expect(log_contents(log)).to contains_log :error,
602
653
  "Error generating data (RuntimeError: foo) for"
603
654
  end
604
-
605
- it "does not update the sample data on the transaction", :only_ruby19 do
606
- klass = Class.new do
607
- def to_s
608
- raise "foo" # Cause a deliberate error
609
- end
610
- end
611
- transaction.set_sample_data("params", klass.new => 1)
612
-
613
- expect(transaction.to_h["sample_data"]).to eq({})
614
- expect(log_contents(log)).to contains_log :error,
615
- "Error generating data (RuntimeError: foo). Can't inspect data."
616
- end
617
655
  end
618
656
  end
619
657
 
@@ -649,6 +687,10 @@ describe Appsignal::Transaction do
649
687
  "tags",
650
688
  Appsignal::Utils::Data.generate({})
651
689
  ).once
690
+ expect(transaction.ext).to receive(:set_sample_data).with(
691
+ "breadcrumbs",
692
+ Appsignal::Utils::Data.generate([])
693
+ ).once
652
694
 
653
695
  transaction.sample_data
654
696
  end