1
- import subprocess
2
- import shutil
3
1
import typing
4
- from pathlib import Path
5
2
3
+ import attr
6
4
import pytest
7
5
import responses
8
- from click .testing import CliRunner
9
- from requests .auth import HTTPBasicAuth
10
6
11
- from labels .cli import labels
12
- from labels .github import Client , Label
7
+ from labels .github import Label
13
8
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 ]
16
11
17
12
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"
22
17
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 ])
26
18
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"
28
23
29
24
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 "
34
29
35
30
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"
40
35
41
36
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."""
46
40
41
+ returncode : int
42
+ stdout : str
47
43
48
- @pytest .fixture (name = "repo" )
49
- def fixture_repo () -> str :
50
- """Return a repository name."""
51
- return "cookiecutter"
52
44
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."""
53
48
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
+ )
57
54
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" ),
70
64
)
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" ),
81
75
)
82
76
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 )
88
77
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"
90
82
91
83
92
84
@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 :
94
88
"""Return a dict respresenting the GitHub API response body for the bug
95
89
label.
96
90
"""
97
91
return {
98
92
"id" : 8888 ,
99
93
"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" ,
101
95
"name" : "bug" ,
102
96
"description" : "Bugs and problems with cookiecutter" ,
103
97
"color" : "ea707a" ,
@@ -106,14 +100,16 @@ def fixture_response_get_bug(base_url: str, owner: str, repo: str) -> Response_L
106
100
107
101
108
102
@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 :
110
106
"""Return a dict respresenting the GitHub API response body for the docs
111
107
label.
112
108
"""
113
109
return {
114
110
"id" : 2222 ,
115
111
"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" ,
117
113
"name" : "docs" ,
118
114
"description" : "Tasks to write and update documentation" ,
119
115
"color" : "2abf88" ,
@@ -122,14 +118,16 @@ def fixture_response_get_docs(base_url: str, owner: str, repo: str) -> Response_
122
118
123
119
124
120
@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 :
126
124
"""Return a dict respresenting the GitHub API response body for the infra
127
125
label.
128
126
"""
129
127
return {
130
128
"id" : 1234 ,
131
129
"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" ,
133
131
"name" : "infra" ,
134
132
"description" : "Tasks related to Docker/CI etc." ,
135
133
"color" : "f9d03b" ,
@@ -139,23 +137,23 @@ def fixture_response_get_infra(base_url: str, owner: str, repo: str) -> Response
139
137
140
138
@pytest .fixture (name = "response_list_labels" )
141
139
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 :
146
144
"""Response body for list_labels()."""
147
145
return [response_get_infra , response_get_docs , response_get_bug ]
148
146
149
147
150
148
@pytest .fixture (name = "mock_list_labels" )
151
149
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
153
151
) -> None :
154
152
"""Mock requests for list labels."""
155
153
with responses .RequestsMock () as rsps :
156
154
rsps .add (
157
155
responses .GET ,
158
- f"{ base_url } /repos/{ owner } /{ repo } /labels" ,
156
+ f"{ base_url } /repos/{ repo_owner } /{ repo_name } /labels" ,
159
157
json = response_list_labels ,
160
158
status = 200 ,
161
159
content_type = "application/json" ,
@@ -165,13 +163,13 @@ def fixture_mock_list_labels(
165
163
166
164
@pytest .fixture (name = "mock_get_label" )
167
165
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
169
167
) -> None :
170
168
"""Mock requests for get label."""
171
169
with responses .RequestsMock () as rsps :
172
170
rsps .add (
173
171
responses .GET ,
174
- f"{ base_url } /repos/{ owner } /{ repo } /labels/bug" ,
172
+ f"{ base_url } /repos/{ repo_owner } /{ repo_name } /labels/bug" ,
175
173
json = response_get_bug ,
176
174
status = 200 ,
177
175
content_type = "application/json" ,
@@ -181,13 +179,13 @@ def fixture_mock_get_label(
181
179
182
180
@pytest .fixture (name = "mock_edit_label" )
183
181
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
185
183
) -> None :
186
184
"""Mock requests for edit label."""
187
185
with responses .RequestsMock () as rsps :
188
186
rsps .add (
189
187
responses .PATCH ,
190
- f"{ base_url } /repos/{ owner } /{ repo } /labels/bug" ,
188
+ f"{ base_url } /repos/{ repo_owner } /{ repo_name } /labels/bug" ,
191
189
json = response_get_bug ,
192
190
status = 200 ,
193
191
content_type = "application/json" ,
@@ -205,13 +203,17 @@ def fixture_label() -> Label:
205
203
206
204
@pytest .fixture (name = "mock_create_label" )
207
205
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 ,
209
211
) -> None :
210
212
"""Mock requests for create label."""
211
213
with responses .RequestsMock () as rsps :
212
214
rsps .add (
213
215
responses .POST ,
214
- f"{ base_url } /repos/{ owner } /{ repo } /labels" ,
216
+ f"{ base_url } /repos/{ repo_owner } /{ repo_name } /labels" ,
215
217
json = response_get_bug ,
216
218
status = 201 ,
217
219
content_type = "application/json" ,
@@ -220,24 +222,26 @@ def fixture_mock_create_label(
220
222
221
223
222
224
@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 :
224
226
"""Mock requests for delete label."""
225
227
with responses .RequestsMock () as rsps :
226
228
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 ,
228
232
)
229
233
yield
230
234
231
235
232
236
@pytest .fixture (name = "mock_sync" )
233
237
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
235
239
) -> None :
236
240
with responses .RequestsMock () as rsps :
237
241
# Response mock for when sync requests the existing remote labels
238
242
rsps .add (
239
243
responses .GET ,
240
- f"{ base_url } /repos/{ owner } /{ repo } /labels" ,
244
+ f"{ base_url } /repos/{ repo_owner } /{ repo_name } /labels" ,
241
245
json = response_list_labels ,
242
246
status = 200 ,
243
247
content_type = "application/json" ,
@@ -246,11 +250,11 @@ def fixture_mock_sync(
246
250
# Response mock for when sync creates the "dependencies" label
247
251
rsps .add (
248
252
responses .POST ,
249
- f"{ base_url } /repos/{ owner } /{ repo } /labels" ,
253
+ f"{ base_url } /repos/{ repo_owner } /{ repo_name } /labels" ,
250
254
json = {
251
255
"id" : 8080 ,
252
256
"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" ,
254
258
"name" : "dependencies" ,
255
259
"description" : "Tasks related to managing dependencies" ,
256
260
"color" : "43a2b7" ,
@@ -263,11 +267,11 @@ def fixture_mock_sync(
263
267
# Response mock for when sync edits the "bug" label
264
268
rsps .add (
265
269
responses .PATCH ,
266
- f"{ base_url } /repos/{ owner } /{ repo } /labels/bug" ,
270
+ f"{ base_url } /repos/{ repo_owner } /{ repo_name } /labels/bug" ,
267
271
json = {
268
272
"id" : 8888 ,
269
273
"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" ,
271
275
"name" : "bug" ,
272
276
"description" : "Bugs and problems with cookiecutter" ,
273
277
"color" : "fcc4db" ,
@@ -280,7 +284,7 @@ def fixture_mock_sync(
280
284
# Response mock for when sync deletes the "infra" label
281
285
rsps .add (
282
286
responses .DELETE ,
283
- f"{ base_url } /repos/{ owner } /{ repo } /labels/infra" ,
287
+ f"{ base_url } /repos/{ repo_owner } /{ repo_name } /labels/infra" ,
284
288
status = 204 ,
285
289
)
286
290
@@ -322,19 +326,10 @@ def fixture_labels() -> typing.List[Label]:
322
326
name = "good first issue" ,
323
327
),
324
328
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"
337
330
),
331
+ Label (color = "f9d03b" , name = "no description" ),
332
+ Label (color = "f9d03b" , description = "" , name = "empty description" ),
338
333
]
339
334
340
335
@@ -391,7 +386,7 @@ def fixture_labels_file_content() -> typing.Dict[str, typing.Any]:
391
386
392
387
393
388
@pytest .fixture (name = "labels_file_write" )
394
- def fixture_labels_file_write (tmpdir ) -> str :
389
+ def fixture_labels_file_write (tmpdir : typing . Any ) -> str :
395
390
"""Return a filepath to a temporary file."""
396
391
labels_file = tmpdir .join ("labels.toml" )
397
392
return str (labels_file )
@@ -404,6 +399,6 @@ def fixture_labels_file_load() -> str:
404
399
405
400
406
401
@pytest .fixture (name = "labels_file_sync" )
407
- def fixture_labels_file_sync (tmpdir ) -> str :
402
+ def fixture_labels_file_sync (tmpdir : typing . Any ) -> str :
408
403
"""Return a filepath to an existing labels file for the sync test."""
409
404
return "tests/sync.toml"
0 commit comments