airbrake-ruby 3.1.0 → 3.2.0

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +197 -43
  3. data/lib/airbrake-ruby/config.rb +43 -11
  4. data/lib/airbrake-ruby/deploy_notifier.rb +47 -0
  5. data/lib/airbrake-ruby/filter_chain.rb +32 -50
  6. data/lib/airbrake-ruby/filters/git_repository_filter.rb +9 -1
  7. data/lib/airbrake-ruby/filters/sql_filter.rb +104 -0
  8. data/lib/airbrake-ruby/hash_keyable.rb +37 -0
  9. data/lib/airbrake-ruby/ignorable.rb +44 -0
  10. data/lib/airbrake-ruby/notice.rb +2 -22
  11. data/lib/airbrake-ruby/{notifier.rb → notice_notifier.rb} +66 -46
  12. data/lib/airbrake-ruby/performance_notifier.rb +161 -0
  13. data/lib/airbrake-ruby/stat.rb +56 -0
  14. data/lib/airbrake-ruby/tdigest.rb +393 -0
  15. data/lib/airbrake-ruby/time_truncate.rb +17 -0
  16. data/lib/airbrake-ruby/version.rb +1 -1
  17. data/spec/airbrake_spec.rb +57 -13
  18. data/spec/async_sender_spec.rb +0 -2
  19. data/spec/backtrace_spec.rb +0 -2
  20. data/spec/code_hunk_spec.rb +0 -2
  21. data/spec/config/validator_spec.rb +0 -2
  22. data/spec/config_spec.rb +16 -4
  23. data/spec/deploy_notifier_spec.rb +41 -0
  24. data/spec/file_cache.rb +0 -2
  25. data/spec/filter_chain_spec.rb +1 -7
  26. data/spec/filters/context_filter_spec.rb +0 -2
  27. data/spec/filters/dependency_filter_spec.rb +0 -2
  28. data/spec/filters/exception_attributes_filter_spec.rb +0 -2
  29. data/spec/filters/gem_root_filter_spec.rb +0 -2
  30. data/spec/filters/git_last_checkout_filter_spec.rb +0 -2
  31. data/spec/filters/git_repository_filter.rb +0 -2
  32. data/spec/filters/git_revision_filter_spec.rb +0 -2
  33. data/spec/filters/keys_blacklist_spec.rb +0 -2
  34. data/spec/filters/keys_whitelist_spec.rb +0 -2
  35. data/spec/filters/root_directory_filter_spec.rb +0 -2
  36. data/spec/filters/sql_filter_spec.rb +219 -0
  37. data/spec/filters/system_exit_filter_spec.rb +0 -2
  38. data/spec/filters/thread_filter_spec.rb +0 -2
  39. data/spec/ignorable_spec.rb +14 -0
  40. data/spec/nested_exception_spec.rb +0 -2
  41. data/spec/{notifier_spec.rb → notice_notifier_spec.rb} +24 -114
  42. data/spec/{notifier_spec → notice_notifier_spec}/options_spec.rb +40 -39
  43. data/spec/notice_spec.rb +2 -4
  44. data/spec/performance_notifier_spec.rb +287 -0
  45. data/spec/promise_spec.rb +0 -2
  46. data/spec/response_spec.rb +0 -2
  47. data/spec/stat_spec.rb +35 -0
  48. data/spec/sync_sender_spec.rb +0 -2
  49. data/spec/tdigest_spec.rb +230 -0
  50. data/spec/time_truncate_spec.rb +13 -0
  51. data/spec/truncator_spec.rb +0 -2
  52. metadata +34 -15
  53. data/lib/airbrake-ruby/route_sender.rb +0 -175
  54. data/spec/route_sender_spec.rb +0 -130
