appsignal 2.11.1 → 2.11.6

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.
@@ -0,0 +1,266 @@
1
+ require "appsignal/integrations/object"
2
+
3
+ def is_ruby_19
4
+ RUBY_VERSION < "2.0"
5
+ end
6
+
7
+ describe Object do
8
+ describe "#instrument_method" do
9
+ context "with instance method" do
10
+ let(:klass) do
11
+ Class.new do
12
+ def foo(param1, options = {})
13
+ [param1, options]
14
+ end
15
+ appsignal_instrument_method :foo
16
+ end
17
+ end
18
+ let(:instance) { klass.new }
19
+
20
+ def call_with_arguments
21
+ instance.foo(
22
+ "abc",
23
+ :foo => "bar"
24
+ )
25
+ end
26
+
27
+ context "when active" do
28
+ let(:transaction) { http_request_transaction }
29
+ before do
30
+ Appsignal.config = project_fixture_config
31
+ expect(Appsignal::Transaction).to receive(:current).at_least(:once).and_return(transaction)
32
+ end
33
+ after { Appsignal.config = nil }
34
+
35
+ context "with anonymous class" do
36
+ it "instruments the method and calls it" do
37
+ expect(Appsignal.active?).to be_truthy
38
+ expect(transaction).to receive(:start_event)
39
+ expect(transaction).to receive(:finish_event).with \
40
+ "foo.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
41
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }])
42
+ end
43
+ end
44
+
45
+ context "with named class" do
46
+ before do
47
+ class NamedClass
48
+ def foo
49
+ 1
50
+ end
51
+ appsignal_instrument_method :foo
52
+ end
53
+ end
54
+ after { Object.send(:remove_const, :NamedClass) }
55
+ let(:klass) { NamedClass }
56
+
57
+ it "instruments the method and calls it" do
58
+ expect(Appsignal.active?).to be_truthy
59
+ expect(transaction).to receive(:start_event)
60
+ expect(transaction).to receive(:finish_event).with \
61
+ "foo.NamedClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
62
+ expect(instance.foo).to eq(1)
63
+ end
64
+ end
65
+
66
+ context "with nested named class" do
67
+ before do
68
+ module MyModule
69
+ module NestedModule
70
+ class NamedClass
71
+ def bar
72
+ 2
73
+ end
74
+ appsignal_instrument_method :bar
75
+ end
76
+ end
77
+ end
78
+ end
79
+ after { Object.send(:remove_const, :MyModule) }
80
+ let(:klass) { MyModule::NestedModule::NamedClass }
81
+
82
+ it "instruments the method and calls it" do
83
+ expect(Appsignal.active?).to be_truthy
84
+ expect(transaction).to receive(:start_event)
85
+ expect(transaction).to receive(:finish_event).with \
86
+ "bar.NamedClass.NestedModule.MyModule.other", nil, nil,
87
+ Appsignal::EventFormatter::DEFAULT
88
+ expect(instance.bar).to eq(2)
89
+ end
90
+ end
91
+
92
+ context "with custom name" do
93
+ let(:klass) do
94
+ Class.new do
95
+ def foo
96
+ 1
97
+ end
98
+ appsignal_instrument_method :foo, :name => "my_method.group"
99
+ end
100
+ end
101
+
102
+ it "instruments with custom name" do
103
+ expect(Appsignal.active?).to be_truthy
104
+ expect(transaction).to receive(:start_event)
105
+ expect(transaction).to receive(:finish_event).with \
106
+ "my_method.group", nil, nil, Appsignal::EventFormatter::DEFAULT
107
+ expect(instance.foo).to eq(1)
108
+ end
109
+ end
110
+
111
+ context "with a method given a block" do
112
+ let(:klass) do
113
+ Class.new do
114
+ def foo
115
+ yield
116
+ end
117
+ appsignal_instrument_method :foo
118
+ end
119
+ end
120
+
121
+ it "should yield the block" do
122
+ expect(instance.foo { 42 }).to eq(42)
123
+ end
124
+ end
125
+ end
126
+
127
+ context "when not active" do
128
+ let(:transaction) { Appsignal::Transaction.current }
129
+
130
+ it "does not instrument, but still calls the method" do
131
+ expect(Appsignal.active?).to be_falsy
132
+ expect(transaction).to_not receive(:start_event)
133
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }])
134
+ end
135
+ end
136
+ end
137
+
138
+ context "with class method" do
139
+ let(:klass) do
140
+ Class.new do
141
+ def self.bar(param1, options = {})
142
+ [param1, options]
143
+ end
144
+ appsignal_instrument_class_method :bar
145
+ end
146
+ end
147
+ def call_with_arguments
148
+ klass.bar(
149
+ "abc",
150
+ :foo => "bar"
151
+ )
152
+ end
153
+
154
+ context "when active" do
155
+ let(:transaction) { http_request_transaction }
156
+ before do
157
+ Appsignal.config = project_fixture_config
158
+ expect(Appsignal::Transaction).to receive(:current).at_least(:once)
159
+ .and_return(transaction)
160
+ end
161
+ after { Appsignal.config = nil }
162
+
163
+ context "with anonymous class" do
164
+ it "instruments the method and calls it" do
165
+ expect(Appsignal.active?).to be_truthy
166
+ expect(transaction).to receive(:start_event)
167
+ expect(transaction).to receive(:finish_event).with \
168
+ "bar.class_method.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
169
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }])
170
+ end
171
+ end
172
+
173
+ context "with named class" do
174
+ before do
175
+ class NamedClass
176
+ def self.bar
177
+ 2
178
+ end
179
+ appsignal_instrument_class_method :bar
180
+ end
181
+ end
182
+ after { Object.send(:remove_const, :NamedClass) }
183
+ let(:klass) { NamedClass }
184
+
185
+ it "instruments the method and calls it" do
186
+ expect(Appsignal.active?).to be_truthy
187
+ expect(transaction).to receive(:start_event)
188
+ expect(transaction).to receive(:finish_event).with \
189
+ "bar.class_method.NamedClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
190
+ expect(klass.bar).to eq(2)
191
+ end
192
+
193
+ context "with nested named class" do
194
+ before do
195
+ module MyModule
196
+ module NestedModule
197
+ class NamedClass
198
+ def self.bar
199
+ 2
200
+ end
201
+ appsignal_instrument_class_method :bar
202
+ end
203
+ end
204
+ end
205
+ end
206
+ after { Object.send(:remove_const, :MyModule) }
207
+ let(:klass) { MyModule::NestedModule::NamedClass }
208
+
209
+ it "instruments the method and calls it" do
210
+ expect(Appsignal.active?).to be_truthy
211
+ expect(transaction).to receive(:start_event)
212
+ expect(transaction).to receive(:finish_event).with \
213
+ "bar.class_method.NamedClass.NestedModule.MyModule.other", nil, nil,
214
+ Appsignal::EventFormatter::DEFAULT
215
+ expect(klass.bar).to eq(2)
216
+ end
217
+ end
218
+ end
219
+
220
+ context "with custom name" do
221
+ let(:klass) do
222
+ Class.new do
223
+ def self.bar
224
+ 2
225
+ end
226
+ appsignal_instrument_class_method :bar, :name => "my_method.group"
227
+ end
228
+ end
229
+
230
+ it "instruments with custom name" do
231
+ expect(Appsignal.active?).to be_truthy
232
+ expect(transaction).to receive(:start_event)
233
+ expect(transaction).to receive(:finish_event).with \
234
+ "my_method.group", nil, nil, Appsignal::EventFormatter::DEFAULT
235
+ expect(klass.bar).to eq(2)
236
+ end
237
+ end
238
+
239
+ context "with a method given a block" do
240
+ let(:klass) do
241
+ Class.new do
242
+ def self.bar
243
+ yield
244
+ end
245
+ appsignal_instrument_class_method :bar
246
+ end
247
+ end
248
+
249
+ it "should yield the block" do
250
+ expect(klass.bar { 42 }).to eq(42)
251
+ end
252
+ end
253
+ end
254
+
255
+ context "when not active" do
256
+ let(:transaction) { Appsignal::Transaction.current }
257
+
258
+ it "does not instrument, but still call the method" do
259
+ expect(Appsignal.active?).to be_falsy
260
+ expect(transaction).to_not receive(:start_event)
261
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }])
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
@@ -1,18 +1,30 @@
1
1
  require "appsignal/integrations/object"
