appsignal 2.8.4.beta.1 → 2.9.18.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
  3. data/.github/ISSUE_TEMPLATE/chore.md +14 -0
  4. data/.gitignore +2 -3
  5. data/.rubocop.yml +3 -0
  6. data/.rubocop_todo.yml +7 -16
  7. data/.travis.yml +28 -27
  8. data/CHANGELOG.md +657 -533
  9. data/README.md +31 -3
  10. data/Rakefile +128 -129
  11. data/SUPPORT.md +16 -0
  12. data/appsignal.gemspec +17 -4
  13. data/build_matrix.yml +21 -9
  14. data/ext/Rakefile +23 -17
  15. data/ext/agent.yml +40 -37
  16. data/ext/base.rb +116 -31
  17. data/ext/extconf.rb +34 -28
  18. data/gemfiles/capistrano2.gemfile +5 -0
  19. data/gemfiles/capistrano3.gemfile +5 -0
  20. data/gemfiles/grape.gemfile +5 -0
  21. data/gemfiles/no_dependencies.gemfile +5 -0
  22. data/gemfiles/padrino.gemfile +5 -0
  23. data/gemfiles/que.gemfile +5 -0
  24. data/gemfiles/que_beta.gemfile +10 -0
  25. data/gemfiles/rails-3.2.gemfile +5 -0
  26. data/gemfiles/rails-4.0.gemfile +5 -0
  27. data/gemfiles/rails-4.1.gemfile +5 -0
  28. data/gemfiles/rails-4.2.gemfile +5 -0
  29. data/gemfiles/rails-6.0.gemfile +5 -0
  30. data/gemfiles/resque.gemfile +5 -0
  31. data/lib/appsignal.rb +14 -492
  32. data/lib/appsignal/cli/demo.rb +5 -2
  33. data/lib/appsignal/cli/diagnose.rb +84 -4
  34. data/lib/appsignal/cli/diagnose/paths.rb +0 -5
  35. data/lib/appsignal/cli/diagnose/utils.rb +19 -0
  36. data/lib/appsignal/cli/helpers.rb +6 -0
  37. data/lib/appsignal/cli/install.rb +45 -15
  38. data/lib/appsignal/cli/notify_of_deploy.rb +10 -0
  39. data/lib/appsignal/config.rb +1 -2
  40. data/lib/appsignal/event_formatter.rb +4 -5
  41. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +10 -8
  42. data/lib/appsignal/event_formatter/moped/query_formatter.rb +60 -59
  43. data/lib/appsignal/extension.rb +2 -2
  44. data/lib/appsignal/helpers/instrumentation.rb +494 -0
  45. data/lib/appsignal/helpers/metrics.rb +54 -0
  46. data/lib/appsignal/hooks.rb +11 -8
  47. data/lib/appsignal/hooks/active_support_notifications.rb +2 -5
  48. data/lib/appsignal/hooks/puma.rb +74 -11
  49. data/lib/appsignal/hooks/sequel.rb +1 -1
  50. data/lib/appsignal/hooks/sidekiq.rb +115 -0
  51. data/lib/appsignal/integrations/mongo_ruby_driver.rb +7 -0
  52. data/lib/appsignal/integrations/que.rb +9 -8
  53. data/lib/appsignal/integrations/railtie.rb +2 -1
  54. data/lib/appsignal/marker.rb +2 -3
  55. data/lib/appsignal/minutely.rb +188 -19
  56. data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -1
  57. data/lib/appsignal/system.rb +16 -18
  58. data/lib/appsignal/transaction.rb +8 -0
  59. data/lib/appsignal/utils/rails_helper.rb +20 -0
  60. data/lib/appsignal/version.rb +1 -1
  61. data/lib/puma/plugin/appsignal.rb +26 -0
  62. data/spec/lib/appsignal/cli/diagnose/utils_spec.rb +40 -0
  63. data/spec/lib/appsignal/cli/diagnose_spec.rb +129 -22
  64. data/spec/lib/appsignal/cli/install_spec.rb +57 -8
  65. data/spec/lib/appsignal/cli/notify_of_deploy_spec.rb +10 -0
  66. data/spec/lib/appsignal/config_spec.rb +13 -11
  67. data/spec/lib/appsignal/event_formatter/action_view/render_formatter_spec.rb +38 -28
  68. data/spec/lib/appsignal/event_formatter/moped/query_formatter_spec.rb +6 -0
  69. data/spec/lib/appsignal/event_formatter_spec.rb +168 -69
  70. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +104 -25
  71. data/spec/lib/appsignal/hooks/puma_spec.rb +251 -34
  72. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +209 -0
  73. data/spec/lib/appsignal/hooks_spec.rb +4 -0
  74. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +24 -1
  75. data/spec/lib/appsignal/minutely_spec.rb +318 -26
  76. data/spec/lib/appsignal/system_spec.rb +0 -35
  77. data/spec/lib/appsignal/transaction_spec.rb +68 -10
  78. data/spec/lib/appsignal/utils/hash_sanitizer_spec.rb +39 -31
  79. data/spec/lib/appsignal/utils/json_spec.rb +7 -3
  80. data/spec/lib/appsignal_spec.rb +98 -22
  81. data/spec/lib/puma/appsignal_spec.rb +91 -0
  82. data/spec/spec_helper.rb +13 -0
  83. data/spec/support/{project_fixture → fixtures/projects/valid}/config/application.rb +0 -0
  84. data/spec/support/{project_fixture → fixtures/projects/valid}/config/appsignal.yml +1 -0
  85. data/spec/support/{project_fixture → fixtures/projects/valid}/config/environments/development.rb +0 -0
  86. data/spec/support/{project_fixture → fixtures/projects/valid}/config/environments/production.rb +0 -0
  87. data/spec/support/{project_fixture → fixtures/projects/valid}/config/environments/test.rb +0 -0
  88. data/spec/support/{project_fixture → fixtures/projects/valid}/log/.gitkeep +0 -0
  89. data/spec/support/helpers/config_helpers.rb +1 -1
  90. data/spec/support/helpers/log_helpers.rb +6 -0
  91. data/spec/support/helpers/wait_for_helper.rb +28 -0
  92. data/spec/support/mocks/mock_probe.rb +11 -0
  93. data/spec/support/stubs/sidekiq/api.rb +4 -0
  94. metadata +43 -31
  95. data/spec/support/fixtures/containers/cgroups/docker +0 -14
  96. data/spec/support/fixtures/containers/cgroups/docker_systemd +0 -8
  97. data/spec/support/fixtures/containers/cgroups/lxc +0 -10
  98. data/spec/support/fixtures/containers/cgroups/no_permission +0 -0
  99. data/spec/support/fixtures/containers/cgroups/none +0 -1
