appsignal 2.9.18-java → 2.10.0-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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +0 -6
  3. data/CHANGELOG.md +17 -1
  4. data/Rakefile +16 -2
  5. data/lib/appsignal/cli.rb +9 -2
  6. data/lib/appsignal/cli/diagnose.rb +20 -19
  7. data/lib/appsignal/cli/helpers.rb +22 -10
  8. data/lib/appsignal/cli/install.rb +2 -1
  9. data/lib/appsignal/config.rb +18 -7
  10. data/lib/appsignal/event_formatter.rb +4 -4
  11. data/lib/appsignal/minutely.rb +4 -4
  12. data/lib/appsignal/rack/js_exception_catcher.rb +6 -0
  13. data/lib/appsignal/transaction.rb +1 -1
  14. data/lib/appsignal/version.rb +1 -1
  15. data/spec/lib/appsignal/cli/diagnose_spec.rb +54 -11
  16. data/spec/lib/appsignal/cli/helpers_spec.rb +11 -3
  17. data/spec/lib/appsignal/cli/install_spec.rb +30 -1
  18. data/spec/lib/appsignal/config_spec.rb +75 -7
  19. data/spec/lib/appsignal/hooks/action_cable_spec.rb +1 -5
  20. data/spec/lib/appsignal/hooks/rake_spec.rb +41 -39
  21. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +2 -15
  22. data/spec/lib/appsignal/integrations/object_spec.rb +2 -2
  23. data/spec/lib/appsignal/integrations/que_spec.rb +26 -39
  24. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +108 -46
  25. data/spec/lib/appsignal/integrations/resque_spec.rb +40 -39
  26. data/spec/lib/appsignal/minutely_spec.rb +3 -3
  27. data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +19 -5
  28. data/spec/lib/appsignal/transaction_spec.rb +4 -12
  29. data/spec/lib/appsignal_spec.rb +7 -8
  30. data/spec/spec_helper.rb +11 -11
  31. data/spec/support/fixtures/projects/broken/config/appsignal.yml +1 -0
  32. data/spec/support/helpers/cli_helpers.rb +15 -1
  33. data/spec/support/helpers/transaction_helpers.rb +53 -0
  34. data/spec/support/matchers/be_completed.rb +5 -0
  35. data/spec/support/matchers/have_colorized_text.rb +28 -0
  36. data/spec/support/testing.rb +113 -0
  37. metadata +10 -2
@@ -24,7 +24,7 @@ describe Appsignal::CLI::Helpers do
24
24
  describe ".colorize" do
25
25
  subject { cli.send(:colorize, "text", :green) }
26
26
 
27
- context "on windows" do
27
+ context "when on windows" do
28
28
  before { allow(Gem).to receive(:win_platform?).and_return(true) }
29
29
 
30
30
  it "outputs plain string" do
@@ -32,11 +32,19 @@ describe Appsignal::CLI::Helpers do
32
32
  end
33
33
  end
34
34
 
35
- context "not on windows" do
35
+ context "when coloring is set to false" do
36
+ before { cli.send(:coloring=, false) }
37
+
38
+ it "outputs plain string" do
39
+ expect(subject).to eq "text"
40
+ end
41
+ end
42
+
43
+ context "when not on windows" do
36
44
  before { allow(Gem).to receive(:win_platform?).and_return(false) }
37
45
 
38
46
  it "wraps text in color tags" do
39
- expect(subject).to eq "\e[32mtext\e[0m"
47
+ expect(subject).to have_colorized_text(:green, "text")
40
48
  end
41
49
  end
42
50
  end
@@ -9,6 +9,7 @@ describe Appsignal::CLI::Install do
9
9
  let(:config) { Appsignal::Config.new(tmp_dir, "") }
10
10
  let(:config_file_path) { File.join(tmp_dir, "config", "appsignal.yml") }
11
11
  let(:config_file) { File.read(config_file_path) }
