diff --git a/spec/lib/appsignal/demo_spec.rb b/spec/lib/appsignal/demo_spec.rb
index 3519fb2fd..dea49761d 100644
--- a/spec/lib/appsignal/demo_spec.rb
+++ b/spec/lib/appsignal/demo_spec.rb
@@ -32,56 +32,53 @@
end
describe ".create_example_error_request" do
- let!(:error_transaction) { http_request_transaction }
- let(:config) { project_fixture_config("production") }
- before do
- Appsignal.config = config
- expect(Appsignal::Transaction).to receive(:new).with(
- kind_of(String),
- Appsignal::Transaction::HTTP_REQUEST,
- kind_of(::Rack::Request),
- kind_of(Hash)
- ).and_return(error_transaction)
- end
- subject { described_class.send(:create_example_error_request) }
+ before { start_agent }
+ around { |example| keep_transactions { example.run } }
it "sets an error" do
- expect(error_transaction).to receive(:set_error).with(kind_of(described_class::TestError))
- expect(error_transaction).to receive(:set_metadata).with("path", "/hello")
- expect(error_transaction).to receive(:set_metadata).with("method", "GET")
- expect(error_transaction).to receive(:set_metadata).with("demo_sample", "true")
- expect(error_transaction).to receive(:complete)
- subject
+ described_class.send(:create_example_error_request)
+
+ transaction = last_transaction
+ expect(transaction).to have_id
+ expect(transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
+ expect(transaction).to have_action("DemoController#hello")
+ expect(transaction).to have_error(
+ "Appsignal::Demo::TestError",
+ "Hello world! This is an error used for demonstration purposes."
+ )
+ expect(transaction).to include_metadata(
+ "path" => "/hello",
+ "method" => "GET",
+ "demo_sample" => "true"
+ )
+ expect(transaction).to be_completed
end
end
describe ".create_example_performance_request" do
- let!(:performance_transaction) { http_request_transaction }
- let(:config) { project_fixture_config("production") }
- before do
- Appsignal.config = config
- expect(Appsignal::Transaction).to receive(:new).with(
- kind_of(String),
- Appsignal::Transaction::HTTP_REQUEST,
- kind_of(::Rack::Request),
- kind_of(Hash)
- ).and_return(performance_transaction)
- end
- subject { described_class.send(:create_example_performance_request) }
+ before { start_agent }
+ around { |example| keep_transactions { example.run } }
it "sends a performance sample" do
- expect(performance_transaction).to receive(:start_event)
- expect(performance_transaction).to receive(:finish_event).with(
- "action_view.render",
- "Render hello.html.erb",
- "
Hello world!
",
- Appsignal::EventFormatter::DEFAULT
+ described_class.send(:create_example_performance_request)
+
+ transaction = last_transaction
+ expect(transaction).to have_id
+ expect(transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
+ expect(transaction).to have_action("DemoController#hello")
+ expect(transaction).to_not have_error
+ expect(transaction).to include_metadata(
+ "path" => "/hello",
+ "method" => "GET",
+ "demo_sample" => "true"
+ )
+ expect(transaction).to include_event(
+ "name" => "action_view.render",
+ "title" => "Render hello.html.erb",
+ "body" => "Hello world!
",
+ "body_format" => Appsignal::EventFormatter::DEFAULT
)
- expect(performance_transaction).to receive(:set_metadata).with("path", "/hello")
- expect(performance_transaction).to receive(:set_metadata).with("method", "GET")
- expect(performance_transaction).to receive(:set_metadata).with("demo_sample", "true")
- expect(performance_transaction).to receive(:complete)
- subject
+ expect(transaction).to be_completed
end
end
end
diff --git a/spec/lib/appsignal/hooks/action_cable_spec.rb b/spec/lib/appsignal/hooks/action_cable_spec.rb
index 17f3f09cf..a016430e5 100644
--- a/spec/lib/appsignal/hooks/action_cable_spec.rb
+++ b/spec/lib/appsignal/hooks/action_cable_spec.rb
@@ -46,12 +46,13 @@ def self.to_s
http_request_env_with_data("action_dispatch.request_id" => request_id, :params => params)
end
let(:instance) { channel.new(connection, identifier, params) }
- subject { transaction.to_h }
before do
start_agent
expect(Appsignal.active?).to be_truthy
transaction
+ set_current_transaction(transaction)
+
expect(Appsignal::Transaction).to receive(:create).with(
transaction_id,
Appsignal::Transaction::ACTION_CABLE,
@@ -70,35 +71,25 @@ def self.to_s
it "creates a transaction for an action" do
instance.perform_action("message" => "foo", "action" => "speak")
- expect(subject).to include(
- "action" => "MyChannel#speak",
- "error" => nil,
- "id" => transaction_id,
- "namespace" => Appsignal::Transaction::ACTION_CABLE,
- "metadata" => {
- "method" => "websocket",
- "path" => "/blog"
- }
+ transaction = last_transaction
+ expect(transaction).to have_id(transaction_id)
+ expect(transaction).to have_namespace(Appsignal::Transaction::ACTION_CABLE)
+ expect(transaction).to have_action("MyChannel#speak")
+ expect(transaction).to_not have_error
+ expect(transaction).to include_metadata(
+ "method" => "websocket",
+ "path" => "/blog"
)
- expect(subject["events"].first).to include(
- "allocation_count" => kind_of(Integer),
+ expect(transaction).to include_event(
"body" => "",
"body_format" => Appsignal::EventFormatter::DEFAULT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
"count" => 1,
- "gc_duration" => kind_of(Float),
- "start" => kind_of(Float),
- "duration" => kind_of(Float),
"name" => "perform_action.action_cable",
"title" => ""
)
- expect(subject["sample_data"]).to include(
- "params" => {
- "action" => "speak",
- "message" => "foo"
- }
+ expect(transaction).to include_params(
+ "action" => "speak",
+ "message" => "foo"
)
end
@@ -120,7 +111,7 @@ def self.to_s
it "uses its own internal request_id set by the subscribed callback" do
# Subscribe action, sets the request_id
instance.subscribe_to_channel
- expect(transaction.to_h["id"]).to eq(transaction_id)
+ expect(transaction).to have_id(transaction_id)
# Expect another transaction for the action.
# This transaction will use the same request_id as the
@@ -136,7 +127,7 @@ def self.to_s
expect(action_transaction.ext).to receive(:complete)
instance.perform_action("message" => "foo", "action" => "speak")
- expect(action_transaction.to_h["id"]).to eq(transaction_id)
+ expect(action_transaction).to have_id(transaction_id)
end
end
@@ -158,25 +149,18 @@ def self.to_s
instance.perform_action("message" => "foo", "action" => "speak")
end.to raise_error(ExampleException)
- expect(subject).to include(
- "action" => "MyChannel#speak",
- "id" => transaction_id,
- "namespace" => Appsignal::Transaction::ACTION_CABLE,
- "metadata" => {
- "method" => "websocket",
- "path" => "/blog"
- }
- )
- expect(subject["error"]).to include(
- "backtrace" => kind_of(String),
- "name" => "ExampleException",
- "message" => "oh no!"
+ transaction = last_transaction
+ expect(transaction).to have_id(transaction_id)
+ expect(transaction).to have_action("MyChannel#speak")
+ expect(transaction).to have_namespace(Appsignal::Transaction::ACTION_CABLE)
+ expect(transaction).to have_error("ExampleException", "oh no!")
+ expect(transaction).to include_metadata(
+ "method" => "websocket",
+ "path" => "/blog"
)
- expect(subject["sample_data"]).to include(
- "params" => {
- "action" => "speak",
- "message" => "foo"
- }
+ expect(transaction).to include_params(
+ "action" => "speak",
+ "message" => "foo"
)
end
end
@@ -188,33 +172,23 @@ def self.to_s
it "creates a transaction for a subscription" do
instance.subscribe_to_channel
- expect(subject).to include(
- "action" => "MyChannel#subscribed",
- "error" => nil,
- "id" => transaction_id,
- "namespace" => Appsignal::Transaction::ACTION_CABLE,
- "metadata" => {
- "method" => "websocket",
- "path" => "/blog"
- }
+ transaction = last_transaction
+ expect(transaction).to have_id(transaction_id)
+ expect(transaction).to have_action("MyChannel#subscribed")
+ expect(transaction).to have_namespace(Appsignal::Transaction::ACTION_CABLE)
+ expect(transaction).to_not have_error
+ expect(transaction).to include_metadata(
+ "method" => "websocket",
+ "path" => "/blog"
)
- expect(subject["events"].first).to include(
- "allocation_count" => kind_of(Integer),
+ expect(transaction).to include_params("internal" => "true")
+ expect(transaction).to include_event(
"body" => "",
"body_format" => Appsignal::EventFormatter::DEFAULT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
"count" => 1,
- "gc_duration" => kind_of(Float),
- "start" => kind_of(Float),
- "duration" => kind_of(Float),
"name" => "subscribed.action_cable",
"title" => ""
)
- expect(subject["sample_data"]).to include(
- "params" => { "internal" => "true" }
- )
end
context "without request_id (standalone server)" do
@@ -226,7 +200,7 @@ def self.to_s
end
it "uses its own internal request_id" do
- expect(subject["id"]).to eq(transaction_id)
+ expect(last_transaction).to have_id(transaction_id)
end
end
@@ -248,23 +222,16 @@ def self.to_s
instance.subscribe_to_channel
end.to raise_error(ExampleException)
- expect(subject).to include(
- "action" => "MyChannel#subscribed",
- "id" => transaction_id,
- "namespace" => Appsignal::Transaction::ACTION_CABLE,
- "metadata" => {
- "method" => "websocket",
- "path" => "/blog"
- }
- )
- expect(subject["error"]).to include(
- "backtrace" => kind_of(String),
- "name" => "ExampleException",
- "message" => "oh no!"
- )
- expect(subject["sample_data"]).to include(
- "params" => { "internal" => "true" }
+ transaction = last_transaction
+ expect(transaction).to have_id(transaction_id)
+ expect(transaction).to have_action("MyChannel#subscribed")
+ expect(transaction).to have_namespace(Appsignal::Transaction::ACTION_CABLE)
+ expect(transaction).to have_error("ExampleException", "oh no!")
+ expect(transaction).to include_metadata(
+ "method" => "websocket",
+ "path" => "/blog"
)
+ expect(transaction).to include_params("internal" => "true")
end
end
@@ -280,33 +247,23 @@ def self.to_s
it "does not fail on missing `#env` method on `ConnectionStub`" do
instance.subscribe_to_channel
- expect(subject).to include(
- "action" => "MyChannel#subscribed",
- "error" => nil,
- "id" => transaction_id,
- "namespace" => Appsignal::Transaction::ACTION_CABLE,
- "metadata" => {
- "method" => "websocket",
- "path" => "" # No path as the ConnectionStub doesn't have the real request env
- }
+ transaction = last_transaction
+ expect(transaction).to have_id(transaction_id)
+ expect(transaction).to have_action("MyChannel#subscribed")
+ expect(transaction).to have_namespace(Appsignal::Transaction::ACTION_CABLE)
+ expect(transaction).to_not have_error
+ expect(transaction).to include_metadata(
+ "method" => "websocket",
+ "path" => "" # No path as the ConnectionStub doesn't have the real request env
)
- expect(subject["events"].first).to include(
- "allocation_count" => kind_of(Integer),
+ expect(transaction).to include_params("internal" => "true")
+ expect(transaction).to include_event(
"body" => "",
"body_format" => Appsignal::EventFormatter::DEFAULT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
"count" => 1,
- "gc_duration" => kind_of(Float),
- "start" => kind_of(Float),
- "duration" => kind_of(Float),
"name" => "subscribed.action_cable",
"title" => ""
)
- expect(subject["sample_data"]).to include(
- "params" => { "internal" => "true" }
- )
end
end
end
@@ -318,33 +275,23 @@ def self.to_s
it "creates a transaction for a subscription" do
instance.unsubscribe_from_channel
- expect(subject).to include(
- "action" => "MyChannel#unsubscribed",
- "error" => nil,
- "id" => transaction_id,
- "namespace" => Appsignal::Transaction::ACTION_CABLE,
- "metadata" => {
- "method" => "websocket",
- "path" => "/blog"
- }
+ transaction = last_transaction
+ expect(transaction).to have_id(transaction_id)
+ expect(transaction).to have_action("MyChannel#unsubscribed")
+ expect(transaction).to have_namespace(Appsignal::Transaction::ACTION_CABLE)
+ expect(transaction).to_not have_error
+ expect(transaction).to include_metadata(
+ "method" => "websocket",
+ "path" => "/blog"
)
- expect(subject["events"].first).to include(
- "allocation_count" => kind_of(Integer),
+ expect(transaction).to include_params("internal" => "true")
+ expect(transaction).to include_event(
"body" => "",
"body_format" => Appsignal::EventFormatter::DEFAULT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
"count" => 1,
- "gc_duration" => kind_of(Float),
- "start" => kind_of(Float),
- "duration" => kind_of(Float),
"name" => "unsubscribed.action_cable",
"title" => ""
)
- expect(subject["sample_data"]).to include(
- "params" => { "internal" => "true" }
- )
end
context "without request_id (standalone server)" do
@@ -356,7 +303,7 @@ def self.to_s
end
it "uses its own internal request_id" do
- expect(subject["id"]).to eq(transaction_id)
+ expect(transaction).to have_id(transaction_id)
end
end
@@ -378,23 +325,16 @@ def self.to_s
instance.unsubscribe_from_channel
end.to raise_error(ExampleException)
- expect(subject).to include(
- "action" => "MyChannel#unsubscribed",
- "id" => transaction_id,
- "namespace" => Appsignal::Transaction::ACTION_CABLE,
- "metadata" => {
- "method" => "websocket",
- "path" => "/blog"
- }
- )
- expect(subject["error"]).to include(
- "backtrace" => kind_of(String),
- "name" => "ExampleException",
- "message" => "oh no!"
- )
- expect(subject["sample_data"]).to include(
- "params" => { "internal" => "true" }
+ transaction = last_transaction
+ expect(transaction).to have_id(transaction_id)
+ expect(transaction).to have_action("MyChannel#unsubscribed")
+ expect(transaction).to have_namespace(Appsignal::Transaction::ACTION_CABLE)
+ expect(transaction).to have_error("ExampleException", "oh no!")
+ expect(transaction).to include_metadata(
+ "method" => "websocket",
+ "path" => "/blog"
)
+ expect(transaction).to include_params("internal" => "true")
end
end
@@ -410,33 +350,23 @@ def self.to_s
it "does not fail on missing `#env` method on `ConnectionStub`" do
instance.unsubscribe_from_channel
- expect(subject).to include(
- "action" => "MyChannel#unsubscribed",
- "error" => nil,
- "id" => transaction_id,
- "namespace" => Appsignal::Transaction::ACTION_CABLE,
- "metadata" => {
- "method" => "websocket",
- "path" => "" # No path as the ConnectionStub doesn't have the real request env
- }
+ transaction = last_transaction
+ expect(transaction).to have_id(transaction_id)
+ expect(transaction).to have_action("MyChannel#unsubscribed")
+ expect(transaction).to have_namespace(Appsignal::Transaction::ACTION_CABLE)
+ expect(transaction).to_not have_error
+ expect(transaction).to include_metadata(
+ "method" => "websocket",
+ "path" => "" # No path as the ConnectionStub doesn't have the real request env
)
- expect(subject["events"].first).to include(
- "allocation_count" => kind_of(Integer),
+ expect(transaction).to include_params("internal" => "true")
+ expect(transaction).to include_event(
"body" => "",
"body_format" => Appsignal::EventFormatter::DEFAULT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
"count" => 1,
- "gc_duration" => kind_of(Float),
- "start" => kind_of(Float),
- "duration" => kind_of(Float),
"name" => "unsubscribed.action_cable",
"title" => ""
)
- expect(subject["sample_data"]).to include(
- "params" => { "internal" => "true" }
- )
end
end
end
diff --git a/spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb b/spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb
index 0f3ec27fc..3e22ee406 100644
--- a/spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb
+++ b/spec/lib/appsignal/hooks/active_support_notifications/finish_with_state_shared_examples.rb
@@ -5,22 +5,13 @@
listeners_state = instrumenter.start("sql.active_record", {})
instrumenter.finish_with_state(listeners_state, "sql.active_record", :sql => "SQL")
- expect(transaction.to_h["events"]).to match([
- {
- "allocation_count" => kind_of(Integer),
- "body" => "SQL",
- "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
- "count" => 1,
- "duration" => kind_of(Float),
- "gc_duration" => kind_of(Float),
- "name" => "sql.active_record",
- "start" => kind_of(Float),
- "title" => ""
- }
- ])
+ expect(transaction).to include_event(
+ "body" => "SQL",
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
+ "count" => 1,
+ "name" => "sql.active_record",
+ "title" => ""
+ )
end
it "does not instrument events whose name starts with a bang" do
@@ -30,6 +21,6 @@
listeners_state = instrumenter.start("!sql.active_record", {})
instrumenter.finish_with_state(listeners_state, "!sql.active_record", :sql => "SQL")
- expect(transaction.to_h["events"]).to be_empty
+ expect(transaction).to_not include_events
end
end
diff --git a/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb b/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb
index 93ff16123..065e82531 100644
--- a/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb
+++ b/spec/lib/appsignal/hooks/active_support_notifications/instrument_shared_examples.rb
@@ -5,22 +5,13 @@
end
expect(return_value).to eq "value"
- expect(transaction.to_h["events"]).to match([
- {
- "allocation_count" => kind_of(Integer),
- "body" => "SQL",
- "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
- "count" => 1,
- "duration" => kind_of(Float),
- "gc_duration" => kind_of(Float),
- "name" => "sql.active_record",
- "start" => kind_of(Float),
- "title" => ""
- }
- ])
+ expect(transaction).to include_event(
+ "body" => "SQL",
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
+ "count" => 1,
+ "name" => "sql.active_record",
+ "title" => ""
+ )
end
it "instruments an ActiveSupport::Notifications.instrument event with no registered formatter" do
@@ -29,53 +20,34 @@
end
expect(return_value).to eq "value"
- expect(transaction.to_h["events"]).to match([
- {
- "allocation_count" => kind_of(Integer),
- "body" => "",
- "body_format" => Appsignal::EventFormatter::DEFAULT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
- "count" => 1,
- "duration" => kind_of(Float),
- "gc_duration" => kind_of(Float),
- "name" => "no-registered.formatter",
- "start" => kind_of(Float),
- "title" => ""
- }
- ])
+ expect(transaction).to include_event(
+ "body" => "",
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
+ "count" => 1,
+ "name" => "no-registered.formatter",
+ "title" => ""
+ )
end
it "converts non-string names to strings" do
as.instrument(:not_a_string) {} # rubocop:disable Lint/EmptyBlock
- expect(transaction.to_h["events"]).to match([
- {
- "allocation_count" => kind_of(Integer),
- "body" => "",
- "body_format" => Appsignal::EventFormatter::DEFAULT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
- "count" => 1,
- "duration" => kind_of(Float),
- "gc_duration" => kind_of(Float),
- "name" => "not_a_string",
- "start" => kind_of(Float),
- "title" => ""
- }
- ])
+ expect(transaction).to include_event(
+ "body" => "",
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
+ "count" => 1,
+ "name" => "not_a_string",
+ "title" => ""
+ )
end
it "does not instrument events whose name starts with a bang" do
- expect(Appsignal::Transaction.current).not_to receive(:start_event)
- expect(Appsignal::Transaction.current).not_to receive(:finish_event)
-
return_value = as.instrument("!sql.active_record", :sql => "SQL") do
"value"
end
expect(return_value).to eq "value"
+
+ expect(transaction).to_not include_events
end
context "when an error is raised in an instrumented block" do
@@ -86,22 +58,13 @@
end
end.to raise_error(ExampleException, "foo")
- expect(transaction.to_h["events"]).to match([
- {
- "allocation_count" => kind_of(Integer),
- "body" => "SQL",
- "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
- "count" => 1,
- "duration" => kind_of(Float),
- "gc_duration" => kind_of(Float),
- "name" => "sql.active_record",
- "start" => kind_of(Float),
- "title" => ""
- }
- ])
+ expect(transaction).to include_event(
+ "body" => "SQL",
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
+ "count" => 1,
+ "name" => "sql.active_record",
+ "title" => ""
+ )
end
end
@@ -113,22 +76,13 @@
end
end.to throw_symbol(:foo)
- expect(transaction.to_h["events"]).to match([
- {
- "allocation_count" => kind_of(Integer),
- "body" => "SQL",
- "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
- "count" => 1,
- "duration" => kind_of(Float),
- "gc_duration" => kind_of(Float),
- "name" => "sql.active_record",
- "start" => kind_of(Float),
- "title" => ""
- }
- ])
+ expect(transaction).to include_event(
+ "body" => "SQL",
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
+ "count" => 1,
+ "name" => "sql.active_record",
+ "title" => ""
+ )
end
end
@@ -139,7 +93,7 @@
Appsignal::Transaction.complete_current!
end
- expect(transaction.to_h["events"]).to match([])
+ expect(transaction).to_not include_events
end
end
end
diff --git a/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb b/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb
index 5289fe6f3..3ed1f7db4 100644
--- a/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb
+++ b/spec/lib/appsignal/hooks/active_support_notifications/start_finish_shared_examples.rb
@@ -5,44 +5,26 @@
instrumenter.start("sql.active_record", :sql => "SQL")
instrumenter.finish("sql.active_record", {})
- expect(transaction.to_h["events"]).to match([
- {
- "allocation_count" => kind_of(Integer),
- "body" => "",
- "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
- "count" => 1,
- "duration" => kind_of(Float),
- "gc_duration" => kind_of(Float),
- "name" => "sql.active_record",
- "start" => kind_of(Float),
- "title" => ""
- }
- ])
+ expect(transaction).to include_event(
+ "body" => "",
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
+ "count" => 1,
+ "name" => "sql.active_record",
+ "title" => ""
+ )
end
it "instruments an ActiveSupport::Notifications.start/finish event with payload on finish" do
instrumenter.start("sql.active_record", {})
instrumenter.finish("sql.active_record", :sql => "SQL")
- expect(transaction.to_h["events"]).to match([
- {
- "allocation_count" => kind_of(Integer),
- "body" => "SQL",
- "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
- "count" => 1,
- "duration" => kind_of(Float),
- "gc_duration" => kind_of(Float),
- "name" => "sql.active_record",
- "start" => kind_of(Float),
- "title" => ""
- }
- ])
+ expect(transaction).to include_event(
+ "body" => "SQL",
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
+ "count" => 1,
+ "name" => "sql.active_record",
+ "title" => ""
+ )
end
it "does not instrument events whose name starts with a bang" do
@@ -52,7 +34,7 @@
instrumenter.start("!sql.active_record", {})
instrumenter.finish("!sql.active_record", {})
- expect(transaction.to_h["events"]).to be_empty
+ expect(transaction).to_not include_events
end
context "when a transaction is completed in an instrumented block" do
@@ -63,7 +45,7 @@
Appsignal::Transaction.complete_current!
instrumenter.finish("sql.active_record", {})
- expect(transaction.to_h["events"]).to match([])
+ expect(transaction).to_not include_events
end
end
end
diff --git a/spec/lib/appsignal/hooks/activejob_spec.rb b/spec/lib/appsignal/hooks/activejob_spec.rb
index 8755068fa..d80bca0e8 100644
--- a/spec/lib/appsignal/hooks/activejob_spec.rb
+++ b/spec/lib/appsignal/hooks/activejob_spec.rb
@@ -119,25 +119,19 @@ def perform(*_args)
queue_job(ActiveJobTestJob)
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "ActiveJobTestJob#perform",
- "error" => nil,
- "namespace" => namespace,
- "metadata" => {},
- "sample_data" => hash_including(
- "params" => [],
- "tags" => {
- "active_job_id" => kind_of(String),
- "queue" => queue,
- "executions" => 1
- }
- )
+ expect(transaction).to have_namespace(namespace)
+ expect(transaction).to have_action("ActiveJobTestJob#perform")
+ expect(transaction).to_not have_error
+ expect(transaction).to_not include_metadata
+ expect(transaction).to include_params([])
+ expect(transaction).to include_tags(
+ "active_job_id" => kind_of(String),
+ "queue" => queue,
+ "executions" => 1
)
- events = transaction_hash["events"]
+ events = transaction.to_h["events"]
.sort_by { |e| e["start"] }
.map { |event| event["name"] }
-
expect(events).to eq(expected_perform_events)
end
@@ -148,13 +142,7 @@ def perform(*_args)
.with("active_job_queue_job_count", 1, tags.merge(:status => :processed))
queue_job(ActiveJobCustomQueueTestJob)
- transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "sample_data" => hash_including(
- "tags" => hash_including("queue" => "custom_queue")
- )
- )
+ expect(last_transaction).to include_tags("queue" => "custom_queue")
end
end
@@ -182,13 +170,7 @@ def perform(*_args)
queue_job(ActiveJobPriorityTestJob)
- transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "sample_data" => hash_including(
- "tags" => hash_including("queue" => queue, "priority" => 10)
- )
- )
+ expect(last_transaction).to include_tags("queue" => queue, "priority" => 10)
end
end
end
@@ -207,29 +189,20 @@ def perform(*_args)
end.to raise_error(RuntimeError, "uh oh")
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "ActiveJobErrorTestJob#perform",
- "error" => {
- "name" => "RuntimeError",
- "message" => "uh oh",
- "backtrace" => kind_of(String)
- },
- "namespace" => namespace,
- "metadata" => {},
- "sample_data" => hash_including(
- "params" => [],
- "tags" => {
- "active_job_id" => kind_of(String),
- "queue" => queue,
- "executions" => 1
- }
- )
+ expect(transaction).to have_namespace(namespace)
+ expect(transaction).to have_action("ActiveJobErrorTestJob#perform")
+ expect(transaction).to have_error("RuntimeError", "uh oh")
+ expect(transaction).to_not include_metadata
+ expect(transaction).to include_params([])
+ expect(transaction).to include_tags(
+ "active_job_id" => kind_of(String),
+ "queue" => queue,
+ "executions" => 1
)
- events = transaction_hash["events"]
+
+ events = transaction.to_h["events"]
.sort_by { |e| e["start"] }
.map { |event| event["name"] }
-
expect(events).to eq(expected_perform_events)
end
@@ -247,9 +220,7 @@ def perform(*_args)
queue_job(ActiveJobErrorTestJob)
end.to raise_error(RuntimeError, "uh oh")
- transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include("error" => nil)
+ expect(last_transaction).to_not have_error
end
end
@@ -270,15 +241,8 @@ def perform(*_args)
end
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "error" => nil,
- "sample_data" => hash_including(
- "tags" => hash_including(
- "executions" => 1
- )
- )
- )
+ expect(transaction).to_not have_error
+ expect(transaction).to include_tags("executions" => 1)
end
it "reports error when discarding the job" do
@@ -294,19 +258,8 @@ def perform(*_args)
end
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "error" => {
- "name" => "RuntimeError",
- "message" => "uh oh",
- "backtrace" => kind_of(String)
- },
- "sample_data" => hash_including(
- "tags" => hash_including(
- "executions" => 2
- )
- )
- )
+ expect(transaction).to have_error("RuntimeError", "uh oh")
+ expect(transaction).to include_tags("executions" => 2)
end
end
end
@@ -343,13 +296,7 @@ def perform(*_args)
queue_job(ActiveJobErrorPriorityTestJob)
end.to raise_error(RuntimeError, "uh oh")
- transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "sample_data" => hash_including(
- "tags" => hash_including("queue" => queue, "priority" => 10)
- )
- )
+ expect(last_transaction).to include_tags("queue" => queue, "priority" => 10)
end
end
end
@@ -363,51 +310,39 @@ def perform(*_args)
end.to raise_error(RuntimeError, "uh oh")
end
- transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "sample_data" => hash_including(
- "tags" => hash_including("executions" => 2)
- )
- )
+ expect(last_transaction).to include_tags("executions" => 2)
end
end
context "when wrapped in another transaction" do
it "does not create a new transaction or close the currently open one" do
current_transaction = background_job_transaction
- allow(current_transaction).to receive(:complete).and_call_original
set_current_transaction current_transaction
queue_job(ActiveJobTestJob)
expect(created_transactions.count).to eql(1)
- expect(current_transaction).to_not have_received(:complete)
- current_transaction.complete
transaction = current_transaction
- transaction_hash = transaction.to_h
+ expect(transaction).to_not be_completed
+ transaction._sample
# It does set data on the transaction
- expect(transaction_hash).to include(
- "id" => current_transaction.transaction_id,
- "action" => "ActiveJobTestJob#perform",
- "error" => nil,
- "namespace" => namespace,
- "metadata" => {},
- "sample_data" => hash_including(
- "params" => [],
- "tags" => {
- "active_job_id" => kind_of(String),
- "queue" => queue,
- "executions" => 1
- }
- )
+ expect(transaction).to have_namespace(namespace)
+ expect(transaction).to have_id(current_transaction.transaction_id)
+ expect(transaction).to have_action("ActiveJobTestJob#perform")
+ expect(transaction).to_not have_error
+ expect(transaction).to_not include_metadata
+ expect(transaction).to include_params([])
+ expect(transaction).to include_tags(
+ "active_job_id" => kind_of(String),
+ "queue" => queue,
+ "executions" => 1
)
- events = transaction_hash["events"]
+
+ events = transaction.to_h["events"]
.reject { |e| e["name"] == "enqueue.active_job" }
.sort_by { |e| e["start"] }
.map { |event| event["name"] }
-
expect(events).to eq(expected_perform_events)
end
end
@@ -467,9 +402,7 @@ def perform(*_args)
it "sets provider_job_id as tag" do
queue_job(ProviderWrappedActiveJobTestJob)
- transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash["sample_data"]["tags"]).to include(
+ expect(last_transaction).to include_tags(
"provider_job_id" => "my_provider_job_id"
)
end
@@ -506,13 +439,10 @@ def perform(*_args)
end
it "sets queue time on transaction" do
- allow_any_instance_of(Appsignal::Transaction).to receive(:set_queue_start).and_call_original
queue_job(ProviderWrappedActiveJobTestJob)
- transaction = last_transaction
queue_time = Time.parse("2020-10-10T10:10:10Z")
- expect(transaction).to have_received(:set_queue_start)
- .with((queue_time.to_f * 1_000).to_i)
+ expect(last_transaction).to have_queue_start((queue_time.to_f * 1_000).to_i)
end
end
@@ -534,18 +464,14 @@ def welcome(_first_arg = nil, _second_arg = nil)
perform_mailer(ActionMailerTestJob, :welcome)
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "ActionMailerTestJob#welcome",
- "sample_data" => hash_including(
- "params" => ["ActionMailerTestJob", "welcome",
- "deliver_now"] + active_job_args_wrapper,
- "tags" => {
- "active_job_id" => kind_of(String),
- "queue" => "mailers",
- "executions" => 1
- }
- )
+ expect(transaction).to have_action("ActionMailerTestJob#welcome")
+ expect(transaction).to include_params(
+ ["ActionMailerTestJob", "welcome", "deliver_now"] + active_job_args_wrapper
+ )
+ expect(transaction).to include_tags(
+ "active_job_id" => kind_of(String),
+ "queue" => "mailers",
+ "executions" => 1
)
end
end
@@ -555,18 +481,15 @@ def welcome(_first_arg = nil, _second_arg = nil)
perform_mailer(ActionMailerTestJob, :welcome, method_given_args)
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "ActionMailerTestJob#welcome",
- "sample_data" => hash_including(
- "params" => ["ActionMailerTestJob", "welcome",
- "deliver_now"] + active_job_args_wrapper(:args => method_expected_args),
- "tags" => {
- "active_job_id" => kind_of(String),
- "queue" => "mailers",
- "executions" => 1
- }
- )
+ expect(transaction).to have_action("ActionMailerTestJob#welcome")
+ expect(transaction).to include_params(
+ ["ActionMailerTestJob", "welcome",
+ "deliver_now"] + active_job_args_wrapper(:args => method_expected_args)
+ )
+ expect(transaction).to include_tags(
+ "active_job_id" => kind_of(String),
+ "queue" => "mailers",
+ "executions" => 1
)
end
end
@@ -577,21 +500,18 @@ def welcome(_first_arg = nil, _second_arg = nil)
perform_mailer(ActionMailerTestJob, :welcome, parameterized_given_args)
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "ActionMailerTestJob#welcome",
- "sample_data" => hash_including(
- "params" => [
- "ActionMailerTestJob",
- "welcome",
- "deliver_now"
- ] + active_job_args_wrapper(:params => parameterized_expected_args),
- "tags" => {
- "active_job_id" => kind_of(String),
- "queue" => "mailers",
- "executions" => 1
- }
- )
+ expect(transaction).to have_action("ActionMailerTestJob#welcome")
+ expect(transaction).to include_params(
+ [
+ "ActionMailerTestJob",
+ "welcome",
+ "deliver_now"
+ ] + active_job_args_wrapper(:params => parameterized_expected_args)
+ )
+ expect(transaction).to include_tags(
+ "active_job_id" => kind_of(String),
+ "queue" => "mailers",
+ "executions" => 1
)
end
end
@@ -618,22 +538,19 @@ def welcome(*_args)
perform_mailer(ActionMailerTestMailDeliveryJob, :welcome)
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "ActionMailerTestMailDeliveryJob#welcome",
- "sample_data" => hash_including(
- "params" => [
- "ActionMailerTestMailDeliveryJob",
- "welcome",
- "deliver_now",
- { active_job_internal_key => ["args"], "args" => [] }
- ],
- "tags" => {
- "active_job_id" => kind_of(String),
- "queue" => "mailers",
- "executions" => 1
- }
- )
+ expect(transaction).to have_action("ActionMailerTestMailDeliveryJob#welcome")
+ expect(transaction).to include_params(
+ [
+ "ActionMailerTestMailDeliveryJob",
+ "welcome",
+ "deliver_now",
+ { active_job_internal_key => ["args"], "args" => [] }
+ ]
+ )
+ expect(transaction).to include_tags(
+ "active_job_id" => kind_of(String),
+ "queue" => "mailers",
+ "executions" => 1
)
end
@@ -642,25 +559,22 @@ def welcome(*_args)
perform_mailer(ActionMailerTestMailDeliveryJob, :welcome, method_given_args)
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "ActionMailerTestMailDeliveryJob#welcome",
- "sample_data" => hash_including(
- "params" => [
- "ActionMailerTestMailDeliveryJob",
- "welcome",
- "deliver_now",
- {
- active_job_internal_key => ["args"],
- "args" => method_expected_args
- }
- ],
- "tags" => {
- "active_job_id" => kind_of(String),
- "queue" => "mailers",
- "executions" => 1
+ expect(transaction).to have_action("ActionMailerTestMailDeliveryJob#welcome")
+ expect(transaction).to include_params(
+ [
+ "ActionMailerTestMailDeliveryJob",
+ "welcome",
+ "deliver_now",
+ {
+ active_job_internal_key => ["args"],
+ "args" => method_expected_args
}
- )
+ ]
+ )
+ expect(transaction).to include_tags(
+ "active_job_id" => kind_of(String),
+ "queue" => "mailers",
+ "executions" => 1
)
end
end
@@ -670,11 +584,9 @@ def welcome(*_args)
perform_mailer(ActionMailerTestMailDeliveryJob, :welcome, parameterized_given_args)
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "ActionMailerTestMailDeliveryJob#welcome",
- "sample_data" => hash_including(
- "params" => [
+ expect(transaction).to have_action("ActionMailerTestMailDeliveryJob#welcome")
+ expect(transaction).to include_params(
+ [
"ActionMailerTestMailDeliveryJob",
"welcome",
"deliver_now",
@@ -683,13 +595,12 @@ def welcome(*_args)
"args" => [],
"params" => parameterized_expected_args
}
- ],
- "tags" => {
- "active_job_id" => kind_of(String),
- "queue" => "mailers",
- "executions" => 1
- }
+ ]
)
+ expect(transaction).to include_tags(
+ "active_job_id" => kind_of(String),
+ "queue" => "mailers",
+ "executions" => 1
)
end
end
diff --git a/spec/lib/appsignal/hooks/delayed_job_spec.rb b/spec/lib/appsignal/hooks/delayed_job_spec.rb
index 072b04112..609286e3f 100644
--- a/spec/lib/appsignal/hooks/delayed_job_spec.rb
+++ b/spec/lib/appsignal/hooks/delayed_job_spec.rb
@@ -64,23 +64,18 @@ def perform
context "with a normal call" do
it "wraps it in a transaction" do
perform
- transaction_data = last_transaction.to_h
- expect(transaction_data).to include(
- "action" => "TestClass#perform",
- "namespace" => "background_job",
- "error" => nil
- )
- expect(transaction_data["events"].map { |e| e["name"] })
- .to eql(["perform_job.delayed_job"])
- expect(transaction_data["sample_data"]).to include(
- "metadata" => {
- "priority" => 1,
- "attempts" => 1,
- "queue" => "default",
- "id" => "123"
- },
- "params" => ["argument"]
+ transaction = last_transaction
+ expect(transaction).to have_action("TestClass#perform")
+ expect(transaction).to have_namespace("background_job")
+ expect(transaction).to_not have_error
+ expect(transaction).to include_event(:name => "perform_job.delayed_job")
+ expect(transaction).to include_sample_metadata(
+ "priority" => 1,
+ "attempts" => 1,
+ "queue" => "default",
+ "id" => "123"
)
+ expect(transaction).to include_params(["argument"])
end
context "with more complex params" do
@@ -93,13 +88,8 @@ def perform
it "adds the more complex arguments" do
perform
- transaction_data = last_transaction.to_h
- expect(transaction_data["sample_data"]).to include(
- "params" => {
- "foo" => "Foo",
- "bar" => "Bar"
- }
- )
+
+ expect(last_transaction).to include_params("foo" => "Foo", "bar" => "Bar")
end
context "with parameter filtering" do
@@ -110,13 +100,8 @@ def perform
it "filters selected arguments" do
perform
- transaction_data = last_transaction.to_h
- expect(transaction_data["sample_data"]).to include(
- "params" => {
- "foo" => "[FILTERED]",
- "bar" => "Bar"
- }
- )
+
+ expect(last_transaction).to include_params("foo" => "[FILTERED]", "bar" => "Bar")
end
end
end
@@ -125,13 +110,9 @@ def perform
let(:run_at) { Time.parse("2017-01-01 10:01:00UTC") }
it "reports queue_start with run_at time" do
- # TODO: Not available in transaction.to_h yet.
- # https://github.com/appsignal/appsignal-agent/issues/293
- expect(Appsignal).to receive(:monitor_transaction).with(
- "perform_job.delayed_job",
- a_hash_including(:queue_start => run_at)
- ).and_call_original
perform
+
+ expect(last_transaction).to have_queue_start(run_at.to_i * 1000)
end
end
@@ -142,7 +123,7 @@ def perform
it "wraps it in a transaction using the class method job name" do
perform
- expect(last_transaction.to_h["action"]).to eql("CustomClassMethod.perform")
+ expect(last_transaction).to have_action("CustomClassMethod.perform")
end
end
@@ -155,7 +136,7 @@ def perform
let(:payload_object) { double(:appsignal_name => "CustomClass#perform") }
it "wraps it in a transaction using the custom name" do
- expect(last_transaction.to_h["action"]).to eql("CustomClass#perform")
+ expect(last_transaction).to have_action("CustomClass#perform")
end
end
@@ -163,7 +144,7 @@ def perform
let(:payload_object) { double(:appsignal_name => Object.new) }
it "wraps it in a transaction using the original job name" do
- expect(last_transaction.to_h["action"]).to eql("TestClass#perform")
+ expect(last_transaction).to have_action("TestClass#perform")
end
end
@@ -172,7 +153,7 @@ def perform
it "wraps it in a transaction using the custom name" do
perform
- expect(last_transaction.to_h["action"]).to eql("CustomClassMethod.perform")
+ expect(last_transaction).to have_action("CustomClassMethod.perform")
end
end
end
@@ -182,7 +163,7 @@ def perform
let(:payload_object) { double(:appsignal_name => "CustomClassHash#perform") }
it "wraps it in a transaction using the custom name" do
- expect(last_transaction.to_h["action"]).to eql("CustomClassHash#perform")
+ expect(last_transaction).to have_action("CustomClassHash#perform")
end
end
@@ -190,7 +171,7 @@ def perform
let(:payload_object) { double(:appsignal_name => Object.new) }
it "wraps it in a transaction using the original job name" do
- expect(last_transaction.to_h["action"]).to eql("TestClass#perform")
+ expect(last_transaction).to have_action("TestClass#perform")
end
end
@@ -199,7 +180,7 @@ def perform
it "wraps it in a transaction using the custom name" do
perform
- expect(last_transaction.to_h["action"]).to eql("CustomClassMethod.perform")
+ expect(last_transaction).to have_action("CustomClassMethod.perform")
end
end
end
@@ -221,7 +202,7 @@ def self.appsignal_name
# of `self.appsignal_name`. Since this isn't a valid `String`
# we return the default job name as action name.
it "wraps it in a transaction using the original job name" do
- expect(last_transaction.to_h["action"]).to eql("TestClass#perform")
+ expect(last_transaction).to have_action("TestClass#perform")
end
end
end
@@ -234,7 +215,7 @@ def self.appsignal_name
it "appends #perform to the class name" do
perform
- expect(last_transaction.to_h["action"]).to eql("Banana#perform")
+ expect(last_transaction).to have_action("Banana#perform")
end
end
@@ -267,23 +248,19 @@ def self.appsignal_name
it "wraps it in a transaction with the correct params" do
perform
- transaction_data = last_transaction.to_h
- expect(transaction_data).to include(
- "action" => "TestClass#perform",
- "namespace" => "background_job",
- "error" => nil
- )
- expect(transaction_data["events"].map { |e| e["name"] })
- .to eql(["perform_job.delayed_job"])
- expect(transaction_data["sample_data"]).to include(
- "metadata" => {
- "priority" => 1,
- "attempts" => 1,
- "queue" => "default",
- "id" => "123"
- },
- "params" => ["activejob_argument"]
+
+ transaction = last_transaction
+ expect(transaction).to have_namespace("background_job")
+ expect(transaction).to have_action("TestClass#perform")
+ expect(transaction).to_not have_error
+ expect(transaction).to include_event("name" => "perform_job.delayed_job")
+ expect(transaction).to include_sample_metadata(
+ "priority" => 1,
+ "attempts" => 1,
+ "queue" => "default",
+ "id" => "123"
)
+ expect(transaction).to include_params(["activejob_argument"])
end
context "with more complex params" do
@@ -296,13 +273,11 @@ def self.appsignal_name
it "adds the more complex arguments" do
perform
- transaction_data = last_transaction.to_h
- expect(transaction_data).to include("action" => "TestClass#perform")
- expect(transaction_data["sample_data"]).to include(
- "params" => {
- "foo" => "Foo",
- "bar" => "Bar"
- }
+ transaction = last_transaction
+ expect(transaction).to have_action("TestClass#perform")
+ expect(transaction).to include_params(
+ "foo" => "Foo",
+ "bar" => "Bar"
)
end
@@ -314,13 +289,11 @@ def self.appsignal_name
it "filters selected arguments" do
perform
- transaction_data = last_transaction.to_h
- expect(transaction_data).to include("action" => "TestClass#perform")
- expect(transaction_data["sample_data"]).to include(
- "params" => {
- "foo" => "[FILTERED]",
- "bar" => "Bar"
- }
+ transaction = last_transaction
+ expect(transaction).to have_action("TestClass#perform")
+ expect(transaction).to include_params(
+ "foo" => "[FILTERED]",
+ "bar" => "Bar"
)
end
end
@@ -330,11 +303,9 @@ def self.appsignal_name
let(:run_at) { Time.parse("2017-01-01 10:01:00UTC") }
it "reports queue_start with run_at time" do
- expect(Appsignal).to receive(:monitor_transaction).with(
- "perform_job.delayed_job",
- a_hash_including(:queue_start => run_at)
- ).and_call_original
perform
+
+ expect(last_transaction).to have_queue_start(run_at.to_i * 1000)
end
end
end
@@ -352,18 +323,10 @@ def self.appsignal_name
perform
end.to raise_error(error)
- transaction_data = last_transaction.to_h
- expect(transaction_data).to include(
- "action" => "TestClass#perform",
- "namespace" => "background_job",
- "error" => {
- "name" => "ExampleException",
- "message" => "uh oh",
- # TODO: backtrace should be an Array of Strings
- # https://github.com/appsignal/appsignal-agent/issues/294
- "backtrace" => kind_of(String)
- }
- )
+ transaction = last_transaction
+ expect(transaction).to have_namespace("background_job")
+ expect(transaction).to have_action("TestClass#perform")
+ expect(transaction).to have_error("ExampleException", "uh oh")
end
end
end
diff --git a/spec/lib/appsignal/hooks/dry_monitor_spec.rb b/spec/lib/appsignal/hooks/dry_monitor_spec.rb
index 5077f7493..0b6dcd9a5 100644
--- a/spec/lib/appsignal/hooks/dry_monitor_spec.rb
+++ b/spec/lib/appsignal/hooks/dry_monitor_spec.rb
@@ -53,22 +53,13 @@
it "creates an sql event" do
notifications.instrument(event_id, payload)
- expect(transaction.to_h["events"]).to match([
- {
- "allocation_count" => kind_of(Integer),
- "body" => "SELECT * FROM users",
- "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
- "count" => 1,
- "duration" => kind_of(Float),
- "gc_duration" => kind_of(Float),
- "name" => "query.postgres",
- "start" => kind_of(Float),
- "title" => "query.postgres"
- }
- ])
+ expect(transaction).to include_event(
+ "body" => "SELECT * FROM users",
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT,
+ "count" => 1,
+ "name" => "query.postgres",
+ "title" => "query.postgres"
+ )
end
end
@@ -82,22 +73,13 @@
it "creates a generic event" do
notifications.instrument(event_id, payload)
- expect(transaction.to_h["events"]).to match([
- {
- "allocation_count" => kind_of(Integer),
- "body" => "",
- "body_format" => Appsignal::EventFormatter::DEFAULT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
- "count" => 1,
- "duration" => kind_of(Float),
- "gc_duration" => kind_of(Float),
- "name" => "foo",
- "start" => kind_of(Float),
- "title" => ""
- }
- ])
+ expect(transaction).to include_event(
+ "body" => "",
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
+ "count" => 1,
+ "name" => "foo",
+ "title" => ""
+ )
end
end
end
diff --git a/spec/lib/appsignal/hooks/excon_spec.rb b/spec/lib/appsignal/hooks/excon_spec.rb
index 8329f752e..cc5bd7f72 100644
--- a/spec/lib/appsignal/hooks/excon_spec.rb
+++ b/spec/lib/appsignal/hooks/excon_spec.rb
@@ -40,12 +40,10 @@ def self.defaults
}
Excon.defaults[:instrumentor].instrument("excon.request", data) {} # rubocop:disable Lint/EmptyBlock
- expect(transaction.to_h["events"]).to include(
- hash_including(
- "name" => "request.excon",
- "title" => "GET http://www.google.com",
- "body" => ""
- )
+ expect(transaction).to include_event(
+ "name" => "request.excon",
+ "title" => "GET http://www.google.com",
+ "body" => ""
)
end
@@ -53,12 +51,10 @@ def self.defaults
data = { :host => "www.google.com" }
Excon.defaults[:instrumentor].instrument("excon.response", data) {} # rubocop:disable Lint/EmptyBlock
- expect(transaction.to_h["events"]).to include(
- hash_including(
- "name" => "response.excon",
- "title" => "www.google.com",
- "body" => ""
- )
+ expect(transaction).to include_event(
+ "name" => "response.excon",
+ "title" => "www.google.com",
+ "body" => ""
)
end
end
diff --git a/spec/lib/appsignal/hooks/rake_spec.rb b/spec/lib/appsignal/hooks/rake_spec.rb
index 6251b3c93..9f56183d6 100644
--- a/spec/lib/appsignal/hooks/rake_spec.rb
+++ b/spec/lib/appsignal/hooks/rake_spec.rb
@@ -15,8 +15,7 @@ def perform
end
it "creates no transaction" do
- expect(Appsignal::Transaction).to_not receive(:new)
- expect { perform }.to_not(change { created_transactions })
+ expect { perform }.to_not(change { created_transactions.count })
end
it "calls the original task" do
@@ -42,20 +41,13 @@ def perform
it "creates a background job transaction" do
perform
- expect(last_transaction).to be_completed
- expect(last_transaction.to_h).to include(
- "id" => kind_of(String),
- "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
- "action" => "task:name",
- "error" => {
- "name" => "ExampleException",
- "message" => "my error message",
- "backtrace" => kind_of(String)
- },
- "sample_data" => hash_including(
- "params" => { "foo" => "bar" }
- )
- )
+ transaction = last_transaction
+ expect(transaction).to have_id
+ expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
+ expect(transaction).to have_action("task:name")
+ expect(transaction).to have_error("ExampleException", "my error message")
+ expect(transaction).to include_params("foo" => "bar")
+ expect(transaction).to be_completed
end
context "when first argument is not a `Rake::TaskArguments`" do
@@ -64,9 +56,7 @@ def perform
it "does not add the params to the transaction" do
perform
- expect(last_transaction.to_h).to include(
- "sample_data" => hash_excluding("params")
- )
+ expect(last_transaction).to_not include_params
end
end
end
diff --git a/spec/lib/appsignal/hooks/redis_client_spec.rb b/spec/lib/appsignal/hooks/redis_client_spec.rb
index 21693a953..73579416c 100644
--- a/spec/lib/appsignal/hooks/redis_client_spec.rb
+++ b/spec/lib/appsignal/hooks/redis_client_spec.rb
@@ -88,13 +88,10 @@ def write(_commands)
connection = RedisClient::RubyConnection.new client_config
expect(connection.write([:get, "key"])).to eql("stub_write")
- transaction_hash = transaction.to_h
- expect(transaction_hash["events"]).to include(
- hash_including(
- "name" => "query.redis",
- "body" => "get ?",
- "title" => "stub_id"
- )
+ expect(transaction).to include_event(
+ "name" => "query.redis",
+ "body" => "get ?",
+ "title" => "stub_id"
)
end
@@ -103,16 +100,13 @@ def write(_commands)
script = "return redis.call('set',KEYS[1],ARGV[1])"
keys = ["foo"]
argv = ["bar"]
- expect(connection.write([:eval, script, keys.size, keys,
- argv])).to eql("stub_write")
+ expect(connection.write([:eval, script, keys.size, keys, argv]))
+ .to eql("stub_write")
- transaction_hash = transaction.to_h
- expect(transaction_hash["events"]).to include(
- hash_including(
- "name" => "query.redis",
- "body" => "#{script} ? ?",
- "title" => "stub_id"
- )
+ expect(transaction).to include_event(
+ "name" => "query.redis",
+ "body" => "#{script} ? ?",
+ "title" => "stub_id"
)
end
end
@@ -181,13 +175,10 @@ def write(_commands)
connection = RedisClient::HiredisConnection.new client_config
expect(connection.write([:get, "key"])).to eql("stub_write")
- transaction_hash = transaction.to_h
- expect(transaction_hash["events"]).to include(
- hash_including(
- "name" => "query.redis",
- "body" => "get ?",
- "title" => "stub_id"
- )
+ expect(transaction).to include_event(
+ "name" => "query.redis",
+ "body" => "get ?",
+ "title" => "stub_id"
)
end
@@ -199,13 +190,10 @@ def write(_commands)
expect(connection.write([:eval, script, keys.size, keys,
argv])).to eql("stub_write")
- transaction_hash = transaction.to_h
- expect(transaction_hash["events"]).to include(
- hash_including(
- "name" => "query.redis",
- "body" => "#{script} ? ?",
- "title" => "stub_id"
- )
+ expect(transaction).to include_event(
+ "name" => "query.redis",
+ "body" => "#{script} ? ?",
+ "title" => "stub_id"
)
end
end
diff --git a/spec/lib/appsignal/hooks/redis_spec.rb b/spec/lib/appsignal/hooks/redis_spec.rb
index 028c05030..dcd474531 100644
--- a/spec/lib/appsignal/hooks/redis_spec.rb
+++ b/spec/lib/appsignal/hooks/redis_spec.rb
@@ -81,14 +81,11 @@ def write(_commands)
client = Redis::Client.new
expect(client.write([:get, "key"])).to eql("stub_write")
- transaction_hash = transaction.to_h
- expect(transaction_hash["events"]).to include(
- hash_including(
- "name" => "query.redis",
- "body" => "get ?",
- "title" => "stub_id"
- )
- )
+ expect(transaction).to include_event(
+ "name" => "query.redis",
+ "body" => "get ?",
+ "title" => "stub_id"
+ )
end
it "instrument a redis script call" do
@@ -98,14 +95,11 @@ def write(_commands)
argv = ["bar"]
expect(client.write([:eval, script, keys.size, keys, argv])).to eql("stub_write")
- transaction_hash = transaction.to_h
- expect(transaction_hash["events"]).to include(
- hash_including(
- "name" => "query.redis",
- "body" => "#{script} ? ?",
- "title" => "stub_id"
- )
- )
+ expect(transaction).to include_event(
+ "name" => "query.redis",
+ "body" => "#{script} ? ?",
+ "title" => "stub_id"
+ )
end
end
end
diff --git a/spec/lib/appsignal/hooks/resque_spec.rb b/spec/lib/appsignal/hooks/resque_spec.rb
index 8758d4311..ed13e53c9 100644
--- a/spec/lib/appsignal/hooks/resque_spec.rb
+++ b/spec/lib/appsignal/hooks/resque_spec.rb
@@ -53,20 +53,14 @@ def self.perform
perform_rescue_job(ResqueTestJob)
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "id" => kind_of(String),
- "action" => "ResqueTestJob#perform",
- "error" => nil,
- "namespace" => namespace,
- "metadata" => {},
- "sample_data" => {
- "breadcrumbs" => [],
- "tags" => { "queue" => queue }
- }
- )
- expect(transaction_hash["events"].map { |e| e["name"] })
- .to eql(["perform.resque"])
+ expect(transaction).to have_id
+ expect(transaction).to have_namespace(namespace)
+ expect(transaction).to have_action("ResqueTestJob#perform")
+ expect(transaction).to_not have_error
+ expect(transaction).to_not include_metadata
+ expect(transaction).to_not include_breadcrumbs
+ expect(transaction).to include_tags("queue" => queue)
+ expect(transaction).to include_event("name" => "perform.resque")
end
context "with error" do
@@ -76,22 +70,14 @@ def self.perform
end.to raise_error(RuntimeError, "resque job error")
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "id" => kind_of(String),
- "action" => "ResqueErrorTestJob#perform",
- "error" => {
- "name" => "RuntimeError",
- "message" => "resque job error",
- "backtrace" => kind_of(String)
- },
- "namespace" => namespace,
- "metadata" => {},
- "sample_data" => {
- "breadcrumbs" => [],
- "tags" => { "queue" => queue }
- }
- )
+ expect(transaction).to have_id
+ expect(transaction).to have_namespace(namespace)
+ expect(transaction).to have_action("ResqueErrorTestJob#perform")
+ expect(transaction).to have_error("RuntimeError", "resque job error")
+ expect(transaction).to_not include_metadata
+ expect(transaction).to_not include_breadcrumbs
+ expect(transaction).to include_tags("queue" => queue)
+ expect(transaction).to include_event("name" => "perform.resque")
end
end
@@ -115,25 +101,23 @@ def self.perform
)
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "id" => kind_of(String),
- "action" => "ResqueTestJob#perform",
- "error" => nil,
- "namespace" => namespace,
- "metadata" => {},
- "sample_data" => {
- "tags" => { "queue" => queue },
- "breadcrumbs" => [],
- "params" => [
- "foo",
- {
- "foo" => "[FILTERED]",
- "bar" => "Bar",
- "baz" => { "1" => "foo" }
- }
- ]
- }
+ expect(transaction).to have_id
+ expect(transaction).to have_namespace(namespace)
+ expect(transaction).to have_action("ResqueTestJob#perform")
+ expect(transaction).to_not have_error
+ expect(transaction).to_not include_metadata
+ expect(transaction).to_not include_breadcrumbs
+ expect(transaction).to include_tags("queue" => queue)
+ expect(transaction).to include_event("name" => "perform.resque")
+ expect(transaction).to include_params(
+ [
+ "foo",
+ {
+ "foo" => "[FILTERED]",
+ "bar" => "Bar",
+ "baz" => { "1" => "foo" }
+ }
+ ]
)
end
end
@@ -173,19 +157,15 @@ def perform(job_data)
)
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "id" => kind_of(String),
- "action" => "ResqueTestJobByActiveJob#perform",
- "error" => nil,
- "namespace" => namespace,
- "metadata" => {},
- "sample_data" => {
- "breadcrumbs" => [],
- "tags" => { "queue" => queue }
- # Params will be set by the ActiveJob integration
- }
- )
+ expect(transaction).to have_id
+ expect(transaction).to have_namespace(namespace)
+ expect(transaction).to have_action("ResqueTestJobByActiveJob#perform")
+ expect(transaction).to_not have_error
+ expect(transaction).to_not include_metadata
+ expect(transaction).to_not include_breadcrumbs
+ expect(transaction).to include_tags("queue" => queue)
+ expect(transaction).to include_event("name" => "perform.resque")
+ expect(transaction).to_not include_params
end
end
end
diff --git a/spec/lib/appsignal/hooks/shoryuken_spec.rb b/spec/lib/appsignal/hooks/shoryuken_spec.rb
index e80fe111a..3591ce6da 100644
--- a/spec/lib/appsignal/hooks/shoryuken_spec.rb
+++ b/spec/lib/appsignal/hooks/shoryuken_spec.rb
@@ -37,37 +37,25 @@ def perform_shoryuken_job(&block)
expect { perform_shoryuken_job }.to change { created_transactions.length }.by(1)
transaction = last_transaction
- expect(transaction).to be_completed
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "DemoShoryukenWorker#perform",
- "id" => kind_of(String), # AppSignal generated id
- "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
- "error" => nil
- )
- expect(transaction_hash["events"].first).to include(
- "allocation_count" => kind_of(Integer),
+ expect(transaction).to have_id
+ expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
+ expect(transaction).to have_action("DemoShoryukenWorker#perform")
+ expect(transaction).to_not have_error
+ expect(transaction).to include_event(
"body" => "",
"body_format" => Appsignal::EventFormatter::DEFAULT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
"count" => 1,
- "gc_duration" => kind_of(Float),
- "start" => kind_of(Float),
- "duration" => kind_of(Float),
"name" => "perform_job.shoryuken",
"title" => ""
)
- expect(transaction_hash["sample_data"]).to include(
- "params" => { "foo" => "Foo", "bar" => "Bar" },
- "metadata" => {
- "message_id" => "msg1",
- "queue" => queue,
- "SentTimestamp" => sent_timestamp
- }
+ expect(transaction).to include_params("foo" => "Foo", "bar" => "Bar")
+ expect(transaction).to include_sample_metadata(
+ "message_id" => "msg1",
+ "queue" => queue,
+ "SentTimestamp" => sent_timestamp
)
- expect(transaction).to have_received(:set_queue_start).with(sent_timestamp)
+ expect(transaction).to have_queue_start(sent_timestamp)
+ expect(transaction).to be_completed
end
context "with parameter filtering" do
@@ -82,10 +70,7 @@ def perform_shoryuken_job(&block)
it "filters selected arguments" do
perform_shoryuken_job
- transaction_hash = last_transaction.to_h
- expect(transaction_hash["sample_data"]).to include(
- "params" => { "foo" => "[FILTERED]", "bar" => "Bar" }
- )
+ expect(last_transaction).to include_params("foo" => "[FILTERED]", "bar" => "Bar")
end
end
end
@@ -96,10 +81,7 @@ def perform_shoryuken_job(&block)
it "handles string arguments" do
perform_shoryuken_job
- transaction_hash = last_transaction.to_h
- expect(transaction_hash["sample_data"]).to include(
- "params" => { "params" => body }
- )
+ expect(last_transaction).to include_params("params" => body)
end
end
@@ -109,10 +91,7 @@ def perform_shoryuken_job(&block)
it "handles primitive types as arguments" do
perform_shoryuken_job
- transaction_hash = last_transaction.to_h
- expect(transaction_hash["sample_data"]).to include(
- "params" => { "params" => body }
- )
+ expect(last_transaction).to include_params("params" => body)
end
end
end
@@ -126,18 +105,11 @@ def perform_shoryuken_job(&block)
end.to change { created_transactions.length }.by(1)
transaction = last_transaction
+ expect(transaction).to have_id
+ expect(transaction).to have_action("DemoShoryukenWorker#perform")
+ expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
+ expect(transaction).to have_error("ExampleException", "error message")
expect(transaction).to be_completed
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "DemoShoryukenWorker#perform",
- "id" => kind_of(String), # AppSignal generated id
- "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
- "error" => {
- "name" => "ExampleException",
- "message" => "error message",
- "backtrace" => kind_of(String)
- }
- )
end
end
@@ -171,41 +143,28 @@ def perform_shoryuken_job(&block)
end.to change { created_transactions.length }.by(1)
transaction = last_transaction
- expect(transaction).to be_completed
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "DemoShoryukenWorker#perform",
- "id" => kind_of(String), # AppSignal generated id
- "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
- "error" => nil
- )
- expect(transaction_hash["events"].first).to include(
- "allocation_count" => kind_of(Integer),
+ expect(transaction).to have_id
+ expect(transaction).to have_action("DemoShoryukenWorker#perform")
+ expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
+ expect(transaction).to_not have_error
+ expect(transaction).to include_event(
"body" => "",
"body_format" => Appsignal::EventFormatter::DEFAULT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
"count" => 1,
- "gc_duration" => kind_of(Float),
- "start" => kind_of(Float),
- "duration" => kind_of(Float),
"name" => "perform_job.shoryuken",
"title" => ""
)
- expect(transaction_hash["sample_data"]).to include(
- "params" => {
- "msg2" => "foo bar",
- "msg1" => { "id" => "123", "foo" => "Foo", "bar" => "Bar" }
- },
- "metadata" => {
- "batch" => true,
- "queue" => "some-funky-queue-name",
- "SentTimestamp" => sent_timestamp.to_s # Earliest/oldest timestamp from messages
- }
+ expect(transaction).to include_params(
+ "msg2" => "foo bar",
+ "msg1" => { "id" => "123", "foo" => "Foo", "bar" => "Bar" }
+ )
+ expect(transaction).to include_sample_metadata(
+ "batch" => true,
+ "queue" => "some-funky-queue-name",
+ "SentTimestamp" => sent_timestamp.to_s # Earliest/oldest timestamp from messages
)
# Queue time based on earliest/oldest timestamp from messages
- expect(transaction).to have_received(:set_queue_start).with(sent_timestamp)
+ expect(transaction).to have_queue_start(sent_timestamp)
end
end
end
diff --git a/spec/lib/appsignal/integrations/hanami_spec.rb b/spec/lib/appsignal/integrations/hanami_spec.rb
index d4247d157..55c51a514 100644
--- a/spec/lib/appsignal/integrations/hanami_spec.rb
+++ b/spec/lib/appsignal/integrations/hanami_spec.rb
@@ -159,9 +159,7 @@ def make_request(env)
it "does not set the action name" do
make_request(env)
- expect(transaction.to_h).to include(
- "action" => nil
- )
+ expect(transaction).to_not have_action
end
end
@@ -171,9 +169,7 @@ def make_request(env)
it "sets action name on the transaction" do
make_request(env)
- expect(transaction.to_h).to include(
- "action" => "HanamiApp::Actions::Books::Index::TestClass"
- )
+ expect(transaction).to have_action("HanamiApp::Actions::Books::Index::TestClass")
end
end
end
diff --git a/spec/lib/appsignal/integrations/http_spec.rb b/spec/lib/appsignal/integrations/http_spec.rb
index a7907fa4e..4e2ca52c3 100644
--- a/spec/lib/appsignal/integrations/http_spec.rb
+++ b/spec/lib/appsignal/integrations/http_spec.rb
@@ -23,9 +23,8 @@
HTTP.get("http://www.google.com")
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include("namespace" => Appsignal::Transaction::HTTP_REQUEST)
- expect(transaction_hash["events"].first).to include(
+ expect(transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
+ expect(transaction).to include_event(
"body" => "",
"body_format" => Appsignal::EventFormatter::DEFAULT,
"name" => "request.http_rb",
@@ -38,9 +37,8 @@
HTTP.get("https://www.google.com")
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include("namespace" => Appsignal::Transaction::HTTP_REQUEST)
- expect(transaction_hash["events"].first).to include(
+ expect(transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
+ expect(transaction).to include_event(
"body" => "",
"body_format" => Appsignal::EventFormatter::DEFAULT,
"name" => "request.http_rb",
@@ -54,7 +52,7 @@
HTTP.get("https://www.google.com", :params => { :q => "Appsignal" })
- expect(transaction.to_h["events"].first).to include(
+ expect(transaction).to include_event(
"body" => "",
"title" => "GET https://www.google.com"
)
@@ -66,7 +64,7 @@
HTTP.post("https://www.google.com", :json => { :q => "Appsignal" })
- expect(transaction.to_h["events"].first).to include(
+ expect(transaction).to include_event(
"body" => "",
"title" => "POST https://www.google.com"
)
@@ -83,20 +81,14 @@
HTTP.get("https://www.google.com")
end.to raise_error(ExampleException)
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include("namespace" => Appsignal::Transaction::HTTP_REQUEST)
- expect(transaction_hash["events"].first).to include(
+ expect(transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
+ expect(transaction).to include_event(
"body" => "",
"body_format" => Appsignal::EventFormatter::DEFAULT,
"name" => "request.http_rb",
"title" => "GET https://www.google.com"
)
-
- expect(transaction_hash["error"]).to include(
- "backtrace" => kind_of(String),
- "name" => error.class.name,
- "message" => error.message
- )
+ expect(transaction).to have_error(error.class.name, error.message)
end
end
@@ -112,7 +104,7 @@ def to_s
HTTP.get(request_uri.new("http://www.google.com"))
- expect(transaction.to_h["events"].first).to include(
+ expect(transaction).to include_event(
"name" => "request.http_rb",
"title" => "GET http://www.google.com"
)
@@ -123,7 +115,7 @@ def to_s
HTTP.get(URI("http://www.google.com"))
- expect(transaction.to_h["events"].first).to include(
+ expect(transaction).to include_event(
"name" => "request.http_rb",
"title" => "GET http://www.google.com"
)
@@ -134,7 +126,7 @@ def to_s
HTTP.get("http://www.google.com")
- expect(transaction.to_h["events"].first).to include(
+ expect(transaction).to include_event(
"name" => "request.http_rb",
"title" => "GET http://www.google.com"
)
diff --git a/spec/lib/appsignal/integrations/que_spec.rb b/spec/lib/appsignal/integrations/que_spec.rb
index a3bc94b64..aa9dc9b04 100644
--- a/spec/lib/appsignal/integrations/que_spec.rb
+++ b/spec/lib/appsignal/integrations/que_spec.rb
@@ -53,38 +53,27 @@ def perform_que_job(job)
perform_que_job(instance)
end.to change { created_transactions.length }.by(1)
- expect(last_transaction).to be_completed
- transaction_hash = last_transaction.to_h
- expect(transaction_hash).to include(
- "action" => "MyQueJob#run",
- "id" => instance_of(String),
- "namespace" => Appsignal::Transaction::BACKGROUND_JOB
- )
- expect(transaction_hash["error"]).to be_nil
- expect(transaction_hash["events"].first).to include(
- "allocation_count" => kind_of(Integer),
+ transaction = last_transaction
+ expect(transaction).to have_id
+ expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
+ expect(transaction).to have_action("MyQueJob#run")
+ expect(transaction).to_not have_error
+ expect(transaction).to include_event(
"body" => "",
"body_format" => Appsignal::EventFormatter::DEFAULT,
- "child_allocation_count" => kind_of(Integer),
- "child_duration" => kind_of(Float),
- "child_gc_duration" => kind_of(Float),
"count" => 1,
- "gc_duration" => kind_of(Float),
- "start" => kind_of(Float),
- "duration" => kind_of(Float),
"name" => "perform_job.que",
"title" => ""
)
- expect(transaction_hash["sample_data"]).to include(
- "params" => %w[1 birds],
- "metadata" => {
- "attempts" => 0,
- "id" => 123,
- "priority" => 100,
- "queue" => "dfl",
- "run_at" => fixed_time.to_s
- }
+ expect(transaction).to include_params(%w[1 birds])
+ expect(transaction).to include_sample_metadata(
+ "attempts" => 0,
+ "id" => 123,
+ "priority" => 100,
+ "queue" => "dfl",
+ "run_at" => fixed_time.to_s
)
+ expect(transaction).to be_completed
end
end
@@ -100,28 +89,20 @@ def perform_que_job(job)
end.to raise_error(ExampleException)
end.to change { created_transactions.length }.by(1)
- expect(last_transaction).to be_completed
- transaction_hash = last_transaction.to_h
- expect(transaction_hash).to include(
- "action" => "MyQueJob#run",
- "id" => instance_of(String),
- "namespace" => Appsignal::Transaction::BACKGROUND_JOB
- )
- expect(transaction_hash["error"]).to include(
- "backtrace" => kind_of(String),
- "name" => error.class.name,
- "message" => error.message
- )
- expect(transaction_hash["sample_data"]).to include(
- "params" => %w[1 birds],
- "metadata" => {
- "attempts" => 0,
- "id" => 123,
- "priority" => 100,
- "queue" => "dfl",
- "run_at" => fixed_time.to_s
- }
+ transaction = last_transaction
+ expect(transaction).to have_id
+ expect(transaction).to have_action("MyQueJob#run")
+ expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
+ expect(transaction).to have_error(error.class.name, error.message)
+ expect(transaction).to include_params(%w[1 birds])
+ expect(transaction).to include_sample_metadata(
+ "attempts" => 0,
+ "id" => 123,
+ "priority" => 100,
+ "queue" => "dfl",
+ "run_at" => fixed_time.to_s
)
+ expect(transaction).to be_completed
end
end
@@ -133,28 +114,20 @@ def perform_que_job(job)
expect { perform_que_job(instance) }.to change { created_transactions.length }.by(1)
- expect(last_transaction).to be_completed
- transaction_hash = last_transaction.to_h
- expect(transaction_hash).to include(
- "action" => "MyQueJob#run",
- "id" => instance_of(String),
- "namespace" => Appsignal::Transaction::BACKGROUND_JOB
- )
- expect(transaction_hash["error"]).to include(
- "backtrace" => kind_of(String),
- "name" => error.class.name,
- "message" => error.message
- )
- expect(transaction_hash["sample_data"]).to include(
- "params" => %w[1 birds],
- "metadata" => {
- "attempts" => 0,
- "id" => 123,
- "priority" => 100,
- "queue" => "dfl",
- "run_at" => fixed_time.to_s
- }
+ transaction = last_transaction
+ expect(transaction).to have_id
+ expect(transaction).to have_action("MyQueJob#run")
+ expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
+ expect(transaction).to have_error(error.class.name, error.message)
+ expect(transaction).to include_params(%w[1 birds])
+ expect(transaction).to include_sample_metadata(
+ "attempts" => 0,
+ "id" => 123,
+ "priority" => 100,
+ "queue" => "dfl",
+ "run_at" => fixed_time.to_s
)
+ expect(transaction).to be_completed
end
end
@@ -170,9 +143,9 @@ def run(*_args)
it "uses the custom action" do
perform_que_job(instance)
- expect(last_transaction).to be_completed
- transaction_hash = last_transaction.to_h
- expect(transaction_hash).to include("action" => "MyCustomJob#perform")
+ transaction = last_transaction
+ expect(transaction).to have_action("MyCustomJob#perform")
+ expect(transaction).to be_completed
end
end
end
diff --git a/spec/lib/appsignal/integrations/railtie_spec.rb b/spec/lib/appsignal/integrations/railtie_spec.rb
index 7a289a8b9..3f9021884 100644
--- a/spec/lib/appsignal/integrations/railtie_spec.rb
+++ b/spec/lib/appsignal/integrations/railtie_spec.rb
@@ -136,8 +136,8 @@ def subscribe(subscriber)
it "does nothing" do
with_rails_error_reporter do
expect do
- Rails.error.record { raise ExampleStandardError }
- end.to raise_error(ExampleStandardError)
+ Rails.error.record { raise ExampleStandardError, "error message" }
+ end.to raise_error(ExampleStandardError, "error message")
end
expect(created_transactions).to be_empty
@@ -156,24 +156,15 @@ def subscribe(subscriber)
with_rails_error_reporter do
with_current_transaction current_transaction do
- Rails.error.handle { raise ExampleStandardError }
+ Rails.error.handle { raise ExampleStandardError, "error message" }
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "CustomAction",
- "namespace" => "custom",
- "error" => {
- "name" => "ExampleStandardError",
- "message" => "ExampleStandardError",
- "backtrace" => kind_of(String)
- },
- "sample_data" => hash_including(
- "tags" => hash_including(
- "duplicated_tag" => "duplicated value",
- "severity" => "warning"
- )
- )
+ expect(transaction).to have_namespace("custom")
+ expect(transaction).to have_action("CustomAction")
+ expect(transaction).to have_error("ExampleStandardError", "error message")
+ expect(transaction).to include_tags(
+ "duplicated_tag" => "duplicated value",
+ "severity" => "warning"
)
end
end
@@ -188,16 +179,10 @@ def subscribe(subscriber)
given_context = { :tag1 => "value1", :tag2 => "value2" }
Rails.error.handle(:context => given_context) { raise ExampleStandardError }
- transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "sample_data" => hash_including(
- "tags" => hash_including(
- "tag1" => "value1",
- "tag2" => "value2",
- "severity" => "warning"
- )
- )
+ expect(last_transaction).to include_tags(
+ "tag1" => "value1",
+ "tag2" => "value2",
+ "severity" => "warning"
)
end
end
@@ -219,14 +204,9 @@ def subscribe(subscriber)
Rails.error.handle(:context => given_context) { raise ExampleStandardError }
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "sample_data" => hash_including(
- "custom_data" => {
- "array" => [1, 2],
- "hash" => { "one" => 1, "two" => 2 }
- }
- )
+ expect(transaction).to include_custom_data(
+ "array" => [1, 2],
+ "hash" => { "one" => 1, "two" => 2 }
)
end
end
@@ -245,11 +225,8 @@ def subscribe(subscriber)
Rails.error.handle(:context => given_context) { raise ExampleStandardError }
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "namespace" => "context",
- "action" => "ContextAction"
- )
+ expect(transaction).to have_namespace("context")
+ expect(transaction).to have_action("ContextAction")
end
end
end
@@ -301,17 +278,9 @@ def arguments
end
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "ExampleRailsControllerMock#index",
- "metadata" => hash_including(
- "path" => "path",
- "method" => "GET"
- ),
- "sample_data" => hash_including(
- "params" => { "user_id" => 123, "password" => "[FILTERED]" }
- )
- )
+ expect(transaction).to have_action("ExampleRailsControllerMock#index")
+ expect(transaction).to include_metadata("path" => "path", "method" => "GET")
+ expect(transaction).to include_params("user_id" => 123, "password" => "[FILTERED]")
end
it "sets no action if no execution context is present" do
@@ -320,11 +289,7 @@ def arguments
Rails.error.handle { raise ExampleStandardError }
end
- transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => nil
- )
+ expect(last_transaction).to_not have_action
end
end
@@ -340,16 +305,10 @@ def arguments
Rails.error.handle(:context => given_context) { raise ExampleStandardError }
end
- transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "sample_data" => hash_including(
- "tags" => hash_including(
- "tag1" => "value1",
- "tag2" => "value2",
- "severity" => "warning"
- )
- )
+ expect(last_transaction).to include_tags(
+ "tag1" => "value1",
+ "tag2" => "value2",
+ "severity" => "warning"
)
end
end
diff --git a/spec/lib/appsignal/integrations/sidekiq_spec.rb b/spec/lib/appsignal/integrations/sidekiq_spec.rb
index 07d3271cd..b7b42d1bd 100644
--- a/spec/lib/appsignal/integrations/sidekiq_spec.rb
+++ b/spec/lib/appsignal/integrations/sidekiq_spec.rb
@@ -20,17 +20,11 @@ def call_handler
end
def expect_error_on_transaction
- expect(last_transaction.to_h).to include(
- "error" => hash_including(
- "name" => "ExampleStandardError",
- "message" => "uh oh",
- "backtrace" => kind_of(String)
- )
- )
+ expect(last_transaction).to have_error("ExampleStandardError", "uh oh")
end
def expect_no_error_on_transaction
- expect(last_transaction.to_h).to include("error" => nil)
+ expect(last_transaction).to_not have_error
end
context "when sidekiq_report_errors = none" do
@@ -90,21 +84,13 @@ def expect_report_internal_error
described_class.new.call(exception, job_context)
end.to(change { created_transactions.count }.by(1))
- transaction_hash = last_transaction.to_h
- expect(transaction_hash).to include(
- "action" => "SidekiqInternal",
- "error" => hash_including(
- "name" => "ExampleStandardError",
- "message" => "uh oh",
- "backtrace" => kind_of(String)
- )
- )
- expect(transaction_hash["sample_data"]).to include(
- "params" => {
- "jobstr" => "{ bad json }"
- }
+ transaction = last_transaction
+ expect(transaction).to have_action("SidekiqInternal")
+ expect(transaction).to have_error("ExampleStandardError", "uh oh")
+ expect(transaction).to include_params(
+ "jobstr" => "{ bad json }"
)
- expect(transaction_hash["metadata"]).to include(
+ expect(transaction).to include_metadata(
"sidekiq_error" => "Sidekiq internal error!"
)
end
@@ -149,17 +135,11 @@ def call_handler
end
def expect_error_on_transaction
- expect(last_transaction.to_h).to include(
- "error" => hash_including(
- "name" => "ExampleStandardError",
- "message" => "uh oh",
- "backtrace" => kind_of(String)
- )
- )
+ expect(last_transaction).to have_error("ExampleStandardError", "uh oh")
end
def expect_no_error_on_transaction
- expect(last_transaction.to_h).to include("error" => nil)
+ expect(last_transaction).to_not have_error
end
context "when sidekiq_report_errors = none" do
@@ -270,9 +250,8 @@ class DelayedTestClass; end
it "filters selected arguments" do
perform_sidekiq_job
- transaction_hash = transaction.to_h
- expect(transaction_hash["sample_data"]).to include(
- "params" => [
+ expect(transaction).to include_params(
+ [
"foo",
{
"foo" => "[FILTERED]",
@@ -293,10 +272,7 @@ class DelayedTestClass; end
it "replaces the last argument (the secret bag) with an [encrypted data] string" do
perform_sidekiq_job
- transaction_hash = transaction.to_h
- expect(transaction_hash["sample_data"]).to include(
- "params" => expected_args << "[encrypted data]"
- )
+ expect(transaction).to include_params(expected_args << "[encrypted data]")
end
end
@@ -319,11 +295,8 @@ class DelayedTestClass; end
it "uses the delayed class and method name for the action" do
perform_sidekiq_job
- transaction_hash = transaction.to_h
- expect(transaction_hash["action"]).to eq("DelayedTestClass.foo_method")
- expect(transaction_hash["sample_data"]).to include(
- "params" => ["bar" => "baz"]
- )
+ expect(transaction).to have_action("DelayedTestClass.foo_method")
+ expect(transaction).to include_params(["bar" => "baz"])
end
context "when job arguments is a malformed YAML object", :with_yaml_parse_error => true do
@@ -332,9 +305,8 @@ class DelayedTestClass; end
it "logs a warning and uses the default argument" do
perform_sidekiq_job
- transaction_hash = transaction.to_h
- expect(transaction_hash["action"]).to eq("Sidekiq::Extensions::DelayedClass#perform")
- expect(transaction_hash["sample_data"]).to include("params" => [])
+ expect(transaction).to have_action("Sidekiq::Extensions::DelayedClass#perform")
+ expect(transaction).to include_params([])
expect(log_contents(log)).to contains_log(:warn, "Unable to load YAML")
end
end
@@ -359,11 +331,8 @@ class DelayedTestClass; end
it "uses the delayed class and method name for the action" do
perform_sidekiq_job
- transaction_hash = transaction.to_h
- expect(transaction_hash["action"]).to eq("DelayedTestClass#foo_method")
- expect(transaction_hash["sample_data"]).to include(
- "params" => ["bar" => "baz"]
- )
+ expect(transaction).to have_action("DelayedTestClass#foo_method")
+ expect(transaction).to include_params(["bar" => "baz"])
end
context "when job arguments is a malformed YAML object", :with_yaml_parse_error => true do
@@ -372,9 +341,8 @@ class DelayedTestClass; end
it "logs a warning and uses the default argument" do
perform_sidekiq_job
- transaction_hash = transaction.to_h
- expect(transaction_hash["action"]).to eq("Sidekiq::Extensions::DelayedModel#perform")
- expect(transaction_hash["sample_data"]).to include("params" => [])
+ expect(transaction).to have_action("Sidekiq::Extensions::DelayedModel#perform")
+ expect(transaction).to include_params([])
expect(log_contents(log)).to contains_log(:warn, "Unable to load YAML")
end
end
@@ -394,31 +362,20 @@ class DelayedTestClass; end
perform_sidekiq_job { raise error, "uh oh" }
end.to raise_error(error)
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "id" => jid,
- "action" => "TestClass#perform",
- "error" => {
- "name" => "ExampleException",
- "message" => "uh oh",
- # TODO: backtrace should be an Array of Strings
- # https://github.com/appsignal/appsignal-agent/issues/294
- "backtrace" => kind_of(String)
- },
- "metadata" => {
- "extra" => "data",
- "queue" => "default",
- "retry_count" => "0"
- },
- "namespace" => namespace,
- "sample_data" => {
- "environment" => {},
- "params" => expected_args,
- "tags" => {},
- "breadcrumbs" => []
- }
+ expect(transaction).to have_id(jid)
+ expect(transaction).to have_namespace(namespace)
+ expect(transaction).to have_action("TestClass#perform")
+ expect(transaction).to have_error("ExampleException", "uh oh")
+ expect(transaction).to include_metadata(
+ "extra" => "data",
+ "queue" => "default",
+ "retry_count" => "0"
)
- expect_transaction_to_have_sidekiq_event(transaction_hash)
+ expect(transaction).to_not include_environment
+ expect(transaction).to include_params(expected_args)
+ expect(transaction).to_not include_tags
+ expect(transaction).to_not include_breadcrumbs
+ expect_transaction_to_have_sidekiq_event(transaction)
end
end
@@ -438,17 +395,17 @@ class DelayedTestClass; end
end
expect(created_transactions.count).to eq(2)
- expected_transaction = {
- "namespace" => "background_job",
- "action" => "TestClass#perform",
- "sample_data" => hash_including(
- "tags" => hash_including("test_tag" => "value")
- )
- }
- sidekiq_transaction = created_transactions.first.to_h
- error_reporter_transaction = created_transactions.last.to_h
- expect(sidekiq_transaction).to include(expected_transaction)
- expect(error_reporter_transaction).to include(expected_transaction)
+ tags = { "test_tag" => "value" }
+ sidekiq_transaction = created_transactions.first
+ error_reporter_transaction = created_transactions.last
+
+ expect(sidekiq_transaction).to have_namespace("background_job")
+ expect(sidekiq_transaction).to have_action("TestClass#perform")
+ expect(sidekiq_transaction).to include_tags(tags)
+
+ expect(error_reporter_transaction).to have_namespace("background_job")
+ expect(error_reporter_transaction).to have_action("TestClass#perform")
+ expect(error_reporter_transaction).to include_tags(tags)
end
end
end
@@ -461,30 +418,21 @@ class DelayedTestClass; end
.with("sidekiq_queue_job_count", 1, { :queue => "default", :status => :processed })
perform_sidekiq_job
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "id" => jid,
- "action" => "TestClass#perform",
- "error" => nil,
- "metadata" => {
- "extra" => "data",
- "queue" => "default",
- "retry_count" => "0"
- },
- "namespace" => namespace,
- "sample_data" => {
- "environment" => {},
- "params" => expected_args,
- "tags" => {},
- "breadcrumbs" => []
- }
- )
- # TODO: Not available in transaction.to_h yet.
- # https://github.com/appsignal/appsignal-agent/issues/293
- expect(transaction.request.env).to eq(
- :queue_start => Time.parse("2001-01-01 10:00:00UTC").to_f
+ expect(transaction).to have_id(jid)
+ expect(transaction).to have_namespace(namespace)
+ expect(transaction).to have_action("TestClass#perform")
+ expect(transaction).to_not have_error
+ expect(transaction).to_not include_tags
+ expect(transaction).to_not include_environment
+ expect(transaction).to_not include_breadcrumbs
+ expect(transaction).to_not include_params(expected_args)
+ expect(transaction).to include_metadata(
+ "extra" => "data",
+ "queue" => "default",
+ "retry_count" => "0"
)
- expect_transaction_to_have_sidekiq_event(transaction_hash)
+ expect(transaction).to have_queue_start(Time.parse("2001-01-01 10:00:00UTC").to_i * 1000)
+ expect_transaction_to_have_sidekiq_event(transaction)
end
end
@@ -505,10 +453,9 @@ def transaction
last_transaction
end
- def expect_transaction_to_have_sidekiq_event(transaction_hash)
- events = transaction_hash["events"]
- expect(events.count).to eq(1)
- expect(events.first).to include(
+ def expect_transaction_to_have_sidekiq_event(transaction)
+ expect(transaction.to_h["events"].count).to eq(1)
+ expect(transaction).to include_event(
"name" => "perform_job.sidekiq",
"title" => "",
"count" => 1,
@@ -621,25 +568,18 @@ def perform(*_args)
perform_sidekiq_job(ActiveJobSidekiqTestJob, given_args)
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "ActiveJobSidekiqTestJob#perform",
- "error" => nil,
- "namespace" => namespace,
- "metadata" => hash_including(
- "queue" => "default"
- ),
- "sample_data" => hash_including(
- "environment" => {},
- "params" => [expected_args],
- "tags" => expected_tags.merge("queue" => "default")
- )
- )
- expect(transaction.request.env).to eq(:queue_start => time.to_f)
- events = transaction_hash["events"]
+ expect(transaction).to have_namespace(namespace)
+ expect(transaction).to have_action("ActiveJobSidekiqTestJob#perform")
+ expect(transaction).to_not have_error
+ expect(transaction).to include_metadata("queue" => "default")
+ expect(transaction).to_not include_environment
+ expect(transaction).to include_params([expected_args])
+ expect(transaction).to include_tags(expected_tags.merge("queue" => "default"))
+ expect(transaction).to have_queue_start(time.to_i * 1000)
+
+ events = transaction.to_h["events"]
.sort_by { |e| e["start"] }
.map { |event| event["name"] }
-
expect(events).to eq(expected_perform_events)
end
@@ -650,29 +590,18 @@ def perform(*_args)
end.to raise_error(RuntimeError, "uh oh")
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "ActiveJobSidekiqErrorTestJob#perform",
- "error" => {
- "name" => "RuntimeError",
- "message" => "uh oh",
- "backtrace" => kind_of(String)
- },
- "namespace" => namespace,
- "metadata" => hash_including(
- "queue" => "default"
- ),
- "sample_data" => hash_including(
- "environment" => {},
- "params" => [expected_args],
- "tags" => expected_tags.merge("queue" => "default")
- )
- )
- expect(transaction.request.env).to eq(:queue_start => time.to_f)
- events = transaction_hash["events"]
+ expect(transaction).to have_namespace(namespace)
+ expect(transaction).to have_action("ActiveJobSidekiqErrorTestJob#perform")
+ expect(transaction).to have_error("RuntimeError", "uh oh")
+ expect(transaction).to include_metadata("queue" => "default")
+ expect(transaction).to_not include_environment
+ expect(transaction).to include_params([expected_args])
+ expect(transaction).to include_tags(expected_tags.merge("queue" => "default"))
+ expect(transaction).to have_queue_start(time.to_i * 1000)
+
+ events = transaction.to_h["events"]
.sort_by { |e| e["start"] }
.map { |event| event["name"] }
-
expect(events).to eq(expected_perform_events)
end
end
@@ -691,13 +620,10 @@ def welcome(*args)
perform_mailer(ActionMailerSidekiqTestJob, :welcome, given_args)
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "ActionMailerSidekiqTestJob#welcome",
- "sample_data" => hash_including(
- "params" => ["ActionMailerSidekiqTestJob", "welcome",
- "deliver_now"] + expected_wrapped_args
- )
+ expect(transaction).to have_action("ActionMailerSidekiqTestJob#welcome")
+ expect(transaction).to include_params(
+ ["ActionMailerSidekiqTestJob", "welcome",
+ "deliver_now"] + expected_wrapped_args
)
end
end
diff --git a/spec/lib/appsignal/rack/abstract_middleware_spec.rb b/spec/lib/appsignal/rack/abstract_middleware_spec.rb
index 2ba9ee93c..54cd2ed62 100644
--- a/spec/lib/appsignal/rack/abstract_middleware_spec.rb
+++ b/spec/lib/appsignal/rack/abstract_middleware_spec.rb
@@ -1,5 +1,5 @@
describe Appsignal::Rack::AbstractMiddleware do
- let(:app) { double(:call => true) }
+ let(:app) { DummyApp.new }
let(:request_path) { "/some/path" }
let(:env) do
Rack::MockRequest.env_for(
@@ -9,224 +9,184 @@
)
end
let(:options) { {} }
- let(:middleware) { Appsignal::Rack::AbstractMiddleware.new(app, options) }
+ let(:middleware) { described_class.new(app, options) }
before(:context) { start_agent }
around { |example| keep_transactions { example.run } }
- def make_request(env)
+ def make_request
middleware.call(env)
end
- def make_request_with_error(env, error_class, error_message)
- expect { make_request(env) }.to raise_error(error_class, error_message)
+ def make_request_with_error(error_class, error_message)
+ expect { make_request }.to raise_error(error_class, error_message)
end
describe "#call" do
- context "when appsignal is not active" do
+ context "when not active" do
before { allow(Appsignal).to receive(:active?).and_return(false) }
- it "does not instrument requests" do
- expect { make_request(env) }.to_not(change { created_transactions.count })
+ it "does not instrument the request" do
+ expect { make_request }.to_not(change { created_transactions.count })
end
it "calls the next middleware in the stack" do
- expect(app).to receive(:call).with(env)
- make_request(env)
+ make_request
+ expect(app).to be_called
end
end
context "when appsignal is active" do
before { allow(Appsignal).to receive(:active?).and_return(true) }
- it "calls the next middleware in the stack" do
- make_request(env)
+ it "creates a transaction for the request" do
+ expect { make_request }.to(change { created_transactions.count }.by(1))
- expect(app).to have_received(:call).with(env)
+ expect(last_transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
end
- context "without an exception" do
- it "create a transaction for the request" do
- expect { make_request(env) }.to(change { created_transactions.count }.by(1))
+ context "without an error" do
+ before { make_request }
- expect(last_transaction.to_h).to include(
- "namespace" => Appsignal::Transaction::HTTP_REQUEST,
- "action" => nil,
- "error" => nil
- )
+ it "calls the next middleware in the stack" do
+ expect(app).to be_called
end
- it "reports a process.abstract event" do
- make_request(env)
-
- expect(last_transaction.to_h).to include(
- "events" => [
- hash_including(
- "body" => "",
- "body_format" => Appsignal::EventFormatter::DEFAULT,
- "count" => 1,
- "name" => "process.abstract",
- "title" => ""
- )
- ]
- )
+ it "does not record an error" do
+ expect(last_transaction).to_not have_error
end
- context "when instrument_span_name option is nil" do
- let(:options) { { :instrument_span_name => nil } }
-
- it "does not report an event" do
- make_request(env)
-
- expect(last_transaction.to_h).to include(
- "events" => []
- )
- end
+ it "records an instrumentation event" do
+ expect(last_transaction).to include_event(:name => "process.abstract")
end
it "completes the transaction" do
- make_request(env)
expect(last_transaction).to be_completed
+ expect(Appsignal::Transaction.current)
+ .to be_kind_of(Appsignal::Transaction::NilTransaction)
+ end
+
+ context "when instrument_span_name option is nil" do
+ let(:options) { { :instrument_span_name => nil } }
+
+ it "does not record an instrumentation event" do
+ expect(last_transaction).to_not include_events
+ end
end
end
- context "with an exception" do
+ context "with an error" do
let(:error) { ExampleException.new("error message") }
let(:app) { lambda { |_env| raise ExampleException, "error message" } }
- before do
- expect { make_request_with_error(env, ExampleException, "error message") }
- .to(change { created_transactions.count }.by(1))
- end
- it "creates a transaction for the request and records the exception" do
- expect(last_transaction.to_h).to include(
- "error" => hash_including(
- "name" => "ExampleException",
- "message" => "error message",
- "backtrace" => kind_of(String)
- )
- )
- end
+ it "create a transaction for the request" do
+ expect { make_request_with_error(ExampleException, "error message") }
+ .to(change { created_transactions.count }.by(1))
- it "completes the transaction" do
- expect(last_transaction).to be_completed
+ expect(last_transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
end
- context "with :report_errors set to false" do
- let(:app) { lambda { |_env| raise ExampleException, "error message" } }
- let(:options) { { :report_errors => false } }
-
- it "does not record the exception on the transaction" do
- make_request_with_error(env, ExampleException, "error message")
+ describe "error" do
+ before do
+ make_request_with_error(ExampleException, "error message")
+ end
- expect(last_transaction.to_h).to include("error" => nil)
+ it "records the error" do
+ expect(last_transaction).to have_error("ExampleException", "error message")
end
- end
- context "with :report_errors set to true" do
- let(:app) { lambda { |_env| raise ExampleException, "error message" } }
- let(:options) { { :report_errors => true } }
+ it "completes the transaction" do
+ expect(last_transaction).to be_completed
+ expect(Appsignal::Transaction.current)
+ .to be_kind_of(Appsignal::Transaction::NilTransaction)
+ end
- it "records the exception on the transaction" do
- make_request_with_error(env, ExampleException, "error message")
+ context "with :report_errors set to false" do
+ let(:app) { lambda { |_env| raise ExampleException, "error message" } }
+ let(:options) { { :report_errors => false } }
- expect(last_transaction.to_h).to include(
- "error" => hash_including(
- "name" => "ExampleException",
- "message" => "error message"
- )
- )
+ it "does not record the exception on the transaction" do
+ expect(last_transaction).to_not have_error
+ end
end
- end
- context "with :report_errors set to a lambda that returns false" do
- let(:app) { lambda { |_env| raise ExampleException, "error message" } }
- let(:options) { { :report_errors => lambda { |_env| false } } }
+ context "with :report_errors set to true" do
+ let(:app) { lambda { |_env| raise ExampleException, "error message" } }
+ let(:options) { { :report_errors => true } }
- it "does not record the exception on the transaction" do
- make_request_with_error(env, ExampleException, "error message")
-
- expect(last_transaction.to_h).to include("error" => nil)
+ it "records the exception on the transaction" do
+ expect(last_transaction).to have_error("ExampleException", "error message")
+ end
end
- end
- context "with :report_errors set to a lambda that returns true" do
- let(:app) { lambda { |_env| raise ExampleException, "error message" } }
- let(:options) { { :report_errors => lambda { |_env| true } } }
+ context "with :report_errors set to a lambda that returns false" do
+ let(:app) { lambda { |_env| raise ExampleException, "error message" } }
+ let(:options) { { :report_errors => lambda { |_env| false } } }
- it "records the exception on the transaction" do
- make_request_with_error(env, ExampleException, "error message")
+ it "does not record the exception on the transaction" do
+ expect(last_transaction).to_not have_error
+ end
+ end
+
+ context "with :report_errors set to a lambda that returns true" do
+ let(:app) { lambda { |_env| raise ExampleException, "error message" } }
+ let(:options) { { :report_errors => lambda { |_env| true } } }
- expect(last_transaction.to_h).to include(
- "error" => hash_including(
- "name" => "ExampleException",
- "message" => "error message"
- )
- )
+ it "records the exception on the transaction" do
+ expect(last_transaction).to have_error("ExampleException", "error message")
+ end
end
end
end
context "without action name metadata" do
it "reports no action name" do
- make_request(env)
+ make_request
- expect(last_transaction.to_h).to include("action" => nil)
+ expect(last_transaction).to_not have_action
end
end
context "with appsignal.route env" do
- before do
- env["appsignal.route"] = "POST /my-route"
- end
-
it "reports the appsignal.route value as the action name" do
- make_request(env)
+ env["appsignal.route"] = "POST /my-route"
+ make_request
- expect(last_transaction.to_h).to include("action" => "POST /my-route")
+ expect(last_transaction).to have_action("POST /my-route")
end
end
context "with appsignal.action env" do
- before do
- env["appsignal.action"] = "POST /my-action"
- end
-
it "reports the appsignal.route value as the action name" do
- make_request(env)
+ env["appsignal.action"] = "POST /my-action"
+ make_request
- expect(last_transaction.to_h).to include("action" => "POST /my-action")
+ expect(last_transaction).to have_action("POST /my-action")
end
end
describe "request metadata" do
- before do
+ it "sets request metadata" do
env.merge!("PATH_INFO" => "/some/path", "REQUEST_METHOD" => "GET")
- end
+ make_request
- it "sets request metadata" do
- make_request(env)
-
- expect(last_transaction.to_h).to include(
- "metadata" => {
- "method" => "GET",
- "path" => "/some/path"
- },
- "sample_data" => hash_including(
- "environment" => hash_including(
- "REQUEST_METHOD" => "GET",
- "PATH_INFO" => "/some/path"
- # and more, but we don't need to test Rack mock defaults
- )
- )
+ expect(last_transaction).to include_metadata(
+ "method" => "GET",
+ "path" => "/some/path"
+ )
+ expect(last_transaction).to include_environment(
+ "REQUEST_METHOD" => "GET",
+ "PATH_INFO" => "/some/path"
+ # and more, but we don't need to test Rack mock defaults
)
end
context "with an invalid HTTP request method" do
it "stores the invalid HTTP request method" do
- make_request(env.merge("REQUEST_METHOD" => "FOO"))
+ env["REQUEST_METHOD"] = "FOO"
+ make_request
- expect(last_transaction.to_h["metadata"]).to include("method" => "FOO")
+ expect(last_transaction).to include_metadata("method" => "FOO")
end
end
@@ -239,54 +199,45 @@ def request_method
let(:options) { { :request_class => BrokenRequestMethodRequest } }
it "does not store the invalid HTTP request method" do
- make_request(env.merge("REQUEST_METHOD" => "FOO"))
+ env["REQUEST_METHOD"] = "FOO"
+ make_request
- expect(last_transaction.to_h["metadata"]).to_not have_key("method")
+ expect(last_transaction).to_not include_metadata("method" => anything)
end
end
it "sets request parameters" do
- make_request(env)
-
- expect(last_transaction.to_h).to include(
- "sample_data" => hash_including(
- "params" => hash_including(
- "page" => "2",
- "query" => "lorem"
- )
- )
+ make_request
+
+ expect(last_transaction).to include_params(
+ "page" => "2",
+ "query" => "lorem"
)
end
context "when setting custom params" do
let(:app) do
- lambda { |_env| Appsignal::Transaction.current.set_params("custom" => "param") }
+ DummyApp.new do |_env|
+ Appsignal::Transaction.current.set_params("custom" => "param")
+ end
end
it "allow custom request parameters to be set" do
- make_request(env)
-
- expect(last_transaction.to_h).to include(
- "sample_data" => hash_including(
- "params" => hash_including(
- "custom" => "param"
- )
- )
- )
+ make_request
+
+ expect(last_transaction).to include_params("custom" => "param")
end
end
end
context "with queue start header" do
let(:queue_start_time) { fixed_time * 1_000 }
- before do
- env["HTTP_X_REQUEST_START"] = "t=#{queue_start_time.to_i}" # in milliseconds
- end
it "sets the queue start" do
- make_request(env)
+ env["HTTP_X_REQUEST_START"] = "t=#{queue_start_time.to_i}" # in milliseconds
+ make_request
- expect(last_transaction.ext.queue_start).to eq(queue_start_time)
+ expect(last_transaction).to have_queue_start(queue_start_time)
end
end
@@ -316,13 +267,9 @@ def filtered_params
end
it "uses the overridden request class and params method to fetch params" do
- make_request(env)
+ make_request
- expect(last_transaction.to_h).to include(
- "sample_data" => hash_including(
- "params" => { "abc" => "123" }
- )
- )
+ expect(last_transaction).to include_params("abc" => "123")
end
end
@@ -332,23 +279,23 @@ def filtered_params
end
it "uses the existing transaction" do
- make_request(env)
+ make_request
- expect { make_request(env) }.to_not(change { created_transactions.count })
+ expect { make_request }.to_not(change { created_transactions.count })
end
- context "with exception" do
+ context "with error" do
let(:app) { lambda { |_env| raise ExampleException, "error message" } }
- it "doesn't record the exception on the transaction" do
- make_request_with_error(env, ExampleException, "error message")
+ it "doesn't record the error on the transaction" do
+ make_request_with_error(ExampleException, "error message")
- expect(last_transaction.to_h).to include("error" => nil)
+ expect(last_transaction).to_not have_error
end
end
it "doesn't complete the existing transaction" do
- make_request(env)
+ make_request
expect(env[Appsignal::Rack::APPSIGNAL_TRANSACTION]).to_not be_completed
end
@@ -357,9 +304,9 @@ def filtered_params
it "does not overwrite the action name" do
env[Appsignal::Rack::APPSIGNAL_TRANSACTION].set_action("My custom action")
env["appsignal.action"] = "POST /my-action"
- make_request(env)
+ make_request
- expect(last_transaction.to_h).to include("action" => "My custom action")
+ expect(last_transaction).to have_action("My custom action")
end
end
@@ -367,10 +314,10 @@ def filtered_params
let(:app) { lambda { |_env| raise ExampleException, "error message" } }
let(:options) { { :report_errors => false } }
- it "does not record the exception on the transaction" do
- make_request_with_error(env, ExampleException, "error message")
+ it "does not record the error on the transaction" do
+ make_request_with_error(ExampleException, "error message")
- expect(last_transaction.to_h).to include("error" => nil)
+ expect(last_transaction).to_not have_error
end
end
@@ -378,15 +325,10 @@ def filtered_params
let(:app) { lambda { |_env| raise ExampleException, "error message" } }
let(:options) { { :report_errors => true } }
- it "records the exception on the transaction" do
- make_request_with_error(env, ExampleException, "error message")
+ it "records the error on the transaction" do
+ make_request_with_error(ExampleException, "error message")
- expect(last_transaction.to_h).to include(
- "error" => hash_including(
- "name" => "ExampleException",
- "message" => "error message"
- )
- )
+ expect(last_transaction).to have_error("ExampleException", "error message")
end
end
@@ -395,9 +337,9 @@ def filtered_params
let(:options) { { :report_errors => lambda { |_env| false } } }
it "does not record the exception on the transaction" do
- make_request_with_error(env, ExampleException, "error message")
+ make_request_with_error(ExampleException, "error message")
- expect(last_transaction.to_h).to include("error" => nil)
+ expect(last_transaction).to_not have_error
end
end
@@ -405,15 +347,10 @@ def filtered_params
let(:app) { lambda { |_env| raise ExampleException, "error message" } }
let(:options) { { :report_errors => lambda { |_env| true } } }
- it "records the exception on the transaction" do
- make_request_with_error(env, ExampleException, "error message")
+ it "records the error on the transaction" do
+ make_request_with_error(ExampleException, "error message")
- expect(last_transaction.to_h).to include(
- "error" => hash_including(
- "name" => "ExampleException",
- "message" => "error message"
- )
- )
+ expect(last_transaction).to have_error("ExampleException", "error message")
end
end
end
diff --git a/spec/lib/appsignal/rack/event_handler_spec.rb b/spec/lib/appsignal/rack/event_handler_spec.rb
index 202a17e2e..aec2d237b 100644
--- a/spec/lib/appsignal/rack/event_handler_spec.rb
+++ b/spec/lib/appsignal/rack/event_handler_spec.rb
@@ -31,12 +31,10 @@ def on_error(error)
expect { on_start }.to change { created_transactions.length }.by(1)
transaction = last_transaction
- expect(transaction.to_h).to include(
- "id" => kind_of(String),
- "namespace" => Appsignal::Transaction::HTTP_REQUEST
- )
+ expect(transaction).to have_id
+ expect(transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
- expect(Appsignal::Transaction.current).to eq(last_transaction)
+ expect(Appsignal::Transaction.current).to eq(transaction)
end
context "when the handler is nested in another EventHandler" do
@@ -127,13 +125,7 @@ def on_error(error)
on_start
on_error(ExampleStandardError.new("the error"))
- expect(last_transaction.to_h).to include(
- "error" => {
- "name" => "ExampleStandardError",
- "message" => "the error",
- "backtrace" => kind_of(String)
- }
- )
+ expect(last_transaction).to have_error("ExampleStandardError", "the error")
end
context "when the handler is nested in another EventHandler" do
@@ -141,7 +133,7 @@ def on_error(error)
on_start
described_class.new.on_error(request, response, ExampleStandardError.new("the error"))
- expect(last_transaction.to_h).to include("error" => nil)
+ expect(last_transaction).to_not have_error
end
end
@@ -174,11 +166,9 @@ def on_finish(given_request = request, given_response = response)
on_finish
- expect(last_transaction.to_h).to include(
- "action" => nil,
- "sample_data" => {},
- "events" => []
- )
+ expect(last_transaction).to_not have_action
+ expect(last_transaction).to_not include_events
+ expect(last_transaction).to include("sample_data" => {})
expect(last_transaction).to_not be_completed
end
@@ -186,18 +176,12 @@ def on_finish(given_request = request, given_response = response)
on_start
on_finish
- expect(last_transaction.to_h).to include(
- # The action is not set on purpose, as we can't set a normalized route
- # It requires the app to set an action name
- "action" => nil,
- "sample_data" => hash_including(
- "environment" => {
- "REQUEST_METHOD" => "GET",
- "PATH_INFO" => "/path"
- }
- )
+ expect(last_transaction).to_not have_action
+ expect(last_transaction).to include_environment(
+ "REQUEST_METHOD" => "GET",
+ "PATH_INFO" => "/path"
)
- expect(last_transaction.ext.queue_start).to eq(queue_start_time)
+ expect(last_transaction).to have_queue_start(queue_start_time)
expect(last_transaction).to be_completed
end
@@ -206,18 +190,14 @@ def on_finish(given_request = request, given_response = response)
on_start
on_finish(request, nil)
- expect(last_transaction.to_h).to include(
- # The action is not set on purpose, as we can't set a normalized route
- # It requires the app to set an action name
- "action" => nil,
- "sample_data" => hash_including(
- "environment" => {
- "REQUEST_METHOD" => "GET",
- "PATH_INFO" => "/path"
- }
- )
+ # The action is not set on purpose, as we can't set a normalized route
+ # It requires the app to set an action name
+ expect(last_transaction).to_not have_action
+ expect(last_transaction).to include_environment(
+ "REQUEST_METHOD" => "GET",
+ "PATH_INFO" => "/path"
)
- expect(last_transaction.ext.queue_start).to eq(queue_start_time)
+ expect(last_transaction).to have_queue_start(queue_start_time)
expect(last_transaction).to be_completed
end
@@ -225,7 +205,7 @@ def on_finish(given_request = request, given_response = response)
on_start
on_finish(request, nil)
- expect(last_transaction.to_h.dig("sample_data", "tags")).to_not have_key("response_status")
+ expect(last_transaction).to_not include_tags("response_status" => anything)
end
it "does not report a response_status counter metric" do
@@ -242,11 +222,7 @@ def on_finish(given_request = request, given_response = response)
on_error(ExampleStandardError.new("the error"))
on_finish(request, nil)
- expect(last_transaction.to_h).to include(
- "sample_data" => hash_including(
- "tags" => { "response_status" => 500 }
- )
- )
+ expect(last_transaction).to include_tags("response_status" => 500)
end
it "increments the response status counter for response status 500" do
@@ -283,12 +259,10 @@ def on_finish(given_request = request, given_response = response)
on_start
described_class.new.on_finish(request, response)
- expect(last_transaction.to_h).to include(
- "action" => nil,
- "metadata" => {},
- "sample_data" => {},
- "events" => []
- )
+ expect(last_transaction).to_not have_action
+ expect(last_transaction).to_not include_metadata
+ expect(last_transaction).to_not include_events
+ expect(last_transaction.to_h).to include("sample_data" => {})
expect(last_transaction).to_not be_completed
end
end
@@ -298,25 +272,14 @@ def on_finish(given_request = request, given_response = response)
last_transaction.set_action("My action")
on_finish
- expect(last_transaction.to_h).to include(
- "action" => "My action"
- )
+ expect(last_transaction).to have_action("My action")
end
it "finishes the process_request.rack event" do
on_start
on_finish
- expect(last_transaction.to_h).to include(
- "events" => [
- hash_including(
- "name" => "process_request.rack",
- "title" => "",
- "body" => "",
- "body_format" => Appsignal::EventFormatter::DEFAULT
- )
- ]
- )
+ expect(last_transaction).to include_event("name" => "process_request.rack")
end
context "with response" do
@@ -324,11 +287,7 @@ def on_finish(given_request = request, given_response = response)
on_start
on_finish
- expect(last_transaction.to_h).to include(
- "sample_data" => hash_including(
- "tags" => { "response_status" => 200 }
- )
- )
+ expect(last_transaction).to include_tags("response_status" => 200)
end
it "increments the response status counter for response status" do
@@ -345,11 +304,7 @@ def on_finish(given_request = request, given_response = response)
on_error(ExampleStandardError.new("the error"))
on_finish
- expect(last_transaction.to_h).to include(
- "sample_data" => hash_including(
- "tags" => { "response_status" => 200 }
- )
- )
+ expect(last_transaction).to include_tags("response_status" => 200)
end
it "increments the response status counter based on the response" do
diff --git a/spec/lib/appsignal/rack/generic_instrumentation_spec.rb b/spec/lib/appsignal/rack/generic_instrumentation_spec.rb
index fef5f1b7b..927562cb3 100644
--- a/spec/lib/appsignal/rack/generic_instrumentation_spec.rb
+++ b/spec/lib/appsignal/rack/generic_instrumentation_spec.rb
@@ -14,17 +14,7 @@ def make_request(env)
it "reports a process_action.generic event" do
make_request(env)
- expect(last_transaction.to_h).to include(
- "events" => [
- hash_including(
- "body" => "",
- "body_format" => Appsignal::EventFormatter::DEFAULT,
- "count" => 1,
- "name" => "process_action.generic",
- "title" => ""
- )
- ]
- )
+ expect(last_transaction).to include_event("name" => "process_action.generic")
end
end
@@ -32,7 +22,7 @@ def make_request(env)
it "reports 'unknown' as the action name" do
make_request(env)
- expect(last_transaction.to_h).to include("action" => "unknown")
+ expect(last_transaction).to have_action("unknown")
end
end
end
diff --git a/spec/lib/appsignal/rack/grape_middleware_spec.rb b/spec/lib/appsignal/rack/grape_middleware_spec.rb
index bfa3a35c3..4312934d8 100644
--- a/spec/lib/appsignal/rack/grape_middleware_spec.rb
+++ b/spec/lib/appsignal/rack/grape_middleware_spec.rb
@@ -83,13 +83,7 @@ def make_request_with_exception(env, exception_class, exception_message)
it "sets the error" do
make_request_with_exception(env, ExampleException, "error message")
- expect(last_transaction.to_h).to include(
- "error" => {
- "name" => "ExampleException",
- "message" => "error message",
- "backtrace" => kind_of(String)
- }
- )
+ expect(last_transaction).to have_error("ExampleException", "error message")
end
context "with env['grape.skip_appsignal_error'] = true" do
@@ -106,7 +100,7 @@ def make_request_with_exception(env, exception_class, exception_message)
it "does not add the error" do
make_request_with_exception(env, ExampleException, "error message")
- expect(last_transaction).to include("error" => nil)
+ expect(last_transaction).to_not have_error
end
end
end
@@ -129,13 +123,8 @@ def make_request_with_exception(env, exception_class, exception_message)
it "sets non-unique route path" do
make_request(env)
- expect(last_transaction.to_h).to include(
- "action" => "GET::GrapeExample::Api#/hello",
- "metadata" => {
- "path" => "/hello",
- "method" => "GET"
- }
- )
+ expect(last_transaction).to have_action("GET::GrapeExample::Api#/hello")
+ expect(last_transaction).to include_metadata("path" => "/hello", "method" => "GET")
end
end
@@ -162,13 +151,8 @@ def make_request_with_exception(env, exception_class, exception_message)
it "sets non-unique route_param path" do
make_request(env)
- expect(last_transaction.to_h).to include(
- "action" => "GET::GrapeExample::Api#/users/:id/",
- "metadata" => {
- "path" => "/users/:id/",
- "method" => "GET"
- }
- )
+ expect(last_transaction).to have_action("GET::GrapeExample::Api#/users/:id/")
+ expect(last_transaction).to include_metadata("path" => "/users/:id/", "method" => "GET")
end
end
@@ -190,13 +174,9 @@ def make_request_with_exception(env, exception_class, exception_message)
it "sets namespaced path" do
make_request(env)
- expect(last_transaction.to_h).to include(
- "action" => "POST::GrapeExample::Api#/v1/beta/ping",
- "metadata" => {
- "path" => "/v1/beta/ping",
- "method" => "POST"
- }
- )
+ expect(last_transaction).to have_action("POST::GrapeExample::Api#/v1/beta/ping")
+ expect(last_transaction).to include_metadata("path" => "/v1/beta/ping",
+ "method" => "POST")
end
end
@@ -218,12 +198,10 @@ def make_request_with_exception(env, exception_class, exception_message)
it "sets namespaced path" do
make_request(env)
- expect(last_transaction.to_h).to include(
- "action" => "POST::GrapeExample::Api#/v1/beta/ping",
- "metadata" => {
- "path" => "/v1/beta/ping",
- "method" => "POST"
- }
+ expect(last_transaction).to have_action("POST::GrapeExample::Api#/v1/beta/ping")
+ expect(last_transaction).to include_metadata(
+ "path" => "/v1/beta/ping",
+ "method" => "POST"
)
end
end
@@ -245,13 +223,9 @@ def make_request_with_exception(env, exception_class, exception_message)
it "sets namespaced path" do
make_request(env)
- expect(last_transaction.to_h).to include(
- "action" => "POST::GrapeExample::Api#/v1/beta/ping",
- "metadata" => {
- "path" => "/v1/beta/ping",
- "method" => "POST"
- }
- )
+ expect(last_transaction).to have_action("POST::GrapeExample::Api#/v1/beta/ping")
+ expect(last_transaction).to include_metadata("path" => "/v1/beta/ping",
+ "method" => "POST")
end
end
end
diff --git a/spec/lib/appsignal/rack/hanami_middleware_spec.rb b/spec/lib/appsignal/rack/hanami_middleware_spec.rb
index 83834e0ae..260e14815 100644
--- a/spec/lib/appsignal/rack/hanami_middleware_spec.rb
+++ b/spec/lib/appsignal/rack/hanami_middleware_spec.rb
@@ -23,28 +23,14 @@ def make_request(env)
it "sets request parameters on the transaction" do
make_request(env)
- expect(last_transaction.to_h).to include(
- "sample_data" => hash_including(
- "params" => { "param1" => "value1", "param2" => "value2" }
- )
- )
+ expect(last_transaction).to include_params("param1" => "value1", "param2" => "value2")
end
end
it "reports a process_action.hanami event" do
make_request(env)
- expect(last_transaction.to_h).to include(
- "events" => [
- hash_including(
- "body" => "",
- "body_format" => Appsignal::EventFormatter::DEFAULT,
- "count" => 1,
- "name" => "process_action.hanami",
- "title" => ""
- )
- ]
- )
+ expect(last_transaction).to include_event("name" => "process_action.hanami")
end
end
end
diff --git a/spec/lib/appsignal/rack/rails_instrumentation_spec.rb b/spec/lib/appsignal/rack/rails_instrumentation_spec.rb
index d8419c6b5..21e83bb16 100644
--- a/spec/lib/appsignal/rack/rails_instrumentation_spec.rb
+++ b/spec/lib/appsignal/rack/rails_instrumentation_spec.rb
@@ -10,7 +10,7 @@ class MockController; end
Rack::Request.new(env)
)
end
- let(:app) { double(:call => true) }
+ let(:app) { DummyApp.new }
let(:params) do
{
"controller" => "blog_posts",
@@ -40,85 +40,80 @@ class MockController; end
env[Appsignal::Rack::APPSIGNAL_TRANSACTION] = transaction
end
- def make_request(env)
+ def make_request
middleware.call(env)
- last_transaction.complete # Manually close transaction to set sample data
+ last_transaction&._sample
end
- def make_request_with_error(env, error_class, error_message)
- expect { make_request(env) }.to raise_error(error_class, error_message)
+ def make_request_with_error(error_class, error_message)
+ expect { make_request }.to raise_error(error_class, error_message)
end
- context "with a request without an error" do
- it "does not report an event" do
- make_request(env)
+ context "with a request that doesn't raise an error" do
+ before { make_request }
- expect(last_transaction.to_h).to include(
- "events" => []
- )
+ it "calls the next middleware in the stack" do
+ expect(app).to be_called
+ end
+
+ it "does not instrument an event" do
+ expect(last_transaction).to_not include_events
end
end
context "with a request that raises an error" do
- let(:app) { lambda { |_env| raise ExampleException, "error message" } }
+ let(:app) do
+ DummyApp.new { |_env| raise ExampleException, "error message" }
+ end
+ before do
+ make_request_with_error(ExampleException, "error message")
+ end
- it "reports the error on the transaction" do
- make_request_with_error(env, ExampleException, "error message")
+ it "calls the next middleware in the stack" do
+ expect(app).to be_called
+ end
- expect(last_transaction.to_h).to include(
- "error" => hash_including(
- "name" => "ExampleException",
- "message" => "error message"
- )
- )
+ it "reports the error on the transaction" do
+ expect(last_transaction).to have_error("ExampleException", "error message")
end
end
it "sets the controller action as the action name" do
- make_request(env)
+ make_request
- expect(last_transaction.to_h).to include(
- "namespace" => Appsignal::Transaction::HTTP_REQUEST,
- "action" => "MockController#index"
- )
+ expect(last_transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
+ expect(last_transaction).to have_action("MockController#index")
end
it "sets request metadata on the transaction" do
- make_request(env)
-
- expect(last_transaction.to_h).to include(
- "metadata" => hash_including(
- "method" => "GET",
- "path" => "/blog"
- ),
- "sample_data" => hash_including(
- "tags" => { "request_id" => "request_id123" }
- )
+ make_request
+
+ expect(last_transaction).to include_metadata(
+ "method" => "GET",
+ "path" => "/blog"
)
+ expect(last_transaction).to include_tags("request_id" => "request_id123")
end
it "reports Rails filter parameters" do
- make_request(env)
-
- expect(last_transaction.to_h).to include(
- "sample_data" => hash_including(
- "params" => {
- "controller" => "blog_posts",
- "action" => "show",
- "id" => "1",
- "my_custom_param" => "[FILTERED]",
- "password" => "[FILTERED]"
- }
- )
+ make_request
+
+ expect(last_transaction).to include_params(
+ "controller" => "blog_posts",
+ "action" => "show",
+ "id" => "1",
+ "my_custom_param" => "[FILTERED]",
+ "password" => "[FILTERED]"
)
end
context "with an invalid HTTP request method" do
it "does not store the invalid HTTP request method" do
- make_request(env.merge(:request_method => "FOO", "REQUEST_METHOD" => "FOO"))
+ env[:request_method] = "FOO"
+ env["REQUEST_METHOD"] = "FOO"
+ make_request
- transaction_hash = last_transaction.to_h
- expect(transaction_hash["metadata"]).to_not have_key("method")
+ expect(last_transaction).to_not include_metadata("method" => anything)
expect(log_contents(log))
.to contains_log(:error, "Unable to report HTTP request method: '")
end
@@ -126,16 +121,11 @@ def make_request_with_error(env, error_class, error_message)
context "with a request path that's not a route" do
it "doesn't set an action name" do
- make_request(
- env.merge(
- :path => "/unknown-route",
- "action_controller.instance" => nil
- )
- )
+ env[:path] = "/unknown-route"
+ env["action_controller.instance"] = nil
+ make_request
- expect(last_transaction.to_h).to include(
- "action" => nil
- )
+ expect(last_transaction).to_not have_action
end
end
end
diff --git a/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb b/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb
index 2e9865685..64d28affa 100644
--- a/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb
+++ b/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb
@@ -2,11 +2,11 @@
require "appsignal/integrations/sinatra"
module SinatraRequestHelpers
- def make_request(env)
+ def make_request
middleware.call(env)
end
- def make_request_with_error(env, error)
+ def make_request_with_error(error)
expect { middleware.call(env) }.to raise_error(error)
end
end
@@ -30,7 +30,7 @@ def make_request_with_error(env, error)
before { allow(middleware).to receive(:raw_payload).and_return({}) }
it "doesn't instrument requests" do
- expect { make_request(env) }.to_not(change { created_transactions.count })
+ expect { make_request }.to_not(change { created_transactions.count })
end
end
@@ -100,54 +100,48 @@ def make_request_with_error(env, error)
before { allow(Appsignal).to receive(:active?).and_return(false) }
it "does not instrument requests" do
- expect { make_request(env) }.to_not(change { created_transactions.count })
+ expect { make_request }.to_not(change { created_transactions.count })
end
it "calls the next middleware in the stack" do
- make_request(env)
+ make_request
expect(app).to have_received(:call).with(env)
end
end
context "when appsignal is active" do
- context "without an exception" do
+ context "without an error" do
+ it "creates a transaction for the request" do
+ expect { make_request }.to(change { created_transactions.count }.by(1))
+
+ expect(last_transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
+ end
+
it "reports a process_action.sinatra event" do
- make_request(env)
-
- expect(last_transaction.to_h).to include(
- "events" => [
- hash_including(
- "body" => "",
- "body_format" => Appsignal::EventFormatter::DEFAULT,
- "count" => 1,
- "name" => "process_action.sinatra",
- "title" => ""
- )
- ]
- )
+ make_request
+
+ expect(last_transaction).to include_event("name" => "process_action.sinatra")
end
end
context "with an error in sinatra.error" do
let(:error) { ExampleException.new("error message") }
- before do
- env["sinatra.error"] = error
+ before { env["sinatra.error"] = error }
+
+ it "creates a transaction for the request" do
+ expect { make_request }.to(change { created_transactions.count }.by(1))
+
+ expect(last_transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
end
context "when raise_errors is off" do
let(:settings) { double(:raise_errors => false) }
- it "record the error" do
- expect { make_request(env) }
- .to(change { created_transactions.count }.by(1))
+ it "records the error" do
+ make_request
- expect(last_transaction.to_h).to include(
- "error" => hash_including(
- "name" => "ExampleException",
- "message" => "error message"
- )
- )
+ expect(last_transaction).to have_error("ExampleException", "error message")
end
end
@@ -155,10 +149,9 @@ def make_request_with_error(env, error)
let(:settings) { double(:raise_errors => true) }
it "does not record the error" do
- expect { make_request(env) }
- .to(change { created_transactions.count }.by(1))
+ make_request
- expect(last_transaction.to_h).to include("error" => nil)
+ expect(last_transaction).to_not have_error
end
end
@@ -171,19 +164,18 @@ def make_request_with_error(env, error)
end
it "does not record the error" do
- expect { make_request(env) }
- .to(change { created_transactions.count }.by(1))
+ make_request
- expect(last_transaction.to_h).to include("error" => nil)
+ expect(last_transaction).to_not have_error
end
end
end
describe "action name" do
it "sets the action to the request method and path" do
- make_request(env)
+ make_request
- expect(last_transaction.to_h).to include("action" => "GET /path")
+ expect(last_transaction).to have_action("GET /path")
end
context "without 'sinatra.route' env" do
@@ -192,9 +184,9 @@ def make_request_with_error(env, error)
end
it "doesn't set an action name" do
- make_request(env)
+ make_request
- expect(last_transaction.to_h).to include("action" => nil)
+ expect(last_transaction).to_not have_action
end
end
@@ -202,9 +194,9 @@ def make_request_with_error(env, error)
before { env["SCRIPT_NAME"] = "/api" }
it "sets the action name with an application prefix path" do
- make_request(env)
+ make_request
- expect(last_transaction.to_h).to include("action" => "GET /api/path")
+ expect(last_transaction).to have_action("GET /api/path")
end
context "without 'sinatra.route' env" do
@@ -213,9 +205,9 @@ def make_request_with_error(env, error)
end
it "doesn't set an action name" do
- make_request(env)
+ make_request
- expect(last_transaction.to_h).to include("action" => nil)
+ expect(last_transaction).to_not have_action
end
end
end
diff --git a/spec/lib/appsignal/transaction_spec.rb b/spec/lib/appsignal/transaction_spec.rb
index 29731946b..7f8d8d655 100644
--- a/spec/lib/appsignal/transaction_spec.rb
+++ b/spec/lib/appsignal/transaction_spec.rb
@@ -40,10 +40,8 @@ def create_transaction(id = transaction_id)
expect(transaction.namespace).to eq namespace
expect(transaction.request).to eq request
- expect(transaction.to_h).to include(
- "id" => transaction_id,
- "namespace" => namespace
- )
+ expect(transaction).to have_id(transaction_id)
+ expect(transaction).to have_namespace(namespace)
end
it "assigns the transaction to current" do
@@ -166,9 +164,7 @@ def current_transaction
it "samples data" do
transaction.set_tags(:foo => "bar")
keep_transactions { transaction.complete }
- expect(transaction.to_h["sample_data"]).to include(
- "tags" => { "foo" => "bar" }
- )
+ expect(transaction).to include_tags("foo" => "bar")
end
end
@@ -181,13 +177,13 @@ def current_transaction
context "when a transaction is marked as discarded" do
it "does not complete the transaction" do
- expect(transaction.ext).to_not receive(:complete)
-
expect do
transaction.discard!
end.to change { transaction.discarded? }.from(false).to(true)
transaction.complete
+
+ expect(transaction).to_not be_completed
end
it "logs a debug message" do
@@ -202,13 +198,13 @@ def current_transaction
before { transaction.discard! }
it "completes the transaction" do
- expect(transaction.ext).to receive(:complete).and_call_original
-
expect do
transaction.restore!
end.to change { transaction.discarded? }.from(true).to(false)
transaction.complete
+
+ expect(transaction).to be_completed
end
end
end
@@ -354,9 +350,9 @@ def current_transaction
params = { "foo" => "bar" }
silence { transaction.params = params }
- transaction.complete # Sample the data
+ transaction._sample
expect(transaction.params).to eq(params)
- expect(transaction.to_h.dig("sample_data", "params")).to eq(params)
+ expect(transaction).to include_params(params)
end
it "logs a deprecation warning" do
@@ -378,9 +374,9 @@ def current_transaction
params = { "key" => "value" }
transaction.set_params(params)
- transaction.complete # Sample the data
+ transaction._sample
expect(transaction.params).to eq(params)
- expect(transaction.to_h.dig("sample_data", "params")).to eq(params)
+ expect(transaction).to include_params(params)
end
end
@@ -390,9 +386,9 @@ def current_transaction
transaction.set_params(params)
transaction.set_params(nil)
- transaction.complete # Sample the data
+ transaction._sample
expect(transaction.params).to eq(params)
- expect(transaction.to_h.dig("sample_data", "params")).to eq(params)
+ expect(transaction).to include_params(params)
end
end
end
@@ -405,9 +401,9 @@ def current_transaction
params = { "key" => "value" }
transaction.set_params_if_nil(params)
- transaction.complete # Sample the data
+ transaction._sample
expect(transaction.params).to eq(params)
- expect(transaction.to_h.dig("sample_data", "params")).to eq(params)
+ expect(transaction).to include_params(params)
end
context "when the given params is nil" do
@@ -416,9 +412,9 @@ def current_transaction
transaction.set_params(params)
transaction.set_params_if_nil(nil)
- transaction.complete # Sample the data
+ transaction._sample
expect(transaction.params).to eq(params)
- expect(transaction.to_h.dig("sample_data", "params")).to eq(params)
+ expect(transaction).to include_params(params)
end
end
end
@@ -430,9 +426,9 @@ def current_transaction
transaction.set_params(preset_params)
transaction.set_params_if_nil(params)
- transaction.complete # Sample the data
+ transaction._sample
expect(transaction.params).to eq(preset_params)
- expect(transaction.to_h.dig("sample_data", "params")).to eq(preset_params)
+ expect(transaction).to include_params(preset_params)
end
end
end
@@ -455,7 +451,7 @@ def current_transaction
end
it "stores tags on the transaction" do
- expect(transaction.to_h["sample_data"]["tags"]).to eq(
+ expect(transaction).to include_tags(
"valid_key" => "valid_value",
"valid_string_key" => "valid_value",
"both_symbols" => "valid_value",
@@ -507,12 +503,13 @@ def current_transaction
transaction.sample_data
timeframe_end = Time.now.utc.to_i
- breadcrumb = transaction.to_h["sample_data"]["breadcrumbs"][0]
- expect(breadcrumb["category"]).to eq("user_action")
- expect(breadcrumb["action"]).to eq("clicked HOME")
- expect(breadcrumb["message"]).to eq("")
- expect(breadcrumb["time"]).to be_between(timeframe_start, timeframe_end)
- expect(breadcrumb["metadata"]).to eq({})
+ expect(transaction).to include_breadcrumb(
+ "clicked HOME",
+ "user_action",
+ "",
+ {},
+ be_between(timeframe_start, timeframe_end)
+ )
end
end
@@ -521,7 +518,7 @@ def current_transaction
transaction.add_breadcrumb("category", "action", "message", "invalid metadata")
transaction.sample_data
- expect(transaction.to_h["sample_data"]["breadcrumbs"]).to be_empty
+ expect(transaction).to_not include_breadcrumbs
expect(log_contents(log)).to contains_log(
:error,
"add_breadcrumb: Cannot add breadcrumb. The given metadata argument is not a Hash."
@@ -537,7 +534,7 @@ def current_transaction
transaction.set_action(action_name)
expect(transaction.action).to eq(action_name)
- expect(transaction.to_h["action"]).to eq(action_name)
+ expect(transaction).to have_action(action_name)
end
end
@@ -548,7 +545,7 @@ def current_transaction
transaction.set_action(nil)
expect(transaction.action).to eq(action_name)
- expect(transaction.to_h["action"]).to eq(action_name)
+ expect(transaction).to have_action(action_name)
end
end
end
@@ -557,13 +554,13 @@ def current_transaction
context "when the action is not set" do
it "updates the action name on the transaction" do
expect(transaction.action).to eq(nil)
- expect(transaction.to_h["action"]).to eq(nil)
+ expect(transaction).to_not have_action
action_name = "PagesController#show"
transaction.set_action_if_nil(action_name)
expect(transaction.action).to eq(action_name)
- expect(transaction.to_h["action"]).to eq(action_name)
+ expect(transaction).to have_action(action_name)
end
context "when the given action is nil" do
@@ -573,7 +570,7 @@ def current_transaction
transaction.set_action_if_nil(nil)
expect(transaction.action).to eq(action_name)
- expect(transaction.to_h["action"]).to eq(action_name)
+ expect(transaction).to have_action(action_name)
end
end
end
@@ -585,7 +582,7 @@ def current_transaction
transaction.set_action_if_nil("something else")
expect(transaction.action).to eq(action_name)
- expect(transaction.to_h["action"]).to eq(action_name)
+ expect(transaction).to have_action(action_name)
end
end
end
@@ -597,7 +594,7 @@ def current_transaction
transaction.set_namespace(namespace)
expect(transaction.namespace).to eq namespace
- expect(transaction.to_h["namespace"]).to eq(namespace)
+ expect(transaction).to have_namespace(namespace)
end
end
@@ -608,7 +605,7 @@ def current_transaction
transaction.set_namespace(nil)
expect(transaction.namespace).to eq(namespace)
- expect(transaction.to_h["namespace"]).to eq(namespace)
+ expect(transaction).to have_namespace(namespace)
end
end
end
@@ -620,21 +617,21 @@ def current_transaction
:controller => "HomeController",
:action => "show"
)
- expect(transaction.to_h["action"]).to eql("HomeController#show")
+ expect(transaction).to have_action("HomeController#show")
end
end
context "for a hash with just action" do
it "sets the action" do
transaction.set_http_or_background_action(:action => "show")
- expect(transaction.to_h["action"]).to eql("show")
+ expect(transaction).to have_action("show")
end
end
context "for a hash with class and method" do
it "sets the action" do
transaction.set_http_or_background_action(:class => "Worker", :method => "perform")
- expect(transaction.to_h["action"]).to eql("Worker#perform")
+ expect(transaction).to have_action("Worker#perform")
end
end
@@ -642,7 +639,7 @@ def current_transaction
it "does not overwrite the set action" do
transaction.set_action("MyCustomAction#perform")
transaction.set_http_or_background_action(:class => "Worker", :method => "perform")
- expect(transaction.to_h["action"]).to eql("MyCustomAction#perform")
+ expect(transaction).to have_action("MyCustomAction#perform")
end
end
end
@@ -716,7 +713,7 @@ def current_transaction
it "updates the metadata on the transaction" do
transaction.set_metadata("request_method", "GET")
- expect(transaction.to_h["metadata"]).to eq("request_method" => "GET")
+ expect(transaction).to include_metadata("request_method" => "GET")
end
context "when filter_metadata includes metadata key" do
@@ -727,7 +724,7 @@ def current_transaction
transaction.set_metadata(:filter_key, "filtered value")
transaction.set_metadata("filter_key", "filtered value")
- expect(transaction.to_h["metadata"].keys).to_not include("filter_key")
+ expect(transaction).to_not include_metadata("filter_key" => anything)
end
end
@@ -735,7 +732,7 @@ def current_transaction
it "does not update the metadata on the transaction" do
transaction.set_metadata(nil, "GET")
- expect(transaction.to_h["metadata"]).to eq({})
+ expect(transaction).to_not include_metadata
end
end
@@ -743,7 +740,7 @@ def current_transaction
it "does not update the metadata on the transaction" do
transaction.set_metadata("request_method", nil)
- expect(transaction.to_h["metadata"]).to eq({})
+ expect(transaction).to_not include_metadata
end
end
end
@@ -757,12 +754,10 @@ def current_transaction
:id => "1"
)
- expect(transaction.to_h["sample_data"]).to eq(
- "params" => {
- "action" => "show",
- "controller" => "blog_posts",
- "id" => "1"
- }
+ expect(transaction).to include_params(
+ "action" => "show",
+ "controller" => "blog_posts",
+ "id" => "1"
)
end
@@ -785,7 +780,7 @@ def to_s
end
transaction.set_sample_data("params", klass.new => 1)
- expect(transaction.to_h["sample_data"]).to eq({})
+ expect(transaction).to_not include_params
expect(log_contents(log)).to contains_log :error,
"Error generating data (RuntimeError: foo) for"
end
@@ -800,27 +795,26 @@ def to_s
transaction.add_breadcrumb "category", "action", "message", "key" => "value"
transaction.sample_data
- sample_data = transaction.to_h["sample_data"]
- expect(sample_data["environment"]).to include(
+ expect(transaction).to include_environment(
"REQUEST_METHOD" => "GET",
"SERVER_NAME" => "example.org",
"SERVER_PORT" => "80",
"PATH_INFO" => "/blog"
)
- expect(sample_data["session_data"]).to eq("session" => "value")
- expect(sample_data["params"]).to eq(
+ expect(transaction).to include_session_data("session" => "value")
+ expect(transaction).to include_params(
"controller" => "blog_posts",
"action" => "show",
"id" => "1"
)
- expect(sample_data["metadata"]).to eq("key" => "value")
- expect(sample_data["tags"]).to eq("tag" => "value")
- expect(sample_data["breadcrumbs"]).to contain_exactly(
- "action" => "action",
- "category" => "category",
- "message" => "message",
- "metadata" => { "key" => "value" },
- "time" => kind_of(Integer)
+ expect(transaction).to include_sample_metadata("key" => "value")
+ expect(transaction).to include_tags("tag" => "value")
+ expect(transaction).to include_breadcrumb(
+ "action",
+ "category",
+ "message",
+ { "key" => "value" },
+ kind_of(Integer)
)
end
end
diff --git a/spec/lib/appsignal_spec.rb b/spec/lib/appsignal_spec.rb
index fa88acbea..19071bff6 100644
--- a/spec/lib/appsignal_spec.rb
+++ b/spec/lib/appsignal_spec.rb
@@ -1,5 +1,6 @@
describe Appsignal do
include EnvironmentMetadataHelper
+ around { |example| keep_transactions { example.run } }
before do
# Make sure we have a clean state because we want to test
@@ -399,8 +400,6 @@
end
describe ".monitor_single_transaction" do
- around { |example| keep_transactions { example.run } }
-
context "with a successful call" do
it "calls monitor_transaction and Appsignal.stop" do
expect(Appsignal).to receive(:stop)
@@ -414,18 +413,8 @@
end
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "my_controller#my_action"
- )
- expect(transaction_hash["events"]).to match([
- hash_including(
- "name" => "perform_job.something",
- "title" => "",
- "body" => "",
- "body_format" => Appsignal::EventFormatter::DEFAULT
- )
- ])
+ expect(transaction).to have_action("my_controller#my_action")
+ expect(transaction).to include_event("name" => "perform_job.something")
end
end
@@ -446,75 +435,55 @@
end.to raise_error(error)
transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "action" => "my_controller#my_action"
- )
- expect(transaction_hash["events"]).to match([
- hash_including(
- "name" => "perform_job.something",
- "title" => "",
- "body" => "",
- "body_format" => Appsignal::EventFormatter::DEFAULT
- )
- ])
+ expect(transaction).to have_action("my_controller#my_action")
+ expect(transaction).to include_event("name" => "perform_job.something")
end
end
end
describe ".tag_request" do
- let(:transaction) { http_request_transaction }
around do |example|
start_agent
- with_current_transaction transaction do
- keep_transactions { example.run }
- end
+ with_current_transaction(transaction) { example.run }
end
context "with transaction" do
+ let(:transaction) { http_request_transaction }
+
it "calls set_tags on the current transaction" do
Appsignal.tag_request("a" => "b")
- transaction.complete # Manually trigger transaction sampling
- expect(transaction.to_h).to include(
- "sample_data" => hash_including(
- "tags" => { "a" => "b" }
- )
- )
+ transaction._sample
+ expect(transaction).to include_tags("a" => "b")
end
end
context "without transaction" do
let(:transaction) { nil }
- it "should call set_tags on transaction" do
+ it "does not set tags on the transaction" do
expect(Appsignal.tag_request).to be_falsy
+ Appsignal.tag_request("a" => "b")
+
+ expect_any_instance_of(Appsignal::Transaction).to_not receive(:set_tags)
end
end
- it "should also listen to tag_job" do
+ it "also listens to tag_job" do
expect(Appsignal).to respond_to(:tag_job)
end
end
describe ".add_breadcrumb" do
- before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
+ around do |example|
+ start_agent
+ with_current_transaction(transaction) { example.run }
+ end
context "with transaction" do
let(:transaction) { http_request_transaction }
- around do |example|
- Appsignal.config = project_fixture_config
- set_current_transaction transaction do
- example.run
- end
- end
-
- it "should call add_breadcrumb on transaction" do
- expect(transaction).to receive(:add_breadcrumb)
- .with("Network", "http", "User made network request", { :response => 200 }, fixed_time)
- end
- after do
+ it "adds the breadcrumb to the transaction" do
Appsignal.add_breadcrumb(
"Network",
"http",
@@ -522,13 +491,22 @@
{ :response => 200 },
fixed_time
)
+
+ transaction._sample
+ expect(transaction).to include_breadcrumb(
+ "http",
+ "Network",
+ "User made network request",
+ { "response" => 200 },
+ fixed_time
+ )
end
end
context "without transaction" do
let(:transaction) { nil }
- it "should not call add_breadcrumb on transaction" do
+ it "does not add a breadcrumb to any transaction" do
expect(Appsignal.add_breadcrumb("Network", "http")).to be_falsy
end
end
@@ -557,8 +535,11 @@
end
it "should not raise an exception when out of range" do
- expect(Appsignal::Extension).to receive(:set_gauge).with("key", 10,
- Appsignal::Extension.data_map_new).and_raise(RangeError)
+ expect(Appsignal::Extension).to receive(:set_gauge).with(
+ "key",
+ 10,
+ Appsignal::Extension.data_map_new
+ ).and_raise(RangeError)
expect(Appsignal.internal_logger).to receive(:warn)
.with("Gauge value 10 for key 'key' is too big")
expect do
@@ -713,46 +694,39 @@
end
describe ".send_error" do
- let(:transaction) do
- Appsignal::Transaction.new(
- SecureRandom.uuid,
- Appsignal::Transaction::HTTP_REQUEST,
- Appsignal::Transaction::GenericRequest.new({})
- )
- end
- let(:error) { ExampleException.new }
+ let(:error) { ExampleException.new("error message") }
let(:err_stream) { std_stream }
let(:stderr) { err_stream.read }
- around { |example| keep_transactions { example.run } }
+ around do |example|
+ keep_transactions { example.run }
+ end
it "sends the error to AppSignal" do
- expect(Appsignal::Transaction).to receive(:new).with(
- kind_of(String),
- Appsignal::Transaction::HTTP_REQUEST,
- kind_of(Appsignal::Transaction::GenericRequest)
- ).and_return(transaction)
- expect(transaction).to receive(:set_error).with(error)
- expect(transaction).to_not receive(:set_tags)
- expect(transaction).to receive(:complete)
+ expect { Appsignal.send_error(error) }.to(change { created_transactions.count }.by(1))
- Appsignal.send_error(error)
+ transaction = last_transaction
+ expect(transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
+ expect(transaction).to_not have_action
+ expect(transaction).to have_error("ExampleException", "error message")
+ expect(transaction).to_not include_tags
+ expect(transaction).to be_completed
end
context "when given error is not an Exception" do
- let(:error) { double }
+ let(:error) { "string value" }
it "logs an error message" do
- expect(Appsignal.internal_logger).to receive(:error).with(
+ logs = capture_logs { Appsignal.send_error(error) }
+ expect(logs).to contains_log(
+ :error,
"Appsignal.send_error: Cannot send error. " \
"The given value is not an exception: #{error.inspect}"
)
end
it "does not send the error" do
- expect(Appsignal::Transaction).to_not receive(:create)
+ expect { Appsignal.send_error(error) }.to_not(change { created_transactions.count })
end
-
- after { Appsignal.send_error(error) }
end
context "with tags" do
@@ -767,13 +741,7 @@
end.to change { created_transactions.count }.by(1)
end
- transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include(
- "sample_data" => hash_including(
- "tags" => { "a" => "a", "b" => "b" }
- )
- )
+ expect(last_transaction).to include_tags("a" => "a", "b" => "b")
message = "The tags argument for `Appsignal.send_error` is deprecated. " \
"Please use the block method to set tags instead.\n\n" \
@@ -798,9 +766,7 @@
end.to change { created_transactions.count }.by(1)
end
- transaction = last_transaction
- transaction_hash = transaction.to_h
- expect(transaction_hash).to include("namespace" => namespace)
+ expect(last_transaction).to have_namespace(namespace)
message = "The namespace argument for `Appsignal.send_error` is deprecated. " \
"Please use the block method to set the namespace instead.\n\n" \
@@ -815,23 +781,15 @@
context "when given a block" do
it "yields the transaction and allows additional metadata to be set" do
- captured_transaction = nil
keep_transactions do
Appsignal.send_error(StandardError.new("my_error")) do |transaction|
- captured_transaction = transaction
transaction.set_action("my_action")
transaction.set_namespace("my_namespace")
end
end
- expect(captured_transaction.to_h).to include(
- "namespace" => "my_namespace",
- "action" => "my_action",
- "error" => {
- "name" => "StandardError",
- "message" => "my_error",
- "backtrace" => kind_of(String) # TODO: should be Array
- }
- )
+ expect(last_transaction).to have_namespace("my_namespace")
+ expect(last_transaction).to have_action("my_action")
+ expect(last_transaction).to have_error("StandardError", "my_error")
end
end
end
@@ -848,17 +806,10 @@
end.to raise_error(ExampleException, "I am an exception")
end.to change { created_transactions.count }.by(1)
- expect(last_transaction.to_h).to include(
- "error" => {
- "name" => "ExampleException",
- "message" => "I am an exception",
- "backtrace" => kind_of(String)
- },
- "namespace" => Appsignal::Transaction::HTTP_REQUEST, # Default namespace
- "sample_data" => hash_including(
- "tags" => {}
- )
- )
+ # Default namespace
+ expect(last_transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
+ expect(last_transaction).to have_error("ExampleException", "I am an exception")
+ expect(last_transaction).to_not include_tags
end
context "with tags" do
@@ -871,17 +822,10 @@
end.to raise_error(ExampleException, "I am an exception")
end.to change { created_transactions.count }.by(1)
- expect(last_transaction.to_h).to include(
- "error" => {
- "name" => "ExampleException",
- "message" => "I am an exception",
- "backtrace" => kind_of(String)
- },
- "namespace" => Appsignal::Transaction::HTTP_REQUEST, # Default namespace
- "sample_data" => hash_including(
- "tags" => { "foo" => "bar" }
- )
- )
+ # Default namespace
+ expect(last_transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
+ expect(last_transaction).to have_error("ExampleException", "I am an exception")
+ expect(last_transaction).to include_tags("foo" => "bar")
end
end
@@ -895,17 +839,10 @@
end.to raise_error(ExampleException, "I am an exception")
end.to change { created_transactions.count }.by(1)
- expect(last_transaction.to_h).to include(
- "error" => {
- "name" => "ExampleException",
- "message" => "I am an exception",
- "backtrace" => kind_of(String)
- },
- "namespace" => "custom_namespace",
- "sample_data" => hash_including(
- "tags" => {}
- )
- )
+ # Default namespace
+ expect(last_transaction).to have_namespace("custom_namespace")
+ expect(last_transaction).to have_error("ExampleException", "I am an exception")
+ expect(last_transaction).to_not include_tags
end
end
end
@@ -914,42 +851,56 @@
let(:err_stream) { std_stream }
let(:stderr) { err_stream.read }
let(:error) { ExampleException.new("I am an exception") }
- before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
+ let(:transaction) { http_request_transaction }
around { |example| keep_transactions { example.run } }
context "when there is an active transaction" do
- it "adds the error to the active transaction" do
- expect(transaction).to receive(:set_error).with(error)
- expect(transaction).to_not receive(:set_tags)
- expect(transaction).to_not receive(:set_namespace)
+ before { set_current_transaction(transaction) }
+ it "adds the error to the active transaction" do
Appsignal.set_error(error)
+
+ transaction._sample
+ expect(transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
+ expect(transaction).to have_error("ExampleException", "I am an exception")
+ expect(transaction).to_not include_tags
end
context "when the error is not an Exception" do
let(:error) { Object.new }
+ it "does not set an error" do
+ silence { Appsignal.set_error(error) }
+
+ transaction._sample
+ expect(transaction).to_not have_error
+ expect(transaction).to_not include_tags
+ end
+
it "logs an error" do
- expect(Appsignal.internal_logger).to receive(:error).with(
+ logs = capture_logs { Appsignal.set_error(error) }
+ expect(logs).to contains_log(
+ :error,
"Appsignal.set_error: Cannot set error. " \
"The given value is not an exception: #{error.inspect}"
)
- expect(transaction).to_not receive(:set_error)
- expect(transaction).to_not receive(:set_tags)
- expect(transaction).to_not receive(:set_namespace)
-
- Appsignal.set_error(error)
end
end
context "with tags" do
let(:tags) { { "foo" => "bar" } }
- it "prints a deprecation warning and tags the transaction" do
- expect(transaction).to receive(:set_error).with(error)
- expect(transaction).to receive(:set_tags).with(tags)
- expect(transaction).to_not receive(:set_namespace)
+ it "tags the transaction" do
+ silence(:allowed => ["set_error", "The tags argument for"]) do
+ Appsignal.set_error(error, tags)
+ end
+
+ transaction._sample
+ expect(transaction).to have_error(error)
+ expect(transaction).to include_tags(tags)
+ end
+ it "prints a deprecation warning and tags the transaction" do
logs = capture_logs do
capture_std_streams(std_stream, err_stream) do
Appsignal.set_error(error, tags)
@@ -970,11 +921,17 @@
context "with namespace" do
let(:namespace) { "admin" }
- it "prints a deprecation warning andsets the namespace on the transaction" do
- expect(transaction).to receive(:set_error).with(error)
- expect(transaction).to_not receive(:set_tags)
- expect(transaction).to receive(:set_namespace).with(namespace)
+ it "sets the namespace on the transaction" do
+ silence(:allowed => ["set_error", "The namespace argument for"]) do
+ Appsignal.set_error(error, nil, namespace)
+ end
+ expect(transaction).to have_error("ExampleException", "I am an exception")
+ expect(transaction).to have_namespace(namespace)
+ expect(transaction).to_not include_tags
+ end
+
+ it "prints a deprecation warning andsets the namespace on the transaction" do
logs = capture_logs do
capture_std_streams(std_stream, err_stream) do
Appsignal.set_error(error, nil, namespace)
@@ -994,135 +951,137 @@
context "when given a block" do
it "yields the transaction and allows additional metadata to be set" do
- captured_transaction = nil
- keep_transactions do
- Appsignal.set_error(StandardError.new("my_error")) do |transaction|
- captured_transaction = transaction
- transaction.set_action("my_action")
- transaction.set_namespace("my_namespace")
- end
+ Appsignal.set_error(StandardError.new("my_error")) do |t|
+ t.set_action("my_action")
+ t.set_namespace("my_namespace")
end
- expect(transaction).to eql(captured_transaction)
- expect(captured_transaction.to_h).to include(
- "namespace" => "my_namespace",
- "action" => "my_action",
- "error" => {
- "name" => "StandardError",
- "message" => "my_error",
- "backtrace" => kind_of(String)
- }
- )
+ expect(transaction).to have_namespace("my_namespace")
+ expect(transaction).to have_action("my_action")
+ expect(transaction).to have_error("StandardError", "my_error")
end
end
end
context "when there is no active transaction" do
it "does nothing" do
- allow(Appsignal::Transaction).to receive(:current).and_return(nil)
-
- expect(transaction).to_not receive(:set_error)
-
Appsignal.set_error(error)
+
+ expect(transaction).to_not have_error
end
end
end
describe ".set_action" do
- before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
+ around { |example| keep_transactions { example.run } }
- it "should set the namespace to the current transaction" do
- expect(transaction).to receive(:set_action).with("custom")
+ context "with current transaction" do
+ before { set_current_transaction(transaction) }
- Appsignal.set_action("custom")
- end
+ it "sets the namespace on the current transaction" do
+ Appsignal.set_action("custom")
- it "should do nothing if there is no current transaction" do
- allow(Appsignal::Transaction).to receive(:current).and_return(nil)
+ expect(transaction).to have_action("custom")
+ end
- expect(transaction).to_not receive(:set_action)
+ it "does not set the action if the action is nil" do
+ Appsignal.set_action(nil)
- Appsignal.set_action("custom")
+ expect(transaction).to_not have_action
+ end
end
- it "should do nothing if the error is nil" do
- expect(transaction).to_not receive(:set_action)
+ context "without current transaction" do
+ it "does not set ther action" do
+ Appsignal.set_action("custom")
- Appsignal.set_action(nil)
+ expect(transaction).to_not have_action
+ end
end
end
describe ".set_namespace" do
- before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
+ around { |example| keep_transactions { example.run } }
- it "should set the namespace to the current transaction" do
- expect(transaction).to receive(:set_namespace).with("custom")
+ context "with current transaction" do
+ before { set_current_transaction(transaction) }
- Appsignal.set_namespace("custom")
- end
+ it "should set the namespace to the current transaction" do
+ Appsignal.set_namespace("custom")
- it "should do nothing if there is no current transaction" do
- allow(Appsignal::Transaction).to receive(:current).and_return(nil)
+ expect(transaction).to have_namespace("custom")
+ end
- expect(transaction).to_not receive(:set_namespace)
+ it "does not update the namespace if the namespace is nil" do
+ Appsignal.set_namespace(nil)
- Appsignal.set_namespace("custom")
+ expect(transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
+ end
end
- it "should do nothing if the error is nil" do
- expect(transaction).to_not receive(:set_namespace)
+ context "without current transaction" do
+ it "does not update the namespace" do
+ expect(transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
- Appsignal.set_namespace(nil)
+ Appsignal.set_namespace("custom")
+
+ expect(transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
+ end
end
end
describe ".instrument" do
it_behaves_like "instrument helper" do
let(:instrumenter) { Appsignal }
- before do
- expect(Appsignal::Transaction).to receive(:current).at_least(:once)
- .and_return(transaction)
- end
+ before { set_current_transaction(transaction) }
end
end
describe ".instrument_sql" do
- before do
- expect(Appsignal::Transaction).to receive(:current).at_least(:once)
- .and_return(transaction)
- end
+ around { |example| keep_transactions { example.run } }
+ before { set_current_transaction(transaction) }
it "creates an SQL event on the transaction" do
- expect(transaction).to receive(:start_event)
- expect(transaction).to receive(:finish_event)
- .with("name", "title", "body", Appsignal::EventFormatter::SQL_BODY_FORMAT)
+ result =
+ Appsignal.instrument_sql "name", "title", "body" do
+ "return value"
+ end
- result = Appsignal.instrument_sql "name", "title", "body" do
- "return value"
- end
expect(result).to eq "return value"
+ expect(transaction).to include_event(
+ "name" => "name",
+ "title" => "title",
+ "body" => "body",
+ "body_format" => Appsignal::EventFormatter::SQL_BODY_FORMAT
+ )
end
end
describe ".without_instrumentation" do
+ around { |example| keep_transactions { example.run } }
let(:transaction) { http_request_transaction }
- before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
- it "does not record events on the transaction" do
- expect(transaction).to receive(:pause!).and_call_original
- expect(transaction).to receive(:resume!).and_call_original
- Appsignal.instrument("register.this.event") { :do_nothing }
- Appsignal.without_instrumentation do
- Appsignal.instrument("dont.register.this.event") { :do_nothing }
+ context "with current transaction" do
+ before { set_current_transaction(transaction) }
+
+ it "does not record events on the transaction" do
+ expect(transaction).to receive(:pause!).and_call_original
+ expect(transaction).to receive(:resume!).and_call_original
+
+ Appsignal.instrument("register.this.event") { :do_nothing }
+ Appsignal.without_instrumentation do
+ Appsignal.instrument("dont.register.this.event") { :do_nothing }
+ end
+
+ expect(transaction).to include_event("name" => "register.this.event")
+ expect(transaction).to_not include_event("name" => "dont.register.this.event")
end
- expect(transaction.to_h["events"].map { |e| e["name"] })
- .to match_array("register.this.event")
end
- context "without transaction" do
+ context "without current transaction" do
let(:transaction) { nil }
- it "should not crash" do
+ it "does not crash" do
Appsignal.without_instrumentation { :do_nothing }
end
end
diff --git a/spec/support/helpers/std_streams_helper.rb b/spec/support/helpers/std_streams_helper.rb
index 0520816ee..cc4ba2bce 100644
--- a/spec/support/helpers/std_streams_helper.rb
+++ b/spec/support/helpers/std_streams_helper.rb
@@ -89,6 +89,6 @@ def filter_allowed_errors(output, allowed_errors)
end
end
reject
- end.join(",")
+ end.join
end
end
diff --git a/spec/support/matchers/be_completed.rb b/spec/support/matchers/be_completed.rb
deleted file mode 100644
index 4eba036c7..000000000
--- a/spec/support/matchers/be_completed.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-RSpec::Matchers.define :be_completed do
- match do |transaction|
- values_match? transaction.ext._completed?, true
- end
-end
diff --git a/spec/support/matchers/transaction.rb b/spec/support/matchers/transaction.rb
new file mode 100644
index 000000000..2d7ef19a6
--- /dev/null
+++ b/spec/support/matchers/transaction.rb
@@ -0,0 +1,185 @@
+def define_transaction_metadata_matcher_for(matcher_key, value_key = matcher_key)
+ value_key = value_key.to_s
+
+ RSpec::Matchers.define "have_#{matcher_key}" do |expected_value|
+ match(:notify_expectation_failures => true) do |transaction|
+ actual_value = transaction.to_h[value_key]
+ if expected_value
+ expect(actual_value).to eq(expected_value)
+ else
+ expect(actual_value).to_not be_nil
+ end
+ end
+
+ match_when_negated(:notify_expectation_failures => true) do |transaction|
+ actual_value = transaction.to_h[value_key]
+ if expected_value
+ expect(actual_value).to_not eq(expected_value)
+ else
+ expect(actual_value).to be_nil
+ end
+ end
+ end
+end
+
+define_transaction_metadata_matcher_for(:id)
+define_transaction_metadata_matcher_for(:namespace)
+define_transaction_metadata_matcher_for(:action)
+
+def define_transaction_sample_matcher_for(matcher_key, value_key = matcher_key)
+ value_key = value_key.to_s
+
+ RSpec::Matchers.define "include_#{matcher_key}" do |expected_value|
+ match(:notify_expectation_failures => true) do |transaction|
+ sample_data = transaction.to_h.dig("sample_data", value_key) || {}
+
+ if expected_value
+ expected_value = hash_including(expected_value) if expected_value.is_a?(Hash)
+ expect(sample_data).to match(expected_value)
+ else
+ expect(sample_data).to be_present
+ end
+ end
+
+ match_when_negated(:notify_expectation_failures => true) do |transaction|
+ sample_data = transaction.to_h.dig("sample_data", value_key) || {}
+
+ if expected_value
+ expect(sample_data).to_not include(expected_value)
+ else
+ expect(sample_data).to be_empty
+ end
+ end
+ end
+end
+
+define_transaction_sample_matcher_for(:sample_metadata, :metadata)
+define_transaction_sample_matcher_for(:params)
+define_transaction_sample_matcher_for(:environment)
+define_transaction_sample_matcher_for(:session_data)
+define_transaction_sample_matcher_for(:tags)
+define_transaction_sample_matcher_for(:custom_data)
+
+RSpec::Matchers.define :be_completed do
+ match(:notify_expectation_failures => true) do |transaction|
+ values_match? transaction.ext._completed?, true
+ end
+end
+
+RSpec::Matchers.define :have_error do |error_class, error_message|
+ match(:notify_expectation_failures => true) do |transaction|
+ transaction_error = transaction.to_h["error"]
+ if error_class && error_message
+ expect(transaction_error).to include(
+ "name" => error_class,
+ "message" => error_message,
+ "backtrace" => kind_of(String)
+ )
+ else
+ expect(transaction_error).to be_any
+ end
+ end
+
+ match_when_negated(:notify_expectation_failures => true) do |transaction|
+ transaction_error = transaction.to_h["error"]
+ if error_class && error_message
+ expect(transaction_error).to_not include(
+ "name" => error_class,
+ "message" => error_message,
+ "backtrace" => kind_of(String)
+ )
+ else
+ expect(transaction_error).to be_nil
+ end
+ end
+end
+
+RSpec::Matchers.define :include_event do |event|
+ match(:notify_expectation_failures => true) do |transaction|
+ events = transaction.to_h["events"]
+ if event
+ expect(events).to include(format_event(event))
+ else
+ expect(events).to be_any
+ end
+ end
+
+ match_when_negated(:notify_expectation_failures => true) do |transaction|
+ events = transaction.to_h["events"]
+ if event
+ expect(events).to_not include(format_event(event))
+ else
+ expect(events).to be_empty
+ end
+ end
+
+ def format_event(event)
+ hash_including({
+ "body" => "",
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
+ "count" => 1,
+ "name" => kind_of(String),
+ "title" => ""
+ }.merge(event.transform_keys(&:to_s)))
+ end
+end
+RSpec::Matchers.alias_matcher :include_events, :include_event
+
+RSpec::Matchers.define :include_metadata do |metadata|
+ match(:notify_expectation_failures => true) do |transaction|
+ actual_metadata = transaction.to_h["metadata"]
+ if metadata
+ expect(actual_metadata).to include(metadata)
+ else
+ expect(actual_metadata).to be_any
+ end
+ end
+
+ match_when_negated(:notify_expectation_failures => true) do |transaction|
+ actual_metadata = transaction.to_h["metadata"]
+ if metadata
+ expect(actual_metadata).to_not include(metadata)
+ else
+ expect(actual_metadata).to be_empty
+ end
+ end
+end
+
+RSpec::Matchers.define :include_breadcrumb do |action, category, message, metadata, time|
+ match(:notify_expectation_failures => true) do |transaction|
+ breadcrumbs = transaction.to_h.dig("sample_data", "breadcrumbs")
+ if action
+ breadcrumb = format_breadcrumb(action, category, message, metadata, time)
+ expect(breadcrumbs).to include(breadcrumb)
+ else
+ expect(transaction.to_h.dig("sample_data", "breadcrumbs")).to be_any
+ end
+ end
+
+ match_when_negated(:notify_expectation_failures => true) do |transaction|
+ breadcrumbs = transaction.to_h.dig("sample_data", "breadcrumbs")
+ if action
+ breadcrumb = format_breadcrumb(action, category, message, metadata, time)
+ expect(breadcrumbs).to_not include(breadcrumb)
+ else
+ expect(breadcrumbs).to_not be_any
+ end
+ end
+
+ def format_breadcrumb(action, category, message, metadata, time)
+ {
+ "action" => action,
+ "category" => category,
+ "message" => message,
+ "metadata" => metadata,
+ "time" => time
+ }
+ end
+end
+RSpec::Matchers.alias_matcher :include_breadcrumbs, :include_breadcrumb
+
+RSpec::Matchers.define :have_queue_start do |queue_start_time|
+ match(:notify_expectation_failures => true) do |transaction|
+ expect(transaction.ext.queue_start).to eq(queue_start_time)
+ end
+end
diff --git a/spec/support/mocks/dummy_app.rb b/spec/support/mocks/dummy_app.rb
new file mode 100644
index 000000000..2a7cc1931
--- /dev/null
+++ b/spec/support/mocks/dummy_app.rb
@@ -0,0 +1,16 @@
+class DummyApp
+ def initialize(&app)
+ @app = app
+ @called = false
+ end
+
+ def call(env)
+ @app&.call(env)
+ ensure
+ @called = true
+ end
+
+ def called?
+ @called
+ end
+end
diff --git a/spec/support/shared_examples/instrument.rb b/spec/support/shared_examples/instrument.rb
index 01933a3e7..2ebcab060 100644
--- a/spec/support/shared_examples/instrument.rb
+++ b/spec/support/shared_examples/instrument.rb
@@ -1,22 +1,14 @@
RSpec.shared_examples "instrument helper" do
- let(:stub) { double }
- before do
- expect(stub).to receive(:method_call).and_return("return value")
-
- expect(transaction).to receive(:start_event)
- expect(transaction).to receive(:finish_event).with(
- "name",
- "title",
- "body",
- 0
- )
- end
+ around { |example| keep_transactions { example.run } }
+ let(:stub) { double(:method_call => "return value") }
it "records an event around the given block" do
return_value = instrumenter.instrument "name", "title", "body" do
stub.method_call
end
expect(return_value).to eq "return value"
+
+ expect_transaction_to_have_event
end
context "with an error raised in the passed block" do
@@ -27,6 +19,8 @@
raise ExampleException, "foo"
end
end.to raise_error(ExampleException, "foo")
+
+ expect_transaction_to_have_event
end
end
@@ -38,6 +32,17 @@
throw :foo
end
end.to throw_symbol(:foo)
+
+ expect_transaction_to_have_event
end
end
+
+ def expect_transaction_to_have_event
+ expect(transaction).to include_event(
+ "name" => "name",
+ "title" => "title",
+ "body" => "body",
+ "body_format" => Appsignal::EventFormatter::DEFAULT
+ )
+ end
end
diff --git a/spec/support/testing.rb b/spec/support/testing.rb
index 66350ed97..95c72b8ac 100644
--- a/spec/support/testing.rb
+++ b/spec/support/testing.rb
@@ -120,16 +120,25 @@ def to_json # rubocop:disable Lint/ToJSON
module AppsignalTest
module Transaction
- # Override the {Appsignal::Transaction.new} method so we can track which
- # transactions are created on the {Appsignal::Testing.transactions} list.
- #
- # @see TransactionHelpers#last_transaction
- def new(*_args)
- transaction = super
- Appsignal::Testing.transactions << transaction
- transaction
+ module ClassMethods
+ # Override the {Appsignal::Transaction.new} method so we can track which
+ # transactions are created on the {Appsignal::Testing.transactions} list.
+ #
+ # @see TransactionHelpers#last_transaction
+ def new(*_args)
+ transaction = super
+ Appsignal::Testing.transactions << transaction
+ transaction
+ end
+ end
+
+ module InstanceMethods
+ def _sample
+ sample_data
+ end
end
end
end
-Appsignal::Transaction.extend(AppsignalTest::Transaction)
+Appsignal::Transaction.extend(AppsignalTest::Transaction::ClassMethods)
+Appsignal::Transaction.prepend(AppsignalTest::Transaction::InstanceMethods)