@@ -116,6 +116,9 @@ describe Appsignal::CLI::NotifyOfDeploy do
116
116
 
117
117
  context "with required options" do
118
118
  let(:options) { { :environment => "production", :revision => "aaaaa", :user => "thijs" } }
119
+ let(:log_stream) { std_stream }
120
+ let(:log) { log_contents(log_stream) }
121
+ before { Appsignal.logger = test_logger(log_stream) }
119
122
 
120
123
  it "notifies of a deploy" do
121
124
  run
@@ -124,6 +127,13 @@ describe Appsignal::CLI::NotifyOfDeploy do
124
127
  expect(output).to include_deploy_notification_with(options)
125
128
  end
126
129
 
130
+ it "prints a deprecation message" do
131
+ run
132
+ deprecation_message = "This command (appsignal notify_of_deploy) has been deprecated"
133
+ expect(output).to include("appsignal WARNING: #{deprecation_message}")
134
+ expect(log).to contains_log :warn, deprecation_message
135
+ end
136
+
127
137
  context "with no app name configured" do
128
138
  before { ENV["APPSIGNAL_APP_NAME"] = "" }
129
139
 
@@ -130,7 +130,7 @@ describe Appsignal::Config do
130
130
  :enable_allocation_tracking => true,
131
131
  :enable_gc_instrumentation => false,
