@@ -101,6 +101,111 @@ public async Task TestOnActionExecutionAsync()
101101 Assert . Contains ( validationFailuresValues [ 2 ] . First ( ) , badRequestObjectResultValidationProblemDetails . Errors [ nameof ( TestModel . Parameter3 ) ] [ 0 ] ) ;
102102 }
103103
104+ [ Fact ]
105+ public async Task OnActionExecutionAsync_WithInstanceTypeDifferentThanParameterType_UsesInstanceTypeValidator ( )
106+ {
107+ // Arrange
108+ var httpContext = Substitute . For < HttpContext > ( ) ;
109+ var modelStateDictionary = new ModelStateDictionary ( ) ;
110+
111+ var validationFailures = new Dictionary < string , string [ ] >
112+ {
113+ { nameof ( CreatePersonRequest . Name ) , [ $ "'{ nameof ( CreatePersonRequest . Name ) } ' must be equal to 'John Doe'."] }
114+ } ;
115+ var validationProblemDetails = new ValidationProblemDetails ( validationFailures ) ;
116+
117+ var problemDetailsFactory = Substitute . For < ProblemDetailsFactory > ( ) ;
118+ problemDetailsFactory . CreateValidationProblemDetails ( httpContext , modelStateDictionary ) . Returns ( validationProblemDetails ) ;
119+
120+ var serviceProvider = Substitute . For < IServiceProvider > ( ) ;
121+ serviceProvider . GetService ( typeof ( IValidator < > ) . MakeGenericType ( typeof ( CreateAnimalRequest ) ) ) . Returns ( new CreateAnimalRequestValidator ( ) ) ;
122+ serviceProvider . GetService ( typeof ( IValidator < > ) . MakeGenericType ( typeof ( CreatePersonRequest ) ) ) . Returns ( new CreatePersonRequestValidator ( ) ) ;
123+ serviceProvider . GetService ( typeof ( ProblemDetailsFactory ) ) . Returns ( problemDetailsFactory ) ;
124+
125+ httpContext . RequestServices . Returns ( serviceProvider ) ;
126+
127+ var controller = Substitute . For < AnimalsController > ( ) ;
128+ var controllerActionDescriptor = new ControllerActionDescriptor
129+ {
130+ Parameters =
131+ [
132+ new ( )
133+ {
134+ Name = "request" ,
135+ ParameterType = typeof ( CreateAnimalRequest ) ,
136+ BindingInfo = new BindingInfo { BindingSource = BindingSource . Body }
137+ }
138+ ]
139+ } ;
140+ var actionContext = Substitute . For < ActionContext > ( httpContext , Substitute . For < RouteData > ( ) , controllerActionDescriptor , modelStateDictionary ) ;
141+
142+ var actionArguments = new Dictionary < string , object ? >
143+ {
144+ {
145+ "request" , new CreatePersonRequest
146+ {
147+ Name = "Jane Doe"
148+ }
149+ } ,
150+ } ;
151+ var actionExecutingContext = Substitute . For < ActionExecutingContext > ( actionContext , new List < IFilterMetadata > ( ) , actionArguments , new object ( ) ) ;
152+ actionExecutingContext . Controller . Returns ( controller ) ;
153+ actionExecutingContext . ActionDescriptor = controllerActionDescriptor ;
154+ actionExecutingContext . ActionArguments . Returns ( actionArguments ) ;
155+
156+ var actionExecutedContext = Substitute . For < ActionExecutedContext > ( actionContext , new List < IFilterMetadata > ( ) , new object ( ) ) ;
157+
158+ var fluentValidationAutoValidationResultFactory = Substitute . For < IFluentValidationAutoValidationResultFactory > ( ) ;
159+ fluentValidationAutoValidationResultFactory . CreateActionResult ( actionExecutingContext , validationProblemDetails ) . Returns ( new BadRequestObjectResult ( validationProblemDetails ) ) ;
160+
161+ var autoValidationMvcConfiguration = Substitute . For < IOptions < AutoValidationMvcConfiguration > > ( ) ;
162+ autoValidationMvcConfiguration . Value . Returns ( new AutoValidationMvcConfiguration ( ) ) ;
163+
164+ var actionFilter = new FluentValidationAutoValidationActionFilter ( fluentValidationAutoValidationResultFactory , autoValidationMvcConfiguration ) ;
165+
166+ // Act
167+ await actionFilter . OnActionExecutionAsync ( actionExecutingContext , ( ) => Task . FromResult ( actionExecutedContext ) ) ;
168+
169+ // Assert
170+ var modelStateDictionaryValues = modelStateDictionary . Values . ToList ( ) ;
171+ var validationFailuresValues = validationFailures . Values . ToList ( ) ;
172+ var badRequestObjectResult = ( BadRequestObjectResult ) actionExecutingContext . Result ! ;
173+ var badRequestObjectResultValidationProblemDetails = ( ValidationProblemDetails ) badRequestObjectResult . Value ! ;
174+
175+ Assert . Contains ( validationFailuresValues [ 0 ] . First ( ) , modelStateDictionaryValues [ 0 ] . Errors . Select ( error => error . ErrorMessage ) ) ;
176+ Assert . Contains ( validationFailuresValues [ 0 ] . First ( ) , badRequestObjectResultValidationProblemDetails . Errors [ nameof ( CreatePersonRequest . Name ) ] [ 0 ] ) ;
177+ }
178+
179+ public class AnimalsController : ControllerBase
180+ {
181+ }
182+
183+ public class CreateAnimalRequest
184+ {
185+ }
186+
187+ public class CreatePersonRequest : CreateAnimalRequest
188+ {
189+ public required string Name { get ; set ; }
190+ }
191+
192+ public class CreateAnimalRequestValidator : AbstractValidator < CreateAnimalRequest >
193+ {
194+ public CreateAnimalRequestValidator ( )
195+ {
196+ }
197+ }
198+
199+ public class CreatePersonRequestValidator : AbstractValidator < CreatePersonRequest >
200+ {
201+ public CreatePersonRequestValidator ( )
202+ {
203+ this . Include ( new CreateAnimalRequestValidator ( ) ) ;
204+
205+ this . RuleFor ( x => x . Name ) . Equal ( "John Doe" ) ;
206+ }
207+ }
208+
104209 public class TestController : ControllerBase
105210 {
106211 }
0 commit comments