12
+ let(:options) { {} }
12
13
  before do
13
14
  stub_api_validation_request
14
15
  # Stub calls to speed up tests
@@ -91,7 +92,7 @@ describe Appsignal::CLI::Install do
91
92
  Dir.chdir tmp_dir do
92
93
  prepare_cli_input
93
94
  capture_stdout(out_stream) do
94
- run_cli(["install", push_api_key])
95
+ run_cli(["install", push_api_key], options)
95
96
  end
96
97
  end
97
98
  end
@@ -667,6 +668,34 @@ describe Appsignal::CLI::Install do
667
668
  it_behaves_like "push_api_key validation"
668
669
  it_behaves_like "demo data"
669
670
 
671
+ context "without color options" do
672
+ let(:options) { {} }
673
+
674
+ it "prints the instructions in color" do
675
+ run
676
+ expect(output).to have_colorized_text(:green, "## Starting AppSignal Installer ##")
677
+ end
678
+ end
679
+
680
+ context "with --color option" do
681
+ let(:options) { { "color" => nil } }
682
+
683
+ it "prints the instructions in color" do
684
+ run
685
+ expect(output).to have_colorized_text(:green, "## Starting AppSignal Installer ##")
686
+ end
687
+ end
688
+
689
+ context "with --no-color option" do
690
+ let(:options) { { "no-color" => nil } }
691
+
692
+ it "prints the instructions without special colors" do
693
+ run
694
+ expect(output).to include("Starting AppSignal Installer")
695
+ expect(output).to_not have_color_markers
696
+ end
697
+ end
698
+
670
699
  it "prints a message about unknown framework" do
671
700
  run
672
701
 
@@ -47,15 +47,43 @@ describe Appsignal::Config do
47
47
  subject { config[:active] }
48
48
 
49
49
  context "with APPSIGNAL_PUSH_API_KEY env variable" do
50
- before { ENV["APPSIGNAL_PUSH_API_KEY"] = "abc" }
50
+ context "when not empty" do
51
+ before { ENV["APPSIGNAL_PUSH_API_KEY"] = "abc" }
51
52
 
52
- it "becomes active" do
53
- expect(subject).to be_truthy
53
+ it "becomes active" do
54
+ expect(subject).to be_truthy
55
+ end
56
+
57
+ it "sets the push_api_key as loaded through the env_config" do
58
+ expect(config.env_config).to eq(:push_api_key => "abc")
59
+ expect(config.system_config).to eq(:active => true)
60
+ end
61
+ end
62
+
63
+ context "when empty string" do
64
+ before { ENV["APPSIGNAL_PUSH_API_KEY"] = "" }
65
+
66
+ it "does not becomes active" do
67
+ expect(subject).to be_falsy
68
+ end
69
+
70
+ it "sets the push_api_key as loaded through the env_config" do
71
+ expect(config.env_config).to eq(:push_api_key => "")
72
+ expect(config.system_config).to be_empty
73
+ end
54
74
  end
55
75
 
56
- it "sets the push_api_key as loaded through the env_config" do
57
- expect(config.env_config).to eq(:push_api_key => "abc")
58
- expect(config.system_config).to eq(:active => true)
76
+ context "when blank string" do
77
+ before { ENV["APPSIGNAL_PUSH_API_KEY"] = " " }
78
+
79
+ it "does not becomes active" do
80
+ expect(subject).to be_falsy
81
+ end
82
+
83
+ it "sets the push_api_key as loaded through the env_config" do
84
+ expect(config.env_config).to eq(:push_api_key => " ")
85
+ expect(config.system_config).to be_empty
86
+ end
59
87
  end
60
88
  end
61
89
 
@@ -215,6 +243,30 @@ describe Appsignal::Config do
215
243
  end
216
244
  end
217
245
 
