1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
import json
import unittest.mock
import pytest
import trytond
from trytond.exceptions import TrytonException as TrytondBaseException
from trytond.exceptions import UserError as TrytondUserError
from trytond.exceptions import UserWarning as TrytondUserWarning
from trytond.exceptions import LoginException
from trytond.wsgi import app as trytond_app
from werkzeug.test import Client
from sentry_sdk.integrations.trytond import TrytondWSGIIntegration
from tests.conftest import unpack_werkzeug_response
@pytest.fixture(scope="function")
def app(sentry_init):
yield trytond_app
@pytest.fixture
def get_client(app):
def inner():
return Client(app)
return inner
@pytest.mark.parametrize(
"exception", [Exception("foo"), type("FooException", (Exception,), {})("bar")]
)
def test_exceptions_captured(
sentry_init, app, capture_exceptions, get_client, exception
):
sentry_init(integrations=[TrytondWSGIIntegration()])
exceptions = capture_exceptions()
unittest.mock.sentinel.exception = exception
@app.route("/exception")
def _(request):
raise unittest.mock.sentinel.exception
client = get_client()
_ = client.get("/exception")
(e,) = exceptions
assert e is exception
@pytest.mark.parametrize(
"exception",
[
TrytondUserError("title"),
TrytondUserWarning("title", "details"),
LoginException("title", "details"),
],
)
def test_trytonderrors_not_captured(
sentry_init, app, capture_exceptions, get_client, exception
):
sentry_init(integrations=[TrytondWSGIIntegration()])
exceptions = capture_exceptions()
unittest.mock.sentinel.exception = exception
@app.route("/usererror")
def _(request):
raise unittest.mock.sentinel.exception
client = get_client()
_ = client.get("/usererror")
assert not exceptions
@pytest.mark.skipif(
trytond.__version__.split(".") < ["5", "4"], reason="At least Trytond-5.4 required"
)
def test_rpc_error_page(sentry_init, app, get_client):
"""Test that, after initializing the Trytond-SentrySDK integration
a custom error handler can be registered to the Trytond WSGI app so as to
inform the event identifiers to the Tryton RPC client"""
sentry_init(integrations=[TrytondWSGIIntegration()])
@app.route("/rpcerror", methods=["POST"])
def _(request):
raise Exception("foo")
@app.error_handler
def _(app, request, e):
if isinstance(e, TrytondBaseException):
return
else:
data = TrytondUserError("Sentry error.", str(e))
return app.make_response(request, data)
client = get_client()
# This would look like a natural Tryton RPC call
_data = dict(
id=42, # request sequence
method="class.method", # rpc call
params=[
[1234], # ids
["bar", "baz"], # values
dict( # context
client="12345678-9abc-def0-1234-56789abc",
groups=[1],
language="ca",
language_direction="ltr",
),
],
)
response = client.post(
"/rpcerror", content_type="application/json", data=json.dumps(_data)
)
(content, status, headers) = unpack_werkzeug_response(response)
data = json.loads(content)
assert status == "200 OK"
assert headers.get("Content-Type") == "application/json"
assert data == dict(id=42, error=["UserError", ["Sentry error.", "foo", None]])
def test_span_origin(sentry_init, app, capture_events, get_client):
sentry_init(
integrations=[TrytondWSGIIntegration()],
traces_sample_rate=1.0,
)
events = capture_events()
@app.route("/something")
def _(request):
return "ok"
client = get_client()
client.get("/something")
(event,) = events
assert event["contexts"]["trace"]["origin"] == "auto.http.trytond_wsgi"
|