File: Extensions\OpenApiServiceCollectionExtensionsTests.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.OpenApi;
using Microsoft.Extensions.ApiDescriptions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting.Internal;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Models;
 
public class OpenApiServiceCollectionExtensions
{
    [Fact]
    public void AddOpenApi_WithDocumentName_ReturnsServiceCollection()
    {
        // Arrange
        var services = new ServiceCollection();
        var documentName = "v2";
 
        // Act
        var returnedServices = services.AddOpenApi(documentName);
 
        // Assert
        Assert.IsAssignableFrom<IServiceCollection>(returnedServices);
    }
 
    [Fact]
    public void AddOpenApi_WithDocumentName_RegistersServices()
    {
        // Arrange
        var services = new ServiceCollection();
        var documentName = "v2";
 
        // Act
        services.AddOpenApi(documentName);
        var serviceProvider = services.BuildServiceProvider();
 
        // Assert
        Assert.Contains(services, sd => sd.ServiceType == typeof(OpenApiSchemaService) && sd.Lifetime == ServiceLifetime.Singleton && (string)sd.ServiceKey == documentName);
        Assert.Contains(services, sd => sd.ServiceType == typeof(OpenApiDocumentService) && sd.Lifetime == ServiceLifetime.Singleton && (string)sd.ServiceKey == documentName);
        Assert.Contains(services, sd => sd.ServiceType == typeof(IDocumentProvider) && sd.Lifetime == ServiceLifetime.Singleton);
        var options = serviceProvider.GetRequiredService<IOptionsSnapshot<OpenApiOptions>>();
        var namedOption = options.Get(documentName);
        Assert.Equal(documentName, namedOption.DocumentName);
    }
 
    [Fact]
    public void AddOpenApi_WithDocumentNameAndConfigureOptions_ReturnsServiceCollection()
    {
        // Arrange
        var services = new ServiceCollection();
        var documentName = "v2";
 
        // Act
        var returnedServices = services.AddOpenApi(documentName, options => { });
 
        // Assert
        Assert.IsAssignableFrom<IServiceCollection>(returnedServices);
    }
 
    [Fact]
    public void AddOpenApi_WithDocumentNameAndConfigureOptions_RegistersServices()
    {
        // Arrange
        var services = new ServiceCollection();
        var documentName = "v2";
 
        // Act
        services.AddOpenApi(documentName, options => { });
        var serviceProvider = services.BuildServiceProvider();
 
        // Assert
        Assert.Contains(services, sd => sd.ServiceType == typeof(OpenApiSchemaService) && sd.Lifetime == ServiceLifetime.Singleton && (string)sd.ServiceKey == documentName);
        Assert.Contains(services, sd => sd.ServiceType == typeof(OpenApiDocumentService) && sd.Lifetime == ServiceLifetime.Singleton && (string)sd.ServiceKey == documentName);
        Assert.Contains(services, sd => sd.ServiceType == typeof(IDocumentProvider) && sd.Lifetime == ServiceLifetime.Singleton);
        var options = serviceProvider.GetRequiredService<IOptionsSnapshot<OpenApiOptions>>();
        var namedOption = options.Get(documentName);
        Assert.Equal(documentName, namedOption.DocumentName);
    }
 
    [Fact]
    public void AddOpenApi_WithoutDocumentName_ReturnsServiceCollection()
    {
        // Arrange
        var services = new ServiceCollection();
 
        // Act
        var returnedServices = services.AddOpenApi();
 
        // Assert
        Assert.IsAssignableFrom<IServiceCollection>(returnedServices);
    }
 
    [Fact]
    public void AddOpenApi_WithoutDocumentName_RegistersServices()
    {
        // Arrange
        var services = new ServiceCollection();
        var documentName = "v1";
 
        // Act
        services.AddOpenApi();
        var serviceProvider = services.BuildServiceProvider();
 
        // Assert
        Assert.Contains(services, sd => sd.ServiceType == typeof(OpenApiSchemaService) && sd.Lifetime == ServiceLifetime.Singleton && (string)sd.ServiceKey == documentName);
        Assert.Contains(services, sd => sd.ServiceType == typeof(OpenApiDocumentService) && sd.Lifetime == ServiceLifetime.Singleton && (string)sd.ServiceKey == documentName);
        Assert.Contains(services, sd => sd.ServiceType == typeof(IDocumentProvider) && sd.Lifetime == ServiceLifetime.Singleton);
        var options = serviceProvider.GetRequiredService<IOptionsSnapshot<OpenApiOptions>>();
        var namedOption = options.Get(documentName);
        Assert.Equal(documentName, namedOption.DocumentName);
    }
 