246
+ context "with the config file causing an error" do
247
+ let(:config_path) do
248
+ File.expand_path(
249
+ File.join(File.dirname(__FILE__), "../../support/fixtures/projects/broken")
250
+ )
251
+ end
252
+ let(:config) { Appsignal::Config.new(config_path, "foo") }
253
+
254
+ it "logs & prints an error, skipping the file source" do
255
+ stdout = std_stream
256
+ stderr = std_stream
257
+ log = capture_logs { capture_std_streams(stdout, stderr) { config } }
258
+ message = "An error occured while loading the AppSignal config file. " \
259
+ "Skipping file config.\n" \
260
+ "File: #{File.join(config_path, "config", "appsignal.yml").inspect}\n" \
261
+ "NotExistingConstant: uninitialized constant NotExistingConstant\n"
262
+ expect(log).to contains_log :error, message
263
+ expect(log).to include("/appsignal/config.rb:") # Backtrace
264
+ expect(stdout.read).to_not include("appsignal:")
265
+ expect(stderr.read).to include "appsignal: #{message}"
266
+ expect(config.file_config).to eql({})
267
+ end
268
+ end
269
+
218
270
  it "sets the file_config" do
219
271
  # config found in spec/support/project_fixture/config/appsignal.yml
220
272
  expect(config.file_config).to match(
@@ -265,7 +317,7 @@ describe Appsignal::Config do
265
317
  expect_any_instance_of(Logger).to receive(:error).once
266
318
  .with("Not loading from config file: config for 'nonsense' not found")
267
319
  expect_any_instance_of(Logger).to receive(:error).once
268
- .with("Push api key not set after loading config")
320
+ .with("Push API key not set after loading config")
269
321
  config
270
322
  end
271
323
  end
@@ -724,6 +776,22 @@ describe Appsignal::Config do
724
776
  end
725
777
  end
726
778
 
779
+ context "with empty push_api_key" do
780
+ let(:push_api_key) { "" }
781
+
782
+ it "sets valid to false" do
783
+ is_expected.to eq(false)
784
+ end
785
+ end
786
+
787
+ context "with blank push_api_key" do
788
+ let(:push_api_key) { " " }
789
+
790
+ it "sets valid to false" do
791
+ is_expected.to eq(false)
792
+ end
793
+ end
794
+
727
795
  context "with push_api_key present" do
728
796
  let(:push_api_key) { "abc" }
729
797
 
@@ -54,16 +54,12 @@ describe Appsignal::Hooks::ActionCableHook do
54
54
  .with(transaction_id, Appsignal::Transaction::ACTION_CABLE, kind_of(ActionDispatch::Request))
55
55
  .and_return(transaction)
56
56
  allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
57
- # Make sure sample data is added
58
- expect(transaction.ext).to receive(:finish).and_return(true)
59
- # Stub complete call, stops it from being cleared in the extension
60
- # And allows us to call `#to_h` on it after it's been completed.
61
- expect(transaction.ext).to receive(:complete)
62
57
 
63
58
  # Stub transmit call for subscribe/unsubscribe tests
64
59
  allow(connection).to receive(:websocket)
65
60
  .and_return(instance_double("ActionCable::Connection::WebSocket", :transmit => nil))
66
61
  end
62
+ around { |example| keep_transactions { example.run } }
67
63
 
68
64
  describe "#perform_action" do
69
65
  it "creates a transaction for an action" do
@@ -4,69 +4,71 @@ describe Appsignal::Hooks::RakeHook do
4
4
  let(:task) { Rake::Task.new("task:name", Rake::Application.new) }
5
5
  let(:arguments) { Rake::TaskArguments.new(["foo"], ["bar"]) }
6
6
  let(:generic_request) { Appsignal::Transaction::GenericRequest.new({}) }
7
- before(:context) do
8
- Appsignal.config = project_fixture_config
9
- expect(Appsignal.active?).to be_truthy
10
- Appsignal::Hooks.load_hooks
11
- end
7
+ before(:context) { start_agent }
12
8
 
13
9
  describe "#execute" do
14
10
  context "without error" do
11
+ before { expect(Appsignal).to_not receive(:stop) }
12
+
13
+ def perform
14
+ task.execute(arguments)
15
+ end
16
+
15
17
  it "creates no transaction" do
16
- expect(Appsignal::Transaction).to_not receive(:create)
18
+ expect(Appsignal::Transaction).to_not receive(:new)
19
+ expect { perform }.to_not(change { created_transactions })
17
20
  end
18
21
 
19
22
  it "calls the original task" do
20
- expect(task).to receive(:execute_without_appsignal).with("foo")
23
+ expect(task).to receive(:execute_without_appsignal).with(arguments)
24
+ perform
21
25
  end
22
-
23
- after { task.execute("foo") }
24
26
  end
25
27
 
26
28
  context "with error" do
27
29
  let(:error) { ExampleException }
28
- let(:transaction) { background_job_transaction }
29
30
  before do
30
- task.enhance { raise error }
31
-
32
- expect(Appsignal::Transaction).to receive(:create).with(
33
- kind_of(String),
34
- Appsignal::Transaction::BACKGROUND_JOB,
35
- kind_of(Appsignal::Transaction::GenericRequest)
36
- ).and_return(transaction)
31
+ task.enhance { raise error, "my error message" }
32
+ # We don't call `and_call_original` here as we don't want AppSignal to
33
+ # stop and start for every spec.
34
+ expect(Appsignal).to receive(:stop).with("rake")
37
35
  end
38
36
 
39
- it "sets the action" do
40
- expect(transaction).to receive(:set_action).with("task:name")
41
- end
42
-
43
- it "sets the error" do
44
- expect(transaction).to receive(:set_error).with(error)
37
+ def perform
38
+ keep_transactions do
39
+ expect { task.execute(arguments) }.to raise_error(error)
40
+ end
45
41
  end
46
42
 
47
- it "completes the transaction and stops" do
48
- expect(transaction).to receive(:complete).ordered
49
- expect(Appsignal).to receive(:stop).with("rake").ordered
50
- end
43
+ it "creates a background job transaction" do
44
+ perform
51
45
 
52
- it "adds the task arguments to the request" do
53
- expect(Appsignal::Transaction::GenericRequest).to receive(:new)
54
- .with(:params => { :foo => "bar" })
55
- .and_return(generic_request)
46
+ expect(last_transaction).to be_completed
47
+ expect(last_transaction.to_h).to include(
48
+ "id" => kind_of(String),
49
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
50
+ "action" => "task:name",
51
+ "error" => {
52
+ "name" => "ExampleException",
53
+ "message" => "my error message",
54
+ "backtrace" => kind_of(String)
55
+ },
56
+ "sample_data" => hash_including(
57
+ "params" => { "foo" => "bar" }
58
+ )
59
+ )
56
60
  end
57
61
 
58
62
  context "when first argument is not a `Rake::TaskArguments`" do
59
63
  let(:arguments) { nil }
60
64
 
61
- it "adds the first argument regardless" do
62
- expect(Appsignal::Transaction::GenericRequest).to receive(:new)
63
- .with(:params => nil)
64
- .and_return(generic_request)
65
- end
66
- end
65
+ it "does not add the params to the transaction" do
66
+ perform
67
67
 
68
- after do
69
- expect { task.execute(arguments) }.to raise_error ExampleException
68
+ expect(last_transaction.to_h).to include(
69
+ "sample_data" => hash_excluding("params")
70
+ )
71
+ end
70
72
  end
71
73
  end
72
74
  end
@@ -38,28 +38,15 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
38
38
  }
