File: Services\OpenApiDocumentService\OpenApiDocumentServiceTests.Responses.cs
Web Access
Project: src\src\OpenApi\test\Microsoft.AspNetCore.OpenApi.Tests\Microsoft.AspNetCore.OpenApi.Tests.csproj (Microsoft.AspNetCore.OpenApi.Tests)
// 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.OpenApi.Models;
 
public partial class OpenApiDocumentServiceTests : OpenApiDocumentServiceTestBase
{
    [Fact]
    public async Task GetOpenApiResponse_SupportsMultipleResponseViaAttributes()
    {
        // Arrange
        var builder = CreateBuilder();
 
        // Act
        builder.MapGet("/api/todos",
            [ProducesResponseType(typeof(TimeSpan), StatusCodes.Status201Created)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        () =>
            { });
 
        // Assert
        await VerifyOpenApiDocument(builder, document =>
        {
            var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values);
            Assert.Collection(operation.Responses.OrderBy(r => r.Key),
                response =>
                {
                    Assert.Equal("201", response.Key);
                    Assert.Equal("Created", response.Value.Description);
                },
                response =>
                {
                    Assert.Equal("400", response.Key);
                    Assert.Equal("Bad Request", response.Value.Description);
                });
        });
    }
 
    [Fact]
    public async Task GetOpenApiResponse_SupportsProblemDetailsResponse()
    {
        // Arrange
        var builder = CreateBuilder();
 
        // Act
        builder.MapGet("/api/todos", () => { })
            .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status400BadRequest, typeof(ProblemDetails), ["application/json+problem"]));
 
        // Assert
        await VerifyOpenApiDocument(builder, document =>
        {
            var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values);
            var response = Assert.Single(operation.Responses);
            Assert.Equal("400", response.Key);
            Assert.Equal("Bad Request", response.Value.Description);
            Assert.Equal("application/json+problem", response.Value.Content.Keys.Single());
        });
    }
 
    [Fact]
    public async Task GetOpenApiResponse_SupportsMultipleResponsesForStatusCode()
    {
        // Arrange
        var builder = CreateBuilder();
 
        // Act
        builder.MapGet("/api/todos", () => { })
            // Simulates metadata provided by IEndpointMetadataProvider
            .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK))
            // Simulates metadata added via `Produces` call
            .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, typeof(string), ["text/plain"]));
 
        // Assert
        await VerifyOpenApiDocument(builder, document =>
        {
            var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values);
            var response = Assert.Single(operation.Responses);
            Assert.Equal("200", response.Key);
            Assert.Equal("OK", response.Value.Description);
            var content = Assert.Single(response.Value.Content);
            Assert.Equal("text/plain", content.Key);
        });
    }
 
    [Fact]
    public async Task GetOpenApiResponse_SupportsMultipleResponseTypesWithTypeForStatusCode()
    {
        // Arrange
        var builder = CreateBuilder();
 
        // Act
        builder.MapGet("/api/todos", () => { })
            // Simulates metadata provided by IEndpointMetadataProvider
            .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, typeof(Todo), ["application/json"]))
            // Simulates metadata added via `Produces` call
            .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, typeof(TodoWithDueDate), ["application/json"]));
 
        // Assert
        await VerifyOpenApiDocument(builder, document =>
        {
            var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values);
            var response = Assert.Single(operation.Responses);
            Assert.Equal("200", response.Key);
            Assert.Equal("OK", response.Value.Description);
            var content = Assert.Single(response.Value.Content);
            Assert.Equal("application/json", content.Key);
            // Todo: Check that this generates a schema using `oneOf`.
        });
    }
 
    [Fact]
    public async Task GetOpenApiResponse_SupportsMultipleResponseTypesWitDifferentContentTypes()
    {
        // Arrange
        var builder = CreateBuilder();
 
        // Act
        builder.MapGet("/api/todos", () => { })
            .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, typeof(Todo), ["application/json", "application/xml"]));
 
        // Assert
        await VerifyOpenApiDocument(builder, document =>
        {
            var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values);
            var response = Assert.Single(operation.Responses);
            Assert.Equal("200", response.Key);
            Assert.Equal("OK", response.Value.Description);
            Assert.Collection(response.Value.Content.OrderBy(c => c.Key),
                content =>
                {
                    Assert.Equal("application/json", content.Key);
                },
                content =>
                {
                    Assert.Equal("application/xml", content.Key);
                });
        });
    }
 
    [Fact]
    public async Task GetOpenApiResponse_SupportsDifferentResponseTypesWitDifferentContentTypes()
    {
        // Arrange
        var builder = CreateBuilder();
 
        // Act
        builder.MapGet("/api/todos", () => { })
            .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, typeof(TodoWithDueDate), ["application/json"]))
            .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, typeof(Todo), ["application/xml"]));
 
        // Assert
        await VerifyOpenApiDocument(builder, document =>
        {
            var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values);
            var response = Assert.Single(operation.Responses);
            Assert.Equal("200", response.Key);
            Assert.Equal("OK", response.Value.Description);
            Assert.Collection(response.Value.Content.OrderBy(c => c.Key),
                content =>
                {
                    Assert.Equal("application/xml", content.Key);
                });
        });
    }
 
    [Fact]
    public async Task GetOpenApiResponse_ProducesDefaultResponse()
    {
        // Arrange
        var builder = CreateBuilder();
 
        // Act
        builder.MapGet("/api/todos", () => { });
 
        // Assert
        await VerifyOpenApiDocument(builder, document =>
        {
            var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values);
            var response = Assert.Single(operation.Responses);
            Assert.Equal("200", response.Key);
            Assert.Equal("OK", response.Value.Description);
        });
    }
 
    [Fact]
    public async Task GetOpenApiResponse_SupportsMvcProducesAttribute()
    {
        // Arrange
        var builder = CreateBuilder();
 
        // Act
        builder.MapGet("/api/todos", [Produces("application/json", "application/xml")] () => new Todo(1, "Test todo", false, DateTime.Now));
 
        // Assert
        await VerifyOpenApiDocument(builder, document =>
        {
            var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values);
            var response = Assert.Single(operation.Responses);
            Assert.Equal("200", response.Key);
            Assert.Equal("OK", response.Value.Description);
            Assert.Collection(response.Value.Content.OrderBy(c => c.Key),
                content =>
                {
                    Assert.Equal("application/json", content.Key);
                },
                content =>
                {
                    Assert.Equal("application/xml", content.Key);
                });
        });
    }
 
    [Fact]
    public async Task GetOpenApiResponse_SupportsGeneratingDefaultResponseField()
    {
        // Arrange
        var builder = CreateBuilder();
 
        // Act
        builder.MapGet("/api/todos", [ProducesDefaultResponseType(typeof(Error))] () => { });
 
        // Assert
        await VerifyOpenApiDocument(builder, document =>
        {
            var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values);
            var response = Assert.Single(operation.Responses);
            Assert.Equal(Microsoft.AspNetCore.OpenApi.OpenApiConstants.DefaultOpenApiResponseKey, response.Key);
            Assert.Empty(response.Value.Description);
            var mediaTypeEntry = Assert.Single(response.Value.Content);
            Assert.Equal("application/json", mediaTypeEntry.Key);
            var schema = mediaTypeEntry.Value.Schema.GetEffective(document);
            Assert.Equal("object", schema.Type);
            Assert.Collection(schema.Properties, property =>
            {
                Assert.Equal("code", property.Key);
                Assert.Equal("integer", property.Value.Type);
            }, property =>
            {
                Assert.Equal("message", property.Key);
                Assert.Equal("string", property.Value.Type);
            });
        });
    }
 
    [Fact]
    public async Task GetOpenApiResponse_SupportsGeneratingDefaultResponseWithSuccessResponse()
    {
        // Arrange
        var builder = CreateBuilder();
 
        // Act
        builder.MapGet("/api/todos", [ProducesDefaultResponseType(typeof(Error))] () => { })
            .WithMetadata(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, typeof(Todo), ["application/json"]));
 
        // Assert
        await VerifyOpenApiDocument(builder, document =>
        {
            var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values);
            var defaultResponse = operation.Responses[Microsoft.AspNetCore.OpenApi.OpenApiConstants.DefaultOpenApiResponseKey];
            // Generates a default response with the `Error` type.
            Assert.NotNull(defaultResponse);
            Assert.Empty(defaultResponse.Description);
            var defaultContent = Assert.Single(defaultResponse.Content.Values);
            var defaultSchema = defaultContent.Schema.GetEffective(document);
            Assert.Collection(defaultSchema.Properties,
            property =>
            {
                Assert.Equal("code", property.Key);
                Assert.Equal("integer", property.Value.GetEffective(document).Type);
            },
            property =>
            {
                Assert.Equal("message", property.Key);
                Assert.Equal("string", property.Value.GetEffective(document).Type);
            });
            // Generates the 200 status code response with the `Todo` response type.
            var okResponse = operation.Responses["200"];
            Assert.NotNull(okResponse);
            Assert.Equal("OK", okResponse.Description);
            var okContent = Assert.Single(okResponse.Content);
            Assert.Equal("application/json", okContent.Key);
            var schema = okContent.Value.Schema.GetEffective(document);
            Assert.Equal("object", schema.Type);
            Assert.Collection(schema.Properties, property =>
            {
                Assert.Equal("id", property.Key);
                Assert.Equal("integer", property.Value.GetEffective(document).Type);
            }, property =>
            {
                Assert.Equal("title", property.Key);
                Assert.Equal("string", property.Value.GetEffective(document).Type);
            }, property =>
            {
                Assert.Equal("completed", property.Key);
                Assert.Equal("boolean", property.Value.Type);
            }, property =>
            {
                Assert.Equal("createdAt", property.Key);
                Assert.Equal("string", property.Value.Type);
                Assert.Equal("date-time", property.Value.Format);
            });
        });
    }
 
    [Fact]
    public async Task GetOpenApiResponse_UsesDescriptionSetByUser()
    {
        // Arrange
        var builder = CreateBuilder();
 
        const string expectedCreatedDescription = "A new todo item was created";
        const string expectedBadRequestDescription = "Validation failed for the request";
 
        // Act
        builder.MapGet("/api/todos",
            [ProducesResponseType(typeof(TimeSpan), StatusCodes.Status201Created, Description = expectedCreatedDescription)]
        [ProducesResponseType(StatusCodes.Status400BadRequest, Description = expectedBadRequestDescription)]
        () =>
            { });
 
        // Assert
        await VerifyOpenApiDocument(builder, document =>
        {
            var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values);
            Assert.Collection(operation.Responses.OrderBy(r => r.Key),
                response =>
                {
                    Assert.Equal("201", response.Key);
                    Assert.Equal(expectedCreatedDescription, response.Value.Description);
                },
                response =>
                {
                    Assert.Equal("400", response.Key);
                    Assert.Equal(expectedBadRequestDescription, response.Value.Description);
                });
        });
    }
 
    [Fact]
    public async Task GetOpenApiResponse_UsesStatusCodeReasonPhraseWhenExplicitDescriptionIsMissing()
    {
        // Arrange
        var builder = CreateBuilder();
 
        // Act
        builder.MapGet("/api/todos",
            [ProducesResponseType(typeof(TimeSpan), StatusCodes.Status201Created, Description = null)] // Explicitly set to NULL
        [ProducesResponseType(StatusCodes.Status400BadRequest)] // Omitted, meaning it should be NULL
        () =>
            { });
 
        // Assert
        await VerifyOpenApiDocument(builder, document =>
        {
            var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values);
            Assert.Collection(operation.Responses.OrderBy(r => r.Key),
                response =>
                {
                    Assert.Equal("201", response.Key);
                    Assert.Equal("Created", response.Value.Description);
                },
                response =>
                {
                    Assert.Equal("400", response.Key);
                    Assert.Equal("Bad Request", response.Value.Description);
                });
        });
    }
}