@@ -0,0 +1,17 @@
1
+ module Airbrake
2
+ # TimeTruncate contains methods for truncating time.
3
+ #
4
+ # @api private
5
+ # @since v3.2.0
6
+ module TimeTruncate
7
+ # Truncate +time+ to floor minute and turn it into an RFC3339 timestamp.
8
+ #
9
+ # @param [Time] time
10
+ # @return [String]
11
+ def self.utc_truncate_minutes(time)
12
+ tm = time.getutc
13
+
14
+ Time.utc(tm.year, tm.month, tm.day, tm.hour, tm.min).to_datetime.rfc3339
15
+ end
16
+ end
17
+ end
@@ -2,5 +2,5 @@
2
2
  # More information: http://semver.org/
3
3
  module Airbrake
4
4
  # @return [String] the library version
5
- AIRBRAKE_RUBY_VERSION = '3.1.0'.freeze
5
+ AIRBRAKE_RUBY_VERSION = '3.2.0'.freeze
6
6
  end
@@ -1,20 +1,30 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake do
4
2
  describe ".[]" do
5
3
  it "returns a NilNotifier" do
6
- expect(described_class[:test]).to be_an(Airbrake::NilNotifier)
4
+ expect(described_class[:test]).to be_an(Airbrake::NilNoticeNotifier)
5
+ end
6
+ end
7
+
8
+ describe ".notifiers" do
9
+ it "returns a Hash of notifiers" do
10
+ expect(described_class.notifiers).to eq(
11
+ notice: {}, performance: {}, deploy: {}
12
+ )
7
13
  end
8
14
  end
9
15
 
10
16
  let(:default_notifier) do
11
- described_class.instance_variable_get(:@notifiers)[:default]
17
+ described_class[:default]
12
18
  end
13
19
 
14
20
  describe ".configure" do
15
21
  let(:config_params) { { project_id: 1, project_key: 'abc' } }
16
22
 
17
- after { described_class.instance_variable_get(:@notifiers).clear }
23
+ after do
24
+ described_class.instance_variable_get(:@notice_notifiers).clear
25
+ described_class.instance_variable_get(:@performance_notifiers).clear
26
+ described_class.instance_variable_get(:@deploy_notifiers).clear
27
+ end
18
28
 
19
29
  it "yields the config" do
20
30
  expect do |b|
@@ -26,17 +36,17 @@ RSpec.describe Airbrake do
26
36
  end.to yield_with_args(Airbrake::Config)
27
37
  end
28
38
 
29
- context "when invoked with a notifier name" do
30
- it "sets notifier name to the provided name" do
39
+ context "when invoked with a notice notifier name" do
40
+ it "sets notice notifier name to the provided name" do
31
41
  described_class.configure(:test) { |c| c.merge(config_params) }
32
- expect(described_class[:test]).to be_an(Airbrake::Notifier)
42
+ expect(described_class[:test]).to be_an(Airbrake::NoticeNotifier)
33
43
  end
34
44
  end
35
45
 
36
46
  context "when invoked without a notifier name" do
37
47
  it "defaults to the :default notifier name" do
38
48
  described_class.configure { |c| c.merge(config_params) }
39
- expect(described_class[:default]).to be_an(Airbrake::Notifier)
49
+ expect(described_class[:default]).to be_an(Airbrake::NoticeNotifier)
40
50
  end
41
51
  end
42
52
 
@@ -50,6 +60,20 @@ RSpec.describe Airbrake do
50
60
  )
51
61
  end
52
62
  end
63
+
64
+ context "when user config doesn't contain a project id" do
65
+ it "raises error" do
66
+ expect { described_class.configure { |c| c.project_key = '1' } }.
67
+ to raise_error(Airbrake::Error, ':project_id is required')
68
+ end
69
+ end
70
+
71
+ context "when user config doesn't contain a project key" do
72
+ it "raises error" do
73
+ expect { described_class.configure { |c| c.project_id = 1 } }.
74
+ to raise_error(Airbrake::Error, ':project_key is required')
75
+ end
76
+ end
53
77
  end
54
78
 
55
79
  describe ".configured?" do
@@ -98,8 +122,10 @@ RSpec.describe Airbrake do
98
122
  end
99
123
 
100
124
  describe ".create_deploy" do
