File: Validation\RuntimeValidatableParameterInfoResolverTests.cs
Web Access
Project: src\src\Http\Http.Abstractions\test\Microsoft.AspNetCore.Http.Abstractions.Tests.csproj (Microsoft.AspNetCore.Http.Abstractions.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.ComponentModel.DataAnnotations;
using System.IO.Pipelines;
using System.Reflection;
using System.Security.Claims;
 
namespace Microsoft.AspNetCore.Http.Validation.Tests;
 
public class RuntimeValidatableParameterInfoResolverTests
{
    private readonly RuntimeValidatableParameterInfoResolver _resolver = new();
 
    [Fact]
    public void TryGetValidatableTypeInfo_AlwaysReturnsFalse()
    {
        var result = _resolver.TryGetValidatableTypeInfo(typeof(string), out var validatableInfo);
 
        Assert.False(result);
        Assert.Null(validatableInfo);
    }
 
    [Fact]
    public void TryGetValidatableParameterInfo_WithNullName_ThrowsInvalidOperationException()
    {
        var parameterInfo = new NullNameParameterInfo();
 
        var exception = Assert.Throws<InvalidOperationException>(() =>
            _resolver.TryGetValidatableParameterInfo(parameterInfo, out _));
 
        Assert.Contains("without a name", exception.Message);
    }
 
    [Theory]
    [InlineData(typeof(string))]
    [InlineData(typeof(int))]
    [InlineData(typeof(bool))]
    [InlineData(typeof(DateTime))]
    [InlineData(typeof(Guid))]
    [InlineData(typeof(decimal))]
    [InlineData(typeof(DayOfWeek))] // Enum
    [InlineData(typeof(ClaimsPrincipal))]
    [InlineData(typeof(PipeReader))]
    [InlineData(typeof(DateTimeOffset))]
    [InlineData(typeof(TimeOnly))]
    [InlineData(typeof(DateOnly))]
    [InlineData(typeof(TimeSpan))]
    [InlineData(typeof(IFormFile))]
    [InlineData(typeof(IFormFileCollection))]
    [InlineData(typeof(IFormCollection))]
    [InlineData(typeof(HttpContext))]
    [InlineData(typeof(HttpRequest))]
    [InlineData(typeof(HttpResponse))]
    [InlineData(typeof(CancellationToken))]
    public void TryGetValidatableParameterInfo_WithSimpleTypesAndNoAttributes_ReturnsFalse(Type parameterType)
    {
        var parameterInfo = GetParameter(parameterType);
 
        var result = _resolver.TryGetValidatableParameterInfo(parameterInfo, out var validatableInfo);
 
        Assert.False(result);
        Assert.Null(validatableInfo);
    }
 
    [Fact]
    public void TryGetValidatableParameterInfo_WithClassTypeAndNoAttributes_ReturnsTrue()
    {
        var parameterInfo = GetParameter(typeof(TestClass));
 
        var result = _resolver.TryGetValidatableParameterInfo(parameterInfo, out var validatableInfo);
 
        Assert.True(result);
        Assert.NotNull(validatableInfo);
        var parameterValidatableInfo = Assert.IsType<RuntimeValidatableParameterInfoResolver.RuntimeValidatableParameterInfo>(validatableInfo);
        Assert.Equal("testParam", parameterValidatableInfo.Name);
        Assert.Equal("testParam", parameterValidatableInfo.DisplayName);
    }
 
    [Fact]
    public void TryGetValidatableParameterInfo_WithSimpleTypeAndAttributes_ReturnsTrue()
    {
        var parameterInfo = typeof(TestController)
            .GetMethod(nameof(TestController.MethodWithAttributedParam))!
            .GetParameters()[0];
 
        var result = _resolver.TryGetValidatableParameterInfo(parameterInfo, out var validatableInfo);
 
        Assert.True(result);
        Assert.NotNull(validatableInfo);
        var parameterValidatableInfo = Assert.IsType<RuntimeValidatableParameterInfoResolver.RuntimeValidatableParameterInfo>(validatableInfo);
        Assert.Equal("value", parameterValidatableInfo.Name);
        Assert.Equal("value", parameterValidatableInfo.DisplayName);
    }
 
    [Fact]
    public void TryGetValidatableParameterInfo_WithDisplayAttribute_UsesDisplayNameFromAttribute()
    {
        var parameterInfo = typeof(TestController)
            .GetMethod(nameof(TestController.MethodWithDisplayAttribute))!
            .GetParameters()[0];
 
        var result = _resolver.TryGetValidatableParameterInfo(parameterInfo, out var validatableInfo);
 
        Assert.True(result);
        Assert.NotNull(validatableInfo);
        var parameterValidatableInfo = Assert.IsType<RuntimeValidatableParameterInfoResolver.RuntimeValidatableParameterInfo>(validatableInfo);
        Assert.Equal("value", parameterValidatableInfo.Name);
        Assert.Equal("Custom Display Name", parameterValidatableInfo.DisplayName);
    }
 
    [Fact]
    public void TryGetValidatableParameterInfo_WithDisplayAttributeWithNullName_UsesParameterName()
    {
        var parameterInfo = typeof(TestController)
            .GetMethod(nameof(TestController.MethodWithNullDisplayName))!
            .GetParameters()[0];
 
        var result = _resolver.TryGetValidatableParameterInfo(parameterInfo, out var validatableInfo);
 
        Assert.True(result);
        Assert.NotNull(validatableInfo);
        var parameterValidatableInfo = Assert.IsType<RuntimeValidatableParameterInfoResolver.RuntimeValidatableParameterInfo>(validatableInfo);
        Assert.Equal("value", parameterValidatableInfo.Name);
        Assert.Equal("value", parameterValidatableInfo.DisplayName);
    }
 
    [Fact]
    public void TryGetValidatableParameterInfo_WithNullableValueType_ReturnsFalse()
    {
        var parameterInfo = GetParameter(typeof(int?));
 
        var result = _resolver.TryGetValidatableParameterInfo(parameterInfo, out var validatableInfo);
 
        Assert.False(result);
        Assert.Null(validatableInfo);
    }
 
    [Fact]
    public void TryGetValidatableParameterInfo_WithNullableReferenceType_ReturnsTrue()
    {
        var parameterInfo = GetNullableParameter(typeof(TestClass));
 
        var result = _resolver.TryGetValidatableParameterInfo(parameterInfo, out var validatableInfo);
 
        Assert.True(result);
        Assert.NotNull(validatableInfo);
        var parameterValidatableInfo = Assert.IsType<RuntimeValidatableParameterInfoResolver.RuntimeValidatableParameterInfo>(validatableInfo);
        Assert.Equal("testParam", parameterValidatableInfo.Name);
        Assert.Equal("testParam", parameterValidatableInfo.DisplayName);
    }
 
    private static ParameterInfo GetParameter(Type parameterType)
    {
        return typeof(TestParameterHolder)
            .GetMethod(nameof(TestParameterHolder.Method))!
            .MakeGenericMethod(parameterType)
            .GetParameters()[0];
    }
 
    private static ParameterInfo GetNullableParameter(Type parameterType)
    {
        return typeof(TestParameterHolder)
            .GetMethod(nameof(TestParameterHolder.MethodWithNullable))!
            .MakeGenericMethod(parameterType)
            .GetParameters()[0];
    }
 
    private class TestClass { }
 
    private class TestParameterHolder
    {
        public void Method<T>(T testParam) { }
        public void MethodWithNullable<T>(T? testParam) { }
    }
 
    private class TestController
    {
        public void MethodWithAttributedParam([Required] string value) { }
 
        public void MethodWithDisplayAttribute([Display(Name = "Custom Display Name")][Required] string value) { }
 
        public void MethodWithNullDisplayName([Display(Name = null)][Required] string value) { }
    }
 
    private class NullNameParameterInfo : ParameterInfo
    {
        public override string? Name => null;
        public override Type ParameterType => typeof(string);
    }
}