    [Fact]
    public void AddOpenApi_WithConfigureOptions_ReturnsServiceCollection()
    {
        // Arrange
        var services = new ServiceCollection();
 
        // Act
        var returnedServices = services.AddOpenApi(options => { });
 
        // Assert
        Assert.IsAssignableFrom<IServiceCollection>(returnedServices);
    }
 
    [Fact]
    public void AddOpenApi_WithConfigureOptions_RegistersServices()
    {
        // Arrange
        var services = new ServiceCollection();
        var documentName = "v1";
 
        // Act
        services.AddOpenApi(options => { });
        var serviceProvider = services.BuildServiceProvider();
 
        // Assert
        Assert.Contains(services, sd => sd.ServiceType == typeof(OpenApiSchemaService) && sd.Lifetime == ServiceLifetime.Singleton && (string)sd.ServiceKey == documentName);
        Assert.Contains(services, sd => sd.ServiceType == typeof(OpenApiDocumentService) && sd.Lifetime == ServiceLifetime.Singleton && (string)sd.ServiceKey == documentName);
        Assert.Contains(services, sd => sd.ServiceType == typeof(IDocumentProvider) && sd.Lifetime == ServiceLifetime.Singleton);
        var options = serviceProvider.GetRequiredService<IOptionsSnapshot<OpenApiOptions>>();
        var namedOption = options.Get(documentName);
        Assert.Equal(documentName, namedOption.DocumentName);
    }
 
    [Fact]
    public void AddOpenApi_WithDuplicateDocumentNames_UsesLastRegistration()
    {
        // Arrange
        var services = new ServiceCollection();
        var documentName = "v2";
 
        // Act
        services
        .AddOpenApi(documentName, options => options.OpenApiVersion = OpenApiSpecVersion.OpenApi2_0)
        .AddOpenApi(documentName, options => options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0);
        var serviceProvider = services.BuildServiceProvider();
 
        // Assert
        Assert.Contains(services, sd => sd.ServiceType == typeof(OpenApiSchemaService) && sd.Lifetime == ServiceLifetime.Singleton && (string)sd.ServiceKey == documentName);
        Assert.Contains(services, sd => sd.ServiceType == typeof(OpenApiDocumentService) && sd.Lifetime == ServiceLifetime.Singleton && (string)sd.ServiceKey == documentName);
        Assert.Contains(services, sd => sd.ServiceType == typeof(IDocumentProvider) && sd.Lifetime == ServiceLifetime.Singleton);
        var options = serviceProvider.GetRequiredService<IOptionsSnapshot<OpenApiOptions>>();
        var namedOption = options.Get(documentName);
        Assert.Equal(documentName, namedOption.DocumentName);
        // Verify last registration is used
        Assert.Equal(OpenApiSpecVersion.OpenApi3_0, namedOption.OpenApiVersion);
    }
 
    [Fact]
    public void AddOpenApi_WithDuplicateDocumentNames_UsesLastRegistration_ValidateOptionsOverride()
    {
        // Arrange
        var services = new ServiceCollection();
        var documentName = "v2";
 
        // Act
        services
        .AddOpenApi(documentName, options => options.OpenApiVersion = OpenApiSpecVersion.OpenApi2_0)
        .AddOpenApi(documentName);
        var serviceProvider = services.BuildServiceProvider();
 
        // Assert
        Assert.Contains(services, sd => sd.ServiceType == typeof(OpenApiSchemaService) && sd.Lifetime == ServiceLifetime.Singleton && (string)sd.ServiceKey == documentName);
        Assert.Contains(services, sd => sd.ServiceType == typeof(OpenApiDocumentService) && sd.Lifetime == ServiceLifetime.Singleton && (string)sd.ServiceKey == documentName);
        Assert.Contains(services, sd => sd.ServiceType == typeof(IDocumentProvider) && sd.Lifetime == ServiceLifetime.Singleton);
        var options = serviceProvider.GetRequiredService<IOptionsSnapshot<OpenApiOptions>>();
        var namedOption = options.Get(documentName);
        Assert.Equal(documentName, namedOption.DocumentName);
        Assert.Equal(OpenApiSpecVersion.OpenApi2_0, namedOption.OpenApiVersion);
    }
 