101
- it "forwards 'create_deploy' to the notifier" do
102
- expect(default_notifier).to receive(:create_deploy).with(foo: 'bar')
125
+ let(:default_notifier) { described_class.notifiers[:deploy][:default] }
126
+
127
+ it "calls 'notify' on the deploy notifier" do
128
+ expect(default_notifier).to receive(:notify).with(foo: 'bar')
103
129
  described_class.create_deploy(foo: 'bar')
104
130
  end
105
131
  end
@@ -112,7 +138,9 @@ RSpec.describe Airbrake do
112
138
  end
113
139
 
114
140
  describe ".notify_request" do
115
- it "forwards 'notify_request' to the notifier" do
141
+ let(:default_notifier) { described_class.notifiers[:performance][:default] }
142
+
143
+ it "calls 'notify' on the route notifier" do
116
144
  params = {
117
145
  method: 'GET',
118
146
  route: '/foo',
@@ -120,8 +148,24 @@ RSpec.describe Airbrake do
120
148
  start_time: Time.new(2018, 1, 1, 0, 20, 0, 0),
121
149
  end_time: Time.new(2018, 1, 1, 0, 19, 0, 0)
122
150
  }
123
- expect(default_notifier).to receive(:notify_request).with(params)
151
+ expect(default_notifier).to receive(:notify).with(Airbrake::Request.new(params))
124
152
  described_class.notify_request(params)
125
153
  end
126
154
  end
155
+
156
+ describe ".notify_query" do
157
+ let(:default_notifier) { described_class.notifiers[:performance][:default] }
158
+
159
+ it "calls 'notify' on the query notifier" do
160
+ params = {
161
+ method: 'GET',
162
+ route: '/foo',
163
+ query: 'SELECT * FROM foos',
164
+ start_time: Time.new(2018, 1, 1, 0, 20, 0, 0),
165
+ end_time: Time.new(2018, 1, 1, 0, 19, 0, 0)
166
+ }
167
+ expect(default_notifier).to receive(:notify).with(Airbrake::Query.new(params))
168
+ described_class.notify_query(params)
169
+ end
170
+ end
127
171
  end
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::AsyncSender do
4
2
  before do
5
3
  stub_request(:post, /.*/).to_return(status: 201, body: '{}')
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Backtrace do
4
2
  let(:config) do
5
3
  Airbrake::Config.new.tap { |c| c.logger = Logger.new('/dev/null') }
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::CodeHunk do
4
2
  let(:config) { Airbrake::Config.new }
5
3
 
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Config::Validator do
4
2
  subject { described_class.new(config) }
5
3
 
data/spec/config_spec.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Config do
4
2
  let(:config) { described_class.new }
5
3
 
@@ -79,12 +77,26 @@ RSpec.describe Airbrake::Config do
79
77
  expect(config.whitelist_keys).to be_empty
80
78
  end
81
79
 
82
- it "enables route stats by default" do
80
+ it "disables route stats by default (deprecated)" do
81
+ out = StringIO.new
82
+ config.logger = Logger.new(out)
83
83
  expect(config.route_stats).to be_falsey
84
+ expect(out.string).to match(/'route_stats'.+is deprecated/)
85
+ end
86
+
87
+ it "disables performance stats by default" do
88
+ expect(config.performance_stats).to be_falsey
84
89
  end
85
90
 
86
- it "sets the default route_stats_flush_period" do
91
+ it "sets the default route_stats_flush_period (deprecated)" do
92
+ out = StringIO.new
93
+ config.logger = Logger.new(out)
87
94
  expect(config.route_stats_flush_period).to eq(15)
95
+ expect(out.string).to match(/'route_stats_flush_period'.+is deprecated/)
96
+ end
97
+
98
+ it "sets the default performance_stats_flush_period" do
99
+ expect(config.performance_stats_flush_period).to eq(15)
88
100
  end
89
101
  end
90
102
  end
