spec/elastic_apm/transport/serializers/span_serializer_spec.rb (243 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 module Transport module Serializers RSpec.describe SpanSerializer do let(:config) { Config.new } subject { described_class.new config } describe '#build', :mock_time do let(:transaction) { Transaction.new(config: config).start } let(:trace_context) do traceparent = TraceContext::Traceparent.parse("00-#{'1' * 32}-#{'2' * 16}-01") TraceContext.new(traceparent: traceparent) end let :span do Span.new( name: 'Span', transaction: transaction, parent: transaction, trace_context: trace_context, sync: true ).tap do |span| span.start travel 10_000 span.stop end end let(:result) { subject.build(span) } it 'builds' do expect(result).to match( span: { id: /.{16}/, transaction_id: transaction.id, parent_id: span.parent_id, trace_id: span.trace_id, name: 'Span', type: 'custom', context: { sync: true }, stacktrace: [], sample_rate: 1, timestamp: 694_224_000_000_000, duration: 10, outcome: nil } ) end context 'with a context' do let(:span) do Span.new( name: 'Span', transaction: transaction, parent: transaction, trace_context: trace_context, context: Span::Context.new( db: { statement: 'asd' }, http: { url: 'dsa' }, sync: false, labels: { foo: 'bar' }, service: {target: {name: 'test', type: 'db'}}, links: [Span::Context::Links::Link.new(trace_id: 'abc', span_id: '123')] ) ) end it 'adds context object' do expect(result.dig(:span, :context, :db, :statement)) .to eq 'asd' expect(result.dig(:span, :context, :http, :url)).to eq 'dsa' expect(result.dig(:span, :context, :sync)).to eq false expect(result.dig(:span, :context, :tags, :foo)).to eq 'bar' expect(result.dig(:span, :context, :service, :target, :name)).to eq 'test' expect(result.dig(:span, :context, :service, :target, :type)).to eq 'db' expect(result.dig(:span, :context, :links).length).to eq 1 end context 'with rows_affected' do let(:span) do Span.new( name: 'Span', transaction: transaction, parent: transaction, trace_context: trace_context, context: Span::Context.new( db: { rows_affected: 2 } ) ) end it 'adds rows_affected' do expect(result.dig(:span, :context, :db, :rows_affected)) .to eq 2 end end context 'when sync is nil' do let(:span) do Span.new( name: 'Span', transaction: transaction, parent: transaction, trace_context: trace_context, context: Span::Context.new( db: { statement: 'asd' }, http: { url: 'dsa' } ) ) end it 'adds context object' do expect(result.dig(:span, :context, :db, :statement)) .to eq 'asd' expect(result.dig(:span, :context, :http, :url)).to eq 'dsa' expect(result[:span][:context].key?(:sync)).to be false end end end context 'with a destination' do it 'adds destination object' do span = Span.new( name: 'Span', transaction: transaction, parent: transaction, trace_context: trace_context, context: Span::Context.new( destination: { service: { name: 'a', resource: 'b', type: 'c', }, address: 'd', port: 8080 } ) ) result = subject.build(span) expect(result.dig(:span, :context, :destination)).to match( { service: { name: 'a', resource: 'b', type: 'c' }, address: 'd', port: 8080 } ) end end context 'with a message' do it 'adds message object' do span = Span.new( name: 'Span', transaction: transaction, parent: transaction, trace_context: trace_context, context: Span::Context.new( message: { queue_name: 'my_queue', age_ms: 1000 } ) ) result = subject.build(span) expect(result.dig(:span, :context, :message)).to match( { queue: { name: 'my_queue' }, age: { ms: 1000 } } ) end end context 'with a large db.statement' do it 'truncates to 10k chars' do span = Span.new( name: 'Span', transaction: transaction, parent: transaction, trace_context: trace_context, context: Span::Context.new( db: { statement: 'X' * 11_000 } ) ) result = subject.build(span) statement = result.dig(:span, :context, :db, :statement) expect(statement.length).to be(10_000) end end context 'with split types' do let(:span) do Span.new( name: 'Span', transaction: transaction, parent: transaction, trace_context: trace_context, type: 'a', subtype: 'b', action: 'c' ) end it 'joins them for sending' do expect(result[:span][:type]).to eq 'a.b.c' end end context 'with outcome' do it 'adds the outcome' do span = Span.new( name: 'Span', transaction: transaction, parent: transaction, trace_context: trace_context ) span.outcome = 'success' result = subject.build(span) expect(result[:span][:outcome]).to eq 'success' end end context 'with a destination and cloud' do it 'adds destination with cloud' do span = Span.new( name: 'Span', transaction: transaction, parent: transaction, trace_context: trace_context, context: Span::Context.new( destination: { service: { resource: 'a' }, cloud: { region: 'b' } } ) ) # set auto-infered destination.service fields span.start.done result = subject.build(span) expect(result.dig(:span, :context, :destination, :service)) .to match({ resource: 'a', name: '', type: '' }) expect(result.dig(:span, :context, :destination, :cloud)) .to match({ region: 'b' }) end end end end end end end