|
3 | 3 | require 'ddtrace/span'
|
4 | 4 |
|
5 | 5 | RSpec.describe Datadog::Span do
|
6 |
| - subject(:span) { described_class.new(tracer, name, context: context) } |
| 6 | + subject(:span) { described_class.new(tracer, name, context: context, **span_options) } |
7 | 7 | let(:tracer) { get_test_tracer }
|
8 | 8 | let(:context) { Datadog::Context.new }
|
9 | 9 | let(:name) { 'my.span' }
|
| 10 | + let(:span_options) { {} } |
| 11 | + |
| 12 | + context 'ids' do |
| 13 | + it do |
| 14 | + expect(span.span_id).to be_nonzero |
| 15 | + expect(span.parent_id).to be_zero |
| 16 | + expect(span.trace_id).to be_nonzero |
| 17 | + |
| 18 | + expect(span.trace_id).to_not eq(span.span_id) |
| 19 | + end |
| 20 | + |
| 21 | + context 'with parent id' do |
| 22 | + let(:span_options) { { parent_id: 2 } } |
| 23 | + it { expect(span.parent_id).to eq(2) } |
| 24 | + end |
| 25 | + |
| 26 | + context 'with trace id' do |
| 27 | + let(:span_options) { { trace_id: 3 } } |
| 28 | + it { expect(span.trace_id).to eq(3) } |
| 29 | + end |
| 30 | + |
| 31 | + context 'set parent span' do |
| 32 | + subject(:parent=) { span.parent = parent } |
| 33 | + |
| 34 | + context 'to a span' do |
| 35 | + let(:parent) { described_class.new(tracer, 'parent', **parent_span_options) } |
| 36 | + let(:parent_span_options) { {} } |
| 37 | + |
| 38 | + before do |
| 39 | + parent.sampled = false |
| 40 | + subject |
| 41 | + end |
| 42 | + |
| 43 | + it do |
| 44 | + expect(span.parent).to eq(parent) |
| 45 | + expect(span.parent_id).to eq(parent.span_id) |
| 46 | + expect(span.trace_id).to eq(parent.trace_id) |
| 47 | + expect(span.sampled).to eq(false) |
| 48 | + end |
| 49 | + |
| 50 | + context 'with service' do |
| 51 | + let(:parent_span_options) { { service: 'parent' } } |
| 52 | + |
| 53 | + it 'copies parent service to child' do |
| 54 | + expect(span.service).to eq('parent') |
| 55 | + end |
| 56 | + |
| 57 | + context 'with existing child service' do |
| 58 | + let(:span_options) { { service: 'child' } } |
| 59 | + |
| 60 | + it 'does not override child service' do |
| 61 | + expect(span.service).to eq('child') |
| 62 | + end |
| 63 | + end |
| 64 | + end |
| 65 | + end |
| 66 | + |
| 67 | + context 'to nil' do |
| 68 | + let(:parent) { nil } |
| 69 | + |
| 70 | + it 'removes the parent' do |
| 71 | + subject |
| 72 | + expect(span.parent).to be_nil |
| 73 | + expect(span.parent_id).to be_zero |
| 74 | + expect(span.trace_id).to eq(span.span_id) |
| 75 | + end |
| 76 | + end |
| 77 | + end |
| 78 | + end |
10 | 79 |
|
11 | 80 | describe '#finish' do
|
12 | 81 | subject(:finish) { span.finish }
|
13 | 82 |
|
| 83 | + it 'calculates duration' do |
| 84 | + expect(span.start_time).to be_nil |
| 85 | + expect(span.end_time).to be_nil |
| 86 | + |
| 87 | + subject |
| 88 | + |
| 89 | + expect(span.end_time).to be <= Time.now |
| 90 | + expect(span.start_time).to be <= span.end_time |
| 91 | + expect(span.to_hash[:duration]).to be >= 0 |
| 92 | + end |
| 93 | + |
| 94 | + context 'with multiple calls to finish' do |
| 95 | + it 'does not flush the span more than once' do |
| 96 | + allow(context).to receive(:close_span).once |
| 97 | + allow(tracer).to receive(:record).once |
| 98 | + |
| 99 | + subject |
| 100 | + expect(span.finish).to be_falsey |
| 101 | + end |
| 102 | + |
| 103 | + it 'does not modify the span' do |
| 104 | + end_time = subject.end_time |
| 105 | + |
| 106 | + expect(span.finish).to be_falsey |
| 107 | + expect(span.end_time).to eq(end_time) |
| 108 | + end |
| 109 | + end |
| 110 | + |
| 111 | + context 'with finish time provided' do |
| 112 | + subject(:finish) { span.finish(time) } |
| 113 | + let(:time) { Time.now } |
| 114 | + |
| 115 | + it 'does not use wall time' do |
| 116 | + sleep(0.0001) |
| 117 | + subject |
| 118 | + |
| 119 | + expect(span.end_time).to eq(time) |
| 120 | + end |
| 121 | + end |
| 122 | + |
| 123 | + context '#finished?' do |
| 124 | + it { expect { subject }.to change { span.finished? }.from(false).to(true) } |
| 125 | + end |
| 126 | + |
14 | 127 | context 'when an error occurs while closing the span on the context' do
|
15 | 128 | include_context 'health metrics'
|
16 | 129 |
|
|
94 | 207 | end
|
95 | 208 | end
|
96 | 209 |
|
| 210 | + describe '#get_metric' do |
| 211 | + subject(:get_metric) { span.get_metric(key) } |
| 212 | + let(:key) { 'key' } |
| 213 | + |
| 214 | + context 'with no metrics' do |
| 215 | + it { is_expected.to be_nil } |
| 216 | + end |
| 217 | + |
| 218 | + context 'with a metric' do |
| 219 | + let(:value) { 1.0 } |
| 220 | + before { span.set_metric(key, value) } |
| 221 | + |
| 222 | + it { is_expected.to eq(1.0) } |
| 223 | + end |
| 224 | + |
| 225 | + context 'with a tag' do |
| 226 | + let(:value) { 'tag' } |
| 227 | + before { span.set_tag(key, value) } |
| 228 | + |
| 229 | + it { is_expected.to eq('tag') } |
| 230 | + end |
| 231 | + end |
| 232 | + |
| 233 | + describe '#set_metric' do |
| 234 | + subject(:set_metric) { span.set_metric(key, value) } |
| 235 | + let(:key) { 'key' } |
| 236 | + |
| 237 | + let(:metrics) { span.to_hash[:metrics] } |
| 238 | + let(:metric) { metrics[key] } |
| 239 | + |
| 240 | + shared_examples 'a metric' do |value, expected| |
| 241 | + let(:value) { value } |
| 242 | + |
| 243 | + it do |
| 244 | + subject |
| 245 | + expect(metric).to eq(expected) |
| 246 | + end |
| 247 | + end |
| 248 | + |
| 249 | + context 'with a valid value' do |
| 250 | + context 'with an integer' do |
| 251 | + it_behaves_like 'a metric', 0, 0.0 |
| 252 | + end |
| 253 | + |
| 254 | + context 'with a float' do |
| 255 | + it_behaves_like 'a metric', 12.34, 12.34 |
| 256 | + end |
| 257 | + |
| 258 | + context 'with a number as string' do |
| 259 | + it_behaves_like 'a metric', '12.34', 12.34 |
| 260 | + end |
| 261 | + end |
| 262 | + |
| 263 | + context 'with an invalid value' do |
| 264 | + context 'with nil' do |
| 265 | + it_behaves_like 'a metric', nil, nil |
| 266 | + end |
| 267 | + |
| 268 | + context 'with a string' do |
| 269 | + it_behaves_like 'a metric', 'foo', nil |
| 270 | + end |
| 271 | + |
| 272 | + context 'with a complex object' do |
| 273 | + it_behaves_like 'a metric', [], nil |
| 274 | + end |
| 275 | + end |
| 276 | + end |
| 277 | + |
97 | 278 | describe '#set_tag' do
|
98 | 279 | subject(:set_tag) { span.set_tag(key, value) }
|
99 | 280 |
|
|
338 | 519 | end
|
339 | 520 | end
|
340 | 521 | end
|
| 522 | + |
| 523 | + describe '#set_error' do |
| 524 | + subject(:set_error) { span.set_error(error) } |
| 525 | + let(:error) { RuntimeError.new('oops') } |
| 526 | + let(:backtrace) { %w[method1 method2 method3] } |
| 527 | + |
| 528 | + before { error.set_backtrace(backtrace) } |
| 529 | + |
| 530 | + it do |
| 531 | + subject |
| 532 | + |
| 533 | + expect(span).to have_error |
| 534 | + expect(span).to have_error_message('oops') |
| 535 | + expect(span).to have_error_type('RuntimeError') |
| 536 | + expect(span).to have_error_stack(backtrace.join($RS)) |
| 537 | + end |
| 538 | + end |
341 | 539 | end
|
0 commit comments