airbrake-ruby 3.2.2-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 (82) hide show
  1. checksums.yaml +7 -0
  2. data/lib/airbrake-ruby.rb +554 -0
  3. data/lib/airbrake-ruby/async_sender.rb +119 -0
  4. data/lib/airbrake-ruby/backtrace.rb +194 -0
  5. data/lib/airbrake-ruby/code_hunk.rb +53 -0
  6. data/lib/airbrake-ruby/config.rb +238 -0
  7. data/lib/airbrake-ruby/config/validator.rb +63 -0
  8. data/lib/airbrake-ruby/deploy_notifier.rb +47 -0
  9. data/lib/airbrake-ruby/file_cache.rb +48 -0
  10. data/lib/airbrake-ruby/filter_chain.rb +95 -0
  11. data/lib/airbrake-ruby/filters/context_filter.rb +29 -0
  12. data/lib/airbrake-ruby/filters/dependency_filter.rb +31 -0
  13. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +45 -0
  14. data/lib/airbrake-ruby/filters/gem_root_filter.rb +33 -0
  15. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +90 -0
  16. data/lib/airbrake-ruby/filters/git_repository_filter.rb +42 -0
  17. data/lib/airbrake-ruby/filters/git_revision_filter.rb +66 -0
  18. data/lib/airbrake-ruby/filters/keys_blacklist.rb +50 -0
  19. data/lib/airbrake-ruby/filters/keys_filter.rb +140 -0
  20. data/lib/airbrake-ruby/filters/keys_whitelist.rb +49 -0
  21. data/lib/airbrake-ruby/filters/root_directory_filter.rb +28 -0
  22. data/lib/airbrake-ruby/filters/sql_filter.rb +104 -0
  23. data/lib/airbrake-ruby/filters/system_exit_filter.rb +23 -0
  24. data/lib/airbrake-ruby/filters/thread_filter.rb +92 -0
  25. data/lib/airbrake-ruby/hash_keyable.rb +37 -0
  26. data/lib/airbrake-ruby/ignorable.rb +44 -0
  27. data/lib/airbrake-ruby/nested_exception.rb +39 -0
  28. data/lib/airbrake-ruby/notice.rb +165 -0
  29. data/lib/airbrake-ruby/notice_notifier.rb +228 -0
  30. data/lib/airbrake-ruby/performance_notifier.rb +161 -0
  31. data/lib/airbrake-ruby/promise.rb +99 -0
  32. data/lib/airbrake-ruby/response.rb +71 -0
  33. data/lib/airbrake-ruby/stat.rb +56 -0
  34. data/lib/airbrake-ruby/sync_sender.rb +111 -0
  35. data/lib/airbrake-ruby/tdigest.rb +393 -0
  36. data/lib/airbrake-ruby/time_truncate.rb +17 -0
  37. data/lib/airbrake-ruby/truncator.rb +115 -0
  38. data/lib/airbrake-ruby/version.rb +6 -0
  39. data/spec/airbrake_spec.rb +171 -0
  40. data/spec/async_sender_spec.rb +154 -0
  41. data/spec/backtrace_spec.rb +438 -0
  42. data/spec/code_hunk_spec.rb +118 -0
  43. data/spec/config/validator_spec.rb +189 -0
  44. data/spec/config_spec.rb +281 -0
  45. data/spec/deploy_notifier_spec.rb +41 -0
  46. data/spec/file_cache.rb +36 -0
  47. data/spec/filter_chain_spec.rb +83 -0
  48. data/spec/filters/context_filter_spec.rb +25 -0
  49. data/spec/filters/dependency_filter_spec.rb +14 -0
  50. data/spec/filters/exception_attributes_filter_spec.rb +63 -0
  51. data/spec/filters/gem_root_filter_spec.rb +44 -0
  52. data/spec/filters/git_last_checkout_filter_spec.rb +48 -0
  53. data/spec/filters/git_repository_filter.rb +53 -0
  54. data/spec/filters/git_revision_filter_spec.rb +126 -0
  55. data/spec/filters/keys_blacklist_spec.rb +236 -0
  56. data/spec/filters/keys_whitelist_spec.rb +205 -0
  57. data/spec/filters/root_directory_filter_spec.rb +42 -0
  58. data/spec/filters/sql_filter_spec.rb +219 -0
  59. data/spec/filters/system_exit_filter_spec.rb +14 -0
  60. data/spec/filters/thread_filter_spec.rb +279 -0
  61. data/spec/fixtures/notroot.txt +7 -0
  62. data/spec/fixtures/project_root/code.rb +221 -0
  63. data/spec/fixtures/project_root/empty_file.rb +0 -0
  64. data/spec/fixtures/project_root/long_line.txt +1 -0
  65. data/spec/fixtures/project_root/short_file.rb +3 -0
  66. data/spec/fixtures/project_root/vendor/bundle/ignored_file.rb +5 -0
  67. data/spec/helpers.rb +9 -0
  68. data/spec/ignorable_spec.rb +14 -0
  69. data/spec/nested_exception_spec.rb +75 -0
  70. data/spec/notice_notifier_spec.rb +436 -0
  71. data/spec/notice_notifier_spec/options_spec.rb +266 -0
  72. data/spec/notice_spec.rb +297 -0
  73. data/spec/performance_notifier_spec.rb +287 -0
  74. data/spec/promise_spec.rb +165 -0
  75. data/spec/response_spec.rb +82 -0
  76. data/spec/spec_helper.rb +102 -0
  77. data/spec/stat_spec.rb +35 -0
  78. data/spec/sync_sender_spec.rb +140 -0
  79. data/spec/tdigest_spec.rb +230 -0
  80. data/spec/time_truncate_spec.rb +13 -0
  81. data/spec/truncator_spec.rb +238 -0
  82. metadata +278 -0
