File: RouteHandlers\DisallowReturningActionResultsFromMapMethodsTest.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 System.Globalization;
using Microsoft.AspNetCore.Analyzer.Testing;
 
namespace Microsoft.AspNetCore.Analyzers.RouteHandlers;
 
public partial class DisallowReturningActionResultsFromMapMethodsTest
{
    private TestDiagnosticAnalyzerRunner Runner { get; } = new(new RouteHandlerAnalyzer());
 
    [Fact]
    public async Task MinimalAction_ReturningIResult_Works()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
 
var webApp = WebApplication.Create();
webApp.MapGet(""/"", () => Results.Ok(""Hello""));
";
        // Act
        var diagnostics = await Runner.GetDiagnosticsAsync(source);
 
        // Assert
        Assert.Empty(diagnostics);
    }
 
    [Fact]
    public async Task MinimalAction_ReturningCustomIResult_Works()
    {
        // Arrange
        var source = @"
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
 
var webApp = WebApplication.Create();
webApp.MapGet(""/"", () => new CustomResult());
 
class CustomResult : IResult
{
    public Task ExecuteAsync(HttpContext context) => Task.CompletedTask;
}
";
        // Act
        var diagnostics = await Runner.GetDiagnosticsAsync(source);
 
        // Assert
        Assert.Empty(diagnostics);
    }
 
    [Fact]
    public async Task MinimalAction_ReturningIResultConditionallyWorks()
    {
        // Arrange
        var source = @"
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
 
var webApp = WebApplication.Create();
webApp.MapGet(""/"", (int id) =>
{
    if (id == 0)
    {
        return Results.NotFound();
    }
 
    return Results.Ok(""Here you go"");
});
 
";
        // Act
        var diagnostics = await Runner.GetDiagnosticsAsync(source);
 
        // Assert
        Assert.Empty(diagnostics);
    }
 
    [Fact]
    public async Task MinimalAction_ReturningTypeThatImplementsIResultAndActionResultDoesNotProduceDiagnostics()
    {
        // Arrange
        var source = @"
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
 
var webApp = WebApplication.Create();
webApp.MapGet(""/"", () => new CustomResult());
 
class CustomResult : IResult, IActionResult
{
    public Task ExecuteAsync(HttpContext context) => Task.CompletedTask;
 
    public Task ExecuteResultAsync(ActionContext context) => Task.CompletedTask;
}
";
        // Act
        var diagnostics = await Runner.GetDiagnosticsAsync(source);
 
        // Assert
        Assert.Empty(diagnostics);
    }
 
    [Fact]
    public async Task MinimalAction_ReturningActionResult_ProducesDiagnostics()
    {
        // Arrange
        var source = TestSource.Read(@"
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
 
var webApp = WebApplication.Create();
webApp.MapGet(""/"", () => /*MM*/new OkObjectResult(""cool story""));
");
        // Act
        var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
 
        // Assert
        var diagnostic = Assert.Single(diagnostics);
        Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, diagnostic.Descriptor);
        AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
        Assert.Equal("IActionResult instances should not be returned from a MapGet Delegate parameter. Consider returning an equivalent result from Microsoft.AspNetCore.Http.Results.", diagnostic.GetMessage(CultureInfo.InvariantCulture));
    }
 
    [Fact]
    public async Task MinimalAction_ReturningActionResultConditionally_ProducesDiagnostics()
    {
        // Arrange
        var source = TestSource.Read(@"
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
 
var webApp = WebApplication.Create();
webApp.MapGet(""/"", (int id) => /*MM*/id == 0 ? (ActionResult)new NotFoundResult() : new OkObjectResult(""cool story""));
");
        // Act
        var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
 
        // Assert
        var diagnostic = Assert.Single(diagnostics);
        Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, diagnostic.Descriptor);
        AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
        Assert.Equal("IActionResult instances should not be returned from a MapGet Delegate parameter. Consider returning an equivalent result from Microsoft.AspNetCore.Http.Results.", diagnostic.GetMessage(CultureInfo.InvariantCulture));
    }
 
    [Fact]
    public async Task MinimalAction_ReturningActionResultFromMethodReference_ProducesDiagnostics()
    {
        // Arrange
        var source = TestSource.Read(@"
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
 
var webApp = WebApplication.Create();
webApp.MapPost(""/"", OkObjectResultReturningMethod);
 
static object OkObjectResultReturningMethod()
{
    /*MM*/return new OkObjectResult(""ok"");
}
");
        // Act
        var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
 
        // Assert
        var diagnostic = Assert.Single(diagnostics);
        Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, diagnostic.Descriptor);
        AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
        Assert.Equal("IActionResult instances should not be returned from a MapPost Delegate parameter. Consider returning an equivalent result from Microsoft.AspNetCore.Http.Results.", diagnostic.GetMessage(CultureInfo.InvariantCulture));
    }
 
    [Fact]
    public async Task MinimalAction_ReturningActionResultOfTFromMethodReference_ProducesDiagnostics()
    {
        // Arrange
        var source = TestSource.Read(@"
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
 
var webApp = WebApplication.Create();
webApp.MapPost(""/"", ActionResultMethod);
 
static async Task<ActionResult<Person>> ActionResultMethod(int id)
{
    await Task.Yield();
    if (id == 0) /*MM*/return new NotFoundResult();
    return new Person(""test"");
}
 
public record Person(string Name);
");
        // Act
        var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
 
        // Assert
        var diagnostic = Assert.Single(diagnostics);
        Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, diagnostic.Descriptor);
        AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
        Assert.Equal("IActionResult instances should not be returned from a MapPost Delegate parameter. Consider returning an equivalent result from Microsoft.AspNetCore.Http.Results.", diagnostic.GetMessage(CultureInfo.InvariantCulture));
    }
 
    [Fact]
    public async Task MinimalAction_ReturningActionResultOfTFromANonLocalFunction_ProducesDiagnostics()
    {
        // Arrange
        var source = TestSource.Read(@"
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
 
var webApp = WebApplication.Create();
webApp.MapPost(""/"", /*MM*/new MyController().ActionResultMethod);
");
 
        var controllerSource = @"
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
 
public class MyController
{
    public async Task<ActionResult<Person>> ActionResultMethod(int id)
    {
        await Task.Yield();
        if (id == 0) return new NotFoundResult();
        return new Person(""test"");
    }
 
    public record Person(string Name);
}
";
        // Act
        var diagnostics = await Runner.GetDiagnosticsAsync(source.Source, controllerSource);
 
        // Assert
        var diagnostic = Assert.Single(diagnostics);
        Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, diagnostic.Descriptor);
        AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
        Assert.Equal("IActionResult instances should not be returned from a MapPost Delegate parameter. Consider returning an equivalent result from Microsoft.AspNetCore.Http.Results.", diagnostic.GetMessage(CultureInfo.InvariantCulture));
    }
 
    [Fact]
    public async Task MinimalAction_ReturningActionResultOfTDeclarationInDifferentFile_ProducesDiagnostics()
    {
        // Arrange
        var source = TestSource.Read(@"
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
 
var webApp = WebApplication.Create();
webApp.MapPost(""/"", /*MM*/MyController.ActionResultMethod);
");
        var controllerSource = @"
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
 
public static class MyController
{
    public static async Task<ActionResult<Person>> ActionResultMethod(int id)
    {
        await Task.Yield();
        if (id == 0) return new NotFoundResult();
        return new Person(""test"");
    }
 
    public record Person(string Name);
}
";
 
        // Act
        var diagnostics = await Runner.GetDiagnosticsAsync(source.Source, controllerSource);
 
        // Assert
        var diagnostic = Assert.Single(diagnostics);
        Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, diagnostic.Descriptor);
        AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
        Assert.Equal("IActionResult instances should not be returned from a MapPost Delegate parameter. Consider returning an equivalent result from Microsoft.AspNetCore.Http.Results.", diagnostic.GetMessage(CultureInfo.InvariantCulture));
    }
}