Skip to content

Commit d7907b7

Browse files
add motor tests and payload to mongo (#512)
1 parent c8fc948 commit d7907b7

File tree

13 files changed

+191
-17
lines changed

13 files changed

+191
-17
lines changed

noxfile.py

+43
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,49 @@ def integration_tests_pymongo(
873873
kill_process_and_clean_outputs(temp_file, "uvicorn", session)
874874

875875

876+
@nox.session()
877+
@nox.parametrize(
878+
"python,motor_version",
879+
[
880+
(python, motor_version)
881+
for python in python_versions()
882+
for motor_version in dependency_versions_to_be_tested(
883+
python=python,
884+
directory="motor",
885+
dependency_name="motor",
886+
)
887+
],
888+
)
889+
def integration_tests_motor(
890+
session,
891+
python,
892+
motor_version,
893+
):
894+
with TestedVersions.save_tests_result("motor", python, "motor", motor_version):
895+
install_package("motor", motor_version, session)
896+
897+
session.install(".")
898+
899+
temp_file = create_it_tempfile("motor")
900+
with session.chdir("src/test/integration/motor"):
901+
session.install("-r", OTHER_REQUIREMENTS)
902+
try:
903+
session.run(
904+
"pytest",
905+
"--tb",
906+
"native",
907+
"--log-cli-level=INFO",
908+
"--color=yes",
909+
"-v",
910+
"./tests/test_motor.py",
911+
env={
912+
"LUMIGO_DEBUG_SPANDUMP": temp_file,
913+
},
914+
)
915+
finally:
916+
kill_process_and_clean_outputs(temp_file, "test_motor", session)
917+
918+
876919
@nox.session()
877920
@nox.parametrize(
878921
"python,pymysql_version",

src/lumigo_opentelemetry/instrumentations/pymongo/__init__.py

+43-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1+
from typing import Union
2+
3+
from opentelemetry.trace.span import Span
4+
15
from lumigo_opentelemetry.instrumentations import AbstractInstrumentor
6+
from lumigo_opentelemetry import logger
7+
from lumigo_opentelemetry.libs.general_utils import lumigo_safe_execute
8+
from lumigo_opentelemetry.libs.json_utils import dump_with_context
29

310

411
class PymongoInstrumentor(AbstractInstrumentor):
@@ -10,8 +17,43 @@ def check_if_applicable(self) -> None:
1017

1118
def install_instrumentation(self) -> None:
1219
from opentelemetry.instrumentation.pymongo import PymongoInstrumentor
20+
from pymongo.monitoring import (
21+
CommandStartedEvent,
22+
CommandSucceededEvent,
23+
CommandFailedEvent,
24+
)
25+
26+
def request_hook(span: Span, event: CommandStartedEvent) -> None:
27+
with lumigo_safe_execute("pymongo_request_hook"):
28+
span.set_attribute("db.statement", event.command_name)
29+
if isinstance(event, CommandStartedEvent):
30+
span.set_attribute(
31+
"db.request.body",
32+
dump_with_context("requestBody", event.command),
33+
)
34+
else:
35+
logger.warning(f"Got unexpected event type {type(event)}")
36+
37+
def response_hook(
38+
span: Span, event: Union[CommandSucceededEvent, CommandFailedEvent]
39+
) -> None:
40+
with lumigo_safe_execute("pymongo_response_hook"):
41+
if isinstance(event, CommandSucceededEvent):
42+
span.set_attribute(
43+
"db.response.body",
44+
dump_with_context("responseBody", event.reply),
45+
)
46+
elif isinstance(event, CommandFailedEvent):
47+
span.set_attribute(
48+
"db.response.body",
49+
dump_with_context("responseBody", event.failure),
50+
)
51+
else:
52+
logger.warning(f"Got unexpected event type {type(event)}")
1353

14-
PymongoInstrumentor().instrument()
54+
PymongoInstrumentor().instrument(
55+
request_hook=request_hook, response_hook=response_hook
56+
)
1557

1658

1759
instrumentor: AbstractInstrumentor = PymongoInstrumentor()

src/test/integration/motor/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from testcontainers.mongodb import MongoDbContainer
2+
3+
import motor.motor_asyncio
4+
5+
6+
async def do_insert():
7+
document = {"my-k": "my-value"}
8+
result = await db.test_collection.insert_one(document)
9+
print(f"result {result.inserted_id}")
10+
11+
12+
async def do_find_one():
13+
document = await db.test_collection.find_one()
14+
print("found", document)
15+
16+
17+
with MongoDbContainer("mongo:4") as mongo:
18+
client = motor.motor_asyncio.AsyncIOMotorClient(mongo.get_connection_url())
19+
db = client.test
20+
21+
loop = client.get_io_loop()
22+
loop.run_until_complete(do_insert())
23+
loop.run_until_complete(do_find_one())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pytest==7.1.1
2+
psutil==5.9.1
3+
testcontainers==3.7.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.2.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.2.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.2.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.2.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.2.0

src/test/integration/motor/tests/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import os
2+
import subprocess
3+
import sys
4+
import unittest
5+
from test.test_utils.span_exporter import wait_for_exporter
6+
from test.test_utils.spans_parser import SpansContainer
7+
8+
9+
class TestMotorSpans(unittest.TestCase):
10+
"""
11+
This package uses pymongo under the hood, so there's no specific instrumentation for it,
12+
we only want to measure our compatibility with it.
13+
"""
14+
15+
def test_motor_set_and_get(self):
16+
sample_path = os.path.join(
17+
os.path.dirname(os.path.abspath(__file__)),
18+
"../app/motor_set_and_get.py",
19+
)
20+
subprocess.check_output(
21+
[sys.executable, sample_path],
22+
env={
23+
**os.environ,
24+
"AUTOWRAPT_BOOTSTRAP": "lumigo_opentelemetry",
25+
"OTEL_SERVICE_NAME": "motor",
26+
},
27+
)
28+
29+
wait_for_exporter()
30+
31+
spans = [
32+
s
33+
for s in SpansContainer.parse_spans_from_file().spans
34+
if s["attributes"].get("db.system") == "mongodb"
35+
]
36+
37+
self.assertEqual(len(spans), 3)
38+
admin_span, set_span, get_span = spans
39+
self.assertEqual(set_span["attributes"]["db.statement"], "insert")
40+
41+
self.assertTrue(
42+
'"my-k": "my-value"' in set_span["attributes"]["db.request.body"]
43+
)
44+
self.assertTrue(
45+
'{"n": 1, "ok": 1.0}' in set_span["attributes"]["db.response.body"]
46+
)
47+
48+
self.assertEqual(get_span["attributes"]["db.statement"], "find")
49+
self.assertTrue(
50+
'"my-k": "my-value"' in get_span["attributes"]["db.response.body"]
51+
)

src/test/integration/pymongo/tests/test_pymongo.py

+23-16
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import requests
66

77

8-
class TestFastApiSpans(unittest.TestCase):
8+
class TestMongoSpans(unittest.TestCase):
99
def test_mongo_instrumentation(self):
1010
response = requests.get("http://localhost:8002/invoke-mongo")
1111

@@ -17,23 +17,30 @@ def test_mongo_instrumentation(self):
1717

1818
wait_for_exporter()
1919

20-
spans_container = SpansContainer.get_spans_from_file()
20+
spans = [
21+
s
22+
for s in SpansContainer.parse_spans_from_file().spans
23+
if s["attributes"].get("db.system") == "mongodb"
24+
]
25+
26+
set_span, get_span = spans[
27+
-2:
28+
] # there are more spans to connect to the mongo container
2129

2230
# assert mongo children spans
23-
assert spans_container.find_child_span(
24-
lambda span: (
25-
span["kind"] == "SpanKind.CLIENT"
26-
and span["attributes"]["db.system"] == "mongodb"
27-
and span["attributes"]["db.name"] == "test"
28-
and span["attributes"]["db.statement"] == "insert items"
29-
)
31+
assert (
32+
set_span["kind"] == "SpanKind.CLIENT"
33+
and set_span["attributes"]["db.system"] == "mongodb"
34+
and set_span["attributes"]["db.name"] == "test"
35+
and set_span["attributes"]["db.statement"] == "insert"
36+
and '"street": "2 Avenue"' in set_span["attributes"]["db.request.body"]
37+
and '{"n": 1, "ok": 1.0}' in set_span["attributes"]["db.response.body"]
3038
)
3139

32-
assert spans_container.find_child_span(
33-
lambda span: (
34-
span["kind"] == "SpanKind.CLIENT"
35-
and span["attributes"]["db.system"] == "mongodb"
36-
and span["attributes"]["db.name"] == "test"
37-
and span["attributes"]["db.statement"] == "find items"
38-
)
40+
assert (
41+
get_span["kind"] == "SpanKind.CLIENT"
42+
and get_span["attributes"]["db.system"] == "mongodb"
43+
and get_span["attributes"]["db.name"] == "test"
44+
and get_span["attributes"]["db.statement"] == "find"
45+
and '"street": "2 Avenue"' in get_span["attributes"]["db.response.body"]
3946
)

0 commit comments

Comments
 (0)