132
132
  :enable_host_metrics => true,
133
- :enable_minutely_probes => false,
133
+ :enable_minutely_probes => true,
134
134
  :ca_file_path => File.join(resources_dir, "cacert.pem"),
135
135
  :dns_servers => [],
136
136
  :files_world_accessible => true,
@@ -202,14 +202,16 @@ describe Appsignal::Config do
202
202
  context "with a config file" do
203
203
  let(:config) { project_fixture_config("production") }
204
204
 
205
- it "is not valid or active" do
206
- expect(config.valid?).to be_truthy
207
- expect(config.active?).to be_truthy
208
- end
205
+ context "with valid config" do
206
+ it "is valid and active" do
207
+ expect(config.valid?).to be_truthy
208
+ expect(config.active?).to be_truthy
209
+ end
209
210
 
210
- it "does not log an error" do
211
- expect_any_instance_of(Logger).to_not receive(:error)
212
- config
211
+ it "does not log an error" do
212
+ log = capture_logs { config }
213
+ expect(log).to_not contains_log(:error)
214
+ end
213
215
  end
214
216
 
215
217
  it "sets the file_config" do
@@ -218,7 +220,8 @@ describe Appsignal::Config do
218
220
  :active => true,
219
221
  :push_api_key => "abc",
220
222
  :name => "TestApp",
221
- :request_headers => kind_of(Array)
223
+ :request_headers => kind_of(Array),
224
+ :enable_minutely_probes => false
222
225
  )
223
226
  end
224
227
 
@@ -476,7 +479,7 @@ describe Appsignal::Config do
476
479
 
477
480
  it "writes the current config to environment variables" do
478
481
  expect(ENV["_APPSIGNAL_ACTIVE"]).to eq "true"
479
- expect(ENV["_APPSIGNAL_APP_PATH"]).to end_with("spec/support/project_fixture")
482
+ expect(ENV["_APPSIGNAL_APP_PATH"]).to end_with("spec/support/fixtures/projects/valid")
480
483
  expect(ENV["_APPSIGNAL_AGENT_PATH"]).to end_with("/ext")
481
484
  expect(ENV["_APPSIGNAL_DEBUG_LOGGING"]).to eq "false"
482
485
  expect(ENV["_APPSIGNAL_LOG"]).to eq "stdout"
@@ -492,7 +495,6 @@ describe Appsignal::Config do
492
495
  expect(ENV["_APPSIGNAL_IGNORE_NAMESPACES"]).to eq "admin,private_namespace"
493
496
  expect(ENV["_APPSIGNAL_RUNNING_IN_CONTAINER"]).to eq "false"
494
497
  expect(ENV["_APPSIGNAL_ENABLE_HOST_METRICS"]).to eq "true"
495
- expect(ENV["_APPSIGNAL_ENABLE_MINUTELY_PROBES"]).to eq "false"
496
498
  expect(ENV["_APPSIGNAL_HOSTNAME"]).to eq ""
497
499
  expect(ENV["_APPSIGNAL_PROCESS_NAME"]).to include "rspec"
498
500
  expect(ENV["_APPSIGNAL_CA_FILE_PATH"]).to eq File.join(resources_dir, "cacert.pem")
@@ -1,43 +1,53 @@
1
- if DependencyHelper.rails_present?
2
- require "action_view"
1
+ describe Appsignal::EventFormatter::ActionView::RenderFormatter do
2
+ let(:klass) { Appsignal::EventFormatter::ActionView::RenderFormatter }
3
3
 
4
- describe Appsignal::EventFormatter::ActionView::RenderFormatter do
5
- before { allow(Rails.root).to receive(:to_s).and_return("/var/www/app/20130101") }
6
- let(:klass) { Appsignal::EventFormatter::ActionView::RenderFormatter }
7
- let(:formatter) { klass.new }
4
+ if DependencyHelper.rails_present?
5
+ require "action_view"
8
6
 
