From 6d71a35652ef0ec826363e550e4e74bac3e1b98a Mon Sep 17 00:00:00 2001 From: Munir Abdinur Date: Wed, 10 Jul 2024 16:58:31 -0400 Subject: [PATCH] fix tests --- lib/datadog/tracing/span.rb | 5 +- lib/datadog/tracing/span_event.rb | 6 + .../tracing/transport/serializable_trace.rb | 4 +- sig/datadog/tracing/span_operation.rbs | 2 +- spec/datadog/tracing/span_event_spec.rb | 59 ++++ spec/datadog/tracing/span_spec.rb | 274 ------------------ .../transport/serializable_trace_spec.rb | 39 +++ 7 files changed, 110 insertions(+), 279 deletions(-) create mode 100644 spec/datadog/tracing/span_event_spec.rb delete mode 100644 spec/datadog/tracing/span_spec.rb diff --git a/lib/datadog/tracing/span.rb b/lib/datadog/tracing/span.rb index 507469fed64..d2fb69bbd23 100644 --- a/lib/datadog/tracing/span.rb +++ b/lib/datadog/tracing/span.rb @@ -145,8 +145,7 @@ def to_hash span_id: @id, trace_id: @trace_id, type: @type, - span_links: @links.map(&:to_hash), - events: @events.map(&:to_hash) + span_links: @links.map(&:to_hash) } if stopped? @@ -154,6 +153,8 @@ def to_hash h[:duration] = duration_nano end + h[:meta]['events'] = @events.map(&:to_hash).to_json unless @events.empty? + h end diff --git a/lib/datadog/tracing/span_event.rb b/lib/datadog/tracing/span_event.rb index ab88919c774..a347885d394 100644 --- a/lib/datadog/tracing/span_event.rb +++ b/lib/datadog/tracing/span_event.rb @@ -28,6 +28,12 @@ def initialize( @attributes = attributes&.map { |key, val| [key, val.to_s] }&.to_h || {} @time_unix_nano = time_unix_nano || Core::Utils::Time.now.to_f * 1e9 end + + def to_hash + h = { :name => @name, :time_unix_nano => @time_unix_nano } + h[:attributes] = @attributes unless @attributes.empty? + h + end end end end diff --git a/lib/datadog/tracing/transport/serializable_trace.rb b/lib/datadog/tracing/transport/serializable_trace.rb index 3dc7070292d..f368e95c61a 100644 --- a/lib/datadog/tracing/transport/serializable_trace.rb +++ b/lib/datadog/tracing/transport/serializable_trace.rb @@ -72,8 +72,8 @@ def to_msgpack(packer = nil) packer.write_map_header(number_of_elements_to_write) # Set header with how many elements in the map end - # convert span events to tags - span.set_tag('events', span.events.map(&:to_hash).to_json) if span.events.count + # serialize span events as meta tags + span.set_tag('events', span.events.map(&:to_hash).to_json) if span.events.count > 0 # DEV: We use strings as keys here, instead of symbols, as # DEV: MessagePack will ultimately convert them to strings. diff --git a/sig/datadog/tracing/span_operation.rbs b/sig/datadog/tracing/span_operation.rbs index c94e112b7d6..8002a33ae2a 100644 --- a/sig/datadog/tracing/span_operation.rbs +++ b/sig/datadog/tracing/span_operation.rbs @@ -30,7 +30,7 @@ module Datadog attr_accessor status: untyped - def initialize: (untyped name, ?child_of: untyped?, ?events: untyped?, ?on_error: untyped?, ?parent_id: ::Integer, ?resource: untyped, ?service: untyped?, ?start_time: untyped?, ?tags: untyped?, ?trace_id: untyped?, ?type: untyped?, ?links: untyped?) -> void + def initialize: (untyped name, ?child_of: untyped?, ?events: untyped?, ?on_error: untyped?, ?parent_id: ::Integer, ?resource: untyped, ?service: untyped?, ?start_time: untyped?, ?tags: untyped?, ?trace_id: untyped?, ?type: untyped?, ?links: untyped?, ?span_events: untyped?) -> void def name=: (untyped name) -> untyped diff --git a/spec/datadog/tracing/span_event_spec.rb b/spec/datadog/tracing/span_event_spec.rb new file mode 100644 index 00000000000..bbd717ecf22 --- /dev/null +++ b/spec/datadog/tracing/span_event_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' +require 'support/object_helpers' + +require 'datadog/tracing/span_event' + +RSpec.describe Datadog::Tracing::SpanEvent do + subject(:span_event) { described_class.new(name, attributes: attributes, time_unix_nano: time_unix_nano) } + + let(:name) { nil } + let(:attributes) { nil } + let(:time_unix_nano) { nil } + + describe '::new' do + context 'by default' do + let(:name) { 'This happened!' } + + it do + expect(span_event.name).to eq(name) + expect(span_event.attributes).to eq({}) + expect(span_event.time_unix_nano / 1e9).to be_within(1).of(Time.now.to_f) + end + end + + context 'given' do + context ':attributes' do + let(:attributes) { { tag: 'value' } } + it { is_expected.to have_attributes(attributes: attributes) } + end + + context ':time_unix_nano' do + let(:time_unix_nano) { 30000 } + it { is_expected.to have_attributes(time_unix_nano: time_unix_nano) } + end + end + end + + describe '#to_hash' do + subject(:to_hash) { span_event.to_hash } + let(:name) { 'Another Event!' } + + context 'with required fields' do + it { is_expected.to eq({ name: name, time_unix_nano: span_event.time_unix_nano }) } + end + + context 'with timestamp' do + let(:time_unix_nano) { 25 } + it { is_expected.to include(time_unix_nano: 25) } + end + + context 'when attributes is set' do + let(:attributes) { { 'event.name' => 'test_event', 'event.id' => 1, 'nested' => [true, [2, 3], 'val'] } } + it { + is_expected.to include( + attributes: { 'event.name' => 'test_event', 'event.id' => '1', 'nested' => '[true, [2, 3], "val"]' } + ) + } + end + end +end diff --git a/spec/datadog/tracing/span_spec.rb b/spec/datadog/tracing/span_spec.rb deleted file mode 100644 index 0c6bca7f3f0..00000000000 --- a/spec/datadog/tracing/span_spec.rb +++ /dev/null @@ -1,274 +0,0 @@ -require 'spec_helper' - -require 'json' -require 'msgpack' -require 'pp' -require 'time' - -require 'datadog/core' -require 'datadog/core/utils' -require 'datadog/core/utils/time' -require 'datadog/tracing/metadata/ext' -require 'datadog/tracing/span' - -RSpec.describe Datadog::Tracing::Span do - subject(:span) { described_class.new(name, **span_options) } - - let(:name) { 'my.span' } - let(:span_options) { {} } - - before do - Datadog.configure {} - end - - after do - without_warnings { Datadog.configuration.reset! } - end - - describe '#initialize' do - context 'resource' do - context 'with no value provided' do - it 'defaults to name' do - expect(span.resource).to eq(name) - end - end - - context 'with nil' do - let(:span_options) { { resource: nil } } - - it 'respects the explicitly provided nil' do - expect(span.resource).to be_nil - end - end - - context 'with a value' do - let(:span_options) { { resource: 'my resource' } } - - it 'honors provided value' do - expect(span.resource).to eq('my resource') - end - end - end - - context 'service_entry' do - context 'with nil' do - let(:span_options) { { service_entry: nil } } - - it 'does not tag as top-level' do - expect(span).to_not have_metadata('_dd.top_level') - end - end - - context 'with false' do - let(:span_options) { { service_entry: false } } - - it 'does not tag as top-level' do - expect(span).to_not have_metadata('_dd.top_level') - end - end - - context 'with true' do - let(:span_options) { { service_entry: true } } - - it 'tags as top-level' do - expect(span).to have_metadata('_dd.top_level' => 1.0) - end - end - end - end - - context 'ids' do - it do - expect(span.id).to be_nonzero - expect(span.parent_id).to be_zero - expect(span.trace_id).to be_nonzero - - expect(span.trace_id).to_not eq(span.id) - end - - context 'with parent id' do - let(:span_options) { { parent_id: 2 } } - - it { expect(span.parent_id).to eq(2) } - end - - context 'with trace id' do - let(:span_options) { { trace_id: 3 } } - - it { expect(span.trace_id).to eq(3) } - end - end - - describe '#started?' do - subject(:started?) { span.started? } - - context 'when span hasn\'t been started or stopped' do - it { is_expected.to be false } - end - - it { expect { span.start_time = Time.now }.to change { span.started? }.from(false).to(true) } - it { expect { span.end_time = Time.now }.to_not change { span.started? }.from(false) } - end - - describe '#stopped?' do - subject(:stopped?) { span.stopped? } - - context 'when span hasn\'t been started or stopped' do - it { is_expected.to be false } - end - - it { expect { span.start_time = Time.now }.to_not change { span.stopped? }.from(false) } - it { expect { span.end_time = Time.now }.to change { span.stopped? }.from(false).to(true) } - end - - describe '#duration' do - subject(:duration) { span.duration } - - it { is_expected.to be nil } - - context 'when :duration is set' do - let(:duration_value) { instance_double(Float) } - before { span.duration = duration_value } - it { is_expected.to be duration_value } - end - - context 'when only :start_time is set' do - let(:start_time) { Time.now } - before { span.start_time = start_time } - it { is_expected.to be nil } - end - - context 'when only :end_time is set' do - let(:end_time) { Time.now } - before { span.end_time = end_time } - it { is_expected.to be nil } - end - - context 'when :start_time and :end_time are set' do - let(:start_time) { Time.now } - let(:end_time) { Time.now } - - before do - span.start_time = start_time - span.end_time = end_time - end - - it { is_expected.to eq(end_time - start_time) } - end - end - - describe '#set_error' do - subject(:set_error) { span.set_error(error) } - - context 'given nil' do - let(:error) { nil } - - before { set_error } - - it do - expect(span.status).to eq(Datadog::Tracing::Metadata::Ext::Errors::STATUS) - expect(span.get_tag(Datadog::Tracing::Metadata::Ext::Errors::TAG_TYPE)).to be nil - expect(span.get_tag(Datadog::Tracing::Metadata::Ext::Errors::TAG_MSG)).to be nil - expect(span.get_tag(Datadog::Tracing::Metadata::Ext::Errors::TAG_STACK)).to be nil - end - end - - context 'given an error' do - let(:error) do - begin - raise message - rescue => e - e - end - end - - let(:message) { 'Test error!' } - - before { set_error } - - it do - expect(span.status).to eq(Datadog::Tracing::Metadata::Ext::Errors::STATUS) - expect(span.get_tag(Datadog::Tracing::Metadata::Ext::Errors::TAG_TYPE)).to eq(error.class.to_s) - expect(span.get_tag(Datadog::Tracing::Metadata::Ext::Errors::TAG_MSG)).to eq(message) - expect(span.get_tag(Datadog::Tracing::Metadata::Ext::Errors::TAG_STACK)).to be_a_kind_of(String) - end - end - end - - describe '#==' do - subject(:equals?) { span_one == span_two } - let(:span_one) { described_class.new('span') } - let(:span_two) { described_class.new('span') } - - # Because #id auto-generates, this is false. - it { is_expected.to be false } - - context 'when the #id doesn\'t match' do - let(:span_one) { described_class.new('span', id: 1) } - let(:span_two) { described_class.new('span', id: 2) } - - it { is_expected.to be false } - end - - context 'when the #id matches' do - let(:span_one) { described_class.new('span', id: 1) } - let(:span_two) { described_class.new('span', id: 1) } - - it { is_expected.to be true } - - context 'but other properties do not' do - let(:span_one) { described_class.new('one', id: 1) } - let(:span_two) { described_class.new('two', id: 1) } - - # Because only #id matters, this is true. - it { is_expected.to be true } - end - end - end - - describe '#to_hash' do - subject(:to_hash) { span.to_hash } - - let(:span_options) { { trace_id: 12 } } - - before { span.id = 34 } - - it do - is_expected.to eq( - trace_id: 12, - span_id: 34, - parent_id: 0, - name: 'my.span', - service: nil, - resource: 'my.span', - type: nil, - meta: {}, - metrics: {}, - span_links: [], - error: 0 - ) - end - - context 'with a stopped span' do - before do - span.start_time = Datadog::Core::Utils::Time.now.utc - span.end_time = Datadog::Core::Utils::Time.now.utc - end - - it 'includes timing information' do - is_expected.to include( - start: be >= 0, - duration: be >= 0 - ) - end - end - end - - describe '#pretty_print' do - subject(:pretty_print) { PP.pp(span) } - - it 'output without errors' do - expect { pretty_print }.to output.to_stdout - end - end -end diff --git a/spec/datadog/tracing/transport/serializable_trace_spec.rb b/spec/datadog/tracing/transport/serializable_trace_spec.rb index 3a016e43894..fa5b40924b8 100644 --- a/spec/datadog/tracing/transport/serializable_trace_spec.rb +++ b/spec/datadog/tracing/transport/serializable_trace_spec.rb @@ -3,6 +3,7 @@ require 'msgpack' require 'datadog/tracing/span' +require 'datadog/tracing/span_event' require 'datadog/tracing/trace_segment' require 'datadog/tracing/transport/serializable_trace' @@ -128,6 +129,44 @@ ) end end + + context 'when given span events' do + subject(:unpacked_trace) { MessagePack.unpack(to_msgpack) } + + let(:spans) do + Array.new(2) do |i| + Datadog::Tracing::Span.new( + 'dummy', + events: [ + Datadog::Tracing::SpanEvent.new( + 'First Event', + time_unix_nano: 1_000_000_000 + ), + Datadog::Tracing::SpanEvent.new( + "Another Event #{i}!", + time_unix_nano: 2_000_000_000, + attributes: { 'id' => i, 'required' => i == 1 }, + ), + ], + ) + end + end + + it 'serializes span events' do + expect( + unpacked_trace.map do |s| + s['meta']['events'] + end + ).to eq( + [ + '[{"name":"First Event","time_unix_nano":1000000000},{"name":"Another Event 0!","time_unix_nano":2000000000,' \ + '"attributes":{"id":"0","required":"false"}}]', + '[{"name":"First Event","time_unix_nano":1000000000},{"name":"Another Event 1!","time_unix_nano":2000000000,' \ + '"attributes":{"id":"1","required":"true"}}]', + ] + ) + end + end end describe '#to_json' do