@@ -0,0 +1,41 @@
1
+ RSpec.describe Airbrake::DeployNotifier do
2
+ let(:user_params) { { project_id: 1, project_key: 'banana' } }
3
+ let(:params) { {} }
4
+ let(:config) { Airbrake::Config.new(user_params.merge(params)) }
5
+
6
+ subject { described_class.new(config) }
7
+
8
+ describe "#notify" do
9
+ it "returns a promise" do
10
+ stub_request(:post, 'https://api.airbrake.io/api/v4/projects/1/deploys').
11
+ to_return(status: 201, body: '{}')
12
+ expect(subject.notify({})).to be_an(Airbrake::Promise)
13
+ end
14
+
15
+ context "when environment is configured" do
16
+ let(:params) { { environment: 'fooenv' } }
17
+
18
+ it "prefers the passed environment to the config env" do
19
+ expect_any_instance_of(Airbrake::SyncSender).to receive(:send).with(
20
+ { environment: 'barenv' },
21
+ instance_of(Airbrake::Promise),
22
+ URI('https://api.airbrake.io/api/v4/projects/1/deploys')
23
+ )
24
+ subject.notify(environment: 'barenv')
25
+ end
26
+ end
27
+
28
+ context "when environment is not configured" do
29
+ let(:params) { { environment: 'fooenv' } }
30
+
31
+ it "sets the environment from the config" do
32
+ expect_any_instance_of(Airbrake::SyncSender).to receive(:send).with(
33
+ { environment: 'fooenv' },
34
+ instance_of(Airbrake::Promise),
35
+ URI('https://api.airbrake.io/api/v4/projects/1/deploys')
36
+ )
37
+ subject.notify({})
38
+ end
39
+ end
40
+ end
41
+ end
data/spec/file_cache.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::FileCache do
4
2
  after do
5
3
  %i[banana mango].each { |k| described_class.delete(k) }
@@ -1,12 +1,6 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::FilterChain do
4
- subject { described_class.new(config, {}) }
5
-
6
- let(:config) { Airbrake::Config.new }
7
-
8
2
  let(:notice) do
9
- Airbrake::Notice.new(config, AirbrakeTestError.new)
3
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
10
4
  end
11
5
 
12
6
  describe "#refine" do
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Filters::ContextFilter do
4
2
  let(:notice) do
5
3
  Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Filters::DependencyFilter do
4
2
  let(:notice) do
5
3
  Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Filters::ExceptionAttributesFilter do
4
2
  describe "#call" do
5
3
  let(:out) { StringIO.new }
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Filters::GemRootFilter do
4
2
  let(:notice) do
5
3
  Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Filters::GitLastCheckoutFilter do
4
2
  subject { described_class.new(Logger.new(STDOUT), '.') }
5
3
 
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Filters::GitRepositoryFilter do
4
2
  subject { described_class.new('.') }
5
3
 
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Filters::GitRevisionFilter do
4
2
  subject { described_class.new('root/dir') }
5
3
 
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Filters::KeysBlacklist do
4
2
  subject { described_class.new(Logger.new('/dev/null'), patterns) }
5
3
 
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Filters::KeysWhitelist do
4
2
  subject { described_class.new(Logger.new('/dev/null'), patterns) }
5
3
 
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Filters::RootDirectoryFilter do
4
2
  subject { described_class.new(root_directory) }
5
3
 
