|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.OpenApi.Models;
public partial class OpenApiDocumentServiceTests : OpenApiDocumentServiceTestBase
{
[Fact]
public async Task GetOpenApiParameters_GeneratesParameterLocationCorrectly()
{
// Arrange
var builder = CreateBuilder();
// Act
builder.MapGet("/api/todos/{id}", (int id) => { });
builder.MapGet("/api/todos", (int id) => { });
builder.MapGet("/api", ([FromHeader(Name = "X-Header")] string header) => { });
// Assert
await VerifyOpenApiDocument(builder, document =>
{
var pathParameter = Assert.Single(document.Paths["/api/todos/{id}"].Operations[OperationType.Get].Parameters);
Assert.Equal("id", pathParameter.Name);
Assert.Equal(ParameterLocation.Path, pathParameter.In);
var queryParameter = Assert.Single(document.Paths["/api/todos"].Operations[OperationType.Get].Parameters);
Assert.Equal("id", queryParameter.Name);
Assert.Equal(ParameterLocation.Query, queryParameter.In);
var headerParameter = Assert.Single(document.Paths["/api"].Operations[OperationType.Get].Parameters);
Assert.Equal("X-Header", headerParameter.Name);
Assert.Equal(ParameterLocation.Header, headerParameter.In);
});
}
#nullable enable
[Fact]
public async Task GetOpenApiParameters_RouteParametersAreAlwaysRequired()
{
// Arrange
var builder = CreateBuilder();
// Act
builder.MapGet("/api/todos/{id}", (int id) => { });
builder.MapGet("/api/todos/{guid}", (Guid? guid) => { });
builder.MapGet("/api/todos/{isCompleted}", (bool isCompleted = false) => { });
// Assert
await VerifyOpenApiDocument(builder, document =>
{
var pathParameter = Assert.Single(document.Paths["/api/todos/{id}"].Operations[OperationType.Get].Parameters);
Assert.Equal("id", pathParameter.Name);
Assert.True(pathParameter.Required);
var guidParameter = Assert.Single(document.Paths["/api/todos/{guid}"].Operations[OperationType.Get].Parameters);
Assert.Equal("guid", guidParameter.Name);
Assert.True(guidParameter.Required);
var isCompletedParameter = Assert.Single(document.Paths["/api/todos/{isCompleted}"].Operations[OperationType.Get].Parameters);
Assert.Equal("isCompleted", isCompletedParameter.Name);
Assert.True(isCompletedParameter.Required);
});
}
[Fact]
public async Task GetOpenApiParameters_SetsRequirednessForQueryParameters()
{
// Arrange
var builder = CreateBuilder();
// Act
builder.MapGet("/api/todos", (int id) => { });
builder.MapGet("/api/users", (int? id) => { });
builder.MapGet("/api/projects", (int id = 1) => { });
// Assert
await VerifyOpenApiDocument(builder, document =>
{
var queryParameter = Assert.Single(document.Paths["/api/todos"].Operations[OperationType.Get].Parameters);
Assert.Equal("id", queryParameter.Name);
Assert.True(queryParameter.Required);
var nullableQueryParameter = Assert.Single(document.Paths["/api/users"].Operations[OperationType.Get].Parameters);
Assert.Equal("id", nullableQueryParameter.Name);
Assert.False(nullableQueryParameter.Required);
var defaultQueryParameter = Assert.Single(document.Paths["/api/projects"].Operations[OperationType.Get].Parameters);
Assert.Equal("id", defaultQueryParameter.Name);
Assert.False(defaultQueryParameter.Required);
});
}
[Fact]
public async Task GetOpenApiParameters_SetsRequirednessForHeaderParameters()
{
// Arrange
var builder = CreateBuilder();
// Act
builder.MapGet("/api/todos", ([FromHeader(Name = "X-Header")] string header) => { });
builder.MapGet("/api/users", ([FromHeader(Name = "X-Header")] Guid? header) => { });
builder.MapGet("/api/projects", ([FromHeader(Name = "X-Header")] string header = "0000-0000-0000-0000") => { });
// Assert
await VerifyOpenApiDocument(builder, document =>
{
var headerParameter = Assert.Single(document.Paths["/api/todos"].Operations[OperationType.Get].Parameters);
Assert.Equal("X-Header", headerParameter.Name);
Assert.True(headerParameter.Required);
var nullableHeaderParameter = Assert.Single(document.Paths["/api/users"].Operations[OperationType.Get].Parameters);
Assert.Equal("X-Header", nullableHeaderParameter.Name);
Assert.False(nullableHeaderParameter.Required);
var defaultHeaderParameter = Assert.Single(document.Paths["/api/projects"].Operations[OperationType.Get].Parameters);
Assert.Equal("X-Header", defaultHeaderParameter.Name);
Assert.False(defaultHeaderParameter.Required);
});
}
#nullable restore
// Test coverage for https://github.com/dotnet/aspnetcore/issues/46746 requires disabling nullability
#nullable disable
[Fact]
public async Task GetOpenApiParameters_RouteParametersAreAlwaysRequired_NullabilityDisabled()
{
// Arrange
var builder = CreateBuilder();
// Act
builder.MapGet("/api/todos/{id}", (int id) => { });
builder.MapGet("/api/todos/{guid}", (Guid? guid) => { });
builder.MapGet("/api/todos/{isCompleted}", (bool isCompleted = false) => { });
// Assert
await VerifyOpenApiDocument(builder, document =>
{
var pathParameter = Assert.Single(document.Paths["/api/todos/{id}"].Operations[OperationType.Get].Parameters);
Assert.Equal("id", pathParameter.Name);
Assert.True(pathParameter.Required);
var guidParameter = Assert.Single(document.Paths["/api/todos/{guid}"].Operations[OperationType.Get].Parameters);
Assert.Equal("guid", guidParameter.Name);
Assert.True(guidParameter.Required);
var isCompletedParameter = Assert.Single(document.Paths["/api/todos/{isCompleted}"].Operations[OperationType.Get].Parameters);
Assert.Equal("isCompleted", isCompletedParameter.Name);
Assert.True(isCompletedParameter.Required);
});
}
#nullable restore
[Fact]
public async Task GetOpenApiRequestBody_SkipsRequestBodyParameters()
{
// Arrange
var builder = CreateBuilder();
// Act
builder.MapPost("/api/users", (IFormFile formFile, IFormFileCollection formFiles) => { });
builder.MapPost("/api/todos", (Todo todo) => { });
// Assert
await VerifyOpenApiDocument(builder, document =>
{
var usersOperation = document.Paths["/api/users"].Operations[OperationType.Post];
Assert.Null(usersOperation.Parameters);
var todosOperation = document.Paths["/api/todos"].Operations[OperationType.Post];
Assert.Null(todosOperation.Parameters);
});
}
[Fact]
public async Task GetOpenApiParameters_SkipsDisallowedHeaders()
{
// Arrange
var builder = CreateBuilder();
// Act
builder.MapGet("/api/accept", ([FromHeader(Name = "Accept")] string value) => { });
builder.MapGet("/api/accept-lower", ([FromHeader(Name = "accept")] string value) => { });
builder.MapGet("/api/authorization", ([FromHeader(Name = "Authorization")] string value) => { });
builder.MapGet("/api/authorization-lower", ([FromHeader(Name = "authorization")] string value) => { });
builder.MapGet("/api/content-type", ([FromHeader(Name = "Content-Type")] string value) => { });
builder.MapGet("/api/content-type-lower", ([FromHeader(Name = "content-type")] string value) => { });
// Assert
await VerifyOpenApiDocument(builder, document =>
{
Assert.Null(document.Paths["/api/accept"].Operations[OperationType.Get].Parameters);
Assert.Null(document.Paths["/api/accept-lower"].Operations[OperationType.Get].Parameters);
Assert.Null(document.Paths["/api/authorization"].Operations[OperationType.Get].Parameters);
Assert.Null(document.Paths["/api/authorization-lower"].Operations[OperationType.Get].Parameters);
Assert.Null(document.Paths["/api/content-type"].Operations[OperationType.Get].Parameters);
Assert.Null(document.Paths["/api/content-type-lower"].Operations[OperationType.Get].Parameters);
});
}
[Fact]
public async Task GetOpenApiParameters_ToleratesCustomBindingSource()
{
var action = CreateActionDescriptor(nameof(ActionWithCustomBinder));
await VerifyOpenApiDocument(action, document =>
{
var operation = document.Paths["/custom-binding"].Operations[OperationType.Get];
var parameter = Assert.Single(operation.Parameters);
Assert.Equal("model", parameter.Name);
Assert.Equal(ParameterLocation.Query, parameter.In);
});
}
[Route("/custom-binding")]
private void ActionWithCustomBinder([ModelBinder(BinderType = typeof(CustomBinder))] Todo model) { }
public class CustomBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
return Task.CompletedTask;
}
}
}
|