@@ -44,6 +44,7 @@ def assertSerialized(self, series_obj, series_json):
44
44
self .assertIn (series_obj .get_mbox_url (), series_json ['mbox' ])
45
45
self .assertIn (series_obj .get_absolute_url (), series_json ['web_url' ])
46
46
47
+ # dependencies
47
48
for dep , item in zip (
48
49
series_obj .dependencies .all (), series_json ['dependencies' ]
49
50
):
@@ -58,6 +59,21 @@ def assertSerialized(self, series_obj, series_json):
58
59
reverse ('api-series-detail' , kwargs = {'pk' : dep .id }), item
59
60
)
60
61
62
+ # versioning
63
+ for ver , item in zip (
64
+ series_obj .supersedes .all (), series_json ['supersedes' ]
65
+ ):
66
+ self .assertIn (
67
+ reverse ('api-series-detail' , kwargs = {'pk' : ver .id }), item
68
+ )
69
+
70
+ for ver , item in zip (
71
+ series_obj .superseded .all (), series_json ['superseded' ]
72
+ ):
73
+ self .assertIn (
74
+ reverse ('api-series-detail' , kwargs = {'pk' : ver .id }), item
75
+ )
76
+
61
77
# nested fields
62
78
63
79
self .assertEqual (series_obj .project .id , series_json ['project' ]['id' ])
@@ -79,7 +95,9 @@ def test_list_empty(self):
79
95
80
96
def _create_series (self ):
81
97
project_obj = create_project (
82
- linkname = 'myproject' , show_dependencies = True
98
+ linkname = 'myproject' ,
99
+ show_dependencies = True ,
100
+ show_series_versions = True ,
83
101
)
84
102
person_obj = create_person (
email = '[email protected] ' )
85
103
series_obj = create_series (project = project_obj , submitter = person_obj )
@@ -197,7 +215,7 @@ def test_list_bug_335(self):
197
215
create_cover (series = series_obj )
198
216
create_patch (series = series_obj )
199
217
200
- with self .assertNumQueries (8 ):
218
+ with self .assertNumQueries (10 ):
201
219
self .client .get (self .api_url ())
202
220
203
221
@utils .store_samples ('series-detail' )
@@ -225,6 +243,8 @@ def test_detail_version_1_3(self):
225
243
self .assertIn ('web_url' , resp .data ['patches' ][0 ])
226
244
self .assertNotIn ('dependents' , resp .data )
227
245
self .assertNotIn ('dependencies' , resp .data )
246
+ self .assertNotIn ('superseded' , resp .data )
247
+ self .assertNotIn ('supersedes' , resp .data )
228
248
229
249
@utils .store_samples ('series-detail-1-0' )
230
250
def test_detail_version_1_0 (self ):
@@ -251,8 +271,8 @@ def test_detail_invalid(self):
251
271
with self .assertRaises (NoReverseMatch ):
252
272
self .client .get (self .api_url ('foo' ))
253
273
254
- def test_create_update_delete (self ):
255
- """Ensure creates, updates and deletes aren't allowed"""
274
+ def test_create_delete (self ):
275
+ """Ensure creates and deletes aren't allowed"""
256
276
user = create_maintainer ()
257
277
user .is_superuser = True
258
278
user .save ()
@@ -263,8 +283,306 @@ def test_create_update_delete(self):
263
283
264
284
series = create_series ()
265
285
266
- resp = self .client .patch (self .api_url (series .id ), {'name' : 'Test' })
267
- self .assertEqual (status .HTTP_405_METHOD_NOT_ALLOWED , resp .status_code )
268
-
269
286
resp = self .client .delete (self .api_url (series .id ))
270
287
self .assertEqual (status .HTTP_405_METHOD_NOT_ALLOWED , resp .status_code )
288
+
289
+ def test_series_versioning (self ):
290
+ """Test toggling versioning on and off."""
291
+ project = create_project (
292
+ show_dependencies = True ,
293
+ show_series_versions = True ,
294
+ )
295
+ submitter = create_person (
email = '[email protected] ' )
296
+ series_a = create_series (project = project , submitter = submitter )
297
+ create_cover (series = series_a )
298
+ create_patch (series = series_a )
299
+ series_b = create_series (project = project , submitter = submitter )
300
+ create_cover (series = series_b )
301
+ create_patch (series = series_b )
302
+ series_a .supersedes .set ([series_b ])
303
+
304
+ resp = self .client .get (self .api_url ())
305
+ self .assertEqual (2 , len (resp .data ))
306
+ for series_data in resp .data :
307
+ self .assertIn ('supersedes' , series_data )
308
+ self .assertIn ('superseded' , series_data )
309
+
310
+ project .show_series_versions = False
311
+ project .save ()
312
+
313
+ resp = self .client .get (self .api_url ())
314
+ self .assertEqual (2 , len (resp .data ))
315
+ for series_data in resp .data :
316
+ self .assertNotIn ('supersedes' , series_data )
317
+ self .assertNotIn ('superseded' , series_data )
318
+
319
+
320
+ @override_settings (PATCHWORK_API_ENABLED = True )
321
+ class TestSeriesDetailUpdate (utils .APITestCase ):
322
+ @staticmethod
323
+ def api_url (item , version = None ):
324
+ kwargs = {}
325
+ if version :
326
+ kwargs ['version' ] = version
327
+
328
+ kwargs ['pk' ] = item
329
+ return reverse ('api-series-detail' , kwargs = kwargs )
330
+
331
+ def _get_series_url (self , series , request = None ):
332
+ url = reverse (
333
+ 'api-series-detail' ,
334
+ kwargs = {'pk' : series .id },
335
+ )
336
+ # Build absolute uri for spec validation
337
+ if request is not None :
338
+ return request .build_absolute_uri (url )
339
+
340
+ return url
341
+
342
+ def _assert_contains_series_url (self , response , key , series ):
343
+ self .assertEqual (response .status_code , status .HTTP_200_OK )
344
+ container = response .json ().get (key )
345
+ for item in container :
346
+ if item .endswith (
347
+ self ._get_series_url (series , response .wsgi_request )
348
+ ):
349
+ return True
350
+ raise AssertionError (
351
+ f'No item in { container } ends with { self ._get_series_url (series )} '
352
+ )
353
+
354
+ def setUp (self ):
355
+ super ().setUp ()
356
+ self .client .defaults .update (
357
+ {'HTTP_HOST' : 'example.com' , 'SERVER_NAME' : 'example.com' }
358
+ )
359
+ self .project = create_project (
360
+ linkname = 'myproject' ,
361
+ show_dependencies = True ,
362
+ show_series_versions = True ,
363
+ )
364
+ user = create_user ()
365
+ self .
submitter = create_person (
email = '[email protected] ' ,
user = user )
366
+
367
+ self .superseded = create_series (
368
+ project = self .project , submitter = self .submitter
369
+ )
370
+ create_cover (series = self .superseded )
371
+ create_patch (series = self .superseded )
372
+
373
+ self .series = create_series (
374
+ project = self .project , submitter = self .submitter
375
+ )
376
+ create_cover (series = self .series )
377
+ create_patch (series = self .series )
378
+
379
+ self .url = self ._get_series_url (self .series )
380
+
381
+ def authenticate_as_submitter (self ):
382
+ self .client .authenticate (user = self .submitter .user )
383
+
384
+ def authenticate_as_maintainer (self ):
385
+ user = create_maintainer (self .project )
386
+ self .client .authenticate (user = user )
387
+
388
+ def authenticate_as_superuser (self ):
389
+ user = create_user ()
390
+ user .is_superuser = True
391
+ user .save ()
392
+ self .client .authenticate (user = user )
393
+
394
+ def authenticate_as_unrelated_user (self ):
395
+ user = create_user ()
396
+ self .client .authenticate (user = user )
397
+
398
+ # PATCH tests
399
+ def test_patch_series_as_submitter (self ):
400
+ series_b = create_series (
401
+ project = self .project , submitter = self .submitter
402
+ )
403
+ self .authenticate_as_submitter ()
404
+ response = self .client .patch (
405
+ self .url ,
406
+ {'supersedes' : [self ._get_series_url (self .superseded )]},
407
+ format = 'json' ,
408
+ )
409
+ self ._assert_contains_series_url (
410
+ response , 'supersedes' , self .superseded
411
+ )
412
+
413
+ # Add series_b and remove superseded
414
+ response = self .client .patch (
415
+ self .url ,
416
+ {'supersedes' : [self ._get_series_url (series_b )]},
417
+ format = 'json' ,
418
+ )
419
+ self ._assert_contains_series_url (response , 'supersedes' , series_b )
420
+ with self .assertRaises (AssertionError ):
421
+ self ._assert_contains_series_url (
422
+ response , 'supersedes' , self .superseded
423
+ )
424
+
425
+ def test_patch_series_as_maintainer (self ):
426
+ self .authenticate_as_maintainer ()
427
+ response = self .client .patch (
428
+ self .url ,
429
+ {'supersedes' : [self ._get_series_url (self .superseded )]},
430
+ format = 'json' ,
431
+ )
432
+ self ._assert_contains_series_url (
433
+ response , 'supersedes' , self .superseded
434
+ )
435
+
436
+ def test_patch_series_as_superuser (self ):
437
+ self .authenticate_as_superuser ()
438
+ response = self .client .patch (
439
+ self .url ,
440
+ {'supersedes' : [self ._get_series_url (self .superseded )]},
441
+ format = 'json' ,
442
+ )
443
+ self ._assert_contains_series_url (
444
+ response , 'supersedes' , self .superseded
445
+ )
446
+
447
+ def test_patch_series_as_unrelated_user_forbidden (self ):
448
+ self .authenticate_as_unrelated_user ()
449
+ response = self .client .patch (
450
+ self .url ,
451
+ {'supersedes' : [self ._get_series_url (self .superseded )]},
452
+ format = 'json' ,
453
+ )
454
+ self .assertEqual (response .status_code , status .HTTP_403_FORBIDDEN )
455
+
456
+ def test_patch_series_unauthenticated_forbidden (self ):
457
+ response = self .client .patch (
458
+ self .url ,
459
+ {'supersedes' : [self ._get_series_url (self .superseded )]},
460
+ format = 'json' ,
461
+ )
462
+ self .assertEqual (response .status_code , status .HTTP_403_FORBIDDEN )
463
+
464
+ # PUT tests
465
+ def test_put_series_as_submitter (self ):
466
+ series_b = create_series (
467
+ project = self .project , submitter = self .submitter
468
+ )
469
+ self .authenticate_as_submitter ()
470
+ response = self .client .put (
471
+ self .url ,
472
+ {'supersedes' : [self ._get_series_url (self .superseded )]},
473
+ format = 'json' ,
474
+ )
475
+ self ._assert_contains_series_url (
476
+ response , 'supersedes' , self .superseded
477
+ )
478
+
479
+ # Rewrite the whole supersedes attribute
480
+ response = self .client .put (
481
+ self .url ,
482
+ {'supersedes' : [self ._get_series_url (series_b )]},
483
+ format = 'json' ,
484
+ )
485
+ self ._assert_contains_series_url (response , 'supersedes' , series_b )
486
+ with self .assertRaises (AssertionError ):
487
+ self ._assert_contains_series_url (
488
+ response , 'supersedes' , self .superseded
489
+ )
490
+
491
+ def test_put_series_as_maintainer (self ):
492
+ self .authenticate_as_maintainer ()
493
+ response = self .client .put (
494
+ self .url ,
495
+ {'supersedes' : [self ._get_series_url (self .superseded )]},
496
+ format = 'json' ,
497
+ )
498
+ self ._assert_contains_series_url (
499
+ response , 'supersedes' , self .superseded
500
+ )
501
+
502
+ def test_put_series_as_superuser (self ):
503
+ self .authenticate_as_superuser ()
504
+ response = self .client .put (
505
+ self .url ,
506
+ {'supersedes' : [self ._get_series_url (self .superseded )]},
507
+ format = 'json' ,
508
+ )
509
+ self ._assert_contains_series_url (
510
+ response , 'supersedes' , self .superseded
511
+ )
512
+
513
+ def test_put_series_as_unrelated_user_forbidden (self ):
514
+ self .authenticate_as_unrelated_user ()
515
+ response = self .client .put (
516
+ self .url ,
517
+ {'supersedes' : [self ._get_series_url (self .superseded )]},
518
+ format = 'json' ,
519
+ )
520
+ self .assertEqual (response .status_code , status .HTTP_403_FORBIDDEN )
521
+
522
+ def test_put_series_unauthenticated_forbidden (self ):
523
+ response = self .client .put (
524
+ self .url ,
525
+ {'supersedes' : [self ._get_series_url (self .superseded )]},
526
+ format = 'json' ,
527
+ )
528
+ self .assertEqual (response .status_code , status .HTTP_403_FORBIDDEN )
529
+
530
+ # Invalid input tests
531
+ def test_patch_invalid_input (self ):
532
+ self .authenticate_as_maintainer ()
533
+ response = self .client .patch (self .url , {'name' : 'name' }, format = 'json' )
534
+ self .assertEqual (response .status_code , status .HTTP_400_BAD_REQUEST )
535
+
536
+ def test_put_invalid_input (self ):
537
+ self .authenticate_as_maintainer ()
538
+ response = self .client .put (self .url , {'name' : 'name' }, format = 'json' )
539
+ self .assertEqual (response .status_code , status .HTTP_400_BAD_REQUEST )
540
+
541
+ def test_patch_invalid_series_id (self ):
542
+ self .authenticate_as_maintainer ()
543
+ response = self .client .patch (
544
+ self .url ,
545
+ {'supersedes' : ['/api/series/99999999/' ]},
546
+ format = 'json' ,
547
+ )
548
+ self .assertEqual (response .status_code , status .HTTP_400_BAD_REQUEST )
549
+
550
+ def test_put_invalid_series_id (self ):
551
+ self .authenticate_as_maintainer ()
552
+ response = self .client .put (
553
+ self .url ,
554
+ {'supersedes' : ['/api/series/99999999/' ]},
555
+ format = 'json' ,
556
+ )
557
+ self .assertEqual (response .status_code , status .HTTP_400_BAD_REQUEST )
558
+
559
+ def test_self_link_validation (self ):
560
+ self .authenticate_as_submitter ()
561
+
562
+ response = self .client .put (
563
+ self .url ,
564
+ {'supersedes' : [self ._get_series_url (self .series )]},
565
+ format = 'json' ,
566
+ )
567
+
568
+ self .assertContains (
569
+ response ,
570
+ 'A series cannot be linked to itself.' ,
571
+ status_code = status .HTTP_400_BAD_REQUEST ,
572
+ )
573
+
574
+ def test_cross_project_validation (self ):
575
+ self .authenticate_as_submitter ()
576
+ series_x = create_series ()
577
+
578
+ response = self .client .put (
579
+ self .url ,
580
+ {'supersedes' : [self ._get_series_url (series_x )]},
581
+ format = 'json' ,
582
+ )
583
+
584
+ self .assertContains (
585
+ response ,
586
+ 'Series must belong to the same project.' ,
587
+ status_code = status .HTTP_400_BAD_REQUEST ,
588
+ )
0 commit comments