39
39
  end
40
40
  let(:plugin) { Appsignal::Hooks::SidekiqPlugin.new }
41
- let(:test_store) { {} }
42
41
  let(:log) { StringIO.new }
43
42
  before do
44
43
  start_agent
45
44
  Appsignal.logger = test_logger(log)
46
-
47
- # Stub calls to extension, because that would remove the transaction
48
- # from the extension.
49
- allow_any_instance_of(Appsignal::Extension::Transaction).to receive(:finish).and_return(true)
50
- allow_any_instance_of(Appsignal::Extension::Transaction).to receive(:complete)
51
-
52
- # Stub removal of current transaction from current thread so we can fetch
53
- # it later.
54
- expect(Appsignal::Transaction).to receive(:clear_current_transaction!) do
55
- transaction = Thread.current[:appsignal_transaction]
56
- test_store[:transaction] = transaction if transaction
57
- end
58
45
  end
46
+ around { |example| keep_transactions { example.run } }
59
47
  after :with_yaml_parse_error => false do
60
48
  expect(log_contents(log)).to_not contains_log(:warn, "Unable to load YAML")
61
49
  end
62
- after { clear_current_transaction! }
63
50
 
64
51
  shared_examples "sidekiq metadata" do
65
52
  describe "internal Sidekiq job values" do
