Skip to content

Commit 55b623a

Browse files
committed
refactor(tests): Use of thekind parameter.
This commit updates all tests to use the `kind` parameter while creating a new `InstanaSpan` or `ReadableSpan`. This commit fixes #813. Signed-off-by: Paulo Vital <paulo.vital@ibm.com>
1 parent fb3a49c commit 55b623a

File tree

4 files changed

+322
-6
lines changed

4 files changed

+322
-6
lines changed

tests/span/test_base_span.py

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
# (c) Copyright IBM Corp. 2024
22

3-
from typing import Generator
43
from unittest.mock import Mock, patch
54

6-
import pytest
5+
from opentelemetry.trace import SpanKind
76

87
from instana.recorder import StanRecorder
98
from instana.span.base_span import BaseSpan
@@ -173,3 +172,82 @@ def test_convert_attribute_value_exception(
173172

174173
converted_value = base_span._convert_attribute_value(mock)
175174
assert not converted_value
175+
176+
177+
def test_basespan_does_not_store_kind(
178+
span_context: SpanContext,
179+
span_processor: StanRecorder,
180+
) -> None:
181+
"""Test that BaseSpan does not directly store or interfere with kind parameter."""
182+
span = InstanaSpan(
183+
"test-base-span", span_context, span_processor, kind=SpanKind.CLIENT
184+
)
185+
base_span = BaseSpan(span, None)
186+
187+
# BaseSpan should not have a kind attribute
188+
assert not hasattr(base_span, "k")
189+
assert not hasattr(base_span, "kind")
190+
191+
# But the original span should still have it
192+
assert span.kind == SpanKind.CLIENT
193+
194+
195+
def test_basespan_with_different_span_kinds(
196+
span_context: SpanContext,
197+
span_processor: StanRecorder,
198+
) -> None:
199+
"""Test that BaseSpan works correctly with spans of different kinds."""
200+
kinds = [
201+
SpanKind.INTERNAL,
202+
SpanKind.SERVER,
203+
SpanKind.CLIENT,
204+
SpanKind.PRODUCER,
205+
SpanKind.CONSUMER,
206+
]
207+
208+
for kind in kinds:
209+
span = InstanaSpan(
210+
f"test-span-{kind.name}", span_context, span_processor, kind=kind
211+
)
212+
base_span = BaseSpan(span, None)
213+
214+
# Verify BaseSpan is created successfully regardless of kind
215+
assert base_span.t == span_context.trace_id
216+
assert base_span.s == span_context.span_id
217+
218+
# Verify original span retains its kind
219+
assert span.kind == kind
220+
221+
222+
def test_basespan_kind_inheritance_to_registered_span(
223+
span_context: SpanContext,
224+
span_processor: StanRecorder,
225+
) -> None:
226+
"""Test that kind is properly inherited by RegisteredSpan through BaseSpan."""
227+
from instana.span.registered_span import RegisteredSpan
228+
229+
span = InstanaSpan("wsgi", span_context, span_processor, kind=SpanKind.SERVER)
230+
reg_span = RegisteredSpan(span, None, "test-service")
231+
232+
# RegisteredSpan should have k field set correctly
233+
assert reg_span.k == SpanKind.SERVER
234+
# Verify it inherits BaseSpan attributes
235+
assert reg_span.t == span_context.trace_id
236+
assert reg_span.s == span_context.span_id
237+
238+
239+
def test_basespan_kind_inheritance_to_sdk_span(
240+
span_context: SpanContext,
241+
span_processor: StanRecorder,
242+
) -> None:
243+
"""Test that kind is accessible by SDKSpan through BaseSpan."""
244+
from instana.span.sdk_span import SDKSpan
245+
246+
span = InstanaSpan("test-sdk", span_context, span_processor, kind=SpanKind.PRODUCER)
247+
sdk_span = SDKSpan(span, None, "test-service")
248+
249+
# SDKSpan should be able to access span.kind
250+
assert span.kind == SpanKind.PRODUCER
251+
# Verify it inherits BaseSpan attributes
252+
assert sdk_span.t == span_context.trace_id
253+
assert sdk_span.s == span_context.span_id

tests/span/test_readable_span.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import Generator
55

66
import pytest
7+
from opentelemetry.trace import SpanKind
78
from opentelemetry.trace.status import Status, StatusCode
89

910
from instana.span.readable_span import Event, ReadableSpan
@@ -45,10 +46,10 @@ def test_readablespan(
4546
assert not self.span.events
4647
assert not self.span.parent_id
4748
assert not self.span.duration
48-
assert self.span.status
49-
5049
assert not self.span.stack
5150
assert self.span.synthetic is False
51+
assert self.span.status
52+
assert self.span.kind == SpanKind.INTERNAL
5253

5354
def test_readablespan_with_params(
5455
self,
@@ -63,6 +64,8 @@ def test_readablespan_with_params(
6364
events = [Event(event_name, attributes, start_time)]
6465
status = Status(StatusCode.OK)
6566
stack = ["span-1", "span-2"]
67+
kind = SpanKind.CLIENT
68+
6669
self.span = ReadableSpan(
6770
span_name,
6871
span_context,
@@ -73,6 +76,7 @@ def test_readablespan_with_params(
7376
events,
7477
status,
7578
stack,
79+
kind,
7680
)
7781

7882
assert self.span.name == span_name
@@ -84,3 +88,51 @@ def test_readablespan_with_params(
8488
assert self.span.status == status
8589
assert self.span.duration == end_time - start_time
8690
assert self.span.stack == stack
91+
assert self.span.kind == kind
92+
assert self.span.kind != SpanKind.INTERNAL
93+
94+
@pytest.mark.parametrize(
95+
"kind",
96+
[
97+
SpanKind.INTERNAL,
98+
SpanKind.SERVER,
99+
SpanKind.CLIENT,
100+
SpanKind.PRODUCER,
101+
SpanKind.CONSUMER,
102+
],
103+
)
104+
def test_readablespan_all_kind_values(
105+
self,
106+
span_context: SpanContext,
107+
kind: SpanKind,
108+
) -> None:
109+
"""Test that ReadableSpan correctly stores all SpanKind enum values."""
110+
span_name = "test-span-kind"
111+
self.span = ReadableSpan(span_name, span_context, kind=kind)
112+
113+
assert self.span.kind == kind
114+
assert isinstance(self.span.kind, SpanKind)
115+
116+
def test_readablespan_kind_default(
117+
self,
118+
span_context: SpanContext,
119+
) -> None:
120+
"""Test that ReadableSpan defaults to SpanKind.INTERNAL when kind is not specified."""
121+
span_name = "test-span-default-kind"
122+
self.span = ReadableSpan(span_name, span_context)
123+
124+
assert self.span.kind == SpanKind.INTERNAL
125+
126+
def test_readablespan_kind_property_readonly(
127+
self,
128+
span_context: SpanContext,
129+
) -> None:
130+
"""Test that kind property is read-only and cannot be modified after creation."""
131+
span_name = "test-span-readonly"
132+
self.span = ReadableSpan(span_name, span_context, kind=SpanKind.SERVER)
133+
134+
assert self.span.kind == SpanKind.SERVER
135+
136+
# Verify kind is stored in private attribute and property returns it
137+
assert hasattr(self.span, "_kind")
138+
assert self.span._kind == SpanKind.SERVER

tests/span/test_registered_span.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,3 +495,70 @@ def test_collect_kafka_attributes(
495495

496496
assert excepted_result["kafka.service"] == reg_span.data["kafka"]["service"]
497497
assert excepted_result["kafka.access"] == reg_span.data["kafka"]["access"]
498+
499+
@pytest.mark.parametrize(
500+
"span_name, expected_kind",
501+
[
502+
("wsgi", SpanKind.SERVER),
503+
("django", SpanKind.SERVER),
504+
("rabbitmq", SpanKind.SERVER),
505+
("redis", SpanKind.CLIENT),
506+
("mysql", SpanKind.CLIENT),
507+
("mongodb", SpanKind.CLIENT),
508+
("urllib", SpanKind.CLIENT),
509+
("asyncio", SpanKind.INTERNAL),
510+
("render", SpanKind.INTERNAL),
511+
("gcps-producer", SpanKind.CLIENT),
512+
("gcps-consumer", SpanKind.SERVER),
513+
("kafka-producer", SpanKind.CLIENT),
514+
("kafka-consumer", SpanKind.SERVER),
515+
],
516+
)
517+
def test_registered_span_kind_from_instana_span(
518+
self,
519+
span_context: SpanContext,
520+
span_processor: StanRecorder,
521+
span_name: str,
522+
expected_kind: SpanKind,
523+
) -> None:
524+
"""Test that RegisteredSpan uses kind from InstanaSpan when provided."""
525+
service_name = "test-service"
526+
527+
# Create InstanaSpan with explicit kind
528+
self.span = InstanaSpan(
529+
span_name, span_context, span_processor, kind=SpanKind.SERVER
530+
)
531+
reg_span = RegisteredSpan(self.span, None, service_name)
532+
533+
# Verify RegisteredSpan has correct kind for ENTRY span
534+
assert reg_span.k == expected_kind
535+
536+
# Verify name unification
537+
if "gcps" in span_name:
538+
assert reg_span.n == "gcps"
539+
elif "kafka" in span_name:
540+
assert reg_span.n == "kafka"
541+
else:
542+
assert reg_span.n == span_name
543+
544+
def test_registered_span_rabbitmq_publish_override(
545+
self,
546+
span_context: SpanContext,
547+
span_processor: StanRecorder,
548+
) -> None:
549+
"""Test that rabbitmq with sort=publish overrides to SpanKind.CLIENT."""
550+
span_name = "rabbitmq"
551+
attributes = {"sort": "publish"}
552+
553+
self.span = InstanaSpan(
554+
span_name,
555+
span_context,
556+
span_processor,
557+
kind=SpanKind.SERVER,
558+
attributes=attributes,
559+
)
560+
reg_span = RegisteredSpan(self.span, None, "test-service")
561+
562+
# Should be overridden to CLIENT for publish operation
563+
assert reg_span.k == SpanKind.CLIENT
564+
assert reg_span.data["rabbitmq"]["sort"] == "publish"

tests/test_tracer.py

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22

33
import pytest
44
from opentelemetry.context.context import Context
5+
from opentelemetry.trace import SpanKind
56
from opentelemetry.trace.span import _SPAN_ID_MAX_VALUE
67

78
from instana.agent.host import HostAgent
89
from instana.recorder import StanRecorder
910
from instana.sampling import InstanaSampler
10-
from instana.span.span import (INVALID_SPAN, INVALID_SPAN_ID, InstanaSpan,
11-
get_current_span)
11+
from instana.span.span import (
12+
INVALID_SPAN,
13+
INVALID_SPAN_ID,
14+
InstanaSpan,
15+
get_current_span,
16+
)
1217
from instana.span_context import SpanContext
1318
from instana.tracer import InstanaTracer, InstanaTracerProvider
1419

@@ -137,3 +142,117 @@ def test_tracer_create_span_context_root(
137142
assert new_span_context.trace_id <= _SPAN_ID_MAX_VALUE
138143

139144
assert new_span_context.trace_id == new_span_context.span_id
145+
146+
147+
@pytest.mark.parametrize(
148+
"kind",
149+
[
150+
SpanKind.INTERNAL,
151+
SpanKind.SERVER,
152+
SpanKind.CLIENT,
153+
SpanKind.PRODUCER,
154+
SpanKind.CONSUMER,
155+
],
156+
)
157+
def test_tracer_start_span_with_kind(
158+
tracer_provider: InstanaTracerProvider, context: Context, kind: SpanKind
159+
) -> None:
160+
"""Test that tracer.start_span correctly passes kind parameter to InstanaSpan."""
161+
span_name = f"test-span-{kind.name.lower()}"
162+
tracer = InstanaTracer(
163+
tracer_provider.sampler,
164+
tracer_provider._span_processor,
165+
tracer_provider._exporter,
166+
tracer_provider._propagators,
167+
)
168+
span = tracer.start_span(name=span_name, context=context, kind=kind)
169+
170+
assert span
171+
assert isinstance(span, InstanaSpan)
172+
assert span.name == span_name
173+
assert span.kind == kind
174+
175+
176+
def test_tracer_start_span_default_kind(
177+
tracer_provider: InstanaTracerProvider, context: Context
178+
) -> None:
179+
"""Test that tracer.start_span defaults to SpanKind.INTERNAL when kind is not specified."""
180+
span_name = "test-span-default-kind"
181+
tracer = InstanaTracer(
182+
tracer_provider.sampler,
183+
tracer_provider._span_processor,
184+
tracer_provider._exporter,
185+
tracer_provider._propagators,
186+
)
187+
span = tracer.start_span(name=span_name, context=context)
188+
189+
assert span
190+
assert isinstance(span, InstanaSpan)
191+
assert span.kind == SpanKind.INTERNAL
192+
193+
194+
def test_tracer_start_as_current_span_with_kind(
195+
tracer_provider: InstanaTracerProvider,
196+
) -> None:
197+
"""Test that tracer.start_as_current_span correctly passes kind parameter."""
198+
span_name = "test-span-context-manager"
199+
tracer = InstanaTracer(
200+
tracer_provider.sampler,
201+
tracer_provider._span_processor,
202+
tracer_provider._exporter,
203+
tracer_provider._propagators,
204+
)
205+
with tracer.start_as_current_span(name=span_name, kind=SpanKind.SERVER) as span:
206+
assert span is not None
207+
assert isinstance(span, InstanaSpan)
208+
assert span.name == span_name
209+
assert span.kind == SpanKind.SERVER
210+
211+
212+
def test_tracer_nested_span_with_different_kinds(
213+
tracer_provider: InstanaTracerProvider,
214+
) -> None:
215+
"""Test that nested spans can have different kind values."""
216+
tracer = InstanaTracer(
217+
tracer_provider.sampler,
218+
tracer_provider._span_processor,
219+
tracer_provider._exporter,
220+
tracer_provider._propagators,
221+
)
222+
parent_span_name = "parent-server-span"
223+
child_span_name = "child-client-span"
224+
225+
with tracer.start_as_current_span(
226+
name=parent_span_name, kind=SpanKind.SERVER
227+
) as pspan:
228+
assert pspan.kind == SpanKind.SERVER
229+
230+
with tracer.start_as_current_span(
231+
name=child_span_name, kind=SpanKind.CLIENT
232+
) as cspan:
233+
assert cspan.kind == SpanKind.CLIENT
234+
assert cspan.parent_id == pspan.context.span_id
235+
# Verify kinds are independent
236+
assert pspan.kind == SpanKind.SERVER
237+
assert cspan.kind == SpanKind.CLIENT
238+
239+
240+
def test_tracer_kind_propagation_to_readable_span(
241+
tracer_provider: InstanaTracerProvider, context: Context
242+
) -> None:
243+
"""Test that kind is properly propagated when span is converted to ReadableSpan."""
244+
span_name = "test-span-readable"
245+
tracer = InstanaTracer(
246+
tracer_provider.sampler,
247+
tracer_provider._span_processor,
248+
tracer_provider._exporter,
249+
tracer_provider._propagators,
250+
)
251+
span = tracer.start_span(name=span_name, context=context, kind=SpanKind.PRODUCER)
252+
253+
assert span.kind == SpanKind.PRODUCER
254+
255+
# Create readable span (this happens internally when span.end() is called)
256+
readable_span = span._readable_span()
257+
258+
assert readable_span.kind == SpanKind.PRODUCER

0 commit comments

Comments
 (0)