9
- it "should register render_partial.action_view and render_template.action_view" do
10
- expect(Appsignal::EventFormatter.registered?("render_partial.action_view", klass)).to be_truthy
11
- expect(Appsignal::EventFormatter.registered?("render_template.action_view", klass)).to be_truthy
12
- end
7
+ context "when in a Rails app" do
8
+ let(:formatter) { klass.new }
9
+ before { allow(Rails.root).to receive(:to_s).and_return("/var/www/app/20130101") }
10
+
11
+ it "registers render_partial.action_view and render_template.action_view" do
12
+ expect(Appsignal::EventFormatter.registered?("render_partial.action_view", klass)).to be_truthy
13
+ expect(Appsignal::EventFormatter.registered?("render_template.action_view", klass)).to be_truthy
14
+ end
13
15
 
14
- describe "#root_path" do
15
- subject { formatter.root_path }
16
+ describe "#root_path" do
17
+ subject { formatter.root_path }
16
18
 
17
- it "returns Rails root path" do
18
- is_expected.to eq "/var/www/app/20130101/"
19
+ it "returns Rails root path" do
20
+ is_expected.to eq "/var/www/app/20130101/"
21
+ end
19
22
  end
20
- end
21
23
 
22
- describe "#format" do
23
- subject { formatter.format(payload) }
24
+ describe "#format" do
25
+ subject { formatter.format(payload) }
24
26
 
25
- context "with an identifier" do
26
- let(:payload) { { :identifier => "/var/www/app/20130101/app/views/home/index/html.erb" } }
27
+ context "with an identifier" do
28
+ let(:payload) { { :identifier => "/var/www/app/20130101/app/views/home/index/html.erb" } }
27
29
 
28
- it { is_expected.to eq ["app/views/home/index/html.erb", nil] }
29
- end
30
+ it { is_expected.to eq ["app/views/home/index/html.erb", nil] }
31
+ end
30
32
 
31
- context "with a frozen identifier" do
32
- let(:payload) { { :identifier => "/var/www/app/20130101/app/views/home/index/html.erb".freeze } }
33
+ context "with a frozen identifier" do
34
+ let(:payload) { { :identifier => "/var/www/app/20130101/app/views/home/index/html.erb".freeze } }
33
35
 
34
- it { is_expected.to eq ["app/views/home/index/html.erb", nil] }
35
- end
36
+ it { is_expected.to eq ["app/views/home/index/html.erb", nil] }
37
+ end
36
38
 
37
- context "without an identifier" do
38
- let(:payload) { {} }
39
+ context "without an identifier" do
40
+ let(:payload) { {} }
39
41
 
40
- it { is_expected.to be_nil }
42
+ it { is_expected.to be_nil }
43
+ end
44
+ end
45
+ end
46
+ else
47
+ context "when not in a Rails app" do
48
+ it "does not register the event formatter" do
49
+ expect(Appsignal::EventFormatter.registered?("render_partial.action_view", klass)).to be_falsy
50
+ expect(Appsignal::EventFormatter.registered?("render_template.action_view", klass)).to be_falsy
41
51
  end
42
52
  end
43
53
  end
@@ -16,6 +16,12 @@ describe Appsignal::EventFormatter::Moped::QueryFormatter do
16
16
  it { is_expected.to be_nil }
17
17
  end
18
18
 
19
+ context "when ops is nil in the payload" do
20
+ let(:payload) { { :ops => nil } }
21
+
22
+ it { is_expected.to be_nil }
23
+ end
24
+
19
25
  context "Moped::Protocol::Command" do
20
26
  let(:op) do
