]> git.madduck.net Git - etc/vim.git/blob - tests/test_blackd.py

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Fix an infinite loop when using `# fmt: on/off` ... (#3158)
[etc/vim.git] / tests / test_blackd.py
1 import re
2 from typing import Any
3 from unittest.mock import patch
4
5 from click.testing import CliRunner
6 import pytest
7
8 from tests.util import read_data, DETERMINISTIC_HEADER
9
10 try:
11     import blackd
12     from aiohttp.test_utils import AioHTTPTestCase
13     from aiohttp import web
14 except ImportError as e:
15     raise RuntimeError("Please install Black with the 'd' extra") from e
16
17 try:
18     from aiohttp.test_utils import unittest_run_loop
19 except ImportError:
20     # unittest_run_loop is unnecessary and a no-op since aiohttp 3.8, and aiohttp 4
21     # removed it. To maintain compatibility we can make our own no-op decorator.
22     def unittest_run_loop(func: Any, *args: Any, **kwargs: Any) -> Any:
23         return func
24
25
26 @pytest.mark.blackd
27 class BlackDTestCase(AioHTTPTestCase):
28     def test_blackd_main(self) -> None:
29         with patch("blackd.web.run_app"):
30             result = CliRunner().invoke(blackd.main, [])
31             if result.exception is not None:
32                 raise result.exception
33             self.assertEqual(result.exit_code, 0)
34
35     async def get_application(self) -> web.Application:
36         return blackd.make_app()
37
38     @unittest_run_loop
39     async def test_blackd_request_needs_formatting(self) -> None:
40         response = await self.client.post("/", data=b"print('hello world')")
41         self.assertEqual(response.status, 200)
42         self.assertEqual(response.charset, "utf8")
43         self.assertEqual(await response.read(), b'print("hello world")\n')
44
45     @unittest_run_loop
46     async def test_blackd_request_no_change(self) -> None:
47         response = await self.client.post("/", data=b'print("hello world")\n')
48         self.assertEqual(response.status, 204)
49         self.assertEqual(await response.read(), b"")
50
51     @unittest_run_loop
52     async def test_blackd_request_syntax_error(self) -> None:
53         response = await self.client.post("/", data=b"what even ( is")
54         self.assertEqual(response.status, 400)
55         content = await response.text()
56         self.assertTrue(
57             content.startswith("Cannot parse"),
58             msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
59         )
60
61     @unittest_run_loop
62     async def test_blackd_unsupported_version(self) -> None:
63         response = await self.client.post(
64             "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "2"}
65         )
66         self.assertEqual(response.status, 501)
67
68     @unittest_run_loop
69     async def test_blackd_supported_version(self) -> None:
70         response = await self.client.post(
71             "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "1"}
72         )
73         self.assertEqual(response.status, 200)
74
75     @unittest_run_loop
76     async def test_blackd_invalid_python_variant(self) -> None:
77         async def check(header_value: str, expected_status: int = 400) -> None:
78             response = await self.client.post(
79                 "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: header_value}
80             )
81             self.assertEqual(response.status, expected_status)
82
83         await check("lol")
84         await check("ruby3.5")
85         await check("pyi3.6")
86         await check("py1.5")
87         await check("2")
88         await check("2.7")
89         await check("py2.7")
90         await check("2.8")
91         await check("py2.8")
92         await check("3.0")
93         await check("pypy3.0")
94         await check("jython3.4")
95
96     @unittest_run_loop
97     async def test_blackd_pyi(self) -> None:
98         source, expected = read_data("miscellaneous", "stub.pyi")
99         response = await self.client.post(
100             "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
101         )
102         self.assertEqual(response.status, 200)
103         self.assertEqual(await response.text(), expected)
104
105     @unittest_run_loop
106     async def test_blackd_diff(self) -> None:
107         diff_header = re.compile(
108             r"(In|Out)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
109         )
110
111         source, _ = read_data("miscellaneous", "blackd_diff")
112         expected, _ = read_data("miscellaneous", "blackd_diff.diff")
113
114         response = await self.client.post(
115             "/", data=source, headers={blackd.DIFF_HEADER: "true"}
116         )
117         self.assertEqual(response.status, 200)
118
119         actual = await response.text()
120         actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
121         self.assertEqual(actual, expected)
122
123     @unittest_run_loop
124     async def test_blackd_python_variant(self) -> None:
125         code = (
126             "def f(\n"
127             "    and_has_a_bunch_of,\n"
128             "    very_long_arguments_too,\n"
129             "    and_lots_of_them_as_well_lol,\n"
130             "    **and_very_long_keyword_arguments\n"
131             "):\n"
132             "    pass\n"
133         )
134
135         async def check(header_value: str, expected_status: int) -> None:
136             response = await self.client.post(
137                 "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
138             )
139             self.assertEqual(
140                 response.status, expected_status, msg=await response.text()
141             )
142
143         await check("3.6", 200)
144         await check("py3.6", 200)
145         await check("3.6,3.7", 200)
146         await check("3.6,py3.7", 200)
147         await check("py36,py37", 200)
148         await check("36", 200)
149         await check("3.6.4", 200)
150         await check("3.4", 204)
151         await check("py3.4", 204)
152         await check("py34,py36", 204)
153         await check("34", 204)
154
155     @unittest_run_loop
156     async def test_blackd_line_length(self) -> None:
157         response = await self.client.post(
158             "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
159         )
160         self.assertEqual(response.status, 200)
161
162     @unittest_run_loop
163     async def test_blackd_invalid_line_length(self) -> None:
164         response = await self.client.post(
165             "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "NaN"}
166         )
167         self.assertEqual(response.status, 400)
168
169     @unittest_run_loop
170     async def test_blackd_response_black_version_header(self) -> None:
171         response = await self.client.post("/")
172         self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
173
174     @unittest_run_loop
175     async def test_cors_preflight(self) -> None:
176         response = await self.client.options(
177             "/",
178             headers={
179                 "Access-Control-Request-Method": "POST",
180                 "Origin": "*",
181                 "Access-Control-Request-Headers": "Content-Type",
182             },
183         )
184         self.assertEqual(response.status, 200)
185         self.assertIsNotNone(response.headers.get("Access-Control-Allow-Origin"))
186         self.assertIsNotNone(response.headers.get("Access-Control-Allow-Headers"))
187         self.assertIsNotNone(response.headers.get("Access-Control-Allow-Methods"))
188
189     @unittest_run_loop
190     async def test_cors_headers_present(self) -> None:
191         response = await self.client.post("/", headers={"Origin": "*"})
192         self.assertIsNotNone(response.headers.get("Access-Control-Allow-Origin"))
193         self.assertIsNotNone(response.headers.get("Access-Control-Expose-Headers"))