File: RouteHandlers\DetectAmbiguousMappedRoutesTest.cs
Web Access
Project: src\src\Framework\AspNetCoreAnalyzers\test\Microsoft.AspNetCore.App.Analyzers.Test.csproj (Microsoft.AspNetCore.App.Analyzers.Test)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.CodeAnalysis.Testing;
using VerifyCS = Microsoft.AspNetCore.Analyzers.Verifiers.CSharpAnalyzerVerifier<Microsoft.AspNetCore.Analyzers.RouteHandlers.RouteHandlerAnalyzer>;
 
namespace Microsoft.AspNetCore.Analyzers.RouteHandlers;
 
public partial class DetectAmbiguousMappedRoutesTest
{
    [Fact]
    public async Task DuplicateRoutes_SameHttpMethod_HasDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
app.MapGet({|#0:""/""|}, () => Hello());
app.MapGet({|#1:""/""|}, () => Hello());
void Hello() { }
";
 
        var expectedDiagnostics = new[]
        {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }
 
    [Fact]
    public async Task DuplicateRoutes_SameHttpMethod_HasRequestDelegate_HasDiagnostics()
    {
        // Arrange
        var source = @"
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
var app = WebApplication.Create();
app.MapGet({|#0:""/""|}, () => Hello());
app.MapGet({|#1:""/""|}, (HttpContext context) => Task.CompletedTask);
void Hello() { }
";
 
        var expectedDiagnostics = new[]
        {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }
 
    [Fact]
    public async Task DuplicateRoutes_SameHttpMethod_InMethod_HasDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
var app = WebApplication.Create();
void RegisterEndpoints(IEndpointRouteBuilder builder)
{
    builder.MapGet({|#0:""/""|}, () => Hello());
    builder.MapGet({|#1:""/""|}, () => Hello());
}
 
RegisterEndpoints(app);
 
void Hello() { }
";
 
        var expectedDiagnostics = new[]
        {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }
 
    [Fact]
    public async Task DuplicateRoutes_TernaryStatement_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
_ = (true)
    ? app.MapGet(""/"", () => Hello())
    : app.MapGet(""/"", () => Hello());
void Hello() { }
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateRoutes_SwitchStatement_NoDiagnostics()
    {
        // Arrange
        var source = @"
using System;
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
switch (Random.Shared.Next())
{
    case 0:
        app.MapGet(""/"", () => Hello());
        return;
    case 1:
        app.MapGet(""/"", () => Hello());
        return;
}
void Hello() { }
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateRoutes_InsideSwitchStatement_HasDiagnostics()
    {
        // Arrange
        var source = @"
using System;
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
switch (Random.Shared.Next())
{
    case 0:
        app.MapGet({|#0:""/""|}, () => Hello());
        app.MapGet({|#1:""/""|}, () => Hello());
        return;
 
}
void Hello() { }
";
 
        var expectedDiagnostics = new[]
        {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }
 
    [Fact]
    public async Task DuplicateRoutes_SwitchExpression_NoDiagnostics()
    {
        // Arrange
        var source = @"
using System;
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
_ = Random.Shared.Next() switch
{
    0 => app.MapGet(""/"", () => Hello()),
    1 => app.MapGet(""/"", () => Hello()),
    _ => throw new Exception()
};
void Hello() { }
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateRoutes_NullCoalescing_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
_ = app.MapGet(""/"", () => Hello()) ?? app.MapGet(""/"", () => Hello());
void Hello() { }
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateRoutes_NullCoalescingAssignment_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
var ep = app.MapPost(""/"", () => Hello());
ep ??= app.MapPost(""/"", () => Hello());
void Hello() { }
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateRoutes_DifferentMethods_HasDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
app.MapGet(""/"", () => Hello());
app.MapPost(""/"", () => Hello());
void Hello() { }
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateMapGetRoutes_InsideConditional_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
if (true)
{
    app.MapGet(""/"", () => Hello());
}
else
{
    app.MapGet(""/"", () => Hello());
}
void Hello() { }
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateMapGetRoutes_DuplicatesInsideConditional_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
if (true)
{
    app.MapGet({|#0:""/""|}, () => Hello());
    app.MapGet({|#1:""/""|}, () => Hello());
}
void Hello() { }
";
 
        var expectedDiagnostics = new[]
        {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }
 
    [Fact]
    public async Task DuplicateRoutes_UnknownUsageOfEndConventionBuilderExtension_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
app.MapGet(""/"", () => Hello()).DoSomething();
app.MapGet(""/"", () => Hello());
void Hello() { }
 
internal static class Extensions
{
    public static void DoSomething(this IEndpointConventionBuilder builder)
    {
        builder.WithMetadata(new object());
    }
}
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateRoutes_UnknownUsageOfEndConventionBuilder_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
Extensions.DoSomething(app.MapGet(""/"", () => Hello()));
app.MapGet(""/"", () => Hello());
void Hello() { }
 
internal static class Extensions
{
    public static void DoSomething(IEndpointConventionBuilder builder)
    {
        builder.WithMetadata(new object());
    }
}
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateRoutes_AddMethod_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
app.MapGet(""/"", () => Hello()).Add(b => {});
app.MapGet(""/"", () => Hello());
void Hello() { }
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateRoutes_AssignedToVariable_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
_ = app.MapGet(""/"", () => Hello());
app.MapGet(""/"", () => Hello());
void Hello() { }
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateRoutes_MultipleGroups_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
var group1 = app.MapGroup(""/group1"");
var group2 = app.MapGroup(""/group2"");
group1.MapGet(""/"", () => Hello());
group2.MapGet(""/"", () => Hello());
void Hello() { }
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateRoutes_MultipleGroups_DirectInvocation_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
app.MapGroup(""/group1"").MapGet(""/"", () => { });
app.MapGroup(""/group2"").MapGet(""/"", () => { });
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateRoutes_SingleGroup_DifferentBuilderVariable_DirectInvocation_NoDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app1 = WebApplication.Create();
var app2 = app1;
app1.MapGroup(""/group1"").MapGet(""/"", () => { });
app2.MapGroup(""/group1"").MapGet(""/"", () => { });
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
 
    [Fact]
    public async Task DuplicateRoutes_SingleGroup_DirectInvocation_HasDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
app.MapGroup(""/group1"").MapGet({|#0:""/""|}, () => { });
app.MapGroup(""/group1"").MapGet({|#1:""/""|}, () => { });
";
 
        // Act & Assert
        var expectedDiagnostics = new[]
        {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };
 
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }
 
    [Fact]
    public async Task DuplicateRoutes_SingleGroup_DirectInvocation_InMethod_HasDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
var app = WebApplication.Create();
void RegisterEndpoints(IEndpointRouteBuilder builder)
{
    builder.MapGroup(""/group1"").MapGet({|#0:""/""|}, () => { });
    builder.MapGroup(""/group1"").MapGet({|#1:""/""|}, () => { });
}
 
RegisterEndpoints(app);
 
void Hello() { }
";
 
        var expectedDiagnostics = new[]
        {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }
 
    [Fact]
    public async Task DuplicateRoutes_SingleGroup_RoutePattern_DirectInvocation_HasDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing.Patterns;
var routePattern = RoutePatternFactory.Parse(""/group1"");
var app = WebApplication.Create();
app.MapGroup(routePattern).MapGet({|#0:""/""|}, () => { });
app.MapGroup(routePattern).MapGet({|#1:""/""|}, () => { });
";
 
        // Act & Assert
        var expectedDiagnostics = new[]
        {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };
 
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }
 
    [Fact]
    public async Task DuplicateRoutes_EndpointsOnGroup_HasDiagnostics()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
var app = WebApplication.Create();
var group1 = app.MapGroup(""/group1"");
group1.MapGet({|#0:""/""|}, () => Hello());
group1.MapGet({|#1:""/""|}, () => Hello());
var group2 = app.MapGroup(""/group2"");
group2.MapGet(""/"", () => Hello());
void Hello() { }
";
 
        // Act & Assert
        var expectedDiagnostics = new[]
        {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };
 
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }
 
    [Theory]
    [InlineData(@"RequireAuthorization()")]
    [InlineData(@"AllowAnonymous()")]
    [InlineData(@"Produces(statusCode:420)")]
    [InlineData(@"WithDisplayName(""test!"")")]
    [InlineData(@"WithName(""test!"")")]
    [InlineData(@"RequireCors(""test!"")")]
    [InlineData(@"CacheOutput(""test!"")")]
    [InlineData(@"DisableRateLimiting()")]
    [InlineData(@"RequireAuthorization().DisableRateLimiting()")]
    public async Task DuplicateRoutes_AllowedBuilderExtensionMethods_HasDiagnostics(string method)
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
var app = WebApplication.Create();
app.MapGet({|#0:""/""|}, () => Hello())." + method + @";
app.MapGet({|#1:""/""|}, () => Hello());
void Hello() { }
";
 
        var expectedDiagnostics = new[]
        {
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(0),
            new DiagnosticResult(DiagnosticDescriptors.AmbiguousRouteHandlerRoute).WithArguments("/").WithLocation(1)
        };
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source, expectedDiagnostics);
    }
 
    [Theory]
    [InlineData(@"RequireHost(""test!"")")]
    [InlineData(@"RequireHost(""test!"").DisableRateLimiting()")]
    [InlineData(@"RequireAuthorization().RequireHost(""test!"")")]
    public async Task DuplicateRoutes_UnknownBuilderExtensionMethods_NoDiagnostics(string method)
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
var app = WebApplication.Create();
app.MapGet({|#0:""/""|}, () => Hello())." + method + @";
app.MapGet({|#1:""/""|}, () => Hello());
void Hello() { }
";
 
        // Act & Assert
        await VerifyCS.VerifyAnalyzerAsync(source);
    }
}