Skip to content

Commit 50a406b

Browse files
committed
Rewrite tests for cli, utils, and github
1 parent 2439933 commit 50a406b

File tree

4 files changed

+303
-324
lines changed

4 files changed

+303
-324
lines changed

tests/conftest.py

Lines changed: 100 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,97 @@
1-
import subprocess
2-
import shutil
31
import typing
4-
from pathlib import Path
52

3+
import attr
64
import pytest
75
import responses
8-
from click.testing import CliRunner
9-
from requests.auth import HTTPBasicAuth
106

11-
from labels.cli import labels
12-
from labels.github import Client, Label
7+
from labels.github import Label
138

14-
Response_Label = typing.Dict[str, typing.Any]
15-
Response_Labels = typing.List[Response_Label]
9+
ResponseLabel = typing.Dict[str, typing.Any]
10+
ResponseLabels = typing.List[ResponseLabel]
1611

1712

18-
@pytest.fixture(name="run_cli")
19-
def fixture_run_cli() -> typing.Callable:
20-
"""Return a function that invokes a click CLI runner."""
21-
runner = CliRunner()
13+
@pytest.fixture(name="username", scope="session")
14+
def fixture_username() -> str:
15+
"""Return a username for GitHub API authentication."""
16+
return "hackebrot"
2217

23-
def run(*args: typing.Any, **kwargs: typing.Any) -> typing.Any:
24-
"""Run the CLI with the given parameters and return the result."""
25-
return runner.invoke(labels, [*args])
2618

27-
return run
19+
@pytest.fixture(name="token", scope="session")
20+
def fixture_token() -> str:
21+
"""Return a token for GitHub API authentication."""
22+
return "1234"
2823

2924

30-
@pytest.fixture(name="base_url")
31-
def fixture_base_url() -> str:
32-
"""Return a URL to be used for creating a client."""
33-
return "https://api.github.com"
25+
@pytest.fixture(name="repo_owner", scope="session")
26+
def fixture_repo_owner() -> str:
27+
"""Return a repository owner."""
28+
return "hackebrot"
3429

3530

36-
@pytest.fixture(name="client")
37-
def fixture_client(base_url: str) -> Client:
38-
"""Return a GitHub API client."""
39-
return Client(HTTPBasicAuth("", ""), base_url=base_url)
31+
@pytest.fixture(name="repo_name", scope="session")
32+
def fixture_repo_name() -> str:
33+
"""Return a repository name."""
34+
return "turtle"
4035

4136

42-
@pytest.fixture(name="owner")
43-
def fixture_owner() -> str:
44-
"""Return a repository owner."""
45-
return "audreyr"
37+
@attr.s(auto_attribs=True, frozen=True, kw_only=True)
38+
class FakeProc:
39+
"""Fake for a CompletedProcess instance."""
4640

41+
returncode: int
42+
stdout: str
4743

48-
@pytest.fixture(name="repo")
49-
def fixture_repo() -> str:
50-
"""Return a repository name."""
51-
return "cookiecutter"
5244

45+
@pytest.fixture(name="mock_repo_info")
46+
def fixture_mock_repo_info(mocker: typing.Any, remote_url: str) -> typing.Any:
47+
"""Patch the subprocess call to git remote get-url."""
5348

