Passed
Push — master ( a7d12b...46dbe8 )
by Dmitry
03:07
created

TracingTest::testNestedSpans()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 17
rs 9.9
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
use OpenTelemetry\Tracing\Builder;
6
use OpenTelemetry\Tracing\Exporter\ZipkinExporter;
7
use OpenTelemetry\Tracing\SpanContext;
8
use OpenTelemetry\Tracing\Status;
9
use OpenTelemetry\Tracing\Tracer;
10
use PHPUnit\Framework\TestCase;
11
12
class TracingTest extends TestCase
13
{
14
    public function testContextGenerationAndRestore()
15
    {
16
        $spanContext = SpanContext::generate();
17
        $this->assertSame(strlen($spanContext->getTraceId()), 32);
18
        $this->assertSame(strlen($spanContext->getSpanId()), 16);
19
20
        $spanContext2 = SpanContext::generate();
21
        $this->assertNotSame($spanContext->getTraceId(), $spanContext2->getTraceId());
22
        $this->assertNotSame($spanContext->getSpanId(), $spanContext2->getSpanId());
23
24
        $spanContext3 = SpanContext::restore($spanContext->getTraceId(), $spanContext->getSpanId());
25
        $this->assertSame($spanContext3->getTraceId(), $spanContext->getTraceId());
26
        $this->assertSame($spanContext3->getSpanId(), $spanContext->getSpanId());
27
    }
28
29
    public function testTracerSpanContextRestore()
30
    {
31
        $tracer = new Tracer();
32
        $spanContext = $tracer->getActiveSpan()->getSpanContext();
33
34
        $spanContext2 = SpanContext::restore($spanContext->getTraceId(), $spanContext->getSpanId());
35
        $tracer2 = new Tracer($spanContext2);
36
37
        $this->assertSame($tracer->getActiveSpan()->getSpanContext()->getTraceId(), $tracer2->getActiveSpan()->getSpanContext()->getTraceId());
38
    }
39
40
    public function testSpanNameUpdate()
41
    {
42
        $database = (new Tracer)->createSpan('database');
43
        $this->assertSame($database->getName(), 'database');
44
        $database->setName('tarantool');
45
        $this->assertSame($database->getName(), 'tarantool');
46
    }
47
48
    public function testNestedSpans()
49
    {
50
        $tracer = new Tracer();
51
52
        $guard = $tracer->createSpan('guard.validate');
53
        $connection = $tracer->createSpan('guard.database.connection');
54
        $procedure = $tracer->createSpan('guard.procedure.registration')->end();
55
        $connection->end();
56
        $policy = $tracer->createSpan('policy.describe')->end();
57
58
        $guard->end();
59
60
        $this->assertSame($connection->getParentSpanContext(), $guard->getSpanContext());
61
        $this->assertSame($procedure->getParentSpanContext(), $connection->getSpanContext());
62
        $this->assertSame($policy->getParentSpanContext(), $guard->getSpanContext());
63
64
        $this->assertCount(5, $tracer->getSpans());
65
    }
66
67
    public function testCreateSpan()
68
    {
69
        $tracer = new Tracer();
70
        $global = $tracer->getActiveSpan();
71
72
        $mysql = $tracer->createSpan('mysql');
73
        $this->assertSame($tracer->getActiveSpan(), $mysql);
74
        $this->assertSame($global->getSpanContext()->getTraceId(), $mysql->getSpanContext()->getTraceId());
75
        $this->assertSame($mysql->getParentSpanContext(), $global->getSpanContext());
76
        $this->assertNotNull($mysql->getStart());
77
        $this->assertTrue($mysql->isRecordingEvents());
78
        $this->assertNull($mysql->getDuration());
79
80
        $mysql->end();
81
        $this->assertFalse($mysql->isRecordingEvents());
82
        $this->assertNotNull($mysql->getDuration());
83
84
        $duration = $mysql->getDuration();
85
        $this->assertSame($duration, $mysql->getDuration());
86
        $mysql->end();
87
        $this->assertGreaterThan($duration, $mysql->getDuration());
88
89
        $this->assertTrue($mysql->getStatus()->isOk());
90
        
91
        // active span rolled back
92
        $this->assertSame($tracer->getActiveSpan(), $global);
93
        $this->assertNull($global->getStatus());
94
        
95
        // active span should be kept for global span
96
        $global->end();
97
        $this->assertSame($tracer->getActiveSpan(), $global);
98
        $this->assertTrue($global->getStatus()->isOk());
99
    }
100
101
    public function testStatusManipulation()
102
    {
103
        $tracer = new Tracer();
104
105
        $cancelled = $tracer->createSpan('cancelled');
106
        $cancelled->end(new Status(Status::CANCELLED));
107
        $this->assertFalse($cancelled->getStatus()->isOk());
108
        $this->assertSame($cancelled->getStatus()->getCanonicalCode(), Status::CANCELLED);
109
        $this->assertSame($cancelled->getStatus()->getDescription(), Status::DESCRIPTION[Status::CANCELLED]);
110
111
        $custom = $tracer->createSpan('custom');
112
        $custom->end(new Status(404, 'Not found'));
113
        $this->assertFalse($custom->getStatus()->isOk());
114
        $this->assertSame($custom->getStatus()->getCanonicalCode(), 404);
115
        $this->assertSame($custom->getStatus()->getDescription(), 'Not found');
116
117
        $noDescription = new Status(500);
118
        $this->assertNull($noDescription->getDescription());
119
120
        $custom->setStatus(new Status(Status::OK));
121
        $this->assertTrue($custom->getStatus()->isOk());
122
123
        $this->assertCount(3, $tracer->getSpans());
124
    }
125
126
    public function testSpanAttributesApi()
127
    {
128
        $span = (new Tracer())->getActiveSpan();
129
130
        // set attributes
131
        $span->setAttributes([ 'username' => 'nekufa' ]);
132
133
        // get attribute
134
        $this->assertSame($span->getAttribute('username'), 'nekufa');
135
        
136
        // otherwrite
137
        $span->setAttributes([ 'email' => '[email protected]', ]);
138
139
        // null attributes
140
        $this->assertNull($span->getAttribute('username'));
141
        $this->assertSame($span->getAttribute('email'), '[email protected]');
142
143
        // set attribute
144
        $span->setAttribute('username', 'nekufa');
145
        $this->assertSame($span->getAttribute('username'), 'nekufa');
146
        $this->assertSame($span->getAttributes(), [
147
            'email' => '[email protected]',
148
            'username' => 'nekufa',
149
        ]);
150
151
        // keep order
152
        $span->setAttributes([ 'a' => 1, 'b' => 2]);
153
        $this->assertSame(array_keys($span->getAttributes()), ['a', 'b']);
154
        $span->setAttributes([ 'b' => 2, 'a' => 1, ]);
155
        $this->assertSame(array_keys($span->getAttributes()), ['b', 'a']);
156
157
        // attribute update don't change the order
158
        $span->setAttribute('a', 3);
159
        $span->setAttribute('b', 4);
160
        $this->assertSame(array_keys($span->getAttributes()), ['b', 'a']);
161
162
        $this->expectExceptionMessage("Span is readonly");
163
        $span->end();
164
        $span->setAttribute('b', 5);
165
    }
166
167
    public function testEventRegistration()
168
    {
169
        $span = (new Tracer)->createSpan('database');
170
        $event = $span->addEvent('select', [
171
            'space' => 'guard.session',
172
            'id' => 67235
173
        ]);
174
        $this->assertSame($event->getName(), 'select');
175
        $this->assertSame($event->getAttributes(), [
176
            'space' => 'guard.session',
177
            'id' => 67235,
178
        ]);
179
        $this->assertSame($event->getAttribute('space'), 'guard.session');
180
        $this->assertNull($event->getAttribute('invalid-attribute'));
181
        $this->assertCount(1, $span->getEvents());
182
        $this->assertSame($span->getEvents(), [$event]);
183
        
184
        $span->addEvent('update')
185
            ->setAttribute('space', 'guard.session')
186
            ->setAttribute('id', 67235)
187
            ->setAttribute('active_at', time());
188
189
        $this->assertCount(2, $span->getEvents());
190
191
        $this->expectExceptionMessage("Span is readonly");
192
        $span->end();
193
        $span->addEvent('update');
194
    }
195
196
    public function testBuilder()
197
    {
198
        $spanContext = SpanContext::generate();
199
        $tracer = Builder::create()
200
            ->setSpanContext($spanContext)
201
            ->getTracer();
202
203
        $this->assertInstanceOf(Tracer::class, $tracer);
204
        $this->assertSame($tracer->getActiveSpan()->getSpanContext(), $spanContext);
205
    }
206
207
    public function testParentSpanContext()
208
    {
209
        $tracer = new Tracer;
210
        $global = $tracer->getActiveSpan();
211
        $request = $tracer->createSpan('request');
212
        $this->assertSame($request->getParentSpanContext()->getSpanId(), $global->getSpanContext()->getSpanId());
213
        $this->assertNull($global->getParentSpanContext());
214
        $this->assertNotNull($request->getParentSpanContext());
215
    }
216
217
    public function testSerialization()
218
    {
219
        $tracer = new Tracer();
220
        $span = $tracer->createSpan('serializable');
221
        $span->setAttribute('attribute', 'value');
222
        $span->addEvent('greet', [ 'name' => 'nekufa' ]);
223
224
        $serialized = serialize($span);
225
        $unserialized = unserialize($serialized);
226
227
        $this->assertSame($span->getName(), $unserialized->getName());
228
        $this->assertSame($span->getStart(), $unserialized->getStart());
229
        $this->assertSame($span->getEnd(), $unserialized->getEnd());
230
231
        $this->assertSame($unserialized->getAttribute('attribute'), 'value');
232
        $this->assertCount(1, $unserialized->getEvents());
233
        [$event] = $unserialized->getEvents();
234
        $this->assertSame($event->getName(), 'greet');
235
        $this->assertSame($event->getAttribute('name'), 'nekufa');
236
237
        return $tracer;
238
    }
239
240
    public function testZipkinConverter()
241
    {
242
        $tracer = new Tracer();
243
        $span = $tracer->createSpan('guard.validate');
244
        $span->setAttribute('service', 'guard');
245
        $event = $span->addEvent('validators.list', [ 'job' => 'stage.updateTime' ]);
246
        $span->end();
247
248
        $exporter = new ZipkinExporter();
249
        $row = $exporter->convert($span);
250
        $this->assertSame($row['name'], $span->getName());
251
252
        $this->assertSame($row['tags'], $span->getAttributes());
253
        $this->assertSame($row['tags']['service'], $span->getAttribute('service'));
254
255
        $this->assertCount(1, $row['annotations']);
256
        [$annotation] = $row['annotations'];
257
        $this->assertSame($annotation['value'], $event->getName());
258
        $this->assertSame($annotation['timestamp'], 1000000 * $event->getTimestamp());
259
    }
260
}