-
-
Notifications
You must be signed in to change notification settings - Fork 501
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
220 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec::Matchers.define :include_sentry_event do |event_message = "", **opts| | ||
match do |sentry_events| | ||
@expected_exception = expected_exception(**opts) | ||
@context = context(**opts) | ||
@tags = tags(**opts) | ||
|
||
@expected_event = expected_event(event_message) | ||
@matched_event = find_matched_event(event_message, sentry_events) | ||
|
||
return false unless @matched_event | ||
|
||
[verify_context(), verify_tags()].all? | ||
end | ||
|
||
chain :with_context do |context| | ||
@context = context | ||
end | ||
|
||
chain :with_tags do |tags| | ||
@tags = tags | ||
end | ||
|
||
failure_message do |sentry_events| | ||
info = ["Failed to find event matching:\n"] | ||
info << " message: #{@expected_event.message.inspect}" | ||
info << " exception: #{@expected_exception.inspect}" | ||
info << " context: #{@context.inspect}" | ||
info << " tags: #{@tags.inspect}" | ||
info << "\n" | ||
info << "Captured events:\n" | ||
info << dump_events(sentry_events) | ||
info.join("\n") | ||
end | ||
|
||
def expected_event(event_message) | ||
if @expected_exception | ||
Sentry.get_current_client.event_from_exception(@expected_exception) | ||
else | ||
Sentry.get_current_client.event_from_message(event_message) | ||
end | ||
end | ||
|
||
def expected_exception(**opts) | ||
opts[:exception].new(opts[:message]) if opts[:exception] | ||
end | ||
|
||
def context(**opts) | ||
opts.fetch(:context, @context || {}) | ||
end | ||
|
||
def tags(**opts) | ||
opts.fetch(:tags, @tags || {}) | ||
end | ||
|
||
def find_matched_event(event_message, sentry_events) | ||
@matched_event ||= sentry_events | ||
.find { |event| | ||
if @expected_exception | ||
# Is it OK that we only compare the first exception? | ||
event_exception = event.exception.values.first | ||
expected_event_exception = @expected_event.exception.values.first | ||
|
||
event_exception.type == expected_event_exception.type && event_exception.value == expected_event_exception.value | ||
else | ||
event.message == @expected_event.message | ||
end | ||
} | ||
end | ||
|
||
def dump_events(sentry_events) | ||
sentry_events.map(&Kernel.method(:Hash)).map do |hash| | ||
hash.select { |k, _| [:message, :contexts, :tags, :exception].include?(k) } | ||
end.map do |hash| | ||
JSON.pretty_generate(hash) | ||
end.join("\n\n") | ||
end | ||
|
||
def verify_context | ||
return true if @context.empty? | ||
|
||
@matched_event.contexts.any? { |key, value| value == @context[key] } | ||
end | ||
|
||
def verify_tags | ||
return true if @tags.empty? | ||
|
||
@tags.all? { |key, value| @matched_event.tags.include?(key) && @matched_event.tags[key] == value } | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
# frozen_string_literal: true | ||
|
||
require "spec_helper" | ||
require "sentry/rspec" | ||
|
||
RSpec.describe "Sentry RSpec Matchers" do | ||
include Sentry::TestHelper | ||
|
||
before do | ||
# simulate normal user setup | ||
Sentry.init do |config| | ||
config.dsn = 'https://[email protected]/5434472' | ||
config.enabled_environments = ["production"] | ||
config.environment = :test | ||
end | ||
|
||
setup_sentry_test | ||
end | ||
|
||
after do | ||
teardown_sentry_test | ||
end | ||
|
||
let(:exception) { StandardError.new("Gaah!") } | ||
|
||
describe "include_sentry_event" do | ||
it "matches events with the given message" do | ||
Sentry.capture_message("Ooops") | ||
|
||
expect(sentry_events).to include_sentry_event("Ooops") | ||
end | ||
|
||
it "does not match events with a different message" do | ||
Sentry.capture_message("Ooops") | ||
|
||
expect(sentry_events).not_to include_sentry_event("Different message") | ||
end | ||
|
||
it "matches events with exception" do | ||
Sentry.capture_exception(exception) | ||
|
||
expect(sentry_events).to include_sentry_event(exception: exception.class, message: exception.message) | ||
end | ||
|
||
it "does not match events with different exception" do | ||
exception = StandardError.new("Gaah!") | ||
|
||
Sentry.capture_exception(exception) | ||
|
||
expect(sentry_events).not_to include_sentry_event(exception: StandardError, message: "Oops!") | ||
end | ||
|
||
it "matches events with context" do | ||
Sentry.set_context("rails.error", { some: "stuff" }) | ||
Sentry.capture_message("Ooops") | ||
|
||
expect(sentry_events).to include_sentry_event("Ooops") | ||
.with_context("rails.error" => { some: "stuff" }) | ||
end | ||
|
||
it "does not match events with different context" do | ||
Sentry.set_context("rails.error", { some: "stuff" }) | ||
Sentry.capture_message("Ooops") | ||
|
||
expect(sentry_events).not_to include_sentry_event("Ooops") | ||
.with_context("rails.error" => { other: "data" }) | ||
end | ||
|
||
it "matches events with tags" do | ||
Sentry.set_tags(foo: "bar", baz: "qux") | ||
Sentry.capture_message("Ooops") | ||
|
||
expect(sentry_events).to include_sentry_event("Ooops") | ||
.with_tags({ foo: "bar", baz: "qux" }) | ||
end | ||
|
||
it "does not match events with missing tags" do | ||
Sentry.set_tags(foo: "bar") | ||
Sentry.capture_message("Ooops") | ||
|
||
expect(sentry_events).not_to include_sentry_event("Ooops") | ||
.with_tags({ foo: "bar", baz: "qux" }) | ||
end | ||
|
||
it "matches error events with tags and context" do | ||
Sentry.set_tags(foo: "bar", baz: "qux") | ||
Sentry.set_context("rails.error", { some: "stuff" }) | ||
|
||
Sentry.capture_exception(exception) | ||
|
||
expect(sentry_events).to include_sentry_event(exception: exception.class, message: exception.message) | ||
.with_tags({ foo: "bar", baz: "qux" }) | ||
.with_context("rails.error" => { some: "stuff" }) | ||
end | ||
|
||
it "matches error events with tags and context provided as arguments" do | ||
Sentry.set_tags(foo: "bar", baz: "qux") | ||
Sentry.set_context("rails.error", { some: "stuff" }) | ||
|
||
Sentry.capture_exception(exception) | ||
|
||
expect(sentry_events).to include_sentry_event( | ||
exception: exception.class, | ||
message: exception.message, | ||
tags: { foo: "bar", baz: "qux" }, | ||
context: { "rails.error" => { some: "stuff" } } | ||
) | ||
end | ||
|
||
it "produces a useful failure message" do | ||
Sentry.capture_message("Actual message") | ||
|
||
expect { | ||
expect(sentry_events).to include_sentry_event("Expected message") | ||
}.to raise_error(RSpec::Expectations::ExpectationNotMetError) do |error| | ||
expect(error.message).to include("Failed to find event matching:") | ||
expect(error.message).to include("message: \"Expected message\"") | ||
expect(error.message).to include("Captured events:") | ||
expect(error.message).to include("\"message\": \"Actual message\"") | ||
end | ||
end | ||
end | ||
end |