@@ -513,7 +500,7 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
513
500
  end
514
501
 
515
502
  def transaction
516
- test_store[:transaction]
503
+ last_transaction
517
504
  end
518
505
 
519
506
  def expect_transaction_to_have_sidekiq_event(transaction_hash)
@@ -16,8 +16,8 @@ describe Object do
16
16
  context "when active" do
17
17
  let(:transaction) { http_request_transaction }
18
18
  before do
19
- expect(Appsignal::Transaction).to receive(:current).at_least(:once).and_return(transaction)
20
19
  Appsignal.config = project_fixture_config
20
+ expect(Appsignal::Transaction).to receive(:current).at_least(:once).and_return(transaction)
21
21
  end
22
22
  after { Appsignal.config = nil }
23
23
 
@@ -137,9 +137,9 @@ describe Object do
137
137
  context "when active" do
138
138
  let(:transaction) { http_request_transaction }
139
139
  before do
140
+ Appsignal.config = project_fixture_config
140
141
  expect(Appsignal::Transaction).to receive(:current).at_least(:once)
141
142
  .and_return(transaction)
142
- Appsignal.config = project_fixture_config
143
143
  end
144
144
  after { Appsignal.config = nil }
145
145
 
@@ -37,47 +37,30 @@ if DependencyHelper.que_present?
37
37
  end
38
38
  end
39
39
  let(:instance) { job.new(job_attrs) }
40
- let(:transaction) do
41
- Appsignal::Transaction.new(
42
- SecureRandom.uuid,
43
- Appsignal::Transaction::BACKGROUND_JOB,
44
- Appsignal::Transaction::GenericRequest.new(env)
45
- )
46
- end
47
40
 
48
41
  before do
49
42
  allow(Que).to receive(:execute)
50
43
 
51
44
  start_agent
52
45
  expect(Appsignal.active?).to be_truthy
53
- transaction
54
-
55
- expect(Appsignal::Transaction).to receive(:create)
56
- .with(
57
- kind_of(String),
58
- Appsignal::Transaction::BACKGROUND_JOB,
59
- kind_of(Appsignal::Transaction::GenericRequest)
60
- ).and_return(transaction)
61
- allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
62
- expect(transaction.ext).to receive(:finish).and_return(true)
63
- expect(transaction.ext).to receive(:complete)
64
46
  end
65
-
66
- subject { transaction.to_h }
47
+ around { |example| keep_transactions { example.run } }
67
48
 
68
49
  context "success" do
69
50
  it "creates a transaction for a job" do
70
51
  expect do
71
52
  instance._run
72
- end.to_not raise_exception
53
+ end.to change { created_transactions.length }.by(1)
73
54
 
