@@ -21,6 +21,7 @@ public CreateResourceWithClientGeneratedIdTests(IntegrationTestContext<TestableS
21
21
22
22
testContext . UseController < WorkItemGroupsController > ( ) ;
23
23
testContext . UseController < RgbColorsController > ( ) ;
24
+ testContext . UseController < UserAccountsController > ( ) ;
24
25
25
26
testContext . ConfigureServices ( services =>
26
27
{
@@ -340,6 +341,231 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
340
341
error . Meta . ShouldContainKey ( "requestBody" ) . With ( value => value . ShouldNotBeNull ( ) . ToString ( ) . ShouldNotBeEmpty ( ) ) ;
341
342
}
342
343
344
+ [ Theory ]
345
+ [ InlineData ( ClientIdGenerationMode . Allowed ) ]
346
+ [ InlineData ( ClientIdGenerationMode . Required ) ]
347
+ public async Task Cannot_create_resource_with_client_generated_zero_guid_ID ( ClientIdGenerationMode mode )
348
+ {
349
+ // Arrange
350
+ var options = ( JsonApiOptions ) _testContext . Factory . Services . GetRequiredService < IJsonApiOptions > ( ) ;
351
+ options . ClientIdGeneration = mode ;
352
+
353
+ WorkItemGroup newGroup = _fakers . WorkItemGroup . Generate ( ) ;
354
+
355
+ var requestBody = new
356
+ {
357
+ data = new
358
+ {
359
+ type = "workItemGroups" ,
360
+ id = Guid . Empty . ToString ( ) ,
361
+ attributes = new
362
+ {
363
+ name = newGroup . Name
364
+ }
365
+ }
366
+ } ;
367
+
368
+ const string route = "/workItemGroups" ;
369
+
370
+ // Act
371
+ ( HttpResponseMessage httpResponse , Document responseDocument ) = await _testContext . ExecutePostAsync < Document > ( route , requestBody ) ;
372
+
373
+ // Assert
374
+ httpResponse . ShouldHaveStatusCode ( HttpStatusCode . UnprocessableEntity ) ;
375
+
376
+ responseDocument . Errors . ShouldHaveCount ( 1 ) ;
377
+
378
+ ErrorObject error = responseDocument . Errors [ 0 ] ;
379
+ error . StatusCode . Should ( ) . Be ( HttpStatusCode . UnprocessableEntity ) ;
380
+ error . Title . Should ( ) . Be ( "Failed to deserialize request body: The 'id' element is invalid." ) ;
381
+ error . Detail . Should ( ) . BeNull ( ) ;
382
+ error . Source . ShouldNotBeNull ( ) ;
383
+ error . Source . Pointer . Should ( ) . Be ( "/data" ) ;
384
+ error . Meta . ShouldContainKey ( "requestBody" ) . With ( value => value . ShouldNotBeNull ( ) . ToString ( ) . ShouldNotBeEmpty ( ) ) ;
385
+ }
386
+
387
+ [ Theory ]
388
+ [ InlineData ( ClientIdGenerationMode . Allowed ) ]
389
+ [ InlineData ( ClientIdGenerationMode . Required ) ]
390
+ public async Task Cannot_create_resource_with_client_generated_empty_guid_ID ( ClientIdGenerationMode mode )
391
+ {
392
+ // Arrange
393
+ var options = ( JsonApiOptions ) _testContext . Factory . Services . GetRequiredService < IJsonApiOptions > ( ) ;
394
+ options . ClientIdGeneration = mode ;
395
+
396
+ WorkItemGroup newGroup = _fakers . WorkItemGroup . Generate ( ) ;
397
+
398
+ var requestBody = new
399
+ {
400
+ data = new
401
+ {
402
+ type = "workItemGroups" ,
403
+ id = string . Empty ,
404
+ attributes = new
405
+ {
406
+ name = newGroup . Name
407
+ }
408
+ }
409
+ } ;
410
+
411
+ const string route = "/workItemGroups" ;
412
+
413
+ // Act
414
+ ( HttpResponseMessage httpResponse , Document responseDocument ) = await _testContext . ExecutePostAsync < Document > ( route , requestBody ) ;
415
+
416
+ // Assert
417
+ httpResponse . ShouldHaveStatusCode ( HttpStatusCode . UnprocessableEntity ) ;
418
+
419
+ responseDocument . Errors . ShouldHaveCount ( 1 ) ;
420
+
421
+ ErrorObject error = responseDocument . Errors [ 0 ] ;
422
+ error . StatusCode . Should ( ) . Be ( HttpStatusCode . UnprocessableEntity ) ;
423
+ error . Title . Should ( ) . Be ( "Failed to deserialize request body: The 'id' element is invalid." ) ;
424
+ error . Detail . Should ( ) . BeNull ( ) ;
425
+ error . Source . ShouldNotBeNull ( ) ;
426
+ error . Source . Pointer . Should ( ) . Be ( "/data" ) ;
427
+ error . Meta . ShouldContainKey ( "requestBody" ) . With ( value => value . ShouldNotBeNull ( ) . ToString ( ) . ShouldNotBeEmpty ( ) ) ;
428
+ }
429
+
430
+ [ Theory ]
431
+ [ InlineData ( ClientIdGenerationMode . Allowed ) ]
432
+ [ InlineData ( ClientIdGenerationMode . Required ) ]
433
+ public async Task Can_create_resource_with_client_generated_empty_string_ID ( ClientIdGenerationMode mode )
434
+ {
435
+ // Arrange
436
+ var options = ( JsonApiOptions ) _testContext . Factory . Services . GetRequiredService < IJsonApiOptions > ( ) ;
437
+ options . ClientIdGeneration = mode ;
438
+
439
+ RgbColor newColor = _fakers . RgbColor . Generate ( ) ;
440
+
441
+ await _testContext . RunOnDatabaseAsync ( async dbContext =>
442
+ {
443
+ await dbContext . ClearTableAsync < RgbColor > ( ) ;
444
+ } ) ;
445
+
446
+ var requestBody = new
447
+ {
448
+ data = new
449
+ {
450
+ type = "rgbColors" ,
451
+ id = string . Empty ,
452
+ attributes = new
453
+ {
454
+ displayName = newColor . DisplayName
455
+ }
456
+ }
457
+ } ;
458
+
459
+ const string route = "/rgbColors?fields[rgbColors]=id" ;
460
+
461
+ // Act
462
+ ( HttpResponseMessage httpResponse , string responseDocument ) = await _testContext . ExecutePostAsync < string > ( route , requestBody ) ;
463
+
464
+ // Assert
465
+ httpResponse . ShouldHaveStatusCode ( HttpStatusCode . NoContent ) ;
466
+
467
+ responseDocument . Should ( ) . BeEmpty ( ) ;
468
+
469
+ await _testContext . RunOnDatabaseAsync ( async dbContext =>
470
+ {
471
+ RgbColor colorInDatabase = await dbContext . RgbColors . FirstWithIdAsync ( ( string ? ) string . Empty ) ;
472
+
473
+ colorInDatabase . DisplayName . Should ( ) . Be ( newColor . DisplayName ) ;
474
+ } ) ;
475
+
476
+ PropertyInfo ? property = typeof ( RgbColor ) . GetProperty ( nameof ( Identifiable < object > . Id ) ) ;
477
+ property . ShouldNotBeNull ( ) ;
478
+ property . PropertyType . Should ( ) . Be ( typeof ( string ) ) ;
479
+ }
480
+
481
+ [ Theory ]
482
+ [ InlineData ( ClientIdGenerationMode . Allowed ) ]
483
+ [ InlineData ( ClientIdGenerationMode . Required ) ]
484
+ public async Task Cannot_create_resource_with_client_generated_zero_long_ID ( ClientIdGenerationMode mode )
485
+ {
486
+ // Arrange
487
+ var options = ( JsonApiOptions ) _testContext . Factory . Services . GetRequiredService < IJsonApiOptions > ( ) ;
488
+ options . ClientIdGeneration = mode ;
489
+
490
+ UserAccount newAccount = _fakers . UserAccount . Generate ( ) ;
491
+
492
+ var requestBody = new
493
+ {
494
+ data = new
495
+ {
496
+ type = "userAccounts" ,
497
+ id = "0" ,
498
+ attributes = new
499
+ {
500
+ firstName = newAccount . FirstName ,
501
+ lastName = newAccount . LastName
502
+ }
503
+ }
504
+ } ;
505
+
506
+ const string route = "/userAccounts" ;
507
+
508
+ // Act
509
+ ( HttpResponseMessage httpResponse , Document responseDocument ) = await _testContext . ExecutePostAsync < Document > ( route , requestBody ) ;
510
+
511
+ // Assert
512
+ httpResponse . ShouldHaveStatusCode ( HttpStatusCode . UnprocessableEntity ) ;
513
+
514
+ responseDocument . Errors . ShouldHaveCount ( 1 ) ;
515
+
516
+ ErrorObject error = responseDocument . Errors [ 0 ] ;
517
+ error . StatusCode . Should ( ) . Be ( HttpStatusCode . UnprocessableEntity ) ;
518
+ error . Title . Should ( ) . Be ( "Failed to deserialize request body: The 'id' element is invalid." ) ;
519
+ error . Detail . Should ( ) . BeNull ( ) ;
520
+ error . Source . ShouldNotBeNull ( ) ;
521
+ error . Source . Pointer . Should ( ) . Be ( "/data" ) ;
522
+ error . Meta . ShouldContainKey ( "requestBody" ) . With ( value => value . ShouldNotBeNull ( ) . ToString ( ) . ShouldNotBeEmpty ( ) ) ;
523
+ }
524
+
525
+ [ Theory ]
526
+ [ InlineData ( ClientIdGenerationMode . Allowed ) ]
527
+ [ InlineData ( ClientIdGenerationMode . Required ) ]
528
+ public async Task Cannot_create_resource_with_client_generated_empty_long_ID ( ClientIdGenerationMode mode )
529
+ {
530
+ // Arrange
531
+ var options = ( JsonApiOptions ) _testContext . Factory . Services . GetRequiredService < IJsonApiOptions > ( ) ;
532
+ options . ClientIdGeneration = mode ;
533
+
534
+ UserAccount newAccount = _fakers . UserAccount . Generate ( ) ;
535
+
536
+ var requestBody = new
537
+ {
538
+ data = new
539
+ {
540
+ type = "userAccounts" ,
541
+ id = string . Empty ,
542
+ attributes = new
543
+ {
544
+ firstName = newAccount . FirstName ,
545
+ lastName = newAccount . LastName
546
+ }
547
+ }
548
+ } ;
549
+
550
+ const string route = "/userAccounts" ;
551
+
552
+ // Act
553
+ ( HttpResponseMessage httpResponse , Document responseDocument ) = await _testContext . ExecutePostAsync < Document > ( route , requestBody ) ;
554
+
555
+ // Assert
556
+ httpResponse . ShouldHaveStatusCode ( HttpStatusCode . UnprocessableEntity ) ;
557
+
558
+ responseDocument . Errors . ShouldHaveCount ( 1 ) ;
559
+
560
+ ErrorObject error = responseDocument . Errors [ 0 ] ;
561
+ error . StatusCode . Should ( ) . Be ( HttpStatusCode . UnprocessableEntity ) ;
562
+ error . Title . Should ( ) . Be ( "Failed to deserialize request body: The 'id' element is invalid." ) ;
563
+ error . Detail . Should ( ) . BeNull ( ) ;
564
+ error . Source . ShouldNotBeNull ( ) ;
565
+ error . Source . Pointer . Should ( ) . Be ( "/data" ) ;
566
+ error . Meta . ShouldContainKey ( "requestBody" ) . With ( value => value . ShouldNotBeNull ( ) . ToString ( ) . ShouldNotBeEmpty ( ) ) ;
567
+ }
568
+
343
569
[ Theory ]
344
570
[ InlineData ( ClientIdGenerationMode . Allowed ) ]
345
571
[ InlineData ( ClientIdGenerationMode . Required ) ]
0 commit comments