spec/elastic_apm/span_spec.rb (171 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 Span do
subject do
described_class.new(
name: 'Spannest name',
transaction: transaction,
parent: transaction,
trace_context: trace_context
)
end
let(:trace_context) do
TraceContext.new(
traceparent: TraceContext::Traceparent.parse(
"00-#{'1' * 32}-#{'2' * 16}-01"
)
)
end
let(:transaction) { Transaction.new config: Config.new }
describe '#initialize' do
its(:name) { should eq 'Spannest name' }
its(:type) { should eq 'custom' }
its(:subtype) { should be nil }
its(:action) { should be nil }
its(:transaction) { should eq transaction }
its(:trace_context) { should eq trace_context }
its(:timestamp) { should be_nil }
its(:context) { should be_a Span::Context }
its(:trace_id) { should eq trace_context.trace_id }
its(:id) { should eq trace_context.id }
its(:parent_id) { should eq trace_context.parent_id }
its(:sample_rate) { is_expected.to eq transaction.sample_rate }
context 'with a dot-separated type' do
it 'splits type' do
span =
described_class.new(
name: 'Spannest name',
type: 'typest.subest.actionest',
transaction: transaction,
parent: transaction,
trace_context: trace_context
)
expect(span.type).to eq 'typest'
expect(span.subtype).to eq 'subest'
expect(span.action).to eq 'actionest'
end
end
end
describe '#start', :mock_time do
let(:transaction) { Transaction.new config: Config.new }
subject do
described_class.new(
name: 'Spannest name',
transaction: transaction,
parent: transaction,
trace_context: trace_context
)
end
it 'has a relative and absolute start time', :mock_time do
transaction.start
travel 100
expect(subject.start).to be subject
expect(subject.timestamp - transaction.timestamp).to eq 100
end
end
describe '#stopped', :mock_time do
let(:transaction) { Transaction.new config: Config.new }
subject do
described_class.new(
name: 'Spannest name',
transaction: transaction,
parent: transaction,
trace_context: trace_context
)
end
it 'sets duration' do
transaction.start
subject.start
travel 100
subject.stop
expect(subject).to be_stopped
expect(subject.duration).to be 100
end
it 'calculates self_time' do
subject.start
travel 100
child = Span.new(
name: 'span',
transaction: transaction,
trace_context: nil,
parent: subject
).start
travel 100
child.stop
travel 100
subject.stop
expect(child.self_time).to eq 100
expect(subject.self_time).to eq 200
end
end
describe '#done', :mock_time do
let(:duration_us) { 5_100 }
let(:config) { Config.new }
subject do
described_class.new(
name: 'Span',
transaction: transaction,
parent: transaction,
trace_context: trace_context,
stacktrace_builder: StacktraceBuilder.new(config)
)
end
before do
subject.original_backtrace = caller
subject.start
travel duration_us
subject.done
end
it { should be_stopped }
its(:duration) { should be duration_us }
end
describe "#prepare_for_serialization", :mock_time do
let(:duration_us) { 5_100 }
let(:span_frames_min_duration) { '5ms' }
let(:config) do
Config.new(span_frames_min_duration: span_frames_min_duration)
end
subject do
described_class.new(
name: 'Span',
transaction: transaction,
parent: transaction,
trace_context: trace_context,
stacktrace_builder: StacktraceBuilder.new(config)
)
end
before do
subject.original_backtrace = caller
subject.start
travel duration_us
subject.done
subject.prepare_for_serialization!
end
its(:stacktrace) { should be_a Stacktrace }
context 'when shorter than min for stacktrace' do
let(:span_frames_min_duration) { '1s' }
its(:stacktrace) { should be_nil }
end
context 'when short, but min duration is off' do
let(:duration) { 0 }
let(:span_frames_min_duration) { '-1' }
its(:stacktrace) { should be_a Stacktrace }
end
end
describe "#set_destination" do
it 'adds destination to context' do
subject.set_destination(
address: 'asdf',
service: { name: 'something', type: 'service', resource: 'xyz' },
cloud: { region: 'abc' }
)
expect(subject.context.destination).to be_a(Span::Context::Destination)
expect(subject.context.destination.address).to eq 'asdf'
expect(subject.context.destination.service.resource).to eq 'xyz'
expect(subject.context.destination.cloud.region).to eq 'abc'
expect(subject.context.service.target.name).to eq 'something'
expect(subject.context.service.target.type).to eq 'service'
end
end
end
end