2
2
 
3
+ def is_ruby_19
4
+ RUBY_VERSION < "2.0"
5
+ end
6
+
3
7
  describe Object do
4
8
  describe "#instrument_method" do
5
9
  context "with instance method" do
6
10
  let(:klass) do
7
11
  Class.new do
8
- def foo
9
- 1
12
+ def foo(param1, options = {}, keyword_param: 1)
13
+ [param1, options, keyword_param]
10
14
  end
11
15
  appsignal_instrument_method :foo
12
16
  end
13
17
  end
14
18
  let(:instance) { klass.new }
15
19
 
20
+ def call_with_arguments
21
+ instance.foo(
22
+ "abc",
23
+ { :foo => "bar" },
24
+ :keyword_param => 2
25
+ )
26
+ end
27
+
16
28
  context "when active" do
17
29
  let(:transaction) { http_request_transaction }
18
30
  before do
@@ -27,7 +39,7 @@ describe Object do
27
39
  expect(transaction).to receive(:start_event)
28
40
  expect(transaction).to receive(:finish_event).with \
29
41
  "foo.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
30
- expect(instance.foo).to eq(1)
42
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }, 2])
31
43
  end
32
44
  end
33
45
 
@@ -116,10 +128,10 @@ describe Object do
116
128
  context "when not active" do
