aspnetcore/test/Mvc.Analyzers.Test/ApiConventionAnalyzerTest.cs

298 lines
11 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Analyzer.Testing;
using Microsoft.AspNetCore.Mvc.Analyzers.Infrastructure;
using Microsoft.CodeAnalysis;
using Xunit;
using static Microsoft.AspNetCore.Mvc.Analyzers.ApiConventionAnalyzer;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
public class ApiConventionAnalyzerTest
{
[Fact]
public async Task GetDefaultStatusCode_ReturnsValueDefinedUsingStatusCodeConstants()
{
// Arrange
var compilation = await GetCompilation();
var attribute = compilation.GetTypeByMetadataName(typeof(TestActionResultUsingStatusCodesConstants).FullName).GetAttributes()[0];
// Act
var actual = ApiConventionAnalyzer.GetDefaultStatusCode(attribute);
// Assert
Assert.Equal(412, actual);
}
[Fact]
public async Task GetDefaultStatusCode_ReturnsValueDefinedUsingHttpStatusCast()
{
// Arrange
var compilation = await GetCompilation();
var attribute = compilation.GetTypeByMetadataName(typeof(TestActionResultUsingHttpStatusCodeCast).FullName).GetAttributes()[0];
// Act
var actual = ApiConventionAnalyzer.GetDefaultStatusCode(attribute);
// Assert
Assert.Equal(302, actual);
}
[Fact]
public async Task InspectReturnExpression_ReturnsNull_ForReturnTypeIf200StatusCodeIsDeclared()
{
// Arrange
var compilation = await GetCompilation();
var returnType = compilation.GetTypeByMetadataName(typeof(ApiConventionAnalyzerBaseModel).FullName);
var context = GetContext(compilation, new[] { 200 });
// Act
var diagnostic = ApiConventionAnalyzer.InspectReturnExpression(context, returnType, Location.None);
// Assert
Assert.Null(diagnostic);
}
[Fact]
public async Task InspectReturnExpression_ReturnsNull_ForReturnTypeIf201StatusCodeIsDeclared()
{
// Arrange
var compilation = await GetCompilation();
var returnType = compilation.GetTypeByMetadataName(typeof(ApiConventionAnalyzerBaseModel).FullName);
var context = GetContext(compilation, new[] { 201 });
// Act
var diagnostic = ApiConventionAnalyzer.InspectReturnExpression(context, returnType, Location.None);
// Assert
Assert.Null(diagnostic);
}
[Fact]
public async Task InspectReturnExpression_ReturnsNull_ForDerivedReturnTypeIf200StatusCodeIsDeclared()
{
// Arrange
var compilation = await GetCompilation();
var declaredReturnType = compilation.GetTypeByMetadataName(typeof(ApiConventionAnalyzerBaseModel).FullName);
var context = GetContext(compilation, new[] { 201 });
var actualReturnType = compilation.GetTypeByMetadataName(typeof(ApiConventionAnalyzerDerivedModel).FullName);
// Act
var diagnostic = ApiConventionAnalyzer.InspectReturnExpression(context, actualReturnType, Location.None);
// Assert
Assert.Null(diagnostic);
}
[Fact]
public async Task InspectReturnExpression_ReturnsDiagnostic_If200IsNotDocumented()
{
// Arrange
var compilation = await GetCompilation();
var context = GetContext(compilation, new[] { 404 });
var actualReturnType = compilation.GetTypeByMetadataName(typeof(ApiConventionAnalyzerDerivedModel).FullName);
// Act
var diagnostic = ApiConventionAnalyzer.InspectReturnExpression(context, actualReturnType, Location.None);
// Assert
Assert.NotNull(diagnostic);
Assert.Same(DiagnosticDescriptors.MVC1005_ActionReturnsUndocumentedSuccessResult, diagnostic.Descriptor);
}
[Fact]
public async Task InspectReturnExpression_ReturnsDiagnostic_IfReturnTypeIsActionResultReturningUndocumentedStatusCode()
{
// Arrange
var compilation = await GetCompilation();
var declaredReturnType = compilation.GetTypeByMetadataName(typeof(ApiConventionAnalyzerBaseModel).FullName);
var context = GetContext(compilation, new[] { 200, 404 });
var actualReturnType = compilation.GetTypeByMetadataName(typeof(BadRequestObjectResult).FullName);
// Act
var diagnostic = ApiConventionAnalyzer.InspectReturnExpression(context, actualReturnType, Location.None);
// Assert
Assert.NotNull(diagnostic);
Assert.Same(DiagnosticDescriptors.MVC1004_ActionReturnsUndocumentedStatusCode, diagnostic.Descriptor);
}
[Fact]
public async Task InspectReturnExpression_DoesNotReturnDiagnostic_IfReturnTypeDoesNotHaveStatusCodeAttribute()
{
// Arrange
var compilation = await GetCompilation();
var context = GetContext(compilation, new[] { 200, 404 });
var actualReturnType = compilation.GetTypeByMetadataName(typeof(EmptyResult).FullName);
// Act
var diagnostic = ApiConventionAnalyzer.InspectReturnExpression(context, actualReturnType, Location.None);
// Assert
Assert.Null(diagnostic);
}
[Fact]
public async Task InspectReturnExpression_DoesNotReturnDiagnostic_IfDeclaredAndActualReturnTypeAreIActionResultInstances()
{
// Arrange
var compilation = await GetCompilation();
var declaredReturnType = compilation.GetTypeByMetadataName(typeof(IActionResult).FullName);
var context = GetContext(compilation, new[] { 404 });
var actualReturnType = compilation.GetTypeByMetadataName(typeof(EmptyResult).FullName);
// Act
var diagnostic = ApiConventionAnalyzer.InspectReturnExpression(context, actualReturnType, Location.None);
// Assert
Assert.Null(diagnostic);
}
[Fact]
public async Task InspectReturnExpression_DoesNotReturnDiagnostic_IfDeclaredAndActualReturnTypeAreIActionResult()
{
// Arrange
var compilation = await GetCompilation();
var context = GetContext(compilation, new[] { 404 });
var actualReturnType = compilation.GetTypeByMetadataName(typeof(IActionResult).FullName);
// Act
var diagnostic = ApiConventionAnalyzer.InspectReturnExpression(context, actualReturnType, Location.None);
// Assert
Assert.Null(diagnostic);
}
[Fact]
public async Task ShouldEvaluateMethod_ReturnsFalse_IfMethodReturnTypeIsInvalid()
{
// Arrange
var source = @"
using Microsoft.AspNetCore.Mvc;
namespace TestNamespace
{
[ApiController]
public class TestController : ControllerBase
{
public DoesNotExist Get(int id)
{
if (id == 0)
{
return NotFound();
}
return new DoesNotExist(id);
}
}
}";
var project = DiagnosticProject.Create(GetType().Assembly, new[] { source });
var compilation = await project.GetCompilationAsync();
var symbolCache = new ApiControllerSymbolCache(compilation);
var method = (IMethodSymbol)compilation.GetTypeByMetadataName("TestNamespace.TestController").GetMembers("Get").First();
// Act
var result = ApiConventionAnalyzer.ShouldEvaluateMethod(symbolCache, method);
// Assert
Assert.False(result);
}
[Fact]
public async Task ShouldEvaluateMethod_ReturnsFalse_IfContainingTypeIsNotController()
{
// Arrange
var compilation = await GetCompilation();
var symbolCache = new ApiControllerSymbolCache(compilation);
var type = compilation.GetTypeByMetadataName(typeof(ApiConventionAnalyzerTest_IndexModel).FullName);
var method = (IMethodSymbol)type.GetMembers(nameof(ApiConventionAnalyzerTest_IndexModel.OnGet)).First();
// Act
var result = ApiConventionAnalyzer.ShouldEvaluateMethod(symbolCache, method);
// Assert
Assert.False(result);
}
[Fact]
public async Task ShouldEvaluateMethod_ReturnsFalse_IfContainingTypeIsNotApiController()
{
// Arrange
var compilation = await GetCompilation();
var symbolCache = new ApiControllerSymbolCache(compilation);
var type = compilation.GetTypeByMetadataName(typeof(ApiConventionAnalyzerTest_NotApiController).FullName);
var method = (IMethodSymbol)type.GetMembers(nameof(ApiConventionAnalyzerTest_NotApiController.Index)).First();
// Act
var result = ApiConventionAnalyzer.ShouldEvaluateMethod(symbolCache, method);
// Assert
Assert.False(result);
}
[Fact]
public async Task ShouldEvaluateMethod_ReturnsFalse_IfContainingTypeIsNotAction()
{
// Arrange
var compilation = await GetCompilation();
var symbolCache = new ApiControllerSymbolCache(compilation);
var type = compilation.GetTypeByMetadataName(typeof(ApiConventionAnalyzerTest_NotAction).FullName);
var method = (IMethodSymbol)type.GetMembers(nameof(ApiConventionAnalyzerTest_NotAction.Index)).First();
// Act
var result = ApiConventionAnalyzer.ShouldEvaluateMethod(symbolCache, method);
// Assert
Assert.False(result);
}
[Fact]
public async Task ShouldEvaluateMethod_ReturnsTrue_ForValidActionMethods()
{
// Arrange
var compilation = await GetCompilation();
var symbolCache = new ApiControllerSymbolCache(compilation);
var type = compilation.GetTypeByMetadataName(typeof(ApiConventionAnalyzerTest_Valid).FullName);
var method = (IMethodSymbol)type.GetMembers(nameof(ApiConventionAnalyzerTest_Valid.Index)).First();
// Act
var result = ApiConventionAnalyzer.ShouldEvaluateMethod(symbolCache, method);
// Assert
Assert.True(result);
}
private static ApiConventionContext GetContext(Compilation compilation, int[] expectedStatusCodes)
{
var symbolCache = new ApiControllerSymbolCache(compilation);
var context = new ApiConventionContext(
symbolCache,
default,
expectedStatusCodes.Select(s => new ApiResponseMetadata(s, null, null)).ToArray(),
new HashSet<int>());
return context;
}
private Task<Compilation> GetCompilation()
{
var testSource = MvcTestSource.Read(GetType().Name, "ApiConventionAnalyzerTestFile");
var project = DiagnosticProject.Create(GetType().Assembly, new[] { testSource.Source });
return project.GetCompilationAsync();
}
}
}