spec/lib/appsignal/transaction_spec.rb in appsignal-0.12.beta.31 vs spec/lib/appsignal/transaction_spec.rb in appsignal-0.12.beta.32
- old
+ new
@@ -10,33 +10,38 @@
before :all do
start_agent
end
let(:time) { Time.at(fixed_time) }
- let(:transaction) { Appsignal::Transaction.create('1', {}) }
+ let(:namespace) { Appsignal::Transaction::HTTP_REQUEST }
+ let(:env) { {} }
+ let(:merged_env) { http_request_env_with_data(env) }
+ let(:options) { {} }
+ let(:request) { Rack::Request.new(merged_env) }
+ let(:transaction) { Appsignal::Transaction.create('1', namespace, request, options) }
before { Timecop.freeze(time) }
after { Timecop.return }
context "class methods" do
- describe '.create' do
- subject { Appsignal::Transaction.create('1', {}) }
+ describe ".create" do
+ subject { transaction }
- it 'should add the transaction to thread local' do
- Appsignal::Extension.should_receive(:start_transaction).with('1')
+ it "should add the transaction to thread local" do
+ Appsignal::Extension.should_receive(:start_transaction).with('1', 'http_request')
subject
Thread.current[:appsignal_transaction].should == subject
end
it "should create a transaction" do
subject.should be_a Appsignal::Transaction
- subject.request_id.should == '1'
+ subject.transaction_id.should == '1'
+ subject.namespace.should == 'http_request'
end
end
describe '.current' do
- let(:transaction) { Appsignal::Transaction.create('1', {}) }
before { transaction }
subject { Appsignal::Transaction.current }
it 'should return the correct transaction' do
should == transaction
@@ -45,11 +50,11 @@
describe "complete_current!" do
before { Thread.current[:appsignal_transaction] = nil }
context "with a current transaction" do
- before { Appsignal::Transaction.create('2', {}) }
+ before { Appsignal::Transaction.create('2', Appsignal::Transaction::HTTP_REQUEST, {}) }
it "should complete the current transaction and set the thread appsignal_transaction to nil" do
Appsignal::Extension.should_receive(:finish_transaction).with(kind_of(Integer))
Appsignal::Transaction.complete_current!
@@ -64,205 +69,229 @@
end
end
end
end
- describe "#pause!" do
- it "should change the pause flag to true" do
- expect{
- transaction.pause!
- }.to change(transaction, :paused).from(false).to(true)
+ context "pausing" do
+ describe "#pause!" do
+ it "should change the pause flag to true" do
+ expect{
+ transaction.pause!
+ }.to change(transaction, :paused).from(false).to(true)
+ end
end
- end
- describe "#resume!" do
- before { transaction.pause! }
+ describe "#resume!" do
+ before { transaction.pause! }
- it "should change the pause flag to false" do
- expect{
- transaction.resume!
- }.to change(transaction, :paused).from(true).to(false)
+ it "should change the pause flag to false" do
+ expect{
+ transaction.resume!
+ }.to change(transaction, :paused).from(true).to(false)
+ end
end
- end
- describe "#paused?" do
+ describe "#paused?" do
- it "should return the pasue state" do
- expect( transaction.paused? ).to be_false
- end
+ it "should return the pause state" do
+ expect( transaction.paused? ).to be_false
+ end
- context "when paused" do
- before { transaction.pause! }
+ context "when paused" do
+ before { transaction.pause! }
- it "should return the pasue state" do
- expect( transaction.paused? ).to be_true
+ it "should return the pause state" do
+ expect( transaction.paused? ).to be_true
+ end
end
end
end
context "with transaction instance" do
- let(:env) do
- {
- 'HTTP_USER_AGENT' => 'IE6',
- 'SERVER_NAME' => 'localhost',
- 'action_dispatch.routes' => 'not_available',
- 'HTTP_X_REQUEST_START' => '1000000'
- }
- end
- let(:transaction) { Appsignal::Transaction.create('3', env) }
-
context "initialization" do
subject { transaction }
- its(:request_id) { should == '3' }
+ its(:transaction_id) { should == '1' }
+ its(:namespace) { should == 'http_request' }
its(:transaction_index) { should be_a Integer }
- its(:root_event_payload) { should be_nil }
- its(:exception) { should be_nil }
- its(:env) { should == env }
+ its(:request) { should_not be_nil }
+ its(:paused) { should be_false }
its(:tags) { should == {} }
- its(:queue_start) { should == -1 }
- end
+ its(:transaction_index) { should be_a Integer }
- describe '#request' do
- subject { transaction.request }
+ context "options" do
+ subject { transaction.options }
- it { should be_a ::Rack::Request }
+ its([:params_method]) { should == :params }
+
+ context "with overridden options" do
+ let(:options) { {:params_method => :filtered_params} }
+
+ its([:params_method]) { should == :filtered_params }
+ end
+ end
end
describe "#set_tags" do
it "should add tags to transaction" do
expect {
transaction.set_tags({'a' => 'b'})
}.to change(transaction, :tags).to({'a' => 'b'})
end
end
- describe '#set_root_event' do
- context "for a process_action event" do
- let(:name) { 'process_action.action_controller' }
- let(:payload) { create_payload }
-
- it "should set the meta data in the transaction and native" do
- Appsignal::Extension.should_receive(:set_transaction_base_data).with(
+ describe "set_action" do
+ it "should set the action in extension" do
+ Appsignal::Extension.should_receive(:set_transaction_action).with(
kind_of(Integer),
- 'http_request',
- 'BlogPostsController#show',
- kind_of(Integer)
- )
+ 'PagesController#show'
+ ).once
- metadata = {
- 'path' => '/blog',
- 'request_format' => 'html',
- 'request_method' => 'GET',
- 'status' => '200'
- }
- metadata.each do |key, value|
- transaction.should_receive(:set_metadata).with(key, value).once
- end
+ transaction.set_action('PagesController#show')
+ end
- transaction.set_root_event(name, payload)
+ it "should not set the action in extension when value is nil" do
+ Appsignal::Extension.should_not_receive(:set_transaction_action)
- transaction.root_event_payload.should == payload
- transaction.action.should == 'BlogPostsController#show'
- transaction.kind.should == 'http_request'
- transaction.queue_start.should be_kind_of(Integer)
+ transaction.set_action(nil)
+ end
+ end
+
+ describe "#set_http_or_background_action" do
+ context "for a hash with controller and action" do
+ let(:from) { {:controller => 'HomeController', :action => 'show'} }
+
+ it "should set the action" do
+ transaction.should_receive(:set_action).with('HomeController#show')
end
end
- context "for a perform_job event" do
- let(:name) { 'perform_job.delayed_job' }
- let(:payload) { create_background_payload }
+ context "for a hash with just action" do
+ let(:from) { {:action => 'show'} }
- it "should set the meta data in the transaction and native" do
- Appsignal::Extension.should_receive(:set_transaction_base_data).with(
- kind_of(Integer),
- 'background_job',
- 'BackgroundJob#perform',
- kind_of(Integer)
- )
+ it "should set the action" do
+ transaction.should_receive(:set_action).with('show')
+ end
+ end
- transaction.set_root_event(name, payload)
+ context "for a hash with class and method" do
+ let(:from) { {:class => 'Worker', :method => 'perform'} }
- transaction.root_event_payload.should == payload
- transaction.action.should == 'BackgroundJob#perform'
- transaction.kind.should == 'background_job'
- transaction.queue_start.should be_kind_of(Integer)
+ it "should set the action" do
+ transaction.should_receive(:set_action).with('Worker#perform')
end
end
+
+ after { transaction.set_http_or_background_action(from) }
end
+ describe "set_queue_start" do
+ it "should set the queue start in extension" do
+ Appsignal::Extension.should_receive(:set_transaction_queue_start).with(
+ kind_of(Integer),
+ 10.0
+ ).once
+
+ transaction.set_queue_start(10.0)
+ end
+
+ it "should not set the queue start in extension when value is nil" do
+ Appsignal::Extension.should_not_receive(:set_transaction_queue_start)
+
+ transaction.set_queue_start(nil)
+ end
+ end
+
+ describe "#set_http_or_background_queue_start" do
+ context "for a http transaction" do
+ let(:namespace) { Appsignal::Transaction::HTTP_REQUEST }
+ let(:env) { {'HTTP_X_REQUEST_START' => (fixed_time * 1000).to_s} }
+
+ it "should set the queue start on the transaction" do
+ transaction.should_receive(:set_queue_start).with(13897836000)
+
+ transaction.set_http_or_background_queue_start
+ end
+ end
+
+ context "for a background transaction" do
+ let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
+ let(:env) { {:queue_start => fixed_time} }
+
+ it "should set the queue start on the transaction" do
+ transaction.should_receive(:set_queue_start).with(1389783600000)
+
+ transaction.set_http_or_background_queue_start
+ end
+ end
+ end
+
describe "#set_metadata" do
- it "should set the metdata in native" do
+ it "should set the metdata in extension" do
Appsignal::Extension.should_receive(:set_transaction_metadata).with(
kind_of(Integer),
'request_method',
'GET'
).once
transaction.set_metadata('request_method', 'GET')
end
- it "should set the metdata in native when value is nil" do
+ it "should not set the metdata in extension when value is nil" do
Appsignal::Extension.should_not_receive(:set_transaction_metadata)
transaction.set_metadata('request_method', nil)
end
end
describe '#set_error' do
+ let(:env) { http_request_env_with_data }
let(:error) { double(:error, :message => 'test message', :backtrace => ['line 1']) }
it "should also respond to add_exception for backwords compatibility" do
transaction.should respond_to(:add_exception)
end
- it "should set an error and it's data in native" do
- Appsignal::Extension.should_receive(:set_transaction_error).with(
- kind_of(Integer),
- 'RSpec::Mocks::Mock',
- 'test message'
- )
- Appsignal::Extension.should_receive(:set_transaction_error_data).with(
- kind_of(Integer),
- 'environment',
- "{\"SERVER_NAME\":\"localhost\",\"HTTP_X_REQUEST_START\":\"1000000\",\"HTTP_USER_AGENT\":\"IE6\"}"
- ).once
- Appsignal::Extension.should_receive(:set_transaction_error_data).with(
- kind_of(Integer),
- 'session_data',
- "{}"
- ).once
- Appsignal::Extension.should_receive(:set_transaction_error_data).with(
- kind_of(Integer),
- 'backtrace',
- "[\"line 1\"]"
- ).once
- Appsignal::Extension.should_receive(:set_transaction_error_data).with(
- kind_of(Integer),
- 'tags',
- "{}"
- ).once
+ it "should not add the error if it's in the ignored list" do
+ Appsignal.stub(:is_ignored_error? => true)
+ Appsignal::Extension.should_not_receive(:set_transaction_error)
transaction.set_error(error)
end
- context "with root event payload" do
- before do
- transaction.set_root_event('process_action.action_controller', create_payload)
- end
-
- it "should also set params" do
+ context "for a http request" do
+ it "should set an error and it's data in native" do
+ Appsignal::Extension.should_receive(:set_transaction_error).with(
+ kind_of(Integer),
+ 'RSpec::Mocks::Mock',
+ 'test message'
+ )
Appsignal::Extension.should_receive(:set_transaction_error_data).with(
kind_of(Integer),
+ 'environment',
+ "{\"CONTENT_LENGTH\":\"0\",\"REQUEST_METHOD\":\"GET\",\"SERVER_NAME\":\"example.org\",\"SERVER_PORT\":\"80\",\"PATH_INFO\":\"/blog\"}"
+ ).once
+ Appsignal::Extension.should_receive(:set_transaction_error_data).with(
+ kind_of(Integer),
+ 'session_data',
+ "{}"
+ ).once
+ Appsignal::Extension.should_receive(:set_transaction_error_data).with(
+ kind_of(Integer),
+ 'backtrace',
+ "[\"line 1\"]"
+ ).once
+ Appsignal::Extension.should_receive(:set_transaction_error_data).with(
+ kind_of(Integer),
'params',
'{"controller":"blog_posts","action":"show","id":"1"}'
).once
Appsignal::Extension.should_receive(:set_transaction_error_data).with(
kind_of(Integer),
- kind_of(String),
- kind_of(String)
- ).exactly(4).times
+ 'tags',
+ "{}"
+ ).once
transaction.set_error(error)
end
end
@@ -286,59 +315,161 @@
transaction.set_error(error)
end
end
end
- # protected
+ context "generic request" do
+ let(:env) { {} }
+ subject { Appsignal::Transaction::GenericRequest.new(env) }
- describe "#set_background_queue_start" do
- before do
- transaction.stub(:root_event_payload => payload)
- transaction.send(:set_background_queue_start)
+ it "should initialize with an empty env" do
+ subject.env.should be_empty
end
- subject { transaction.queue_start }
+ context "with a filled env" do
+ let(:env) do
+ {
+ :params => {:id => 1},
+ :queue_start => 10
+ }
+ end
- context "when queue start is nil" do
- let(:payload) { create_background_payload(:queue_start => nil) }
+ its(:env) { should == env }
+ its(:params) { should == {:id => 1} }
+ end
+ end
- it { should == -1 }
+ # protected
+
+ describe "#background_queue_start" do
+ subject { transaction.send(:background_queue_start) }
+
+ context "when queue start is nil" do
+ it { should == nil }
end
context "when queue start is set" do
- let(:payload) { create_background_payload }
+ let(:env) { background_env_with_data }
it { should == 1389783590000 }
end
end
+ describe "#http_queue_start" do
+ let(:slightly_earlier_time) { fixed_time - 0.4 }
+ let(:slightly_earlier_time_value) { (slightly_earlier_time * factor).to_i }
+ subject { transaction.send(:http_queue_start) }
+
+ shared_examples "http queue start" do
+ context "when env is nil" do
+ before { transaction.request.stub(:env => nil) }
+
+ it { should be_nil }
+ end
+
+ context "with no relevant header set" do
+ let(:env) { {} }
+
+ it { should be_nil }
+ end
+
+ context "with the HTTP_X_REQUEST_START header set" do
+ let(:env) { {'HTTP_X_REQUEST_START' => "t=#{slightly_earlier_time_value}"} }
+
+ it { should == 1389783599 }
+
+ context "with unparsable content" do
+ let(:env) { {'HTTP_X_REQUEST_START' => 'something'} }
+
+ it { should be_nil }
+ end
+
+ context "with some cruft" do
+ let(:env) { {'HTTP_X_REQUEST_START' => "t=#{slightly_earlier_time_value}aaaa"} }
+
+ it { should == 1389783599 }
+ end
+
+ context "with a really low number" do
+ let(:env) { {'HTTP_X_REQUEST_START' => "t=100"} }
+
+ it { should be_nil }
+ end
+
+ context "with the alternate HTTP_X_QUEUE_START header set" do
+ let(:env) { {'HTTP_X_QUEUE_START' => "t=#{slightly_earlier_time_value}"} }
+
+ it { should == 1389783599 }
+ end
+ end
+ end
+
+ context "time in miliseconds" do
+ let(:factor) { 1_000 }
+
+ it_should_behave_like "http queue start"
+ end
+
+ context "time in microseconds" do
+ let(:factor) { 1_000_000 }
+
+ it_should_behave_like "http queue start"
+ end
+ end
+
describe "#sanitized_params" do
subject { transaction.send(:sanitized_params) }
- context "without a root event payload" do
+ context "without params" do
+ before { transaction.request.stub(:params => nil) }
+
it { should be_nil }
end
- context "with a root event payload" do
- before { transaction.stub(:root_event_payload => create_payload) }
+ context "when not sending params" do
+ before { Appsignal.config.config_hash[:send_params] = false }
+ after { Appsignal.config.config_hash[:send_params] = true }
+ it { should be_nil }
+ end
+
+ context "when params method does not exist" do
+ let(:options) { {:params_method => :nonsense} }
+
+ it { should be_nil }
+ end
+
+ context "with an array" do
+ let(:request) { Appsignal::Transaction::GenericRequest.new(background_env_with_data(:params => ['arg1', 'arg2'])) }
+
+ it { should == ['arg1', 'arg2'] }
+ end
+
+ context "with env" do
it "should call the params sanitizer" do
- Appsignal::ParamsSanitizer.should_receive(:sanitize).with(kind_of(Hash)).and_return({:id => 1})
+ Appsignal::ParamsSanitizer.should_receive(:sanitize).with(kind_of(Hash)).and_return({
+ 'controller' => 'blog_posts',
+ 'action' => 'show',
+ 'id' => '1'
+ })
- subject.should == {:id => 1}
+ subject.should == {
+ 'controller' => 'blog_posts',
+ 'action' => 'show',
+ 'id' => '1'
+ }
end
end
end
describe "#sanitized_environment" do
let(:whitelisted_keys) { Appsignal::Transaction::ENV_METHODS }
- let(:transaction) { Appsignal::Transaction.create('1', env) }
subject { transaction.send(:sanitized_environment) }
context "when env is nil" do
- let(:env) { nil }
+ before { transaction.request.stub(:env => nil) }
it { should be_nil }
end
context "when env is present" do
@@ -356,23 +487,28 @@
describe '#sanitized_session_data' do
subject { transaction.send(:sanitized_session_data) }
context "when env is nil" do
- let(:transaction) { Appsignal::Transaction.create('1', nil) }
+ before { transaction.request.stub(:session => nil) }
it { should be_nil }
end
context "when env is empty" do
- let(:transaction) { Appsignal::Transaction.create('1', {}) }
+ before { transaction.request.stub(:session => {}) }
it { should == {} }
end
+ context "when request class does not have a session method" do
+ let(:request) { Appsignal::Transaction::GenericRequest.new({}) }
+
+ it { should be_nil }
+ end
+
context "when there is a session" do
- let(:transaction) { Appsignal::Transaction.create('1', {}) }
before do
transaction.should respond_to(:request)
transaction.stub_chain(:request, :session => {:foo => :bar})
transaction.stub_chain(:request, :fullpath => :bar)
end
@@ -404,25 +540,24 @@
}.new
ActionDispatch::Request::Session.create(store, {}, {})
end
end
end
- end
- context "when skipping session data" do
- before do
- Appsignal.config = {:skip_session_data => true}
- end
+ context "when skipping session data" do
+ before do
+ Appsignal.config = {:skip_session_data => true}
+ end
- it "does not pass the session data into the params sanitizer" do
- Appsignal::ParamsSanitizer.should_not_receive(:sanitize)
- subject.should be_nil
+ it "does not pass the session data into the params sanitizer" do
+ Appsignal::ParamsSanitizer.should_not_receive(:sanitize)
+ subject.should be_nil
+ end
end
end
end
describe '#sanitized_tags' do
- let(:transaction) { Appsignal::Transaction.create('1', {}) }
before do
transaction.set_tags(
{
:valid_key => 'valid_value',
'valid_string_key' => 'valid_value',