appsignal 3.0.15-java → 3.0.19-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -3
  3. data/.semaphore/semaphore.yml +513 -104
  4. data/CHANGELOG.md +57 -0
  5. data/appsignal.gemspec +0 -2
  6. data/build_matrix.yml +45 -17
  7. data/ext/agent.yml +25 -25
  8. data/ext/appsignal_extension.c +201 -0
  9. data/ext/base.rb +2 -1
  10. data/gemfiles/rails-6.1.gemfile +7 -0
  11. data/gemfiles/rails-7.0.gemfile +7 -0
  12. data/lib/appsignal/cli/diagnose/utils.rb +0 -14
  13. data/lib/appsignal/cli/diagnose.rb +19 -8
  14. data/lib/appsignal/config.rb +89 -57
  15. data/lib/appsignal/event_formatter/sequel/sql_formatter.rb +24 -0
  16. data/lib/appsignal/extension/jruby.rb +147 -0
  17. data/lib/appsignal/extension.rb +5 -0
  18. data/lib/appsignal/integrations/sidekiq.rb +5 -1
  19. data/lib/appsignal/span.rb +92 -0
  20. data/lib/appsignal/system.rb +0 -4
  21. data/lib/appsignal/transaction.rb +12 -1
  22. data/lib/appsignal/version.rb +1 -1
  23. data/lib/appsignal.rb +4 -10
  24. data/script/lint_git +1 -1
  25. data/spec/lib/appsignal/cli/diagnose_spec.rb +18 -13
  26. data/spec/lib/appsignal/config_spec.rb +104 -20
  27. data/spec/lib/appsignal/event_formatter/active_record/sql_formatter_spec.rb +2 -2
  28. data/spec/lib/appsignal/event_formatter/sequel/sql_formatter_spec.rb +30 -0
  29. data/spec/lib/appsignal/event_formatter_spec.rb +2 -2
  30. data/spec/lib/appsignal/hooks/activejob_spec.rb +17 -6
  31. data/spec/lib/appsignal/hooks/sequel_spec.rb +1 -1
  32. data/spec/lib/appsignal/integrations/padrino_spec.rb +8 -2
  33. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +23 -5
  34. data/spec/lib/appsignal/span_spec.rb +141 -0
  35. data/spec/lib/appsignal/transaction_spec.rb +25 -0
  36. data/spec/lib/appsignal/utils/data_spec.rb +0 -2
  37. data/spec/lib/appsignal/utils/json_spec.rb +0 -2
  38. data/spec/lib/appsignal_spec.rb +2 -3
  39. data/spec/support/helpers/activejob_helpers.rb +27 -0
  40. data/spec/support/helpers/dependency_helper.rb +13 -1
  41. metadata +13 -3
@@ -251,7 +251,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
251
251
 
252
252
  it "adds the installation report to the diagnostics report" do
253
253
  run
