File: ValidationsGenerator\ValidationsGenerator.Recursion.cs
Web Access
Project: src\src\Http\Http.Extensions\test\Microsoft.AspNetCore.Http.Extensions.Tests.csproj (Microsoft.AspNetCore.Http.Extensions.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
namespace Microsoft.AspNetCore.Http.ValidationsGenerator.Tests;
 
public partial class ValidationsGeneratorTests : ValidationsGeneratorTestBase
{
    [Fact]
    public async Task CanValidateRecursiveTypes()
    {
        var source = """
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
 
var builder = WebApplication.CreateBuilder();
builder.Services.AddValidation(options =>
{
    options.MaxDepth = 8;
});
 
var app = builder.Build();
 
app.MapPost("/recursive-type", (RecursiveType model) => Results.Ok());
 
app.Run();
 
public class RecursiveType
{
    [Range(10, 100)]
    public int Value { get; set; }
    public RecursiveType? Next { get; set; }
}
""";
        await Verify(source, out var compilation);
 
        await VerifyEndpoint(compilation, "/recursive-type", async (endpoint, serviceProvider) =>
        {
            await ThrowsExceptionForDeeplyNestedType(endpoint);
            await ValidatesTypeWithLimitedNesting(endpoint);
 
            async Task ThrowsExceptionForDeeplyNestedType(Endpoint endpoint)
            {
                var httpContext = CreateHttpContextWithPayload("""
                {
                    "value": 1,
                    "next": {
                        "value": 2,
                        "next": {
                            "value": 3,
                            "next": {
                                "value": 4,
                                "next": {
                                    "value": 5,
                                    "next": {
                                        "value": 6,
                                        "next": {
                                            "value": 7,
                                            "next": {
                                                "value": 8,
                                                "next": {
                                                    "value": 9,
                                                    "next": {
                                                        "value": 10
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                """{
                    "value": 1,
                    "next": {
                        "value": 2,
                        "next": {
                            "value": 3,
                            "next": {
                                "value": 4,
                                "next": {
                                    "value": 5,
                                    "next": {
                                        "value": 6,
                                        "next": {
                                            "value": 7,
                                            "next": {
                                                "value": 8,
                                                "next": {
                                                    "value": 9,
                                                    "next": {
                                                        "value": 10
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                """, serviceProvider);
 
                var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await endpoint.RequestDelegate(httpContext));
            }
 
            async Task ValidatesTypeWithLimitedNesting(Endpoint endpoint)
            {
                var httpContext = CreateHttpContextWithPayload("""
                {
                    "value": 1,
                    "next": {
                        "value": 2,
                        "next": {
                            "value": 3,
                            "next": {
                                "value": 4,
                                "next": {
                                    "value": 5,
                                    "next": {
                                        "value": 6,
                                        "next": {
                                            "value": 7,
                                            "next": {
                                                "value": 8
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                """{
                    "value": 1,
                    "next": {
                        "value": 2,
                        "next": {
                            "value": 3,
                            "next": {
                                "value": 4,
                                "next": {
                                    "value": 5,
                                    "next": {
                                        "value": 6,
                                        "next": {
                                            "value": 7,
                                            "next": {
                                                "value": 8
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                """, serviceProvider);
 
                await endpoint.RequestDelegate(httpContext);
 
                var problemDetails = await AssertBadRequest(httpContext);
                Assert.Collection(problemDetails.Errors,
                    error =>
                    {
                        Assert.Equal("Value", error.Key);
                        Assert.Equal("The field Value must be between 10 and 100.", error.Value.Single());
                    },
                    error =>
                    {
                        Assert.Equal("Next.Value", error.Key);
                        Assert.Equal("The field Value must be between 10 and 100.", error.Value.Single());
                    },
                    error =>
                    {
                        Assert.Equal("Next.Next.Value", error.Key);
                        Assert.Equal("The field Value must be between 10 and 100.", error.Value.Single());
                    },
                    error =>
                    {
                        Assert.Equal("Next.Next.Next.Value", error.Key);
                        Assert.Equal("The field Value must be between 10 and 100.", error.Value.Single());
                    },
                    error =>
                    {
                        Assert.Equal("Next.Next.Next.Next.Value", error.Key);
                        Assert.Equal("The field Value must be between 10 and 100.", error.Value.Single());
                    },
                    error =>
                    {
                        Assert.Equal("Next.Next.Next.Next.Next.Value", error.Key);
                        Assert.Equal("The field Value must be between 10 and 100.", error.Value.Single());
                    },
                    error =>
                    {
                        Assert.Equal("Next.Next.Next.Next.Next.Next.Value", error.Key);
                        Assert.Equal("The field Value must be between 10 and 100.", error.Value.Single());
                    },
                    error =>
                    {
                        Assert.Equal("Next.Next.Next.Next.Next.Next.Next.Value", error.Key);
                        Assert.Equal("The field Value must be between 10 and 100.", error.Value.Single());
                    });
            }
        });
    }
}