@@ -0,0 +1,219 @@
1
+ RSpec.describe Airbrake::Filters::SqlFilter do
2
+ shared_examples "query filtering" do |test|
3
+ test[:dialects].each do |dialect|
4
+ it "correctly filters SQL like `#{test[:input]}' (#{dialect} dialect)" do
5
+ filter = described_class.new(dialect)
6
+ q = OpenStruct.new(query: test[:input])
7
+ filter.call(q)
8
+ expect(q.query).to eq(test[:output])
9
+ end
10
+ end
11
+ end
12
+
13
+ ALL_DIALECTS = %i[mysql postgres sqlite cassandra oracle].freeze
14
+
15
+ # rubocop:disable Metrics/LineLength
16
+ [
17
+ {
18
+ input: 'SELECT * FROM things;',
19
+ output: 'SELECT * FROM things;',
20
+ dialects: ALL_DIALECTS
21
+ }, {
22
+ input: "SELECT `t001`.`c2` FROM `t001` WHERE `t001`.`c2` = 'value' AND c3=\"othervalue\" LIMIT ?",
23
+ output: "SELECT `t001`.`c2` FROM `t001` WHERE `t001`.`c2` = ? AND c3=? LIMIT ?",
24
+ dialects: %i[mysql]
25
+ }, {
26
+ input: "SELECT * FROM t WHERE foo=\"bar/*\" AND baz=\"whatever */qux\"",
27
+ output: "SELECT * FROM t WHERE foo=? AND baz=?",
28
+ dialects: %i[mysql]
29
+ }, {
30
+ input: "SELECT * FROM t WHERE foo='bar/*' AND baz='whatever */qux'",
31
+ output: "SELECT * FROM t WHERE foo=? AND baz=?",
32
+ dialects: ALL_DIALECTS
33
+ }, {
34
+ input: "SELECT \"t001\".\"c2\" FROM \"t001\" WHERE \"t001\".\"c2\" = 'value' AND c3=1234 LIMIT 1",
35
+ output: "SELECT \"t001\".\"c2\" FROM \"t001\" WHERE \"t001\".\"c2\" = ? AND c3=? LIMIT ?",
36
+ dialects: %i[postgres oracle]
37
+ }, {
38
+ input: "SELECT * FROM t WHERE foo=\"bar--\" AND\n baz=\"qux--\"",
39
+ output: "SELECT * FROM t WHERE foo=? AND\n baz=?",
40
+ dialects: %i[mysql]
41
+ }, {
42
+ input: "SELECT * FROM t WHERE foo='bar--' AND\n baz='qux--'",
43
+ output: "SELECT * FROM t WHERE foo=? AND\n baz=?",
44
+ dialects: ALL_DIALECTS
45
+ }, {
46
+ input: "SELECT * FROM foo WHERE bar='baz' /* Hide Me */",
47
+ output: "SELECT * FROM foo WHERE bar=? ?",
48
+ dialects: ALL_DIALECTS
49
+ }, {
50
+ input: "SELECT * FROM foobar WHERE password='hunter2'\n-- No peeking!",
51
+ output: "SELECT * FROM foobar WHERE password=?\n?",
52
+ dialects: ALL_DIALECTS
53
+ }, {
54
+ input: "SELECT foo, bar FROM baz WHERE password='hunter2' # Secret",
55
+ output: "SELECT foo, bar FROM baz WHERE password=? ?",
56
+ dialects: ALL_DIALECTS
57
+ }, {
58
+ input: "SELECT \"col1\", \"col2\" from \"table\" WHERE \"col3\"=E'foo\\'bar\\\\baz' AND country=e'foo\\'bar\\\\baz'",
59
+ output: "SELECT \"col1\", \"col2\" from \"table\" WHERE \"col3\"=E?",
60
+ dialects: %i[postgres]
61
+ }, {
62
+ input: "INSERT INTO `X` values(\"test\",0, 1 , 2, 'test')",
63
+ output: "INSERT INTO `X` values(?,?, ? , ?, ?)",
64
+ dialects: %i[mysql]
65
+ }, {
66
+ input: "SELECT c11.col1, c22.col2 FROM table c11, table c22 WHERE value='nothing'",
67
+ output: "SELECT c11.col1, c22.col2 FROM table c11, table c22 WHERE value=?",
68
+ dialects: ALL_DIALECTS
69
+ }, {
70
+ input: "INSERT INTO X VALUES(1, 23456, 123.456, 99+100)",
71
+ output: "INSERT INTO X VALUES(?, ?, ?, ?+?)",
72
+ dialects: ALL_DIALECTS
73
+ }, {
74
+ input: "SELECT * FROM table WHERE name=\"foo\" AND value=\"don't\"",
75
+ output: "SELECT * FROM table WHERE name=? AND value=?",
76
+ dialects: %i[mysql]
77
+ }, {
78
+ input: "SELECT * FROM table WHERE name='foo' AND value = 'bar'",
79
+ output: "SELECT * FROM table WHERE name=? AND value = ?",
80
+ dialects: ALL_DIALECTS
81
+ }, {
82
+ input: "SELECT * FROM table WHERE col='foo\\''bar'",
83
+ output: "SELECT * FROM table WHERE col=?",
84
+ dialects: ALL_DIALECTS
85
+ }, {
86
+ input: "SELECT * FROM table WHERE col1='foo\"bar' AND col2='what\"ever'",
87
+ output: "SELECT * FROM table WHERE col1=? AND col2=?",
88
+ dialects: ALL_DIALECTS
89
+ }, {
90
+ input: "select * from accounts where accounts.name != 'dude\n newline' order by accounts.name",
91
+ output: "select * from accounts where accounts.name != ? order by accounts.name",
92
+ dialects: ALL_DIALECTS
93
+ }, {
94
+ input: "SELECT * FROM table WHERE col1=\"don't\" AND col2=\"won't\"",
95
+ output: "SELECT * FROM table WHERE col1=? AND col2=?",
96
+ dialects: %i[mysql]
97
+ }, {
98
+ input: "INSERT INTO X values('', 'jim''s ssn',0, 1 , 'jim''s son''s son', \"\"\"jim''s\"\" hat\", \"\\\"jim''s secret\\\"\")",
99
+ output: "INSERT INTO X values(?, ?,?, ? , ?, ?, ?",
100
+ dialects: %i[mysql]
101
+ }, {
102
+ input: "SELECT * FROM table WHERE name='foo\\' AND color='blue'",
103
+ output: "SELECT * FROM table WHERE name=?",
104
+ dialects: ALL_DIALECTS
105
+ }, {
106
+ input: "SELECT * FROM table WHERE foo=\"this string ends with a backslash\\\\\"",
107
+ output: "SELECT * FROM table WHERE foo=?",
108
+ dialects: %i[mysql]
109
+ }, {
110
+ input: "SELECT * FROM table WHERE foo='this string ends with a backslash\\\\'",
111
+ output: "SELECT * FROM table WHERE foo=?",
112
+ dialects: ALL_DIALECTS
113
+ }, {
114
+ # TODO: fix this example.
115
+ input: "SELECT * FROM table WHERE name='foo\'' AND color='blue'",
116
+ output: "Error: Airbrake::Query was not filtered",
117
+ dialects: ALL_DIALECTS
118
+ }, {
119
+ input: "INSERT INTO X values('', 'a''b c',0, 1 , 'd''e f''s h')",
120
+ output: "INSERT INTO X values(?, ?,?, ? , ?)",
121
+ dialects: ALL_DIALECTS
122
+ }, {
123
+ input: "SELECT * FROM t WHERE -- '\n bar='baz' -- '",
124
+ output: "SELECT * FROM t WHERE ?\n bar=? ?",
125
+ dialects: ALL_DIALECTS
126
+ }, {
127
+ input: "SELECT * FROM t WHERE /* ' */\n bar='baz' -- '",
128
+ output: "SELECT * FROM t WHERE ?\n bar=? ?",
129
+ dialects: ALL_DIALECTS
130
+ }, {
131
+ input: "SELECT * FROM t WHERE -- '\n /* ' */ c2='xxx' /* ' */\n c='x\n xx' -- '",
132
+ output: "SELECT * FROM t WHERE ?\n ? c2=? ?\n c=? ?",
133
+ dialects: ALL_DIALECTS
134
+ }, {
135
+ input: "SELECT * FROM t WHERE -- '\n c='x\n xx' -- '",
136
+ output: "SELECT * FROM t WHERE ?\n c=? ?",
137
+ dialects: ALL_DIALECTS
138
+ }, {
139
+ input: "SELECT * FROM foo WHERE col='value1' AND /* don't */ col2='value1' /* won't */",
140
+ output: "SELECT * FROM foo WHERE col=? AND ? col2=? ?",
141
+ dialects: ALL_DIALECTS
142
+ }, {
143
+ input: "SELECT * FROM table WHERE foo='bar' AND baz=\"nothing to see here'",
144
+ output: "Error: Airbrake::Query was not filtered",
145
+ dialects: %i[mysql]
146
+ }, {
147
+ input: "SELECT * FROM table WHERE foo='bar' AND baz='nothing to see here",
148
+ output: "Error: Airbrake::Query was not filtered",
149
+ dialects: ALL_DIALECTS
150
+ }, {
151
+ input: "SELECT * FROM \"foo\" WHERE \"foo\" = $a$dollar quotes can be $b$nested$b$$a$ and bar = 'baz'",
152
+ output: "SELECT * FROM \"foo\" WHERE \"foo\" = ? and bar = ?",
153
+ dialects: %i[postgres]
154
+ }, {
155
+ input: "INSERT INTO \"foo\" (\"bar\", \"baz\", \"qux\") VALUES ($1, $2, $3) RETURNING \"id\"",
156
+ output: "INSERT INTO \"foo\" (\"bar\", \"baz\", \"qux\") VALUES ($?, $?, $?) RETURNING \"id\"",
157
+ dialects: %i[postgres]
158
+ }, {
159
+ input: "select * from foo where bar = 'some\\tthing' and baz = 10",
160
+ output: "select * from foo where bar = ? and baz = ?",
161
+ dialects: ALL_DIALECTS
162
+ }, {
163
+ input: "select * from users where user = 'user1\\' password = 'hunter 2' -- ->don't count this quote",
164
+ output: "select * from users where user = ?",
165
+ dialects: ALL_DIALECTS
166
+ }, {
167
+ input: "select * from foo where bar=q'[baz's]' and x=5",
168
+ output: "select * from foo where bar=? and x=?",
169
+ dialects: %i[oracle]
170
+ }, {
171
+ input: "select * from foo where bar=q'{baz's}' and x=5",
172
+ output: "select * from foo where bar=? and x=?",
173
+ dialects: %i[oracle]
174
+ }, {
175
+ input: "select * from foo where bar=q'<baz's>' and x=5",
176
+ output: "select * from foo where bar=? and x=?",
177
+ dialects: %i[oracle]
178
+ }, {
179
+ input: "select * from foo where bar=q'(baz's)' and x=5",
180
+ output: "select * from foo where bar=? and x=?",
181
+ dialects: %i[oracle]
182
+ }, {
183
+ input: "select * from foo where bar=0xabcdef123 and x=5",
184
+ output: "select * from foo where bar=? and x=?",
185
+ dialects: %i[cassandra sqlite]
186
+ }, {
187
+ input: "select * from foo where bar=0x2F and x=5",
188
+ output: "select * from foo where bar=? and x=?",
189
+ dialects: %i[mysql cassandra sqlite]
190
+ }, {
191
+ input: "select * from foo where bar=1.234e-5 and x=5",
192
+ output: "select * from foo where bar=? and x=?",
193
+ dialects: ALL_DIALECTS
194
+ }, {
195
+ input: "select * from foo where bar=01234567-89ab-cdef-0123-456789abcdef and x=5",
196
+ output: "select * from foo where bar=? and x=?",
197
+ dialects: %i[postgres cassandra]
198
+ }, {
199
+ input: "select * from foo where bar={01234567-89ab-cdef-0123-456789abcdef} and x=5",
200
+ output: "select * from foo where bar=? and x=?",
201
+ dialects: %i[postgres]
202
+ }, {
203
+ input: "select * from foo where bar=0123456789abcdef0123456789abcdef and x=5",
204
+ output: "select * from foo where bar=? and x=?",
205
+ dialects: %i[postgtes]
206
+ }, {
207
+ input: "select * from foo where bar={012-345678-9abc-def012345678-9abcdef} and x=5",
208
+ output: "select * from foo where bar=? and x=?",
209
+ dialects: %i[postgres]
210
+ }, {
211
+ input: "select * from foo where bar=true and x=FALSE",
212
+ output: "select * from foo where bar=? and x=?",
213
+ dialects: %i[mysql postgres cassandra sqlite]
214
+ }
215
+ ].each do |test|
216
+ include_examples 'query filtering', test
217
+ end
218
+ # rubocop:enable Metrics/LineLength
219
+ end