    [Fact]
    public void AddOpenApi_WithDefaultDocumentName_RegistersIOpenApiDocumentProviderInterface()
    {
        // Arrange
        var services = new ServiceCollection();
        // Include dependencies for OpenApiDocumentService
        services.AddSingleton<IHostEnvironment>(new HostingEnvironment
        {
            EnvironmentName = Environments.Development,
            ApplicationName = "Test Application"
        });
        services.AddLogging();
        services.AddRouting();
 
        // Act
        services.AddOpenApi();
        var serviceProvider = services.BuildServiceProvider();
 
        // Assert
        var documentProvider = serviceProvider.GetRequiredKeyedService<IOpenApiDocumentProvider>(Microsoft.AspNetCore.OpenApi.OpenApiConstants.DefaultDocumentName);
        Assert.NotNull(documentProvider);
        Assert.IsType<OpenApiDocumentService>(documentProvider);
    }
 
    [Fact]
    public void AddOpenApi_WithCustomDocumentName_RegistersIOpenApiDocumentProviderInterface()
    {
        // Arrange
        var services = new ServiceCollection();
        // Include dependencies for OpenApiDocumentService
        services.AddSingleton<IHostEnvironment>(new HostingEnvironment
        {
            EnvironmentName = Environments.Development,
            ApplicationName = "Test Application"
        });
        services.AddLogging();
        services.AddRouting();
        var documentName = "v1";
 
        // Act
        services.AddOpenApi(documentName);
        var serviceProvider = services.BuildServiceProvider();
 
        // Assert
        var documentProvider = serviceProvider.GetRequiredKeyedService<IOpenApiDocumentProvider>(documentName.ToLowerInvariant());
        Assert.NotNull(documentProvider);
        Assert.IsType<OpenApiDocumentService>(documentProvider);
    }
 
    [Fact]
    public async Task GetOpenApiDocumentAsync_ReturnsDocument()
    {
        // Arrange
        var services = new ServiceCollection();
        // Include dependencies for OpenApiDocumentService
        services.AddSingleton<IHostEnvironment>(new HostingEnvironment
        {
            EnvironmentName = Environments.Development,
            ApplicationName = "Test Application"
        });
        services.AddLogging();
        services.AddRouting();
 
        var documentName = "v1";
        services.AddOpenApi(documentName);
        var serviceProvider = services.BuildServiceProvider();
        var documentProvider = serviceProvider.GetRequiredKeyedService<IOpenApiDocumentProvider>(documentName.ToLowerInvariant());
 
        // Act
        var document = await documentProvider.GetOpenApiDocumentAsync();
 
        // Assert
        Assert.NotNull(document);
        Assert.IsType<OpenApiDocument>(document);
 
        // Verify basic document structure
        Assert.NotNull(document.Info);
        Assert.Equal($"Test Application | {documentName.ToLowerInvariant()}", document.Info.Title);
        Assert.Equal("1.0.0", document.Info.Version);
    }
 
    [Fact]
    public async Task GetOpenApiDocumentAsync_HandlesCancellation()
    {
        // Arrange
        var services = new ServiceCollection();
        services.AddSingleton<IHostEnvironment>(new HostingEnvironment
        {
            EnvironmentName = Environments.Development,
            ApplicationName = "Test Application"
        });
        services.AddLogging();
        services.AddRouting();
        var documentName = "v1";
        services.AddOpenApi(documentName);
        var serviceProvider = services.BuildServiceProvider();
        var documentProvider = serviceProvider.GetRequiredKeyedService<IOpenApiDocumentProvider>(documentName.ToLowerInvariant());
 
        using var cts = new CancellationTokenSource();
        cts.Cancel();
 
        // Act & Assert
        await Assert.ThrowsAsync<OperationCanceledException>(async () =>
        {
            await documentProvider.GetOpenApiDocumentAsync(cts.Token);
        });
    }
}