mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
215 lines
5.3 KiB
Python
215 lines
5.3 KiB
Python
from __future__ import annotations
|
|
|
|
import ast
|
|
|
|
import pytest
|
|
|
|
from tools.flake8_plugin import SentryCheck
|
|
|
|
|
|
def _run(src: str, filename: str = "getsentry/t.py") -> list[str]:
|
|
tree = ast.parse(src)
|
|
errors = sorted(SentryCheck(tree=tree, filename=filename).run())
|
|
return ["t.py:{}:{}: {}".format(*error) for error in errors]
|
|
|
|
|
|
def test_S001():
|
|
S001_py = """\
|
|
class A:
|
|
def called_once():
|
|
pass
|
|
|
|
|
|
A().called_once()
|
|
"""
|
|
|
|
errors = _run(S001_py)
|
|
assert errors == [
|
|
"t.py:6:0: S001 Avoid using the called_once mock call as it is confusing and "
|
|
"prone to causing invalid test behavior.",
|
|
]
|
|
|
|
|
|
def test_S002():
|
|
S002_py = """\
|
|
print("print statements are not allowed")
|
|
"""
|
|
|
|
errors = _run(S002_py)
|
|
assert errors == ["t.py:1:0: S002 print functions or statements are not allowed."]
|
|
|
|
|
|
def test_S003():
|
|
S003_py = """\
|
|
import json
|
|
import simplejson
|
|
from json import loads, load
|
|
from simplejson import JSONDecoder, JSONDecodeError, _default_encoder
|
|
import sentry.utils.json as good_json
|
|
from sentry.utils.json import JSONDecoder, JSONDecodeError
|
|
from .json import Validator
|
|
|
|
|
|
def bad_code():
|
|
a = json.loads("''")
|
|
b = simplejson.loads("''")
|
|
c = loads("''")
|
|
d = load()
|
|
"""
|
|
|
|
errors = _run(S003_py)
|
|
assert errors == [
|
|
"t.py:1:0: S003 Use ``from sentry.utils import json`` instead.",
|
|
"t.py:2:0: S003 Use ``from sentry.utils import json`` instead.",
|
|
"t.py:3:0: S003 Use ``from sentry.utils import json`` instead.",
|
|
"t.py:4:0: S003 Use ``from sentry.utils import json`` instead.",
|
|
]
|
|
|
|
|
|
def test_S004():
|
|
S004_py = """\
|
|
import unittest
|
|
from something import func
|
|
|
|
|
|
class Test(unittest.TestCase):
|
|
def test(self):
|
|
with self.assertRaises(ValueError):
|
|
func()
|
|
"""
|
|
errors = _run(S004_py)
|
|
assert errors == [
|
|
"t.py:7:13: S004 Use `pytest.raises` instead for better debuggability.",
|
|
]
|
|
|
|
|
|
def test_S005():
|
|
S005_py = """\
|
|
from sentry.models import User
|
|
"""
|
|
errors = _run(S005_py)
|
|
assert errors == [
|
|
"t.py:1:0: S005 Do not import models from sentry.models but the actual module",
|
|
]
|
|
|
|
|
|
def test_S006():
|
|
src = """\
|
|
from django.utils.encoding import force_bytes
|
|
from django.utils.encoding import force_str
|
|
"""
|
|
# only error in tests until we can fix the rest
|
|
assert _run(src, filename="src/sentry/whatever.py") == []
|
|
errors = _run(src, filename="tests/test_foo.py")
|
|
assert errors == [
|
|
"t.py:1:0: S006 Do not use force_bytes / force_str -- test the types directly",
|
|
"t.py:2:0: S006 Do not use force_bytes / force_str -- test the types directly",
|
|
]
|
|
|
|
|
|
def test_S007():
|
|
src = """\
|
|
from sentry.testutils.outbox import outbox_runner
|
|
"""
|
|
# no errors in tests/
|
|
assert _run(src, filename="tests/test_foo.py") == []
|
|
|
|
# no errors in src/sentry/testutils/
|
|
assert _run(src, filename="src/sentry/testutils/silo.py") == []
|
|
|
|
# errors in other paths
|
|
errors = _run(src, filename="src/sentry/api/endpoints/organization_details.py")
|
|
assert errors == [
|
|
"t.py:1:0: S007 Do not import sentry.testutils into production code.",
|
|
]
|
|
|
|
# Module imports should have errors too.
|
|
src = """\
|
|
import sentry.testutils.outbox as outbox_utils
|
|
"""
|
|
assert _run(src, filename="tests/test_foo.py") == []
|
|
|
|
errors = _run(src, filename="src/sentry/api/endpoints/organization_details.py")
|
|
assert errors == [
|
|
"t.py:1:0: S007 Do not import sentry.testutils into production code.",
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"src",
|
|
(
|
|
"from pytz import utc",
|
|
"from pytz import UTC",
|
|
"pytz.utc",
|
|
"pytz.UTC",
|
|
),
|
|
)
|
|
def test_S008(src):
|
|
expected = ["t.py:1:0: S008 Use stdlib datetime.timezone.utc instead of pytz.utc / pytz.UTC"]
|
|
assert _run(src) == expected
|
|
|
|
|
|
def test_S009():
|
|
src = """\
|
|
try:
|
|
...
|
|
except OSError:
|
|
raise # ok: what we want people to do!
|
|
except TypeError as e:
|
|
raise RuntimeError() # ok: reraising a different exception
|
|
except ValueError as e:
|
|
raise e # bad!
|
|
"""
|
|
expected = ["t.py:8:4: S009 Use `raise` with no arguments to reraise exceptions"]
|
|
assert _run(src) == expected
|
|
|
|
|
|
def test_S010():
|
|
src = """\
|
|
try:
|
|
...
|
|
except ValueError:
|
|
... # ok: not a reraise body
|
|
except Exception:
|
|
raise # bad!
|
|
|
|
try:
|
|
...
|
|
except Exception:
|
|
...
|
|
raise # ok: non just a reraise body
|
|
"""
|
|
expected = ["t.py:5:0: S010 Except handler does nothing and should be removed"]
|
|
assert _run(src) == expected
|
|
|
|
|
|
def test_S011():
|
|
src = """\
|
|
from sentry.testutils.cases import APITestCase
|
|
from django.test import override_settings
|
|
|
|
def test():
|
|
with override_settings(SENTRY_OPTIONS={"foo": "bar"}): # bad
|
|
...
|
|
|
|
with override_settings(
|
|
SENTRY_OPTIONS={"foo": "bar"}, # bad
|
|
OTHER_SETTING=2, # ok
|
|
):
|
|
...
|
|
|
|
with override_settings(OTHER_SETTING=2): # ok
|
|
...
|
|
|
|
class Test(ApiTestCase):
|
|
def test(self):
|
|
with self.settings(SENTRY_OPTIONS={"foo": "bar"}): # bad
|
|
...
|
|
"""
|
|
expected = [
|
|
"t.py:5:27: S011 Use override_options(...) instead to ensure proper cleanup",
|
|
"t.py:9:8: S011 Use override_options(...) instead to ensure proper cleanup",
|
|
"t.py:19:27: S011 Use override_options(...) instead to ensure proper cleanup",
|
|
]
|
|
assert _run(src, filename="tests/test_example.py") == expected
|