Skip to content

Commit 7e25294

Browse files
committed
chore(confluent-kafka): Add test for poll and consume
1 parent f042dea commit 7e25294

File tree

2 files changed

+202
-2
lines changed

2 files changed

+202
-2
lines changed

Diff for: instrumentation/opentelemetry-instrumentation-confluent-kafka/tests/test_instrumentation.py

+163-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414

1515
# pylint: disable=no-name-in-module
1616

17-
from unittest import TestCase
17+
from unittest.mock import patch
18+
19+
from opentelemetry.semconv.trace import SpanAttributes, MessagingDestinationKindValues
20+
from opentelemetry.test.test_base import TestBase
21+
from .utils import MockConsumer, MockedMessage
1822

1923
from confluent_kafka import Consumer, Producer
2024

@@ -29,7 +33,7 @@
2933
)
3034

3135

32-
class TestConfluentKafka(TestCase):
36+
class TestConfluentKafka(TestBase):
3337
def test_instrument_api(self) -> None:
3438
instrumentation = ConfluentKafkaInstrumentor()
3539

@@ -104,3 +108,160 @@ def test_context_getter(self) -> None:
104108
context_setter.set(carrier_list, "key1", "val1")
105109
self.assertEqual(context_getter.get(carrier_list, "key1"), ["val1"])
106110
self.assertEqual(["key1"], context_getter.keys(carrier_list))
111+
112+
def test_poll(self) -> None:
113+
instrumentation = ConfluentKafkaInstrumentor()
114+
mocked_messages = [
115+
MockedMessage("topic-10", 0, 0, []),
116+
MockedMessage("topic-20", 2, 4, []),
117+
MockedMessage("topic-30", 1, 3, []),
118+
]
119+
expected_spans= [
120+
{
121+
"name": "recv",
122+
"attributes": {}
123+
},
124+
{
125+
"name": "topic-10 process",
126+
"attributes": {
127+
SpanAttributes.MESSAGING_OPERATION: "process",
128+
SpanAttributes.MESSAGING_KAFKA_PARTITION: 0,
129+
SpanAttributes.MESSAGING_SYSTEM: "kafka",
130+
SpanAttributes.MESSAGING_DESTINATION: "topic-10",
131+
SpanAttributes.MESSAGING_DESTINATION_KIND: MessagingDestinationKindValues.QUEUE.value,
132+
SpanAttributes.MESSAGING_MESSAGE_ID: "topic-10.0.0",
133+
}
134+
},
135+
{
136+
"name": "recv",
137+
"attributes": {}
138+
},
139+
{
140+
"name": "topic-20 process",
141+
"attributes": {
142+
SpanAttributes.MESSAGING_OPERATION: "process",
143+
SpanAttributes.MESSAGING_KAFKA_PARTITION: 2,
144+
SpanAttributes.MESSAGING_SYSTEM: "kafka",
145+
SpanAttributes.MESSAGING_DESTINATION: "topic-20",
146+
SpanAttributes.MESSAGING_DESTINATION_KIND: MessagingDestinationKindValues.QUEUE.value,
147+
SpanAttributes.MESSAGING_MESSAGE_ID: "topic-20.2.4",
148+
}
149+
},
150+
{
151+
"name": "recv",
152+
"attributes": {}
153+
},
154+
{
155+
"name": "topic-30 process",
156+
"attributes": {
157+
SpanAttributes.MESSAGING_OPERATION: "process",
158+
SpanAttributes.MESSAGING_KAFKA_PARTITION: 1,
159+
SpanAttributes.MESSAGING_SYSTEM: "kafka",
160+
SpanAttributes.MESSAGING_DESTINATION: "topic-30",
161+
SpanAttributes.MESSAGING_DESTINATION_KIND: MessagingDestinationKindValues.QUEUE.value,
162+
SpanAttributes.MESSAGING_MESSAGE_ID: "topic-30.1.3",
163+
}
164+
},
165+
{
166+
"name": "recv",
167+
"attributes": {}
168+
},
169+
]
170+
171+
consumer = MockConsumer(
172+
mocked_messages,
173+
{
174+
"bootstrap.servers": "localhost:29092",
175+
"group.id": "mygroup",
176+
"auto.offset.reset": "earliest",
177+
}
178+
)
179+
span_list = self.memory_exporter.clear()
180+
consumer = instrumentation.instrument_consumer(consumer)
181+
consumer.poll(1)
182+
consumer.poll(1)
183+
consumer.poll(1)
184+
consumer.poll(1)
185+
186+
span_list = self.memory_exporter.get_finished_spans()
187+
self._compare_spans(span_list, expected_spans)
188+
189+
def test_consume(self) -> None:
190+
instrumentation = ConfluentKafkaInstrumentor()
191+
mocked_messages = [
192+
MockedMessage("topic-1", 0, 0, []),
193+
MockedMessage("topic-1", 2, 1, []),
194+
MockedMessage("topic-1", 3, 2, []),
195+
MockedMessage("topic-2", 0, 0, []),
196+
MockedMessage("topic-3", 0, 3, []),
197+
MockedMessage("topic-2", 0, 1, []),
198+
]
199+
expected_spans= [
200+
{
201+
"name": "recv",
202+
"attributes": {}
203+
},
204+
{
205+
"name": "topic-1 process",
206+
"attributes": {
207+
SpanAttributes.MESSAGING_OPERATION: "process",
208+
SpanAttributes.MESSAGING_SYSTEM: "kafka",
209+
SpanAttributes.MESSAGING_DESTINATION: "topic-1",
210+
SpanAttributes.MESSAGING_DESTINATION_KIND: MessagingDestinationKindValues.QUEUE.value,
211+
}
212+
},
213+
{
214+
"name": "recv",
215+
"attributes": {}
216+
},
217+
{
218+
"name": "topic-2 process",
219+
"attributes": {
220+
SpanAttributes.MESSAGING_OPERATION: "process",
221+
SpanAttributes.MESSAGING_SYSTEM: "kafka",
222+
SpanAttributes.MESSAGING_DESTINATION: "topic-2",
223+
SpanAttributes.MESSAGING_DESTINATION_KIND: MessagingDestinationKindValues.QUEUE.value,
224+
}
225+
},
226+
{
227+
"name": "recv",
228+
"attributes": {}
229+
},
230+
{
231+
"name": "topic-3 process",
232+
"attributes": {
233+
SpanAttributes.MESSAGING_OPERATION: "process",
234+
SpanAttributes.MESSAGING_SYSTEM: "kafka",
235+
SpanAttributes.MESSAGING_DESTINATION: "topic-3",
236+
SpanAttributes.MESSAGING_DESTINATION_KIND: MessagingDestinationKindValues.QUEUE.value,
237+
}
238+
},
239+
{
240+
"name": "recv",
241+
"attributes": {}
242+
},
243+
]
244+
245+
consumer = MockConsumer(
246+
mocked_messages,
247+
{
248+
"bootstrap.servers": "localhost:29092",
249+
"group.id": "mygroup",
250+
"auto.offset.reset": "earliest",
251+
}
252+
)
253+
254+
span_list = self.memory_exporter.clear()
255+
consumer = instrumentation.instrument_consumer(consumer)
256+
consumer.consume(3)
257+
consumer.consume(1)
258+
consumer.consume(2)
259+
consumer.consume(1)
260+
span_list = self.memory_exporter.get_finished_spans()
261+
self._compare_spans(span_list, expected_spans)
262+
263+
def _compare_spans(self, spans, expected_spans):
264+
for (span, expected_span) in zip(spans, expected_spans):
265+
self.assertEqual(expected_span['name'], span.name)
266+
for attribute_key, expected_attribute_value in expected_span['attributes'].items():
267+
self.assertEqual(expected_attribute_value, span.attributes[attribute_key])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from confluent_kafka import Consumer
2+
3+
4+
class MockConsumer(Consumer):
5+
6+
def __init__(self, queue, config):
7+
self._queue = queue
8+
super().__init__(config)
9+
10+
def consume(self, num_messages=1, *args, **kwargs):
11+
messages = self._queue[:num_messages]
12+
self._queue = self._queue[num_messages:]
13+
return messages
14+
15+
def poll(self, timeout=None):
16+
if len(self._queue) > 0:
17+
return self._queue.pop(0)
18+
else:
19+
return None
20+
21+
22+
class MockedMessage:
23+
def __init__(self, topic: str, partition: int, offset: int, headers):
24+
self._topic = topic
25+
self._partition = partition
26+
self._offset = offset
27+
self._headers = headers
28+
29+
def topic(self):
30+
return self._topic
31+
32+
def partition(self):
33+
return self._partition
34+
35+
def offset(self):
36+
return self._offset
37+
38+
def headers(self):
39+
return self._headers

0 commit comments

Comments
 (0)