21
27
  double(
@@ -10,9 +10,10 @@ class MockFormatter < Appsignal::EventFormatter
10
10
  end
11
11
  end
12
12
 
13
- class MissingFormatMockFormatter < Appsignal::EventFormatter
14
- def transform(_payload)
15
- end
13
+ class MockFormatterDouble < MockFormatter
14
+ end
15
+
16
+ class MissingFormatMockFormatter
16
17
  end
17
18
 
18
19
  class IncorrectFormatMockFormatter < Appsignal::EventFormatter
@@ -20,109 +21,207 @@ class IncorrectFormatMockFormatter < Appsignal::EventFormatter
20
21
  end
21
22
  end
22
23
 
24
+ class IncorrectFormatMock2Formatter < Appsignal::EventFormatter
25
+ def format(_payload, _foo = nil)
26
+ end
27
+ end
28
+
23
29
  class MockDependentFormatter < Appsignal::EventFormatter
24
30
  def initialize
25
31
  NonsenseDependency.something
26
32
  end
27
- end
28
33
 
29
- Appsignal::EventFormatter.register "mock", MockFormatter
30
- Appsignal::EventFormatter.register "mock.dependent", MockDependentFormatter
34
+ def format(_payload)
35
+ end
36
+ end
31
37
 
32
38
  describe Appsignal::EventFormatter do
33
- before do
34
- klass.register("mock", MockFormatter)
39
+ let(:klass) { described_class }
40
+ around do |example|
41
+ original_formatters = described_class.formatters
42
+ example.run
43
+ described_class.class_variable_set(:@@formatters, original_formatters)
35
44
  end
36
45
 
37
- let(:klass) { Appsignal::EventFormatter }
38
-
39
- let(:deprecated_formatter) do
40
- Class.new(Appsignal::EventFormatter) do
41
- register "mock.deprecated"
42
-
43
- def format(_payload)
44
- end
45
- end
46
- end
46
+ describe ".register" do
47
+ it "registers a formatter" do
48
+ expect(klass.registered?("mock")).to be_falsy
47
49
 
48
- context "registering and unregistering formatters" do
49
- let(:out_stream) { std_stream }
50
- let(:output) { out_stream.read }
50
+ klass.register "mock", MockFormatter
51
51
 
52
- it "registers a formatter" do
53
52
  expect(klass.formatters["mock"]).to be_instance_of(MockFormatter)
53
+ expect(klass.formatter_classes["mock"]).to eq MockFormatter
54
54
  end
55
55
 
56
- it "knows whether a formatter is registered" do
57
- expect(klass.registered?("mock")).to be_truthy
58
- expect(klass.registered?("mock", MockFormatter)).to be_truthy
59
- expect(klass.registered?("mock", Hash)).to be_falsy
60
- expect(klass.registered?("nonsense")).to be_falsy
56
+ context "when a formatter with the name already exists" do
57
+ it "does not register the formatter again" do
58
+ logs = capture_logs do
59
+ klass.register("mock.twice", MockFormatter)
60
+ klass.register("mock.twice", MockFormatter)
61
+ end
62
+ expect(klass.registered?("mock.twice")).to be_truthy
63
+ expect(logs).to contains_log :warn, \
64
+ "Formatter for 'mock.twice' already registered, not registering 'MockFormatter'"
65
+ end
61
66
  end
62
67
 
63
- it "doesn't register formatters that raise a name error in the initializer" do
64
- expect(klass.registered?("mock.dependent")).to be_falsy
68
+ context "when there is an error initializing the formatter" do
69
+ it "does not register the formatter and logs an error" do
70
+ logs = capture_logs do
71
+ described_class.register "mock.dependent", MockDependentFormatter
72
+ end
73
+ expect(klass.registered?("mock.dependent")).to be_falsy
74
+ expect(logs).to contains_log :error, \
75
+ "'uninitialized constant MockDependentFormatter::NonsenseDependency' " \
76
+ "when initializing mock.dependent event formatter"
77
+ end
65
78
  end
66
79
 
67
- it "registers a custom formatter" do
68
- klass.register("mock.specific", MockFormatter)
80
+ context "when formatter has no format/1 method" do
81
+ context "when the formatter has no format method" do
82
+ it "does not register the formatter and logs an error" do
83
+ logs = capture_logs do
84
+ described_class.register "mock.missing", MissingFormatMockFormatter
85
+ end
86
+ expect(klass.registered?("mock.missing")).to be_falsy
87
+ expect(logs).to contains_log :error, \
88
+ "'MissingFormatMockFormatter does not have a format(payload) " \
89
+ "method' when initializing mock.missing event formatter"
90
+ end
91
+ end
69
92
 
70
- expect(klass.formatter_classes["mock.specific"]).to eq MockFormatter
71
- expect(klass.registered?("mock.specific")).to be_truthy
72
- expect(klass.formatters["mock.specific"]).to be_instance_of(MockFormatter)
73
- expect(klass.formatters["mock.specific"].body).to eq "some value"
74
- end
93
+ context "when the formatter has an format/0 method" do
94
+ it "does not register the formatter and logs an error" do
95
+ logs = capture_logs do
96
+ described_class.register "mock.incorrect", IncorrectFormatMockFormatter
97
+ end
98
+ expect(klass.registered?("mock.incorrect")).to be_falsy
99
+ expect(logs).to contains_log :error, \
100
+ "'IncorrectFormatMockFormatter does not have a format(payload) " \
101
+ "method' when initializing mock.incorrect event formatter"
102
+ end
103
+ end
75
104
 
76
- it "does not know a formatter that's not registered" do
77
- expect(klass.formatters["nonsense"]).to be_nil
105
+ context "when formatter has an format/2 method" do
106
+ it "does not register the formatter and logs an error" do
107
+ logs = capture_logs do
108
+ described_class.register "mock.incorrect", IncorrectFormatMock2Formatter
109
+ end
110
+ expect(klass.registered?("mock.incorrect")).to be_falsy
111
+ expect(logs).to contains_log :error, \
112
+ "'IncorrectFormatMock2Formatter does not have a format(payload) " \
113
+ "method' when initializing mock.incorrect event formatter"
114
+ end
115
+ end
78
116
  end
79
117
 
80
- it "unregistering a formatter if the registered one has the same class" do
81
- klass.register("mock.unregister", MockFormatter)
118
+ context "when registering deprecated formatters" do
119
+ let(:stdout_stream) { std_stream }
120
+ let(:deprecated_formatter) do
121
+ Class.new(Appsignal::EventFormatter) do
122
+ register "mock.deprecated"
82
123
 
83
- klass.unregister("mock.unregister", Hash)
84
- expect(klass.registered?("mock.unregister")).to be_truthy
124
+ def format(_payload)
125
+ end
126
+ end
127
+ end
85
128
 
86
- klass.unregister("mock.unregister", MockFormatter)
87
- expect(klass.registered?("mock.unregister")).to be_falsy
88
- end
129
+ it "registers deprecated formatters and logs & prints a warning" do
130
+ message = "Formatter for 'mock.deprecated' is using a deprecated registration method. " \
131
+ "This event formatter will not be loaded. " \
132
+ "Please update the formatter according to the documentation at: " \
133
+ "https://docs.appsignal.com/ruby/instrumentation/event-formatters.html"
134
+
135
+ logs = capture_logs do
136
+ capture_stdout(stdout_stream) { deprecated_formatter }
137
+ end
138
+ expect(logs).to contains_log :warn, message
139
+ expect(stdout_stream.read).to include "appsignal WARNING: #{message}"
140
+
141
+ expect(klass.deprecated_formatter_classes.keys).to include("mock.deprecated")
142
+ end
143
+
144
+ it "initializes deprecated formatters" do
145
+ capture_stdout(stdout_stream) { deprecated_formatter }
146
+ klass.initialize_deprecated_formatters
89
147
 
90
- it "does not register two formatters for the same name" do
91
- expect(Appsignal.logger).to receive(:warn)
92
- .with("Formatter for 'mock.twice' already registered, not registering 'MockFormatter'")
93
- klass.register("mock.twice", MockFormatter)
94
- klass.register("mock.twice", MockFormatter)
148
+ expect(klass.registered?("mock.deprecated")).to be_truthy
149
+ expect(klass.formatters["mock.deprecated"]).to be_instance_of(deprecated_formatter)
150
+ expect(klass.deprecated_formatter_classes["mock.deprecated"]).to eq(deprecated_formatter)
151
+ end
95
152
  end
153
+ end
96
154
 
97
- it "does not register deprecated formatters" do
98
- message = "Formatter for 'mock.deprecated' is using a deprecated registration method. " \
99
- "This event formatter will not be loaded. " \
100
- "Please update the formatter according to the documentation at: " \
101
- "https://docs.appsignal.com/ruby/instrumentation/event-formatters.html"
102
- expect(Appsignal.logger).to receive(:warn).with(message)
155
+ describe ".registered?" do
156
+ context "when checking by name" do
157
+ context "when there is a formatter with that name" do
158
+ it "returns true" do
159
+ klass.register "mock", MockFormatter
160
+ expect(klass.registered?("mock")).to be_truthy
161
+ end
162
+ end
163
+
164
+ context "when there is no formatter with that name" do
165
+ it "returns false" do
166
+ expect(klass.registered?("nonsense")).to be_falsy
167
+ end
168
+ end
169
+ end
103
170
 
104
- capture_stdout(out_stream) { deprecated_formatter }
105
- expect(output).to include "appsignal WARNING: #{message}"
171
+ context "when checking by name and class" do
172
+ context "when there is a formatter with that name and class" do
173
+ it "returns true" do
174
+ klass.register "mock", MockFormatter
175
+ expect(klass.registered?("mock", MockFormatter)).to be_truthy
176
+ end
177
+ end
106
178
 
107
- expect(Appsignal::EventFormatter.deprecated_formatter_classes.keys).to include("mock.deprecated")
179
+ context "when there is no formatter with that name and class" do
180
+ it "returns false" do
181
+ klass.register "mock", MockFormatterDouble
182
+ expect(klass.registered?("mock", MockFormatter)).to be_falsy
183
+ end
184
+ end
108
185
  end
186
+ end
187
+
188
+ describe ".unregister" do
189
+ context "when a formatter with the name is registered" do
190
+ it "unregisters the formatter has the same class" do
191
+ klass.register("mock.unregister", MockFormatter)
192
+ expect(klass.registered?("mock.unregister")).to be_truthy
109
193
 
110
- it "initializes deprecated formatters" do
111
- # Silence deprecation warning
112
- capture_stdout(out_stream) { deprecated_formatter }
113
- Appsignal::EventFormatter.initialize_deprecated_formatters
194
+ klass.unregister("mock.unregister", Hash)
195
+ expect(klass.registered?("mock.unregister")).to be_truthy
114
196
 
115
- expect(klass.registered?("mock.deprecated")).to be_truthy
197
+ klass.unregister("mock.unregister", MockFormatter)
198
+ expect(klass.registered?("mock.unregister")).to be_falsy
199
+ end
200
+ end
201
+
202
+ context "when a formatter with the same name and class is not registered" do
203
+ it "unregisters nothing" do
204
+ expect do
205
+ expect do
206
+ klass.unregister("nonse.unregister", MockFormatter)
207
+ end.to_not(change { klass.formatters })
208
+ end.to_not(change { klass.formatter_classes })
209
+ end
116
210
  end
117
211
  end
118
212
 
119
- context "calling formatters" do
120
- it "returns nil if there is no formatter registered" do
121
- expect(klass.format("nonsense", {})).to be_nil
213
+ describe ".format" do
214
+ context "when no formatter with the name is registered" do
215
+ it "returns nil" do
216
+ expect(klass.format("nonsense", {})).to be_nil
217
+ end
122
218
  end
123
219
 
124
- it "calls the formatter if it is registered and use a value set in the initializer" do
125
- expect(klass.format("mock", {})).to eq ["title", "some value"]
220
+ context "when a formatter with the name is registered" do
221
+ it "calls the formatter and use a value set in the initializer" do
222
+ klass.register "mock", MockFormatter
223
+ expect(klass.format("mock", {})).to eq ["title", "some value"]
224
+ end
126
225
  end
127
226
  end
128
227
  end