117
129
  let(:transaction) { Appsignal::Transaction.current }
118
130
 
119
- it "should not instrument, but still call the method" do
131
+ it "does not instrument, but still calls the method" do
120
132
  expect(Appsignal.active?).to be_falsy
121
133
  expect(transaction).to_not receive(:start_event)
122
- expect(instance.foo).to eq(1)
134
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }, 2])
123
135
  end
124
136
  end
125
137
  end
@@ -127,12 +139,19 @@ describe Object do
127
139
  context "with class method" do
128
140
  let(:klass) do
129
141
  Class.new do
130
- def self.bar
131
- 2
142
+ def self.bar(param1, options = {}, keyword_param: 1)
143
+ [param1, options, keyword_param]
132
144
  end
133
145
  appsignal_instrument_class_method :bar
134
146
  end
135
147
  end
148
+ def call_with_arguments
149
+ klass.bar(
150
+ "abc",
151
+ { :foo => "bar" },
152
+ :keyword_param => 2
153
+ )
154
+ end
136
155
 
137
156
  context "when active" do
138
157
  let(:transaction) { http_request_transaction }
@@ -149,7 +168,7 @@ describe Object do
149
168
  expect(transaction).to receive(:start_event)
150
169
  expect(transaction).to receive(:finish_event).with \
151
170
  "bar.class_method.AnonymousClass.other", nil, nil, Appsignal::EventFormatter::DEFAULT
152
- expect(klass.bar).to eq(2)
171
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }, 2])
153
172
  end
154
173
  end
155
174
 
@@ -238,10 +257,10 @@ describe Object do
238
257
  context "when not active" do
239
258
  let(:transaction) { Appsignal::Transaction.current }
240
259
 
241
- it "should not instrument, but still call the method" do
260
+ it "does not instrument, but still call the method" do
242
261
  expect(Appsignal.active?).to be_falsy
243
262
  expect(transaction).to_not receive(:start_event)
244
- expect(klass.bar).to eq(2)
263
+ expect(call_with_arguments).to eq(["abc", { :foo => "bar" }, 2])
245
264
  end
246
265
  end
247
266
  end
@@ -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
@@ -649,6 +700,10 @@ describe Appsignal::Transaction do
649
700
  "tags",
650
701
  Appsignal::Utils::Data.generate({})
651
702
  ).once
703
+ expect(transaction.ext).to receive(:set_sample_data).with(
704
+ "breadcrumbs",
705
+ Appsignal::Utils::Data.generate([])
706
+ ).once
652
707
 
653
708
  transaction.sample_data
654
709
  end
@@ -491,6 +491,36 @@ describe Appsignal do
491
491
  end
492
492
  end
493
493
 
494
+ describe ".add_breadcrumb" do
495
+ before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
496
+
497
+ context "with transaction" do
498
+ let(:transaction) { double }
499
+ it "should call add_breadcrumb on transaction" do
500
+ expect(transaction).to receive(:add_breadcrumb)
501
+ .with("Network", "http", "User made network request", { :response => 200 }, fixed_time)
502
+ end
503
+
504
+ after do
505
+ Appsignal.add_breadcrumb(
506
+ "Network",
507
+ "http",
508
+ "User made network request",
509
+ { :response => 200 },
510
+ fixed_time
511
+ )
512
+ end
513
+ end
514
+
515
+ context "without transaction" do
516
+ let(:transaction) { nil }
517
+
518
+ it "should not call add_breadcrumb on transaction" do
519
+ expect(Appsignal.add_breadcrumb("Network", "http")).to be_falsy
520
+ end
521
+ end
522
+ end
523
+
494
524
  describe "custom stats" do
495
525
  let(:tags) { { :foo => "bar" } }
496
526