254
- jruby = DependencyHelper.running_jruby?
254
+ jruby = Appsignal::System.jruby?
255
255
  expect(received_report["installation"]).to match(
256
256
  "result" => {
257
257
  "status" => "success"
@@ -321,7 +321,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
321
321
  expect(File).to receive(:read)
322
322
  .with(File.expand_path("../../../../../ext/install.report", __FILE__))
323
323
  .and_return(
324
- YAML.dump(
324
+ JSON.generate(
325
325
  "result" => {
326
326
  "status" => "error",
327
327
  "error" => "RuntimeError: some error",
@@ -384,8 +384,8 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
384
384
  end
385
385
  end
386
386
 
387
- context "when report is invalid YAML" do
388
- let(:raw_report) { "foo:\nbar" }
387
+ context "when report is invalid JSON" do
388
+ let(:raw_report) { "{}-" }
389
389
  before do
390
390
  allow(File).to receive(:read).and_call_original
391
391
  expect(File).to receive(:read)
@@ -750,13 +750,13 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
750
750
  end
751
751
 
752
752
  it "outputs a warning that no config is loaded" do
753
- expect(output).to include "Environment: \"\"\n#{warning_message}"
753
+ expect(output).to include "environment: \"\"\n#{warning_message}"
754
754
  expect(output).to_not have_color_markers
755
755
  end
756
756
 
757
757
  context "with color", :color => true do
758
758
  it "outputs a warning that no config is loaded in color" do
759
- expect(output).to include "Environment: \"\"\n"
759
+ expect(output).to include "environment: \"\"\n"
760
760
  expect(output).to have_colorized_text :red, warning_message
761
761
  end
762
762
  end
@@ -785,7 +785,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
785
785
  describe "environment" do
786
786
  it "outputs environment" do
787
787
  run
788
- expect(output).to include(%(Environment: "production"))
788
+ expect(output).to include(%(environment: "production"))
789
789
  end
790
790
 
791
791
  context "when the source is a single source" do
@@ -793,7 +793,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
793
793
 
794
794
  it "outputs the label source after the value" do
795
795
  expect(output).to include(
796
- %(Environment: "#{Appsignal.config.env}" (Loaded from: initial)\n)
796
+ %(environment: "#{Appsignal.config.env}" (Loaded from: initial)\n)
797
797
  )
798
798
  end
799
799
  end
@@ -810,7 +810,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
810
810
 
811
811
  it "outputs a list of sources with their values" do
812
812
  expect(output).to include(
813
- %( Environment: "production"\n) +
813
+ %( environment: "production"\n) +
814
814
  %( Sources:\n) +
815
815
  %( initial: "development"\n) +
816
816
  %( env: "production"\n)
@@ -904,7 +904,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
904
904
  before { run_within_dir tmp_dir }
905
905
 
906
906
  it "outputs environment" do
907
- expect(output).to include(%(Environment: "foobar"))
907
+ expect(output).to include(%(environment: "foobar"))
908
908
  end
909
909
 
910
910
  it "outputs config defaults" do
@@ -1007,7 +1007,7 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
1007
1007
 
1008
1008
  it "outputs failure with status code" do
1009
1009
  expect(output).to include "Validation",
1010
- "Validating Push API key: Failed with status 500\n" +
1010
+ "Validating Push API key: Failed to validate: status 500\n" +
1011
1011
  %("Could not confirm authorization: 500")
1012
1012
  end
1013
1013
 
@@ -1015,14 +1015,19 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i
1015
1015
  it "outputs error in color" do
1016
1016
  expect(output).to include "Validation",
1017
1017
  "Validating Push API key: " +
1018
- colorize(%(Failed with status 500\n"Could not confirm authorization: 500"), :red)
1018
+ colorize(
1019
+ "Failed to validate: status 500\n" +
1020
+ %("Could not confirm authorization: 500"),
1021
+ :red
1022
+ )
1019
1023
  end
1020
1024
  end
1021
1025
 
1022
1026
  it "transmits validation in report" do
1023
1027
  expect(received_report).to include(
1024
1028
  "validation" => {
1025
- "push_api_key" => %(Failed with status 500\n\"Could not confirm authorization: 500")
1029
+ "push_api_key" => "Failed to validate: status 500\n" +
1030
+ %("Could not confirm authorization: 500")
1026
1031
  }
1027
1032
  )
1028
1033
  end
@@ -151,34 +151,34 @@ describe Appsignal::Config do
151
151
 
152
152
  it "merges with the default config" do
153
153
  expect(config.config_hash).to eq(
154
+ :active => true,
155
+ :ca_file_path => File.join(resources_dir, "cacert.pem"),
154
156
  :debug => false,
155
- :log => "file",
157
+ :dns_servers => [],
158
+ :enable_allocation_tracking => true,
159
+ :enable_gc_instrumentation => false,
160
+ :enable_host_metrics => true,
161
+ :enable_minutely_probes => true,
162
+ :enable_statsd => true,
163
+ :endpoint => "https://push.appsignal.com",
164
+ :files_world_accessible => true,
165
+ :filter_parameters => [],
166
+ :filter_session_data => [],
156
167
  :ignore_actions => [],
157
168
  :ignore_errors => [],
158
169
  :ignore_namespaces => [],
159
- :filter_parameters => [],
160
- :filter_session_data => [],
161
170
  :instrument_net_http => true,
162
171
  :instrument_redis => true,
163
172
  :instrument_sequel => true,
164
- :skip_session_data => false,
165
- :send_environment_metadata => true,
166
- :send_params => true,
167
- :endpoint => "https://push.appsignal.com",
168
- :push_api_key => "abc",
173
+ :log => "file",
169
174
  :name => "TestApp",
170
- :active => true,
171
- :enable_allocation_tracking => true,
172
- :enable_gc_instrumentation => false,
173
- :enable_host_metrics => true,
174
- :enable_minutely_probes => true,
175
- :enable_statsd => true,
176
- :ca_file_path => File.join(resources_dir, "cacert.pem"),
177
- :dns_servers => [],
178
- :files_world_accessible => true,
179
- :transaction_debug_mode => false,
175
+ :push_api_key => "abc",
176
+ :request_headers => [],
180
177
  :revision => "v2.5.1",
181
- :request_headers => []
178
+ :send_environment_metadata => true,
179
+ :send_params => true,
180
+ :skip_session_data => false,
181
+ :transaction_debug_mode => false
182
182
  )
183
183
  end
184
184
 
@@ -319,13 +319,15 @@ describe Appsignal::Config do
319
319
  "non-existing-path",
320
320
  "production",
321
321
  :running_in_container => true,
322
- :debug => true
322
+ :debug => true,
323
+ :log_level => "debug"
323
324
  )
324
325
  end
325
326
 
326
327
  it "overrides system detected and defaults config" do
327
328
  expect(config[:running_in_container]).to be_truthy
328
329
  expect(config[:debug]).to be_truthy
330
+ expect(config[:log_level]).to eq("debug")
329
331
  end
330
332
  end
331
333
 
@@ -502,6 +504,7 @@ describe Appsignal::Config do
502
504
  config[:log] = "stdout"
503
505
  config[:log_path] = "/tmp"
504
506
  config[:filter_parameters] = %w[password confirm_password]
507
+ config[:filter_session_data] = %w[key1 key2]
505
508
  config[:running_in_container] = false
506
509
  config[:dns_servers] = ["8.8.8.8", "8.8.4.4"]
507
510
  config[:transaction_debug_mode] = true
@@ -535,6 +538,8 @@ describe Appsignal::Config do
535
538
  expect(ENV["_APPSIGNAL_FILES_WORLD_ACCESSIBLE"]).to eq "true"
536
539
  expect(ENV["_APPSIGNAL_TRANSACTION_DEBUG_MODE"]).to eq "true"
537
540
  expect(ENV["_APPSIGNAL_SEND_ENVIRONMENT_METADATA"]).to eq "false"
541
+ expect(ENV["_APPSIGNAL_FILTER_PARAMETERS"]).to eq "password,confirm_password"
542
+ expect(ENV["_APPSIGNAL_FILTER_SESSION_DATA"]).to eq "key1,key2"
538
543
  expect(ENV["_APP_REVISION"]).to eq "v2.5.1"
539
544
  expect(ENV).to_not have_key("_APPSIGNAL_WORKING_DIR_PATH")
540
545
  expect(ENV).to_not have_key("_APPSIGNAL_WORKING_DIRECTORY_PATH")
@@ -781,4 +786,83 @@ describe Appsignal::Config do
781
786
  end
782
787
  end
783
788
  end
789
+
790
+ describe "#log_level" do
791
+ let(:options) { {} }
792
+ let(:config) { described_class.new("", nil, options) }
793
+ subject { config.log_level }
794
+
795
+ context "without any config" do
796
+ it "returns info by default" do
797
+ is_expected.to eq(Logger::INFO)
798
+ end
799
+ end
800
+
801
+ context "with debug set to true" do
802
+ let(:options) { { :debug => true } }
803
+ it { is_expected.to eq(Logger::DEBUG) }
804
+ end
805
+
806
+ context "with transaction_debug_mode set to true" do
807
+ let(:options) { { :transaction_debug_mode => true } }
808
+ it { is_expected.to eq(Logger::DEBUG) }
809
+ end
810
+
811
+ context "with log_level set to error" do
812
+ let(:options) { { :log_level => "error" } }
813
+ it { is_expected.to eq(Logger::ERROR) }
814
+ end
815
+
816
+ context "with log_level set to warn" do
817
+ let(:options) { { :log_level => "warn" } }
818
+ it { is_expected.to eq(Logger::WARN) }
819
+ end
820
+
821
+ context "with log_level set to info" do
822
+ let(:options) { { :log_level => "info" } }
823
+ it { is_expected.to eq(Logger::INFO) }
824
+ end
825
+
826
+ context "with log_level set to debug" do
827
+ let(:options) { { :log_level => "debug" } }
828
+ it { is_expected.to eq(Logger::DEBUG) }
829
+ end
830
+
831
+ context "with log_level set to trace" do
832
+ let(:options) { { :log_level => "trace" } }
833
+ it { is_expected.to eq(Logger::DEBUG) }
834
+ end
835
+
836
+ context "with debug and log_level set" do
837
+ let(:options) { { :log_level => "error", :debug => true } }
838
+
839
+ it "the log_level option is leading" do
840
+ is_expected.to eq(Logger::ERROR)
841
+ end
842
+ end
843
+
844
+ context "with transaction_debug_mode and log_level set" do
845
+ let(:options) { { :log_level => "error", :transaction_debug_mode => true } }
846
+
847
+ it "the log_level option is leading" do
848
+ is_expected.to eq(Logger::ERROR)
849
+ end
850
+ end
851
+
852
+ context "with log level set to an unknown value" do
853
+ let(:options) { { :log_level => "fatal" } }
854
+
855
+ it "prints a warning and doesn't use the log_level" do
856
+ is_expected.to eql(Logger::INFO)
857
+ end
858
+
859
+ context "with debug option set to true" do
860
+ let(:options) { { :log_level => "fatal", :debug => true } }
861
+
862
+ it "prints a warning and sets it to debug" do
863
+ is_expected.to eql(Logger::DEBUG)
864
+ end
865
+ end
866
+ end
867
+ end
784
868
  end
@@ -1,5 +1,5 @@
1
- describe Appsignal::EventFormatter::ActiveRecord::InstantiationFormatter do
2
- let(:klass) { Appsignal::EventFormatter::ActiveRecord::SqlFormatter }
1
+ describe Appsignal::EventFormatter::ActiveRecord::SqlFormatter do
2
+ let(:klass) { described_class }
3
3
  let(:formatter) { klass.new }
4
4
 
5
5
  it "should register sql.active_record" do
@@ -0,0 +1,30 @@
1
+ describe Appsignal::EventFormatter::Sequel::SqlFormatter do
2
+ let(:klass) { described_class }
3
+ let(:formatter) { klass.new }
4
+
5
+ it "registers the sql.sequel event formatter" do
6
+ expect(Appsignal::EventFormatter.registered?("sql.sequel", klass)).to be_truthy
7
+ end
8
+
9
+ describe "#format" do
10
+ before do
11
+ stub_const(
12
+ "SequelDatabaseTypeClass",
13
+ Class.new do
14
+ def self.to_s
15
+ "SequelDatabaseTypeClassToString"
16
+ end
17
+ end
18
+ )
19
+ end
20
+ let(:payload) do
21
+ {
22
+ :name => SequelDatabaseTypeClass,
23
+ :sql => "SELECT * FROM users"
24
+ }
25
+ end
26
+ subject { formatter.format(payload) }
27
+
28
+ it { is_expected.to eq ["SequelDatabaseTypeClassToString", "SELECT * FROM users", 1] }
29
+ end
30
+ end
@@ -28,7 +28,7 @@ end
28
28
 
29
29
  class MockDependentFormatter < Appsignal::EventFormatter
30
30
  def initialize
31
- NonsenseDependency.something
31
+ raise "There is an error"
32
32
  end
33
33
 
34
34
  def format(_payload)
@@ -72,7 +72,7 @@ describe Appsignal::EventFormatter do
72
72
  end
73
73
  expect(klass.registered?("mock.dependent")).to be_falsy
74
74
  expect(logs).to contains_log :error, \
75
- "'uninitialized constant MockDependentFormatter::NonsenseDependency' " \
75
+ "'There is an error' " \
76
76
  "when initializing mock.dependent event formatter"
77
77
  end
78
78
  end
@@ -30,6 +30,7 @@ if DependencyHelper.active_job_present?
30
30
  end
31
31
 
32
32
  describe Appsignal::Hooks::ActiveJobHook::ActiveJobClassInstrumentation do
33
+ include ActiveJobHelpers
33
34
  let(:time) { Time.parse("2001-01-01 10:00:00UTC") }
34
35
  let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
35
36
  let(:queue) { "default" }
@@ -64,6 +65,13 @@ if DependencyHelper.active_job_present?
64
65
  parameterized_expected_args
65
66
  ]
66
67
  end
68
+ let(:expected_perform_events) do
69
+ if DependencyHelper.rails7_present?
70
+ ["perform.active_job", "perform_start.active_job"]
71
+ else
72
+ ["perform_start.active_job", "perform.active_job"]
73
+ end
74
+ end
67
75
  before do
68
76
  ActiveJob::Base.queue_adapter = :inline
69
77
 
@@ -119,7 +127,8 @@ if DependencyHelper.active_job_present?
119
127
  events = transaction_hash["events"]
120
128
  .sort_by { |e| e["start"] }
121
129
  .map { |event| event["name"] }
122
- expect(events).to eq(["perform_start.active_job", "perform.active_job"])
130
+
131
+ expect(events).to eq(expected_perform_events)
123
132
  end
124
133
 
125
134
  context "with custom queue" do
@@ -208,7 +217,8 @@ if DependencyHelper.active_job_present?
208
217
  events = transaction_hash["events"]
209
218
  .sort_by { |e| e["start"] }
210
219
  .map { |event| event["name"] }
211
- expect(events).to eq(["perform_start.active_job", "perform.active_job"])
220
+
221
+ expect(events).to eq(expected_perform_events)
212
222
  end
213
223
 
214
224
  if DependencyHelper.rails_version >= Gem::Version.new("5.0.0")
@@ -286,7 +296,8 @@ if DependencyHelper.active_job_present?
286
296
  .reject { |e| e["name"] == "enqueue.active_job" }
287
297
  .sort_by { |e| e["start"] }
288
298
  .map { |event| event["name"] }
289
- expect(events).to eq(["perform_start.active_job", "perform.active_job"])
299
+
300
+ expect(events).to eq(expected_perform_events)
290
301
  end
291
302
  end
292
303
 
@@ -414,7 +425,7 @@ if DependencyHelper.active_job_present?
414
425
  expect(transaction_hash).to include(
415
426
  "action" => "ActionMailerTestJob#welcome",
416
427
  "sample_data" => hash_including(
417
- "params" => ["ActionMailerTestJob", "welcome", "deliver_now"],
428
+ "params" => ["ActionMailerTestJob", "welcome", "deliver_now"] + active_job_args_wrapper,
418
429
  "tags" => {
419
430
  "active_job_id" => kind_of(String),
420
431
  "queue" => "mailers"
@@ -433,7 +444,7 @@ if DependencyHelper.active_job_present?
433
444
  expect(transaction_hash).to include(
434
445
  "action" => "ActionMailerTestJob#welcome",
435
446
  "sample_data" => hash_including(
436
- "params" => ["ActionMailerTestJob", "welcome", "deliver_now"] + method_expected_args,
447
+ "params" => ["ActionMailerTestJob", "welcome", "deliver_now"] + active_job_args_wrapper(:args => method_expected_args),
437
448
  "tags" => {
438
449
  "active_job_id" => kind_of(String),
439
450
  "queue" => "mailers"
@@ -453,7 +464,7 @@ if DependencyHelper.active_job_present?
453
464
  expect(transaction_hash).to include(
454
465
  "action" => "ActionMailerTestJob#welcome",
455
466
  "sample_data" => hash_including(
456
- "params" => ["ActionMailerTestJob", "welcome", "deliver_now", parameterized_expected_args],
467
+ "params" => ["ActionMailerTestJob", "welcome", "deliver_now"] + active_job_args_wrapper(:params => parameterized_expected_args),
457
468
  "tags" => {
458
469
  "active_job_id" => kind_of(String),
459
470
  "queue" => "mailers"
@@ -1,7 +1,7 @@
1
1
  describe Appsignal::Hooks::SequelHook do
2
2
  if DependencyHelper.sequel_present?
3
3
  let(:db) do
4
- if Appsignal::System.jruby?
4
+ if DependencyHelper.running_jruby?
5
5
  Sequel.connect("jdbc:sqlite::memory:")
6
6
  else
7
7
  Sequel.sqlite
@@ -98,7 +98,13 @@ if DependencyHelper.padrino_present?
98
98
  RSpec::Matchers.define :match_response do |expected_status, expected_content|
99
99
  match do |response|
100
100
  status, _headers, content = response
101
- status == expected_status && content == [expected_content].compact
101
+ matches_content =
102
+ if expected_content.is_a?(Regexp)
103
+ content.join =~ expected_content
104
+ else
105
+ content == [expected_content].compact
106
+ end
107
+ status == expected_status && matches_content
102
108
  end
103
109
  end
104
110
 
@@ -142,7 +148,7 @@ if DependencyHelper.padrino_present?
142
148
  expect_a_transaction_to_be_created
143
149
  # Uses path for action name
144
150
  expect(transaction).to receive(:set_action_if_nil).with("PadrinoTestApp#unknown")
145
- expect(response).to match_response(404, "GET /404")
151
+ expect(response).to match_response(404, %r{^GET /404})
146
152
  end
147
153
  end
148
154
 
@@ -339,6 +339,7 @@ if DependencyHelper.active_job_present?
339
339
  require "sidekiq/testing"
340
340
 
341
341
  describe "Sidekiq ActiveJob integration" do
342
+ include ActiveJobHelpers
342
343
  let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
343
344
  let(:time) { Time.parse("2001-01-01 10:00:00UTC") }
344
345
  let(:log) { StringIO.new }
@@ -366,6 +367,16 @@ if DependencyHelper.active_job_present?
366
367
  }
367
368
  ]
368
369
  end
370
+ let(:expected_wrapped_args) do
371
+ if (DependencyHelper.rails6_1_present? && DependencyHelper.ruby_3_1_or_newer?) || DependencyHelper.rails7_present?
372
+ [{
373
+ "_aj_ruby2_keywords" => ["args"],
374
+ "args" => expected_args
375
+ }]
376
+ else
377
+ expected_args
378
+ end
379
+ end
369
380
  let(:expected_tags) do
370
381
  {}.tap do |hash|
371
382
  hash["active_job_id"] = kind_of(String)
@@ -374,6 +385,13 @@ if DependencyHelper.active_job_present?
374
385
  end
375
386
  end
376
387
  end
388
+ let(:expected_perform_events) do
389
+ if DependencyHelper.rails7_present?
390
+ ["perform_job.sidekiq", "perform.active_job", "perform_start.active_job"]
391
+ else
392
+ ["perform_job.sidekiq", "perform_start.active_job", "perform.active_job"]
393
+ end
394
+ end
377
395
  before do
378
396
  start_agent
379
397
  Appsignal.logger = test_logger(log)
@@ -434,8 +452,8 @@ if DependencyHelper.active_job_present?
434
452
  events = transaction_hash["events"]
435
453
  .sort_by { |e| e["start"] }
436
454
  .map { |event| event["name"] }
437
- expect(events)
438
- .to eq(["perform_job.sidekiq", "perform_start.active_job", "perform.active_job"])
455
+
456
+ expect(events).to eq(expected_perform_events)
439
457
  end
440
458
 
441
459
  context "with error" do
@@ -467,8 +485,8 @@ if DependencyHelper.active_job_present?
467
485
  events = transaction_hash["events"]
468
486
  .sort_by { |e| e["start"] }
469
487
  .map { |event| event["name"] }
470
- expect(events)
471
- .to eq(["perform_job.sidekiq", "perform_start.active_job", "perform.active_job"])
488
+
489
+ expect(events).to eq(expected_perform_events)
472
490
  end
473
491
  end
474
492
 
@@ -490,7 +508,7 @@ if DependencyHelper.active_job_present?
490
508
  expect(transaction_hash).to include(
491
509
  "action" => "ActionMailerSidekiqTestJob#welcome",
492
510
  "sample_data" => hash_including(
493
- "params" => ["ActionMailerSidekiqTestJob", "welcome", "deliver_now"] + expected_args
511
+ "params" => ["ActionMailerSidekiqTestJob", "welcome", "deliver_now"] + expected_wrapped_args
494
512
  )
495
513
  )
496
514
  end
@@ -0,0 +1,141 @@
1
+ require "appsignal/span"
2
+
3
+ describe Appsignal::Span do
4
+ before :context do
5
+ start_agent
6
+ end
7
+
8
+ let(:namespace) { "web" }
9
+ let(:root) { Appsignal::Span.new(namespace) }
10
+
11
+ describe "creating a span" do
12
+ it "creates an empty span" do
13
+ expect(root.to_h["namespace"]).to eq "web"
14
+ expect(root.to_h["trace_id"].length).to eq 16
15
+ expect(root.to_h["span_id"].length).to eq 8
16
+ expect(root.to_h["parent_span_id"]).to be_empty
17
+ expect(root.to_h["name"]).to be_empty
18
+ expect(root.to_h["start_time"]).to be > 1_600_000_000
19
+ expect(root.to_h["closed"]).to be false
20
+ end
21
+ end
22
+
23
+ describe "#child" do
24
+ let(:child) { root.child }
25
+
26
+ it "creates a child span" do
27
+ expect(child.to_h["namespace"]).to be_empty
28
+ expect(child.to_h["trace_id"].length).to eq 16
29
+ expect(child.to_h["span_id"].length).to eq 8
30
+ expect(child.to_h["parent_span_id"]).to eq root.to_h["span_id"]
31
+ expect(child.to_h["name"]).to be_empty
32
+ expect(child.to_h["start_time"]).to be > 1_600_000_000
33
+ expect(child.to_h["closed"]).to be false
34
+ end
35
+ end
36
+
37
+ describe "#add_error" do
38
+ it "adds an error" do
39
+ begin
40
+ raise "Error"
41
+ rescue => error
42
+ root.add_error(error)
43
+ end
44
+
45
+ error = root.to_h["error"]
46
+ expect(error["name"]).to eq "RuntimeError"
47
+ expect(error["message"]).to eq "Error"
48
+ expect(error["backtrace"]).not_to be_empty
49
+ end
50
+ end
51
+
52
+ describe "set_sample_data" do
53
+ it "sets sample data" do
54
+ root.set_sample_data(:params, "key" => "value")
55
+
56
+ sample_data = root.to_h["sample_data"]
57
+ expect(sample_data["params"]).to eq "{\"key\":\"value\"}"
58
+ end
59
+ end
60
+
61
+ describe "#name=" do
62
+ it "sets the name" do
63
+ root.name = "Span name"
64
+
65
+ expect(root.to_h["name"]).to eq "Span name"
66
+ end
67
+ end
68
+
69
+ describe "#[]=" do
70
+ let(:attributes) { root.to_h["attributes"] }
71
+
72
+ it "sets a string attribute" do
73
+ root["string"] = "attribute"
74
+
75
+ expect(attributes["string"]).to eq "attribute"
76
+ end
77
+
78
+ it "sets an integer attribute" do
79
+ root["integer"] = 1001
80
+
81
+ expect(attributes["integer"]).to eq 1001
82
+ end
83
+
84
+ it "sets a bigint attribute" do
85
+ root["bigint"] = 1 << 64
86
+
87
+ expect(attributes["bigint"]).to eq "bigint:#{1 << 64}"
88
+ end
89
+
90
+ it "sets a boolean attribute" do
91
+ root["true"] = true
92
+ root["false"] = false
93
+
94
+ expect(attributes["true"]).to eq true
95
+ expect(attributes["false"]).to eq false
96
+ end
97
+
98
+ it "sets a float attribute" do
99
+ root["float"] = 10.01
100
+
101
+ expect(attributes["float"]).to eq 10.01
102
+ end
103
+
104
+ it "raises an error for other types" do
105
+ expect do
106
+ root["something"] = Object.new
107
+ end.to raise_error TypeError
108
+ end
109
+ end
110
+
111
+ describe "#instrument" do
112
+ it "closes the span after yielding" do
113
+ root.instrument do
114
+ # Nothing happening
115
+ end
116
+ expect(root.closed?).to eq true
117
+ end
118
+
119
+ context "with an error raised in the passed block" do
120
+ it "closes the span after yielding" do
121
+ expect do
122
+ root.instrument do
123
+ raise ExampleException, "foo"
124
+ end
125
+ end.to raise_error(ExampleException, "foo")
126
+ expect(root.closed?).to eq true
127
+ end
128
+ end
129
+ end
130
+
131
+ describe "#close" do
132
+ it "closes a span" do
133
+ expect(root.closed?).to eq false
134
+
135
+ root.close
136
+
137
+ expect(root.to_h).to be_nil
138
+ expect(root.closed?).to eq true
139
+ end
140
+ end
141
+ end