74
- expect(subject).to include(
55
+ expect(last_transaction).to be_completed
56
+ transaction_hash = last_transaction.to_h
57
+ expect(transaction_hash).to include(
75
58
  "action" => "MyQueJob#run",
76
59
  "id" => instance_of(String),
77
60
  "namespace" => Appsignal::Transaction::BACKGROUND_JOB
78
61
  )
79
- expect(subject["error"]).to be_nil
80
- expect(subject["events"].first).to include(
62
+ expect(transaction_hash["error"]).to be_nil
63
+ expect(transaction_hash["events"].first).to include(
81
64
  "allocation_count" => kind_of(Integer),
82
65
  "body" => "",
83
66
  "body_format" => Appsignal::EventFormatter::DEFAULT,
@@ -91,7 +74,7 @@ if DependencyHelper.que_present?
91
74
  "name" => "perform_job.que",
92
75
  "title" => ""
93
76
  )
94
- expect(subject["sample_data"]).to include(
77
+ expect(transaction_hash["sample_data"]).to include(
95
78
  "params" => %w[1 birds],
96
79
  "metadata" => {
97
80
  "attempts" => 0,
@@ -107,24 +90,28 @@ if DependencyHelper.que_present?
107
90
  context "with exception" do
108
91
  let(:error) { ExampleException.new("oh no!") }
109
92
 
110
- it "should report exceptions and re-raise them" do
93
+ it "reports exceptions and re-raise them" do
111
94
  allow(instance).to receive(:run).and_raise(error)
112
95
 
113
96
  expect do
114
- instance._run
115
- end.to raise_error(ExampleException)
116
-
117
- expect(subject).to include(
97
+ expect do
98
+ instance._run
99
+ end.to raise_error(ExampleException)
100
+ end.to change { created_transactions.length }.by(1)
101
+
102
+ expect(last_transaction).to be_completed
103
+ transaction_hash = last_transaction.to_h
104
+ expect(transaction_hash).to include(
118
105
  "action" => "MyQueJob#run",
119
106
  "id" => instance_of(String),
120
107
  "namespace" => Appsignal::Transaction::BACKGROUND_JOB
121
108
  )
122
- expect(subject["error"]).to include(
109
+ expect(transaction_hash["error"]).to include(
123
110
  "backtrace" => kind_of(String),
124
111
  "name" => error.class.name,
125
112
  "message" => error.message
126
113
  )
127
- expect(subject["sample_data"]).to include(
114
+ expect(transaction_hash["sample_data"]).to include(
128
115
  "params" => %w[1 birds],
129
116
  "metadata" => {
130
117
  "attempts" => 0,
@@ -140,24 +127,24 @@ if DependencyHelper.que_present?
140
127
  context "with error" do
141
128
  let(:error) { ExampleStandardError.new("oh no!") }
142
129
 
143
- it "should report errors and not re-raise them" do
130
+ it "reports errors and not re-raise them" do
144
131
  allow(instance).to receive(:run).and_raise(error)
145
132
 
146
- expect do
147
- instance._run
148
- end.to_not raise_error
133
+ expect { instance._run }.to change { created_transactions.length }.by(1)
149
134
 
150
- expect(subject).to include(
135
+ expect(last_transaction).to be_completed
136
+ transaction_hash = last_transaction.to_h
137
+ expect(transaction_hash).to include(
151
138
  "action" => "MyQueJob#run",
152
139
  "id" => instance_of(String),
153
140
  "namespace" => Appsignal::Transaction::BACKGROUND_JOB
154
141
  )
155
- expect(subject["error"]).to include(
142
+ expect(transaction_hash["error"]).to include(
156
143
  "backtrace" => kind_of(String),
157
144
  "name" => error.class.name,
158
145
  "message" => error.message
159
146
  )
160
- expect(subject["sample_data"]).to include(
147
+ expect(transaction_hash["sample_data"]).to include(
161
148
  "params" => %w[1 birds],
162
149
  "metadata" => {
163
150
  "attempts" => 0,