spec/elastic_apm/agent_spec.rb (159 lines of code) (raw):
# Licensed to Elasticsearch B.V. under one or more contributor
# license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you under
# the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# frozen_string_literal: true
require 'spec_helper'
module ElasticAPM
RSpec.describe Agent do
let(:config) { Config.new }
subject { Agent.new config }
describe '#initialize' do
its(:transport) { should be_a Transport::Base }
its(:instrumenter) { should be_a Instrumenter }
its(:stacktrace_builder) { should be_a StacktraceBuilder }
its(:context_builder) { should be_a ContextBuilder }
its(:error_builder) { should be_a ErrorBuilder }
its(:metrics) { should be_a Metrics::Registry }
end
context 'life cycle', :mock_intake do
describe '.start' do
it 'starts an instance and only one' do
first_instance = Agent.start Config.new
expect(Agent.instance).to_not be_nil
expect(Agent.start(Config.new)).to be first_instance
Agent.stop # clean up
end
it 'starts subservices' do
expect(subject.central_config).to receive(:start) { nil }
expect(subject.transport).to receive(:start) { nil }
expect(subject.instrumenter).to receive(:start) { nil }
expect(subject.metrics).to receive(:start) { nil }
subject.start
subject.stop
end
context 'when enabled: false' do
let(:config) { Config.new(enabled: false) }
it "doesn't start" do
Agent.start(config)
expect(Agent.instance).to be nil
end
end
end
describe '.stop' do
it 'kill the running instance' do
Agent.start Config.new
Agent.stop
expect(Agent.instance).to be_nil
end
it 'stops subservices' do
expect(subject.central_config).to receive(:stop)
expect(subject.transport).to receive(:stop)
expect(subject.instrumenter).to receive(:stop)
expect(subject.metrics).to receive(:stop)
subject.stop
end
end
end
context 'instrumenting' do
let(:instrumenter) { subject.instrumenter }
it 'should delegate methods to instrumenter' do
{
current_transaction: nil,
current_span: nil,
end_transaction: [nil],
start_span: [
nil,
nil,
{
subtype: nil,
action: nil,
backtrace: nil,
context: nil,
trace_context: nil,
parent: nil,
sync: nil
}
],
end_span: [nil],
set_label: [nil, nil],
set_custom_context: [nil],
set_user: [nil]
}.each do |name, args|
expect(subject).to delegate(name, to: instrumenter, args: args)
end
end
it 'passes the config when starting a transaction' do
expect(instrumenter).to receive(:start_transaction).with(
nil, nil, context: nil, trace_context: nil, config: subject.config
)
subject.start_transaction
end
end
context 'reporting', :intercept do
describe '#report' do
it 'queues a request' do
expect { subject.report(actual_exception) }
.to change(@intercepted.errors, :length).by 1
end
it 'returns error object' do
result = subject.report(actual_exception)
expect(result).to be_a String
end
context 'with filtered exception types' do
class AgentTestError < StandardError; end
let(:config) do
Config.new(filter_exception_types: %w[ElasticAPM::AgentTestError])
end
it 'ignores exception' do
exception = AgentTestError.new("It's ok!")
expect { subject.report(exception) }
.to change(@intercepted.errors, :length).by 0
end
end
end
describe '#report_message', :intercept do
it 'queues a request' do
expect { subject.report_message('Everything went 💥') }
.to change(@intercepted.errors, :length).by 1
end
it 'returns error object' do
result = subject.report_message(actual_exception)
expect(result).to be_a String
end
end
end
context 'metrics', :mock_intake do
it 'starts' do
subject.start
expect(subject.metrics).to be_running
subject.stop
end
context 'when interval is zero' do
let(:config) { Config.new metrics_interval: 0 }
it "doesn't start" do
subject.start
expect(subject.metrics).to_not be_running
subject.stop
end
end
end
describe '#add_filter' do
it 'may add a filter' do
expect do
subject.add_filter :key, -> {}
end.to change(subject.transport.filters, :length).by 1
end
end
context 'when the process is forked' do
around do |ex|
subject.start
ex.run
subject.stop
end
it 'calls handle_forking! on all associated objects' do
allow(Process).to receive(:pid).and_return(1)
expect(subject.central_config).to receive(:handle_forking!)
expect(subject.transport).to receive(:handle_forking!)
expect(subject.instrumenter).to receive(:handle_forking!)
expect(subject.metrics).to receive(:handle_forking!)
subject.report_message('Everything went boom')
end
end
end
end