|
| 1 | +import pytest |
| 2 | +from mockupdb import MockupDB, OpQuery |
| 3 | +from pymongo import MongoClient |
| 4 | + |
| 5 | +import sentry_sdk |
1 | 6 | from sentry_sdk import capture_message, start_transaction |
2 | 7 | from sentry_sdk.consts import SPANDATA |
3 | 8 | from sentry_sdk.integrations.pymongo import PyMongoIntegration, _strip_pii |
4 | 9 |
|
5 | | -from mockupdb import MockupDB, OpQuery |
6 | | -from pymongo import MongoClient |
7 | | -import pytest |
8 | | - |
9 | 10 |
|
10 | 11 | @pytest.fixture(scope="session") |
11 | 12 | def mongo_server(): |
@@ -109,6 +110,74 @@ def test_transactions(sentry_init, capture_events, mongo_server, with_pii): |
109 | 110 | assert insert_fail["tags"]["status"] == "internal_error" |
110 | 111 |
|
111 | 112 |
|
| 113 | +@pytest.mark.parametrize("with_pii", [False, True]) |
| 114 | +def test_transactions_span_streaming( |
| 115 | + sentry_init, capture_items, mongo_server, with_pii |
| 116 | +): |
| 117 | + sentry_init( |
| 118 | + integrations=[PyMongoIntegration()], |
| 119 | + traces_sample_rate=1.0, |
| 120 | + send_default_pii=with_pii, |
| 121 | + _experiments={"trace_lifecycle": "stream"}, |
| 122 | + ) |
| 123 | + items = capture_items("span") |
| 124 | + |
| 125 | + connection = MongoClient(mongo_server.uri) |
| 126 | + |
| 127 | + with sentry_sdk.traces.start_span(name="test_transaction"): |
| 128 | + list( |
| 129 | + connection["test_db"]["test_collection"].find({"foobar": 1}) |
| 130 | + ) # force query execution |
| 131 | + connection["test_db"]["test_collection"].insert_one({"foo": 2}) |
| 132 | + try: |
| 133 | + connection["test_db"]["erroneous"].insert_many([{"bar": 3}, {"baz": 4}]) |
| 134 | + pytest.fail("Request should raise") |
| 135 | + except Exception: |
| 136 | + pass |
| 137 | + sentry_sdk.flush() |
| 138 | + |
| 139 | + spans = [item.payload for item in items] |
| 140 | + assert len(spans) == 4 |
| 141 | + |
| 142 | + (find, insert_success, insert_fail, segment) = spans |
| 143 | + assert segment["name"] == "test_transaction" |
| 144 | + |
| 145 | + for span in find, insert_success, insert_fail: |
| 146 | + attrs = span["attributes"] |
| 147 | + assert attrs[SPANDATA.DB_SYSTEM] == "mongodb" |
| 148 | + assert attrs[SPANDATA.DB_DRIVER_NAME] == "pymongo" |
| 149 | + assert attrs["db.name"] == "test_db" |
| 150 | + assert attrs[SPANDATA.SERVER_ADDRESS] == "localhost" |
| 151 | + assert attrs[SPANDATA.SERVER_PORT] == mongo_server.port |
| 152 | + assert attrs["sentry.op"] == "db" |
| 153 | + assert attrs["sentry.origin"] == "auto.db.pymongo" |
| 154 | + |
| 155 | + assert find["attributes"][SPANDATA.DB_OPERATION] == "find" |
| 156 | + assert insert_success["attributes"][SPANDATA.DB_OPERATION] == "insert" |
| 157 | + assert insert_fail["attributes"][SPANDATA.DB_OPERATION] == "insert" |
| 158 | + |
| 159 | + assert find["name"].startswith('{"find') |
| 160 | + assert insert_success["name"].startswith('{"insert') |
| 161 | + assert insert_fail["name"].startswith('{"insert') |
| 162 | + |
| 163 | + assert find["attributes"]["db.collection.name"] == "test_collection" |
| 164 | + assert insert_success["attributes"]["db.collection.name"] == "test_collection" |
| 165 | + assert insert_fail["attributes"]["db.collection.name"] == "erroneous" |
| 166 | + |
| 167 | + if with_pii: |
| 168 | + assert "1" in find["name"] |
| 169 | + assert "2" in insert_success["name"] |
| 170 | + assert "3" in insert_fail["name"] and "4" in insert_fail["name"] |
| 171 | + else: |
| 172 | + assert "1" not in find["name"] |
| 173 | + assert "2" not in insert_success["name"] |
| 174 | + assert "3" not in insert_fail["name"] and "4" not in insert_fail["name"] |
| 175 | + |
| 176 | + assert find["status"] == "ok" |
| 177 | + assert insert_success["status"] == "ok" |
| 178 | + assert insert_fail["status"] == "error" |
| 179 | + |
| 180 | + |
112 | 181 | @pytest.mark.parametrize("with_pii", [False, True]) |
113 | 182 | def test_breadcrumbs(sentry_init, capture_events, mongo_server, with_pii): |
114 | 183 | sentry_init( |
@@ -146,6 +215,46 @@ def test_breadcrumbs(sentry_init, capture_events, mongo_server, with_pii): |
146 | 215 | } |
147 | 216 |
|
148 | 217 |
|
| 218 | +@pytest.mark.parametrize("with_pii", [False, True]) |
| 219 | +def test_breadcrumbs_span_streaming(sentry_init, capture_items, mongo_server, with_pii): |
| 220 | + sentry_init( |
| 221 | + integrations=[PyMongoIntegration()], |
| 222 | + traces_sample_rate=1.0, |
| 223 | + send_default_pii=with_pii, |
| 224 | + _experiments={"trace_lifecycle": "stream"}, |
| 225 | + ) |
| 226 | + items = capture_items("event") |
| 227 | + |
| 228 | + connection = MongoClient(mongo_server.uri) |
| 229 | + |
| 230 | + list( |
| 231 | + connection["test_db"]["test_collection"].find({"foobar": 1}) |
| 232 | + ) # force query execution |
| 233 | + capture_message("hi") |
| 234 | + |
| 235 | + event = items[0].payload |
| 236 | + (crumb,) = event["breadcrumbs"]["values"] |
| 237 | + |
| 238 | + assert crumb["category"] == "query" |
| 239 | + assert crumb["message"].startswith('{"find') |
| 240 | + if with_pii: |
| 241 | + assert "1" in crumb["message"] |
| 242 | + else: |
| 243 | + assert "1" not in crumb["message"] |
| 244 | + assert crumb["type"] == "db" |
| 245 | + |
| 246 | + data = crumb["data"] |
| 247 | + assert data["db.name"] == "test_db" |
| 248 | + assert data[SPANDATA.DB_SYSTEM] == "mongodb" |
| 249 | + assert data[SPANDATA.DB_DRIVER_NAME] == "pymongo" |
| 250 | + assert data[SPANDATA.DB_OPERATION] == "find" |
| 251 | + assert data["db.collection.name"] == "test_collection" |
| 252 | + assert data["sentry.op"] == "db" |
| 253 | + assert data["sentry.origin"] == "auto.db.pymongo" |
| 254 | + assert data[SPANDATA.SERVER_ADDRESS] == "localhost" |
| 255 | + assert data[SPANDATA.SERVER_PORT] == mongo_server.port |
| 256 | + |
| 257 | + |
149 | 258 | @pytest.mark.parametrize( |
150 | 259 | "testcase", |
151 | 260 | [ |
@@ -460,3 +569,74 @@ def test_span_origin(sentry_init, capture_events, mongo_server): |
460 | 569 |
|
461 | 570 | assert event["contexts"]["trace"]["origin"] == "manual" |
462 | 571 | assert event["spans"][0]["origin"] == "auto.db.pymongo" |
| 572 | + |
| 573 | + |
| 574 | +def test_span_origin_span_streaming(sentry_init, capture_items, mongo_server): |
| 575 | + sentry_init( |
| 576 | + integrations=[PyMongoIntegration()], |
| 577 | + traces_sample_rate=1.0, |
| 578 | + _experiments={"trace_lifecycle": "stream"}, |
| 579 | + ) |
| 580 | + items = capture_items("span") |
| 581 | + |
| 582 | + connection = MongoClient(mongo_server.uri) |
| 583 | + |
| 584 | + with sentry_sdk.traces.start_span(name="test_transaction"): |
| 585 | + list( |
| 586 | + connection["test_db"]["test_collection"].find({"foobar": 1}) |
| 587 | + ) # force query execution |
| 588 | + sentry_sdk.flush() |
| 589 | + |
| 590 | + spans = [item.payload for item in items] |
| 591 | + assert len(spans) == 2 |
| 592 | + (db_span, segment) = spans |
| 593 | + assert segment["name"] == "test_transaction" |
| 594 | + assert db_span["attributes"]["sentry.origin"] == "auto.db.pymongo" |
| 595 | + |
| 596 | + |
| 597 | +def test_span_streaming_status_on_success(sentry_init, capture_items, mongo_server): |
| 598 | + sentry_init( |
| 599 | + integrations=[PyMongoIntegration()], |
| 600 | + traces_sample_rate=1.0, |
| 601 | + _experiments={"trace_lifecycle": "stream"}, |
| 602 | + ) |
| 603 | + items = capture_items("span") |
| 604 | + |
| 605 | + connection = MongoClient(mongo_server.uri) |
| 606 | + |
| 607 | + with sentry_sdk.traces.start_span(name="test_transaction"): |
| 608 | + connection["test_db"]["test_collection"].insert_one({"foo": 1}) |
| 609 | + sentry_sdk.flush() |
| 610 | + |
| 611 | + spans = [item.payload for item in items] |
| 612 | + assert len(spans) == 2 |
| 613 | + (db_span, segment) = spans |
| 614 | + assert segment["name"] == "test_transaction" |
| 615 | + assert db_span["status"] == "ok" |
| 616 | + |
| 617 | + |
| 618 | +def test_span_streaming_status_on_failure(sentry_init, capture_items, mongo_server): |
| 619 | + sentry_init( |
| 620 | + integrations=[PyMongoIntegration()], |
| 621 | + traces_sample_rate=1.0, |
| 622 | + _experiments={"trace_lifecycle": "stream"}, |
| 623 | + ) |
| 624 | + items = capture_items("span") |
| 625 | + |
| 626 | + connection = MongoClient(mongo_server.uri) |
| 627 | + |
| 628 | + with sentry_sdk.traces.start_span(name="test_transaction"): |
| 629 | + try: |
| 630 | + connection["test_db"]["erroneous"].insert_many([{"bar": 3}]) |
| 631 | + pytest.fail("Request should raise") |
| 632 | + except Exception: |
| 633 | + pass |
| 634 | + sentry_sdk.flush() |
| 635 | + |
| 636 | + spans = [item.payload for item in items] |
| 637 | + |
| 638 | + assert len(spans) == 2 |
| 639 | + (db_span, segment) = spans |
| 640 | + |
| 641 | + assert segment["name"] == "test_transaction" |
| 642 | + assert db_span["status"] == "error" |
0 commit comments