test_flake8_plugin.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. from __future__ import annotations
  2. import ast
  3. import pytest
  4. from tools.flake8_plugin import SentryCheck
  5. def _run(src: str, filename: str = "getsentry/t.py") -> list[str]:
  6. tree = ast.parse(src)
  7. return sorted(
  8. "t.py:{}:{}: {}".format(*error) for error in SentryCheck(tree=tree, filename=filename).run()
  9. )
  10. def test_S001():
  11. S001_py = """\
  12. class A:
  13. def called_once():
  14. pass
  15. A().called_once()
  16. """
  17. errors = _run(S001_py)
  18. assert errors == [
  19. "t.py:6:0: S001 Avoid using the called_once mock call as it is confusing and "
  20. "prone to causing invalid test behavior.",
  21. ]
  22. def test_S002():
  23. S002_py = """\
  24. print("print statements are not allowed")
  25. """
  26. errors = _run(S002_py)
  27. assert errors == ["t.py:1:0: S002 print functions or statements are not allowed."]
  28. def test_S003():
  29. S003_py = """\
  30. import json
  31. import simplejson
  32. from json import loads, load
  33. from simplejson import JSONDecoder, JSONDecodeError, _default_encoder
  34. import sentry.utils.json as good_json
  35. from sentry.utils.json import JSONDecoder, JSONDecodeError
  36. from .json import Validator
  37. def bad_code():
  38. a = json.loads("''")
  39. b = simplejson.loads("''")
  40. c = loads("''")
  41. d = load()
  42. """
  43. errors = _run(S003_py)
  44. assert errors == [
  45. "t.py:1:0: S003 Use ``from sentry.utils import json`` instead.",
  46. "t.py:2:0: S003 Use ``from sentry.utils import json`` instead.",
  47. "t.py:3:0: S003 Use ``from sentry.utils import json`` instead.",
  48. "t.py:4:0: S003 Use ``from sentry.utils import json`` instead.",
  49. ]
  50. def test_S004():
  51. S004_py = """\
  52. import unittest
  53. from something import func
  54. class Test(unittest.TestCase):
  55. def test(self):
  56. with self.assertRaises(ValueError):
  57. func()
  58. """
  59. errors = _run(S004_py)
  60. assert errors == [
  61. "t.py:7:13: S004 Use `pytest.raises` instead for better debuggability.",
  62. ]
  63. def test_S005():
  64. S005_py = """\
  65. from sentry.models import User
  66. """
  67. errors = _run(S005_py)
  68. assert errors == [
  69. "t.py:1:0: S005 Do not import models from sentry.models but the actual module",
  70. ]
  71. def test_S006():
  72. src = """\
  73. from django.utils.encoding import force_bytes
  74. from django.utils.encoding import force_str
  75. """
  76. # only error in tests until we can fix the rest
  77. assert _run(src, filename="src/sentry/whatever.py") == []
  78. errors = _run(src, filename="tests/test_foo.py")
  79. assert errors == [
  80. "t.py:1:0: S006 Do not use force_bytes / force_str -- test the types directly",
  81. "t.py:2:0: S006 Do not use force_bytes / force_str -- test the types directly",
  82. ]
  83. def test_S007():
  84. src = """\
  85. from sentry.testutils.outbox import outbox_runner
  86. """
  87. # no errors in tests/
  88. assert _run(src, filename="tests/test_foo.py") == []
  89. # no errors in src/sentry/testutils/
  90. assert _run(src, filename="src/sentry/testutils/silo.py") == []
  91. # errors in other paths
  92. errors = _run(src, filename="src/sentry/api/endpoints/organization_details.py")
  93. assert errors == [
  94. "t.py:1:0: S007 Do not import sentry.testutils into production code.",
  95. ]
  96. # Module imports should have errors too.
  97. src = """\
  98. import sentry.testutils.outbox as outbox_utils
  99. """
  100. assert _run(src, filename="tests/test_foo.py") == []
  101. errors = _run(src, filename="src/sentry/api/endpoints/organization_details.py")
  102. assert errors == [
  103. "t.py:1:0: S007 Do not import sentry.testutils into production code.",
  104. ]
  105. @pytest.mark.parametrize(
  106. "src",
  107. (
  108. "from pytz import utc",
  109. "from pytz import UTC",
  110. "pytz.utc",
  111. "pytz.UTC",
  112. ),
  113. )
  114. def test_S008(src):
  115. expected = ["t.py:1:0: S008 Use stdlib datetime.timezone.utc instead of pytz.utc / pytz.UTC"]
  116. assert _run(src) == expected