@@ -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
@@ -0,0 +1,36 @@
1
+ RSpec.describe Airbrake::FileCache do
2
+ after do
3
+ %i[banana mango].each { |k| described_class.delete(k) }
4
+ expect(described_class).to be_empty
5
+ end
6
+
7
+ describe ".[]=" do
8
+ context "when cache limit isn't reached" do
9
+ before do
10
+ stub_const("#{described_class.name}::MAX_SIZE", 10)
11
+ end
12
+
13
+ it "adds objects" do
14
+ described_class[:banana] = 1
15
+ described_class[:mango] = 2
16
+
17
+ expect(described_class[:banana]).to eq(1)
18
+ expect(described_class[:mango]).to eq(2)
19
+ end
20
+ end
21
+
22
+ context "when cache limit is reached" do
23
+ before do
24
+ stub_const("#{described_class.name}::MAX_SIZE", 1)
25
+ end
26
+
27
+ it "replaces old objects with new ones" do
28
+ described_class[:banana] = 1
29
+ described_class[:mango] = 2
30
+
31
+ expect(described_class[:banana]).to be_nil
32
+ expect(described_class[:mango]).to eq(2)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,83 @@
1
+ RSpec.describe Airbrake::FilterChain do
2
+ let(:notice) do
3
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
4
+ end
5
+
6
+ describe "#refine" do
7
+ let(:filter) do
8
+ Class.new do
9
+ attr_reader :weight
10
+
11
+ def initialize(weight)
12
+ @weight = weight
13
+ end
14
+
15
+ def call(notice)
16
+ notice[:params][:bingo] << @weight
17
+ end
18
+ end
19
+ end
20
+
21
+ it "executes filters from heaviest to lightest" do
22
+ notice[:params][:bingo] = []
23
+
24
+ (0...3).reverse_each { |i| subject.add_filter(filter.new(i)) }
25
+ subject.refine(notice)
26
+
27
+ expect(notice[:params][:bingo]).to eq([2, 1, 0])
28
+ end
29
+
30
+ it "stops execution once a notice was ignored" do
31
+ f2 = filter.new(2)
32
+ expect(f2).to receive(:call)
33
+
34
+ f1 = proc { |notice| notice.ignore! }
35
+
36
+ f0 = filter.new(-1)
37
+ expect(f0).not_to receive(:call)
38
+
39
+ [f2, f1, f0].each { |f| subject.add_filter(f) }
40
+
41
+ subject.refine(notice)
42
+ end
43
+ end
44
+
45
+ describe "#delete_filter" do
46
+ let(:filter) do
47
+ Class.new do
48
+ class << self
49
+ def name
50
+ 'FooFilter'
51
+ end
52
+ end
53
+
54
+ def initialize(foo)
55
+ @foo = foo
56
+ end
57
+
58
+ def call(notice)
59
+ notice[:params][:foo] << @foo
60
+ end
61
+ end
62
+ end
63
+
64
+ it "deletes a class filter" do
65
+ notice[:params][:foo] = []
66
+
67
+ f1 = filter.new(1)
68
+ subject.add_filter(f1)
69
+
70
+ foo_filter_mock = double
71
+ expect(foo_filter_mock).to(
72
+ receive(:name).at_least(:once).and_return('FooFilter')
73
+ )
74
+ subject.delete_filter(foo_filter_mock)
75
+
76
+ f2 = filter.new(2)
77
+ subject.add_filter(f2)
78
+
79
+ subject.refine(notice)
80
+ expect(notice[:params][:foo]).to eq([2])
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,25 @@
1
+ RSpec.describe Airbrake::Filters::ContextFilter do
2
+ let(:notice) do
3
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
4
+ end
5
+
6
+ context "when the current context is empty" do
7
+ it "doesn't merge anything with params" do
8
+ described_class.new({}).call(notice)
9
+ expect(notice[:params]).to be_empty
10
+ end
11
+ end
12
+
13
+ context "when the current context has some data" do
14
+ it "merges the data with params" do
15
+ described_class.new(apples: 'oranges').call(notice)
16
+ expect(notice[:params]).to eq(airbrake_context: { apples: 'oranges' })
17
+ end
18
+
19
+ it "clears the data from the provided context" do
20
+ context = { apples: 'oranges' }
21
+ described_class.new(context).call(notice)
22
+ expect(context).to be_empty
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ RSpec.describe Airbrake::Filters::DependencyFilter do
2
+ let(:notice) do
3
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
4
+ end
5
+
6
+ describe "#call" do
7
+ it "attaches loaded dependencies to context/versions/dependencies" do
8
+ subject.call(notice)
9
+ expect(notice[:context][:versions][:dependencies]).to include(
10
+ 'airbrake-ruby' => Airbrake::AIRBRAKE_RUBY_VERSION
11
+ )
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,63 @@
1
+ RSpec.describe Airbrake::Filters::ExceptionAttributesFilter do
2
+ describe "#call" do
3
+ let(:out) { StringIO.new }
4
+ let(:notice) { Airbrake::Notice.new(Airbrake::Config.new, ex) }
5
+
6
+ subject { described_class.new(Logger.new(out)) }
7
+
8
+ context "when #to_airbrake returns a non-Hash object" do
9
+ let(:ex) do
10
+ Class.new(AirbrakeTestError) do
11
+ def to_airbrake
12
+ Object.new
13
+ end
14
+ end.new
15
+ end
16
+
17
+ it "doesn't raise" do
18
+ expect { subject.call(notice) }.not_to raise_error
19
+ expect(notice[:params]).to be_empty
20
+ end
21
+
22
+ it "logs the error" do
23
+ expect { subject.call(notice) }.not_to raise_error
24
+ expect(out.string).to match(/wanted Hash, got Object/)
25
+ end
26
+ end
27
+
28
+ context "when #to_airbrake errors out" do
29
+ let(:ex) do
30
+ Class.new(AirbrakeTestError) do
31
+ def to_airbrake
32
+ 1 / 0
33
+ end
34
+ end.new
35
+ end
36
+
37
+ it "doesn't raise" do
38
+ expect { subject.call(notice) }.not_to raise_error
39
+ expect(notice[:params]).to be_empty
40
+ end
41
+
42
+ it "logs the error" do
43
+ expect { subject.call(notice) }.not_to raise_error
44
+ expect(out.string).to match(/#to_airbrake failed.+ZeroDivisionError/)
45
+ end
46
+ end
47
+
48
+ context "when #to_airbrake returns a hash" do
49
+ let(:ex) do
50
+ Class.new(AirbrakeTestError) do
51
+ def to_airbrake
52
+ { params: { foo: '1' } }
53
+ end
54
+ end.new
55
+ end
56
+
57
+ it "merges parameters with the notice" do
58
+ subject.call(notice)
59
+ expect(notice[:params]).to eq(foo: '1')
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,44 @@
1
+ RSpec.describe Airbrake::Filters::GemRootFilter do
2
+ let(:notice) do
3
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
4
+ end
5
+
6
+ let(:root1) { '/my/gem/root' }
7
+ let(:root2) { '/my/other/gem/root' }
8
+
9
+ before { Gem.path << root1 << root2 }
10
+ after { 2.times { Gem.path.pop } }
11
+
12
+ it "replaces gem root in the backtrace with a label" do
13
+ # rubocop:disable Metrics/LineLength
14
+ notice[:errors].first[:backtrace] = [
15
+ { file: "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb" },
16
+ { file: "#{root1}/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb" },
17
+ { file: "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb" },
18
+ { file: "#{root2}/gems/rspec-core-3.3.2/exe/rspec" }
19
+ ]
20
+ # rubocop:enable Metrics/LineLength
21
+
22
+ subject.call(notice)
23
+
24
+ # rubocop:disable Metrics/LineLength
25
+ expect(notice[:errors].first[:backtrace]).to(
26
+ eq(
27
+ [
28
+ { file: "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb" },
29
+ { file: "/GEM_ROOT/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb" },
30
+ { file: "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb" },
31
+ { file: "/GEM_ROOT/gems/rspec-core-3.3.2/exe/rspec" }
32
+ ]
33
+ )
34
+ )
35
+ # rubocop:enable Metrics/LineLength
36
+ end
37
+
38
+ it "does not filter file when it is nil" do
39
+ expect(notice[:errors].first[:file]).to be_nil
40
+ expect { subject.call(notice) }.not_to(
41
+ change { notice[:errors].first[:file] }
42
+ )
43
+ end
44
+ end
@@ -0,0 +1,48 @@
1
+ RSpec.describe Airbrake::Filters::GitLastCheckoutFilter do
2
+ subject { described_class.new(Logger.new(STDOUT), '.') }
3
+
4
+ let(:notice) do
5
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
6
+ end
7
+
8
+ context "when context/lastCheckout is defined" do
9
+ it "doesn't attach anything to context/lastCheckout" do
10
+ notice[:context][:lastCheckout] = '123'
11
+ subject.call(notice)
12
+ expect(notice[:context][:lastCheckout]).to eq('123')
13
+ end
14
+ end
15
+
16
+ context "when .git directory doesn't exist" do
17
+ subject { described_class.new(Logger.new(STDOUT), 'root/dir') }
18
+
19
+ it "doesn't attach anything to context/lastCheckout" do
20
+ subject.call(notice)
21
+ expect(notice[:context][:lastCheckout]).to be_nil
22
+ end
23
+ end
24
+
25
+ context "when .git directory exists" do
26
+ before { subject.call(notice) }
27
+
28
+ it "attaches last checkouted username" do
29
+ expect(notice[:context][:lastCheckout][:username]).not_to be_empty
30
+ end
31
+
32
+ it "attaches last checkouted email" do
33
+ expect(notice[:context][:lastCheckout][:email]).to(
34
+ match(/\A\w+[\w.-]*@\w+\.?\w+?\z/)
35
+ )
36
+ end
37
+
38
+ it "attaches last checkouted revision" do
39
+ expect(notice[:context][:lastCheckout][:revision]).not_to be_empty
40
+ expect(notice[:context][:lastCheckout][:revision].size).to eq(40)
41
+ end
42
+
43
+ it "attaches last checkouted time" do
44
+ expect(notice[:context][:lastCheckout][:time]).not_to be_empty
45
+ expect(notice[:context][:lastCheckout][:time].size).to eq(25)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,53 @@
1
+ RSpec.describe Airbrake::Filters::GitRepositoryFilter do
2
+ subject { described_class.new('.') }
3
+
4
+ let(:notice) do
5
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
6
+ end
7
+
8
+ describe "#initialize" do
9
+ it "parses standard git version" do
10
+ allow_any_instance_of(Kernel).
11
+ to receive(:`).and_return('git version 2.18.0')
12
+ expect { subject }.not_to raise_error
13
+ end
14
+
15
+ it "parses release candidate git version" do
16
+ allow_any_instance_of(Kernel).
17
+ to receive(:`).and_return('git version 2.21.0-rc0')
18
+ expect { subject }.not_to raise_error
19
+ end
20
+
21
+ it "parses git version with brackets" do
22
+ allow_any_instance_of(Kernel).
23
+ to receive(:`).and_return('git version 2.17.2 (Apple Git-113)')
24
+ expect { subject }.not_to raise_error
25
+ end
26
+ end
27
+
28
+ context "when context/repository is defined" do
29
+ it "doesn't attach anything to context/repository" do
30
+ notice[:context][:repository] = 'git@github.com:kyrylo/test.git'
31
+ subject.call(notice)
32
+ expect(notice[:context][:repository]).to eq('git@github.com:kyrylo/test.git')
33
+ end
34
+ end
35
+
36
+ context "when .git directory doesn't exist" do
37
+ subject { described_class.new('root/dir') }
38
+
39
+ it "doesn't attach anything to context/repository" do
40
+ subject.call(notice)
41
+ expect(notice[:context][:repository]).to be_nil
42
+ end
43
+ end
44
+
45
+ context "when .git directory exists" do
46
+ it "attaches context/repository" do
47
+ subject.call(notice)
48
+ expect(notice[:context][:repository]).to eq(
49
+ 'ssh://git@github.com/airbrake/airbrake-ruby.git'
50
+ )
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,126 @@
1
+ RSpec.describe Airbrake::Filters::GitRevisionFilter do
2
+ subject { described_class.new('root/dir') }
3
+
4
+ let(:notice) do
5
+ Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
6
+ end
7
+
8
+ context "when context/revision is defined" do
9
+ it "doesn't attach anything to context/revision" do
10
+ notice[:context][:revision] = '1.2.3'
11
+ subject.call(notice)
12
+ expect(notice[:context][:revision]).to eq('1.2.3')
13
+ end
14
+ end
15
+
16
+ context "when .git directory doesn't exist" do
17
+ it "doesn't attach anything to context/revision" do
18
+ subject.call(notice)
19
+ expect(notice[:context][:revision]).to be_nil
20
+ end
21
+ end
22
+
23
+ context "when .git directory exists" do
24
+ before do
25
+ expect(File).to receive(:exist?).with('root/dir/.git').and_return(true)
26
+ end
27
+
28
+ context "and when HEAD doesn't exist" do
29
+ before do
30
+ expect(File).to receive(:exist?).with('root/dir/.git/HEAD').and_return(false)
31
+ end
32
+
33
+ it "doesn't attach anything to context/revision" do
34
+ subject.call(notice)
35
+ expect(notice[:context][:revision]).to be_nil
36
+ end
37
+ end
38
+
39
+ context "and when HEAD exists" do
40
+ before do
41
+ expect(File).to receive(:exist?).with('root/dir/.git/HEAD').and_return(true)
42
+ end
43
+
44
+ context "and also when HEAD doesn't start with 'ref: '" do
45
+ before do
46
+ expect(File).to(
47
+ receive(:read).with('root/dir/.git/HEAD').and_return('refs/foo')
48
+ )
49
+ end
50
+
51
+ it "attaches the content of HEAD to context/revision" do
52
+ subject.call(notice)
53
+ expect(notice[:context][:revision]).to eq('refs/foo')
54
+ end
55
+ end
56
+
57
+ context "and also when HEAD starts with 'ref: " do
58
+ before do
59
+ expect(File).to(
60
+ receive(:read).with('root/dir/.git/HEAD').and_return("ref: refs/foo\n")
61
+ )
62
+ end
63
+
64
+ context "when the ref exists" do
65
+ before do
66
+ expect(File).to(
67
+ receive(:exist?).with('root/dir/.git/refs/foo').and_return(true)
68
+ )
69
+ expect(File).to(
70
+ receive(:read).with('root/dir/.git/refs/foo').and_return("d34db33f\n")
71
+ )
72
+ end
73
+
74
+ it "attaches the revision from the ref to context/revision" do
75
+ subject.call(notice)
76
+ expect(notice[:context][:revision]).to eq('d34db33f')
77
+ end
78
+ end
79
+
80
+ context "when the ref doesn't exist" do
81
+ before do
82
+ expect(File).to(
83
+ receive(:exist?).with('root/dir/.git/refs/foo').and_return(false)
84
+ )
85
+ end
86
+
87
+ context "and when '.git/packed-refs' exists" do
88
+ before do
89
+ expect(File).to(
90
+ receive(:exist?).with('root/dir/.git/packed-refs').and_return(true)
91
+ )
92
+ expect(File).to(
93
+ receive(:readlines).with('root/dir/.git/packed-refs').and_return(
94
+ [
95
+ "# pack-refs with: peeled fully-peeled\n",
96
+ "ccb316eecff79c7528d1ad43e5fa165f7a44d52e refs/tags/v3.0.30\n",
97
+ "^d358900f73ee5bfd6ca3a592cf23ac6e82df83c1",
98
+ "d34db33f refs/foo\n"
99
+ ]
100
+ )
101
+ )
102
+ end
103
+
104
+ it "attaches the revision from 'packed-refs' to context/revision" do
105
+ subject.call(notice)
106
+ expect(notice[:context][:revision]).to eq('d34db33f')
107
+ end
108
+ end
109
+
110
+ context "and when '.git/packed-refs' doesn't exist" do
111
+ before do
112
+ expect(File).to(
113
+ receive(:exist?).with('root/dir/.git/packed-refs').and_return(false)
114
+ )
115
+ end
116
+
117
+ it "attaches the content of HEAD to context/revision" do
118
+ subject.call(notice)
119
+ expect(notice[:context][:revision]).to eq('refs/foo')
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end