2
2
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
4
4
using System . Collections . Generic ;
5
- using System . Diagnostics ;
6
5
using System . Linq ;
7
6
using System . Net ;
8
7
using System . Net . Http ;
17
16
18
17
namespace Microsoft . AspNetCore . Mvc . FunctionalTests
19
18
{
20
- public class ApiBehaviorTest : IClassFixture < MvcTestFixture < BasicWebSite . StartupWithoutEndpointRouting > >
19
+ public abstract class ApiBehaviorTestBase < TStartup > : IClassFixture < MvcTestFixture < TStartup > > where TStartup : class
21
20
{
22
- public ApiBehaviorTest ( MvcTestFixture < BasicWebSite . StartupWithoutEndpointRouting > fixture )
21
+ protected ApiBehaviorTestBase ( MvcTestFixture < TStartup > fixture )
23
22
{
24
- Client = fixture . CreateDefaultClient ( ) ;
25
-
26
- var factory = fixture . WithWebHostBuilder ( ConfigureWebHostBuilder ) ;
27
- CustomInvalidModelStateClient = factory . CreateDefaultClient ( ) ;
23
+ var factory = fixture . Factories . FirstOrDefault ( ) ?? fixture . WithWebHostBuilder ( ConfigureWebHostBuilder ) ;
24
+ Client = factory . CreateDefaultClient ( ) ;
28
25
}
29
26
30
27
private static void ConfigureWebHostBuilder ( IWebHostBuilder builder ) =>
31
- builder . UseStartup < BasicWebSite . StartupWithCustomInvalidModelStateFactory > ( ) ;
28
+ builder . UseStartup < TStartup > ( ) ;
32
29
33
30
public HttpClient Client { get ; }
34
- public HttpClient CustomInvalidModelStateClient { get ; }
35
31
36
32
[ Fact ]
37
- public async Task ActionsReturnBadRequest_WhenModelStateIsInvalid ( )
33
+ public virtual async Task ActionsReturnBadRequest_WhenModelStateIsInvalid ( )
38
34
{
39
35
// Arrange
40
36
using ( new ActivityReplacer ( ) )
@@ -122,34 +118,6 @@ public async Task ActionsReturnUnsupportedMediaType_WhenEncodingIsUnsupported()
122
118
Assert . Equal ( "Unsupported Media Type" , problemDetails . Title ) ;
123
119
}
124
120
125
- [ Fact ]
126
- public async Task ActionsReturnBadRequest_UsesProblemDescriptionProviderAndApiConventionsToConfigureErrorResponse ( )
127
- {
128
- // Arrange
129
- var contactModel = new Contact
130
- {
131
- Name = "Abc" ,
132
- City = "Redmond" ,
133
- State = "WA" ,
134
- Zip = "Invalid" ,
135
- } ;
136
- var expected = new Dictionary < string , string [ ] >
137
- {
138
- { "Name" , new [ ] { "The field Name must be a string with a minimum length of 5 and a maximum length of 30." } } ,
139
- { "Zip" , new [ ] { @"The field Zip must match the regular expression '\d{5}'." } }
140
- } ;
141
-
142
- // Act
143
- var response = await CustomInvalidModelStateClient . PostAsJsonAsync ( "/contact/PostWithVnd" , contactModel ) ;
144
-
145
- // Assert
146
- await response . AssertStatusCodeAsync ( HttpStatusCode . BadRequest ) ;
147
- Assert . Equal ( "application/vnd.error+json" , response . Content . Headers . ContentType . MediaType ) ;
148
- var content = await response . Content . ReadAsStringAsync ( ) ;
149
- var actual = JsonConvert . DeserializeObject < Dictionary < string , string [ ] > > ( content ) ;
150
- Assert . Equal ( expected , actual ) ;
151
- }
152
-
153
121
[ Fact ]
154
122
public Task ActionsWithApiBehavior_InferFromBodyParameters ( )
155
123
=> ActionsWithApiBehaviorInferFromBodyParameters ( "ActionWithInferredFromBodyParameter" ) ;
@@ -171,7 +139,7 @@ private async Task ActionsWithApiBehaviorInferFromBodyParameters(string action)
171
139
var response = await Client . PostAsJsonAsync ( $ "/contact/{ action } ", input ) ;
172
140
173
141
// Assert
174
- Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
142
+ await response . AssertStatusCodeAsync ( HttpStatusCode . OK ) ;
175
143
var result = JsonConvert . DeserializeObject < Contact > ( await response . Content . ReadAsStringAsync ( ) ) ;
176
144
Assert . Equal ( input . ContactId , result . ContactId ) ;
177
145
Assert . Equal ( input . Name , result . Name ) ;
@@ -188,7 +156,7 @@ public async Task ActionsWithApiBehavior_InferQueryAndRouteParameters()
188
156
var response = await Client . PostAsync ( url , new StringContent ( string . Empty ) ) ;
189
157
190
158
// Assert
191
- Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
159
+ await response . AssertStatusCodeAsync ( HttpStatusCode . OK ) ;
192
160
var result = JsonConvert . DeserializeObject < Contact > ( await response . Content . ReadAsStringAsync ( ) ) ;
193
161
Assert . Equal ( id , result . ContactId ) ;
194
162
Assert . Equal ( name , result . Name ) ;
@@ -208,7 +176,7 @@ public async Task ActionsWithApiBehavior_InferEmptyPrefixForComplexValueProvider
208
176
var response = await Client . GetAsync ( url ) ;
209
177
210
178
// Assert
211
- Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
179
+ await response . AssertStatusCodeAsync ( HttpStatusCode . OK ) ;
212
180
213
181
var result = await response . Content . ReadAsAsync < Contact > ( ) ;
214
182
Assert . Equal ( id , result . ContactId ) ;
@@ -229,7 +197,7 @@ public async Task ActionsWithApiBehavior_InferEmptyPrefixForComplexValueProvider
229
197
var response = await Client . GetAsync ( url ) ;
230
198
231
199
// Assert
232
- Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
200
+ await response . AssertStatusCodeAsync ( HttpStatusCode . OK ) ;
233
201
234
202
var result = await response . Content . ReadAsAsync < Contact > ( ) ;
235
203
Assert . Equal ( id , result . ContactId ) ;
@@ -247,7 +215,7 @@ public async Task ActionsWithApiBehavior_InferModelBinderType()
247
215
var response = await Client . GetAsync ( "/contact/ActionWithInferredModelBinderType?foo=Hello!" ) ;
248
216
249
217
// Assert
250
- Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
218
+ await response . AssertStatusCodeAsync ( HttpStatusCode . OK ) ;
251
219
var result = await response . Content . ReadAsStringAsync ( ) ;
252
220
Assert . Equal ( expected , result ) ;
253
221
}
@@ -262,13 +230,13 @@ public async Task ActionsWithApiBehavior_InferModelBinderTypeWithExplicitModelNa
262
230
var response = await Client . GetAsync ( "/contact/ActionWithInferredModelBinderTypeWithExplicitModelName?bar=Hello!" ) ;
263
231
264
232
// Assert
265
- Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
233
+ await response . AssertStatusCodeAsync ( HttpStatusCode . OK ) ;
266
234
var result = await response . Content . ReadAsStringAsync ( ) ;
267
235
Assert . Equal ( expected , result ) ;
268
236
}
269
237
270
238
[ Fact ]
271
- public async Task ClientErrorResultFilterExecutesForStatusCodeResults ( )
239
+ public virtual async Task ClientErrorResultFilterExecutesForStatusCodeResults ( )
272
240
{
273
241
using ( new ActivityReplacer ( ) )
274
242
{
@@ -296,7 +264,7 @@ public async Task ClientErrorResultFilterExecutesForStatusCodeResults()
296
264
}
297
265
298
266
[ Fact ]
299
- public async Task SerializingProblemDetails_IgnoresNullValuedProperties ( )
267
+ public virtual async Task SerializingProblemDetails_IgnoresNullValuedProperties ( )
300
268
{
301
269
// Arrange
302
270
var expected = new [ ] { "status" , "title" , "traceId" , "type" } ;
@@ -314,7 +282,7 @@ public async Task SerializingProblemDetails_IgnoresNullValuedProperties()
314
282
}
315
283
316
284
[ Fact ]
317
- public async Task SerializingProblemDetails_WithAllValuesSpecified ( )
285
+ public virtual async Task SerializingProblemDetails_WithAllValuesSpecified ( )
318
286
{
319
287
// Arrange
320
288
var expected = new [ ] { "detail" , "instance" , "status" , "title" , "tracking-id" , "type" } ;
@@ -330,7 +298,7 @@ public async Task SerializingProblemDetails_WithAllValuesSpecified()
330
298
}
331
299
332
300
[ Fact ]
333
- public async Task SerializingValidationProblemDetails_WithExtensionData ( )
301
+ public virtual async Task SerializingValidationProblemDetails_WithExtensionData ( )
334
302
{
335
303
// Act
336
304
var response = await Client . GetAsync ( "/contact/ActionReturningValidationProblemDetails" ) ;
@@ -364,4 +332,132 @@ public async Task SerializingValidationProblemDetails_WithExtensionData()
364
332
} ) ;
365
333
}
366
334
}
335
+
336
+ public class ApiBehaviorTest : ApiBehaviorTestBase < BasicWebSite . StartupWithSystemTextJson >
337
+ {
338
+ public ApiBehaviorTest ( MvcTestFixture < BasicWebSite . StartupWithSystemTextJson > fixture )
339
+ : base ( fixture )
340
+ {
341
+ }
342
+
343
+ [ Fact ]
344
+ public override async Task ActionsReturnBadRequest_WhenModelStateIsInvalid ( )
345
+ {
346
+ // Arrange
347
+ using var _ = new ActivityReplacer ( ) ;
348
+
349
+ var contactModel = new Contact
350
+ {
351
+ Name = "Abc" ,
352
+ City = "Redmond" ,
353
+ State = "WA" ,
354
+ Zip = "Invalid" ,
355
+ } ;
356
+ var contactString = JsonConvert . SerializeObject ( contactModel ) ;
357
+
358
+ // Act
359
+ var response = await Client . PostAsJsonAsync ( "/contact" , contactModel ) ;
360
+
361
+ // Assert
362
+ await response . AssertStatusCodeAsync ( HttpStatusCode . BadRequest ) ;
363
+ Assert . Equal ( "application/problem+json" , response . Content . Headers . ContentType . MediaType ) ;
364
+ var problemDetails = JsonConvert . DeserializeObject < ValidationProblemDetails > (
365
+ await response . Content . ReadAsStringAsync ( ) ,
366
+ new JsonSerializerSettings
367
+ {
368
+ Converters = { new ValidationProblemDetailsConverter ( ) }
369
+ } ) ;
370
+ Assert . Collection (
371
+ problemDetails . Errors . OrderBy ( kvp => kvp . Key ) ,
372
+ kvp =>
373
+ {
374
+ Assert . Equal ( "Name" , kvp . Key ) ;
375
+ var error = Assert . Single ( kvp . Value ) ;
376
+ Assert . Equal ( "The field Name must be a string with a minimum length of 5 and a maximum length of 30." , error ) ;
377
+ } ,
378
+ kvp =>
379
+ {
380
+ Assert . Equal ( "Zip" , kvp . Key ) ;
381
+ var error = Assert . Single ( kvp . Value ) ;
382
+ Assert . Equal ( "The field Zip must match the regular expression '\\ d{5}'." , error ) ;
383
+ }
384
+ ) ;
385
+
386
+ Assert . Collection (
387
+ problemDetails . Extensions ,
388
+ kvp =>
389
+ {
390
+ Assert . Equal ( "extensions" , kvp . Key ) ;
391
+ var jObject = Assert . IsType < JObject > ( kvp . Value ) ;
392
+ Assert . Equal ( "traceId" , Assert . Single ( jObject . Properties ( ) ) . Name ) ;
393
+ } ) ;
394
+ }
395
+
396
+ [ Fact ( Skip = "https://github.com/dotnet/corefx/issues/38769" ) ]
397
+ public override Task ClientErrorResultFilterExecutesForStatusCodeResults ( )
398
+ {
399
+ return base . ClientErrorResultFilterExecutesForStatusCodeResults ( ) ;
400
+ }
401
+
402
+ [ Fact ( Skip = "https://github.com/dotnet/corefx/issues/38769" ) ]
403
+ public override Task SerializingProblemDetails_IgnoresNullValuedProperties ( )
404
+ {
405
+ return base . SerializingProblemDetails_IgnoresNullValuedProperties ( ) ;
406
+ }
407
+
408
+ [ Fact ( Skip = "https://github.com/dotnet/corefx/issues/38769" ) ]
409
+ public override Task SerializingProblemDetails_WithAllValuesSpecified ( )
410
+ {
411
+ return base . SerializingProblemDetails_WithAllValuesSpecified ( ) ;
412
+ }
413
+
414
+ [ Fact ( Skip = "https://github.com/dotnet/corefx/issues/38769" ) ]
415
+ public override Task SerializingValidationProblemDetails_WithExtensionData ( )
416
+ {
417
+ return base . SerializingValidationProblemDetails_WithExtensionData ( ) ;
418
+ }
419
+ }
420
+
421
+ public class ApiBehaviorTestNewtonsoftJson : ApiBehaviorTestBase < BasicWebSite . StartupWithoutEndpointRouting >
422
+ {
423
+ public ApiBehaviorTestNewtonsoftJson ( MvcTestFixture < BasicWebSite . StartupWithoutEndpointRouting > fixture )
424
+ : base ( fixture )
425
+ {
426
+ var factory = fixture . WithWebHostBuilder ( ConfigureWebHostBuilder ) ;
427
+ CustomInvalidModelStateClient = factory . CreateDefaultClient ( ) ;
428
+ }
429
+
430
+ private static void ConfigureWebHostBuilder ( IWebHostBuilder builder ) =>
431
+ builder . UseStartup < BasicWebSite . StartupWithCustomInvalidModelStateFactory > ( ) ;
432
+
433
+ public HttpClient CustomInvalidModelStateClient { get ; }
434
+
435
+ [ Fact ]
436
+ public async Task ActionsReturnBadRequest_UsesProblemDescriptionProviderAndApiConventionsToConfigureErrorResponse ( )
437
+ {
438
+ // Arrange
439
+ var contactModel = new Contact
440
+ {
441
+ Name = "Abc" ,
442
+ City = "Redmond" ,
443
+ State = "WA" ,
444
+ Zip = "Invalid" ,
445
+ } ;
446
+ var expected = new Dictionary < string , string [ ] >
447
+ {
448
+ { "Name" , new [ ] { "The field Name must be a string with a minimum length of 5 and a maximum length of 30." } } ,
449
+ { "Zip" , new [ ] { @"The field Zip must match the regular expression '\d{5}'." } }
450
+ } ;
451
+
452
+ // Act
453
+ var response = await CustomInvalidModelStateClient . PostAsJsonAsync ( "/contact/PostWithVnd" , contactModel ) ;
454
+
455
+ // Assert
456
+ await response . AssertStatusCodeAsync ( HttpStatusCode . BadRequest ) ;
457
+ Assert . Equal ( "application/vnd.error+json" , response . Content . Headers . ContentType . MediaType ) ;
458
+ var content = await response . Content . ReadAsStringAsync ( ) ;
459
+ var actual = JsonConvert . DeserializeObject < Dictionary < string , string [ ] > > ( content ) ;
460
+ Assert . Equal ( expected , actual ) ;
461
+ }
462
+ }
367
463
}
0 commit comments