54-
@pytest.fixture(name="tmp_local_repo")
55-
def fixture_tmp_local_repo(tmpdir, owner: str, repo: str) -> None:
56-
"""Return a temporary local git repository.
49+
return mocker.patch(
50+
"labels.utils.subprocess.run",
51+
autospec=True,
52+
return_value=FakeProc(returncode=0, stdout=remote_url),
53+
)
5754

58-
Mocks a repository cloned from
59-
https://github.com/audreyr/cookiecutter.git
60-
and within which a labels file for the sync test is created
61-
./tests/sync.toml
62-
"""
63-
subprocess.call(
64-
[
65-
"git",
66-
"-C",
67-
str(tmpdir),
68-
"init"
69-
]
55+
56+
@pytest.fixture(name="mock_repo_info_error")
57+
def fixture_mock_repo_info_error(mocker: typing.Any) -> typing.Any:
58+
"""Patch the subprocess call to git remote get-url with an error."""
59+
60+
return mocker.patch(
61+
"labels.utils.subprocess.run",
62+
autospec=True,
63+
return_value=FakeProc(returncode=1, stdout="error"),
7064
)
71-
subprocess.call(
72-
[
73-
"git",
74-
"-C",
75-
str(tmpdir),
76-
"remote",
77-
"add",
78-
"origin",
79-
f"https://github.com/{owner}/{repo}.git"
80-
]
65+
66+
67+
@pytest.fixture(name="mock_repo_info_bad_url")
68+
def fixture_mock_repo_info_bad_url(mocker: typing.Any) -> typing.Any:
69+
"""Patch the subprocess call to git remote get-url wuth a bad URL."""
70+
71+
return mocker.patch(
72+
"labels.utils.subprocess.run",
73+
autospec=True,
74+
return_value=FakeProc(returncode=0, stdout="abcd"),
8175
)
8276

83-
# copy labels file for the sync test to the directory
84-
tmp = Path(str(tmpdir), "tests")
85-
tmp.mkdir(exist_ok=True)
86-
perm = Path(__file__).parent.joinpath("sync.toml")
87-
shutil.copy(perm, tmp)
8877

89-
return tmpdir
78+
@pytest.fixture(name="base_url", scope="session")
79+
def fixture_base_url() -> str:
80+
"""Return a URL to the GitHub API."""
81+
return "https://api.github.com"
9082

9183

9284
@pytest.fixture(name="response_get_bug")
93-
def fixture_response_get_bug(base_url: str, owner: str, repo: str) -> Response_Label:
85+
def fixture_response_get_bug(
86+
base_url: str, repo_owner: str, repo_name: str
87+
) -> ResponseLabel:
9488
"""Return a dict respresenting the GitHub API response body for the bug
9589
label.
9690
"""
9791
return {
9892
"id": 8888,
9993
"node_id": "1010",
100-
"url": f"{base_url}/repos/{owner}/{repo}/labels/bug",
94+
"url": f"{base_url}/repos/{repo_owner}/{repo_name}/labels/bug",
10195
"name": "bug",
10296
"description": "Bugs and problems with cookiecutter",
10397
"color": "ea707a",
@@ -106,14 +100,16 @@ def fixture_response_get_bug(base_url: str, owner: str, repo: str) -> Response_L
106100

107101

108102
@pytest.fixture(name="response_get_docs")
109-
def fixture_response_get_docs(base_url: str, owner: str, repo: str) -> Response_Label:
103+
def fixture_response_get_docs(
104+
base_url: str, repo_owner: str, repo_name: str
105+
) -> ResponseLabel:
110106
"""Return a dict respresenting the GitHub API response body for the docs
111107
label.
112108
"""
113109
return {
114110
"id": 2222,
115111
"node_id": "4444",
116-
"url": f"{base_url}/repos/{owner}/{repo}/labels/docs",
112+
"url": f"{base_url}/repos/{repo_owner}/{repo_name}/labels/docs",
117113
"name": "docs",
118114
"description": "Tasks to write and update documentation",
119115
"color": "2abf88",
@@ -122,14 +118,16 @@ def fixture_response_get_docs(base_url: str, owner: str, repo: str) -> Response_
122118

123119

124120
@pytest.fixture(name="response_get_infra")
125-
def fixture_response_get_infra(base_url: str, owner: str, repo: str) -> Response_Label:
121+
def fixture_response_get_infra(
122+
base_url: str, repo_owner: str, repo_name: str
123+
) -> ResponseLabel:
126124
"""Return a dict respresenting the GitHub API response body for the infra
127125
label.
128126
"""
129127
return {
130128
"id": 1234,
131129
"node_id": "5678",
132-
"url": f"{base_url}/repos/{owner}/{repo}/labels/infra",
130+
"url": f"{base_url}/repos/{repo_owner}/{repo_name}/labels/infra",
133131
"name": "infra",
134132
"description": "Tasks related to Docker/CI etc.",
135133
"color": "f9d03b",
@@ -139,23 +137,23 @@ def fixture_response_get_infra(base_url: str, owner: str, repo: str) -> Response
139137

140138
@pytest.fixture(name="response_list_labels")
141139
def fixture_response_list_labels(
142-
response_get_infra: Response_Label,
143-
response_get_docs: Response_Label,
144-
response_get_bug: Response_Label,
145-
) -> Response_Labels:
140+
response_get_infra: ResponseLabel,
141+
response_get_docs: ResponseLabel,
142+
response_get_bug: ResponseLabel,
143+
) -> ResponseLabels:
146144
"""Response body for list_labels()."""
147145
return [response_get_infra, response_get_docs, response_get_bug]
148146

149147

150148
@pytest.fixture(name="mock_list_labels")
151149
def fixture_mock_list_labels(
152-
base_url: str, owner: str, repo: str, response_list_labels: Response_Labels
150+
base_url: str, repo_owner: str, repo_name: str, response_list_labels: ResponseLabels
153151
) -> None:
154152
"""Mock requests for list labels."""
155153
with responses.RequestsMock() as rsps:
156154
rsps.add(
157155
responses.GET,
158-
f"{base_url}/repos/{owner}/{repo}/labels",
156+
f"{base_url}/repos/{repo_owner}/{repo_name}/labels",
159157
json=response_list_labels,
160158
status=200,
161159
content_type="application/json",
@@ -165,13 +163,13 @@ def fixture_mock_list_labels(
165163

166164
@pytest.fixture(name="mock_get_label")
167165
def fixture_mock_get_label(
168-
base_url: str, owner: str, repo: str, response_get_bug: Response_Label
166+
base_url: str, repo_owner: str, repo_name: str, response_get_bug: ResponseLabel
169167
) -> None:
170168
"""Mock requests for get label."""
171169
with responses.RequestsMock() as rsps:
172170
rsps.add(
173171
responses.GET,
174-
f"{base_url}/repos/{owner}/{repo}/labels/bug",
172+
f"{base_url}/repos/{repo_owner}/{repo_name}/labels/bug",
175173
json=response_get_bug,
176174
status=200,
177175
content_type="application/json",
@@ -181,13 +179,13 @@ def fixture_mock_get_label(
181179

182180
@pytest.fixture(name="mock_edit_label")
183181
def fixture_mock_edit_label(
184-
base_url: str, owner: str, repo: str, response_get_bug: Response_Label
182+
base_url: str, repo_owner: str, repo_name: str, response_get_bug: ResponseLabel
185183
) -> None:
186184
"""Mock requests for edit label."""
187185
with responses.RequestsMock() as rsps:
188186
rsps.add(
189187
responses.PATCH,
190-
f"{base_url}/repos/{owner}/{repo}/labels/bug",
188+
f"{base_url}/repos/{repo_owner}/{repo_name}/labels/bug",
191189
json=response_get_bug,
192190
status=200,
193191
content_type="application/json",
@@ -205,13 +203,17 @@ def fixture_label() -> Label:
205203

206204
@pytest.fixture(name="mock_create_label")
207205
def fixture_mock_create_label(
208-
base_url: str, owner: str, repo: str, label: Label, response_get_bug: Response_Label
206+
base_url: str,
207+
repo_owner: str,
208+
repo_name: str,
209+
label: Label,
210+
response_get_bug: ResponseLabel,
209211
) -> None:
210212
"""Mock requests for create label."""
211213
with responses.RequestsMock() as rsps:
212214
rsps.add(
213215
responses.POST,
214-
f"{base_url}/repos/{owner}/{repo}/labels",
216+
f"{base_url}/repos/{repo_owner}/{repo_name}/labels",
215217
json=response_get_bug,
216218
status=201,
217219
content_type="application/json",
@@ -220,24 +222,26 @@ def fixture_mock_create_label(
220222

221223

222224
@pytest.fixture(name="mock_delete_label")
223-
def fixture_mock_delete_label(base_url: str, owner: str, repo: str) -> None:
225+
def fixture_mock_delete_label(base_url: str, repo_owner: str, repo_name: str) -> None:
224226
"""Mock requests for delete label."""
225227
with responses.RequestsMock() as rsps:
226228
rsps.add(
227-
responses.DELETE, f"{base_url}/repos/{owner}/{repo}/labels/bug", status=204
229+
responses.DELETE,
230+
f"{base_url}/repos/{repo_owner}/{repo_name}/labels/bug",
231+
status=204,
228232
)
229233
yield
230234

231235

232236
@pytest.fixture(name="mock_sync")
233237
def fixture_mock_sync(
234-
base_url: str, owner: str, repo: str, response_list_labels: Response_Labels
238+
base_url: str, repo_owner: str, repo_name: str, response_list_labels: ResponseLabels
235239
) -> None:
236240
with responses.RequestsMock() as rsps:
237241
# Response mock for when sync requests the existing remote labels
238242
rsps.add(
239243
responses.GET,
240-
f"{base_url}/repos/{owner}/{repo}/labels",
244+
f"{base_url}/repos/{repo_owner}/{repo_name}/labels",
241245
json=response_list_labels,
242246
status=200,
243247
content_type="application/json",
@@ -246,11 +250,11 @@ def fixture_mock_sync(
246250
# Response mock for when sync creates the "dependencies" label
247251
rsps.add(
248252
responses.POST,
249-
f"{base_url}/repos/{owner}/{repo}/labels",
253+
f"{base_url}/repos/{repo_owner}/{repo_name}/labels",
250254
json={
251255
"id": 8080,
252256
"node_id": "4848",
253-
"url": f"{base_url}/repos/{owner}/{repo}/labels/dependencies",
257+
"url": f"{base_url}/repos/{repo_owner}/{repo_name}/labels/dependencies",
254258
"name": "dependencies",
255259
"description": "Tasks related to managing dependencies",
256260
"color": "43a2b7",
@@ -263,11 +267,11 @@ def fixture_mock_sync(
263267
# Response mock for when sync edits the "bug" label
264268
rsps.add(
265269
responses.PATCH,
266-
f"{base_url}/repos/{owner}/{repo}/labels/bug",
270+
f"{base_url}/repos/{repo_owner}/{repo_name}/labels/bug",
267271
json={
268272
"id": 8888,
269273
"node_id": "1010",
270-
"url": f"{base_url}/repos/{owner}/{repo}/labels/bug",
274+
"url": f"{base_url}/repos/{repo_owner}/{repo_name}/labels/bug",
271275
"name": "bug",
272276
"description": "Bugs and problems with cookiecutter",
273277
"color": "fcc4db",
@@ -280,7 +284,7 @@ def fixture_mock_sync(
280284
# Response mock for when sync deletes the "infra" label
281285
rsps.add(
282286
responses.DELETE,
283-
f"{base_url}/repos/{owner}/{repo}/labels/infra",
287+
f"{base_url}/repos/{repo_owner}/{repo_name}/labels/infra",
284288
status=204,
285289
)
286290

@@ -322,19 +326,10 @@ def fixture_labels() -> typing.List[Label]:
322326
name="good first issue",
323327
),
324328
Label(
325-
color="f9d03b",
326-
description="Tasks related to Docker/CI etc.",
327-
name="infra"
328-
),
329-
Label(
330-
color="f9d03b",
331-
name="no description"
332-
),
333-
Label(
334-
color="f9d03b",
335-
description="",
336-
name="empty description"
329+
color="f9d03b", description="Tasks related to Docker/CI etc.", name="infra"
337330
),
331+
Label(color="f9d03b", name="no description"),
332+
Label(color="f9d03b", description="", name="empty description"),
338333
]
339334

340335

@@ -391,7 +386,7 @@ def fixture_labels_file_content() -> typing.Dict[str, typing.Any]:
391386

392387

393388
@pytest.fixture(name="labels_file_write")
394-
def fixture_labels_file_write(tmpdir) -> str:
389+
def fixture_labels_file_write(tmpdir: typing.Any) -> str:
395390
"""Return a filepath to a temporary file."""
396391
labels_file = tmpdir.join("labels.toml")
397392
return str(labels_file)
@@ -404,6 +399,6 @@ def fixture_labels_file_load() -> str:
404399

405400

406401
@pytest.fixture(name="labels_file_sync")
407-
def fixture_labels_file_sync(tmpdir) -> str:
402+
def fixture_labels_file_sync(tmpdir: typing.Any) -> str:
408403
"""Return a filepath to an existing labels file for the sync test."""
409404
return "tests/sync.toml"

0 commit comments

Comments
 (0)