1
+ import dataclasses
1
2
import tempfile
2
3
import time
3
4
import uuid
4
5
from pathlib import Path
5
6
from subprocess import check_output
7
+ from typing import Any , Dict , List
6
8
7
9
import pytest
8
10
@@ -30,6 +32,36 @@ def reset_gitlab(gl):
30
32
if user .username != "root" :
31
33
user .delete (hard_delete = True )
32
34
35
+ sleep_interval = 0.1
36
+ timeout = 60
37
+ max_iterations = int (timeout / sleep_interval )
38
+
39
+ # Ensure everything has been reset
40
+ start_time = time .perf_counter ()
41
+
42
+ def ensure_list_empty (pg_object , description : str ) -> None :
43
+ for _ in range (max_iterations ):
44
+ if pg_object .list () == []:
45
+ break
46
+ time .sleep (sleep_interval )
47
+ assert pg_object .list () == [], (
48
+ f"Did not delete all { description } . "
49
+ f"Elapsed_time: { time .perf_counter () - start_time } "
50
+ )
51
+
52
+ ensure_list_empty (pg_object = gl .projects , description = "projects" )
53
+ ensure_list_empty (pg_object = gl .groups , description = "groups" )
54
+ ensure_list_empty (pg_object = gl .variables , description = "variables" )
55
+
56
+ for _ in range (max_iterations ):
57
+ if len (gl .users .list ()) <= 1 :
58
+ break
59
+ time .sleep (sleep_interval )
60
+ assert len (gl .users .list ()) <= 1 , (
61
+ f"Did not delete all users. "
62
+ f"elapsed_time: { time .perf_counter () - start_time } "
63
+ )
64
+
33
65
34
66
def set_token (container , fixture_dir ):
35
67
set_token_rb = fixture_dir / "set_token.rb"
@@ -105,25 +137,59 @@ def _check(container):
105
137
return _check
106
138
107
139
140
+ @dataclasses .dataclass
141
+ class WaitSidekiq :
142
+ iterations : int
143
+ step : float
144
+ elapsed_time : float
145
+ success : bool
146
+
147
+
108
148
@pytest .fixture
109
- def wait_for_sidekiq (gl ):
149
+ def wait_for_sidekiq (gl : gitlab .Gitlab ):
150
+ """
151
+ Return a helper function to wait until there are no busy sidekiq processes.
152
+
153
+ Use this with asserts for slow tasks (group/project/user creation/deletion).
154
+ """
155
+ return _wait_for_sidekiq (gl = gl )
156
+
157
+
158
+ def _wait_for_sidekiq (gl : gitlab .Gitlab ):
110
159
"""
111
160
Return a helper function to wait until there are no busy sidekiq processes.
112
161
113
162
Use this with asserts for slow tasks (group/project/user creation/deletion).
114
163
"""
115
164
116
- def _wait (timeout = 30 , step = 0.5 ):
117
- for _ in range (timeout ):
165
+ def _wait (
166
+ timeout : int = 60 ,
167
+ step : float = 0.1 ,
168
+ ) -> WaitSidekiq :
169
+ # timeout is approximately the timeout in seconds
170
+ max_iterations = int (timeout / step )
171
+ start_time = time .perf_counter ()
172
+ success = False
173
+ for count in range (max_iterations ):
174
+ print (f"_wait: count: { count } " )
118
175
time .sleep (step )
119
176
busy = False
120
177
processes = gl .sidekiq .process_metrics ()["processes" ]
121
178
for process in processes :
179
+ print (f"_wait: process['busy']: { process ['busy' ]} " )
122
180
if process ["busy" ]:
123
181
busy = True
124
182
if not busy :
125
- return True
126
- return False
183
+ success = True
184
+ break
185
+ result = WaitSidekiq (
186
+ iterations = count ,
187
+ step = step ,
188
+ elapsed_time = time .perf_counter () - start_time ,
189
+ success = success ,
190
+ )
191
+ print (f"_wait: { result } " )
192
+ return result
127
193
128
194
return _wait
129
195
@@ -194,6 +260,73 @@ def gitlab_runner(gl):
194
260
check_output (docker_exec + unregister ).decode ()
195
261
196
262
263
+ @pytest .fixture
264
+ def ensure_raises_exception ():
265
+ """
266
+ Return a helper function to wait until specified exception is raised
267
+
268
+ Use this with asserts for slow tasks (group/project/user creation/deletion).
269
+ """
270
+ return _ensure_raises_exception_wrapped ()
271
+
272
+
273
+ def _ensure_raises_exception (
274
+ * ,
275
+ func ,
276
+ exception : Exception ,
277
+ args : List [Any ],
278
+ kwargs : Dict [str , Any ],
279
+ description : str ,
280
+ ) -> None :
281
+ """Ensure the exception is specified when calling `func`. If no exception is raided
282
+ after timeout period, fail the test"""
283
+ sleep_interval = 0.1
284
+ timeout = 60
285
+ max_iterations = int (timeout / sleep_interval )
286
+
287
+ for _ in range (max_iterations ):
288
+ try :
289
+ func (* args , ** kwargs )
290
+ except exception :
291
+ return
292
+ time .sleep (sleep_interval )
293
+ pytest .fail (f"{ description } did not raise exception { exception } " )
294
+
295
+
296
+ def _ensure_raises_exception_wrapped ():
297
+ return _ensure_raises_exception
298
+
299
+
300
+ @pytest .fixture
301
+ def ensure_deleted ():
302
+ """
303
+ Return a helper function to wait until specified object is deleted
304
+
305
+ Use this with asserts for slow tasks (group/project/user creation/deletion).
306
+ """
307
+ return _ensure_deleted_wrapped ()
308
+
309
+
310
+ def _ensure_deleted (* , pg_object , object_id : int , description : str ) -> None :
311
+ """Ensure the object specified can not be retrieved. If object still exists after
312
+ timeout period, fail the test"""
313
+ sleep_interval = 0.1
314
+ timeout = 60
315
+ max_iterations = int (timeout / sleep_interval )
316
+
317
+ for _ in range (max_iterations ):
318
+ try :
319
+ pg_object .get (object_id )
320
+ except gitlab .exceptions .GitlabGetError :
321
+ return
322
+ time .sleep (sleep_interval )
323
+ pytest .fail (f"{ description } { object_id } was not deleted" )
324
+
325
+
326
+ def _ensure_deleted_wrapped ():
327
+ return _ensure_deleted
328
+
329
+
197
330
@pytest .fixture (scope = "module" )
198
331
def group (gl ):
199
332
"""Group fixture for group API resource tests."""
@@ -203,6 +336,7 @@ def group(gl):
203
336
"path" : f"group-{ _id } " ,
204
337
}
205
338
group = gl .groups .create (data )
339
+ group_id = group .id
206
340
207
341
yield group
208
342
@@ -211,6 +345,8 @@ def group(gl):
211
345
except gitlab .exceptions .GitlabDeleteError as e :
212
346
print (f"Group already deleted: { e } " )
213
347
348
+ _ensure_deleted (pg_object = gl .groups , object_id = group_id , description = "Group" )
349
+
214
350
215
351
@pytest .fixture (scope = "module" )
216
352
def project (gl ):
@@ -219,6 +355,7 @@ def project(gl):
219
355
name = f"test-project-{ _id } "
220
356
221
357
project = gl .projects .create (name = name )
358
+ project_id = project .id
222
359
223
360
yield project
224
361
@@ -227,6 +364,8 @@ def project(gl):
227
364
except gitlab .exceptions .GitlabDeleteError as e :
228
365
print (f"Project already deleted: { e } " )
229
366
367
+ _ensure_deleted (pg_object = gl .projects , object_id = project_id , description = "Project" )
368
+
230
369
231
370
@pytest .fixture (scope = "function" )
232
371
def merge_request (project , wait_for_sidekiq ):
@@ -249,8 +388,10 @@ def _merge_request(*, source_branch: str):
249
388
# NOTE(jlvillal): Sometimes the CI would give a "500 Internal Server
250
389
# Error". Hoping that waiting until all other processes are done will
251
390
# help with that.
252
- result = wait_for_sidekiq (timeout = 60 )
253
- assert result is True , "sidekiq process should have terminated but did not"
391
+ result = wait_for_sidekiq ()
392
+ assert (
393
+ result .success is True
394
+ ), f"sidekiq process should have terminated but did not: { result } "
254
395
255
396
project .refresh () # Gets us the current default branch
256
397
project .branches .create (
@@ -274,8 +415,10 @@ def _merge_request(*, source_branch: str):
274
415
"remove_source_branch" : True ,
275
416
}
276
417
)
277
- result = wait_for_sidekiq (timeout = 60 )
278
- assert result is True , "sidekiq process should have terminated but did not"
418
+ result = wait_for_sidekiq ()
419
+ assert (
420
+ result .success is True
421
+ ), f"sidekiq process should have terminated but did not: { result } "
279
422
280
423
mr_iid = mr .iid
281
424
for _ in range (60 ):
@@ -298,6 +441,18 @@ def _merge_request(*, source_branch: str):
298
441
# Ignore if branch was already deleted
299
442
pass
300
443
444
+ for mr_iid , source_branch in to_delete :
445
+ _ensure_deleted (
446
+ pg_object = project .mergerequests ,
447
+ object_id = mr_iid ,
448
+ description = "Project mergerequest" ,
449
+ )
450
+ _ensure_deleted (
451
+ pg_object = project .branches ,
452
+ object_id = source_branch ,
453
+ description = "Project branch" ,
454
+ )
455
+
301
456
302
457
@pytest .fixture (scope = "module" )
303
458
def project_file (project ):
@@ -342,6 +497,7 @@ def user(gl):
342
497
password = "fakepassword"
343
498
344
499
user = gl .users .create (email = email , username = username , name = name , password = password )
500
+ user_id = user .id
345
501
346
502
yield user
347
503
@@ -350,6 +506,8 @@ def user(gl):
350
506
except gitlab .exceptions .GitlabDeleteError as e :
351
507
print (f"User already deleted: { e } " )
352
508
509
+ _ensure_deleted (pg_object = gl .users , object_id = user_id , description = "User" )
510
+
353
511
354
512
@pytest .fixture (scope = "module" )
355
513
def issue (project ):
0 commit comments