File: FakesExtensionsTests.cs
Web Access
Project: src\test\Libraries\Microsoft.AspNetCore.Testing.Tests\Microsoft.AspNetCore.Testing.Tests.csproj (Microsoft.AspNetCore.Testing.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Testing.Test.TestResources;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting.Testing;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
 
namespace Microsoft.AspNetCore.Testing.Test;
 
[SuppressMessage("Reliability", "CA2000", Justification = "HttpClient shouldn't be deliberately disposed.")]
public class FakesExtensionsTests
{
    private static readonly string[] _urlAddresses = { "https://first.com/", "https://second.com/", "https://third.com/" };
 
    [Fact]
    public async Task UseTestStartup_UsesFakeStartup()
    {
        using var host = FakeHost.CreateBuilder()
            .ConfigureWebHost(webHost => webHost.UseFakeStartup().ListenHttpOnAnyPort())
            .Build();
 
        Assert.Null(await Record.ExceptionAsync(() => host.StartAsync()));
    }
 
    [Fact]
    public async Task ListenHttpOnAnyPort_AddsListener()
    {
        using var host = await FakeHost.CreateBuilder()
            .ConfigureWebHost(webHost => webHost.ListenHttpOnAnyPort().UseFakeStartup())
            .StartAsync();
 
        Assert.Null(Record.Exception(() => host.GetListenUris()));
    }
 
    [Fact]
    public async Task ListenHttpsOnAnyPort_WithoutCertificate_CertificateProvided()
    {
        using var host = await FakeHost.CreateBuilder()
            .ConfigureWebHost(webHost => webHost.UseStartup<Startup>().ListenHttpsOnAnyPort())
            .StartAsync();
 
        var certificate = host.Services.GetRequiredService<IOptions<FakeCertificateOptions>>().Value.Certificate;
 
        Assert.NotNull(certificate);
 
        var client = new HttpClient(new FakeCertificateHttpClientHandler(certificate))
        {
            BaseAddress = host.GetListenUris().First()
        };
 
        Assert.Equal(HttpStatusCode.OK, (await client.GetAsync("/")).StatusCode);
    }
 
    [Fact]
    public async Task ListenHttpsOnAnyPort_WithCertificate_UsesTheCertificate()
    {
        var certificate = FakeSslCertificateFactory.CreateSslCertificate();
 
        using var host = await FakeHost.CreateBuilder()
            .ConfigureWebHost(webHost => webHost.UseStartup<Startup>().ListenHttpsOnAnyPort(certificate))
            .StartAsync();
 
        var client = host.CreateClient(new FakeCertificateHttpClientHandler(certificate));
 
        Assert.Equal(HttpStatusCode.OK, (await client.GetAsync("/")).StatusCode);
    }
 
    [Fact]
    public async Task CreateClient_HandlerGiven_UsesTheHandler()
    {
        var hostMock = CreateHostMock(_urlAddresses);
 
        using var handler = new ReturningHttpClientHandler();
        using var client = hostMock.Object.CreateClient(handler);
        using var response = await client.SendAsync(new HttpRequestMessage());
 
        Assert.Equal(HttpStatusCode.Gone, response.StatusCode);
    }
 
    [Fact]
    public void CreateClient_NoAddressFilter_UseFirstAddress()
    {
        var hostMock = CreateHostMock(_urlAddresses);
 
        using var client = hostMock.Object.CreateClient();
 
        Assert.Equal(_urlAddresses[0], client.BaseAddress?.AbsoluteUri);
    }
 
    [Fact]
    public void CreateClient_NoAddress_Throws()
    {
        var hostMock = CreateHostMock();
 
        var exception = Record.Exception(() => hostMock.Object.CreateClient(null, _ => false));
 
        Assert.IsType<InvalidOperationException>(exception);
        Assert.Equal("No suitable address found to call the server.", exception.Message);
    }
 
    [Fact]
    public void CreateClient_NoSuitableAddress_Throws()
    {
        var hostMock = CreateHostMock(_urlAddresses);
 
        var exception = Record.Exception(() => hostMock.Object.CreateClient(null, _ => false));
 
        Assert.IsType<InvalidOperationException>(exception);
        Assert.Equal("No suitable address found to call the server.", exception.Message);
    }
 
    [Fact]
    public void CreateClient_NoHost_Throws()
    {
        var exception = Record.Exception(() => ((IHost)null!).CreateClient(new TestHandler(), _ => true));
        Assert.IsType<ArgumentNullException>(exception);
    }
 
    [Fact]
    public void CreateClient_NoServer_Throws()
    {
        var hostMock = CreateHostMock(_urlAddresses);
        var services = Mock.Get(hostMock.Object.Services);
        services.Setup(x => x.GetService(typeof(IServer))).Returns(null);
 
        var exception = Record.Exception(() => hostMock.Object.CreateClient(new TestHandler(), _ => true));
 
        Assert.IsType<InvalidOperationException>(exception);
    }
 
    [Fact]
    public void CreateClient_AddressFilterGiven_UseFirstAddressPassingFilter()
    {
        var hostMock = CreateHostMock(_urlAddresses);
 
        using var client = hostMock.Object.CreateClient(null, x => x.AbsoluteUri == _urlAddresses[1]);
 
        Assert.Equal(_urlAddresses[1], client.BaseAddress?.AbsoluteUri);
    }
 
    [Fact]
    public async Task CreateClient_UsingHttpsWithoutCertificate_CreatesCertificateAndMakesItWorking()
    {
        using var host = await FakeHost.CreateBuilder()
            .ConfigureWebHost(webHost => webHost.UseStartup<Startup>().ListenHttpsOnAnyPort())
            .StartAsync();
 
        using var client = host.CreateClient();
 
        Assert.Equal(HttpStatusCode.OK, (await client.GetAsync("/")).StatusCode);
    }
 
    [Fact]
    public async Task CreateClient_UsingHttp_CreatesClient()
    {
        using var host = await FakeHost.CreateBuilder()
            .ConfigureWebHost(webHost => webHost.UseStartup<Startup>().ListenHttpOnAnyPort())
            .StartAsync();
 
        using var client = host.CreateClient();
 
        Assert.Equal(HttpStatusCode.OK, (await client.GetAsync("/")).StatusCode);
    }
 
    [Fact]
    public void GetListenUris_NoServer_Throws()
    {
        var hostMock = CreateHostMock(_urlAddresses);
 
        var services = Mock.Get(hostMock.Object.Services);
        services.Setup(x => x.GetService(typeof(IServer))).Returns(null);
 
        var exception = Record.Exception(() => hostMock.Object.GetListenUris());
 
        Assert.IsType<InvalidOperationException>(exception);
    }
 
    [Fact]
    public void GetListenUris_NoAddressesFeatureInServer_Throws()
    {
        var hostMock = CreateHostMock(_urlAddresses);
 
        hostMock.Object.Services.GetRequiredService<IServer>().Features[typeof(IServerAddressesFeature)] = null;
 
        var services = Mock.Get(hostMock.Object.Services);
        services.Setup(x => x.GetService(typeof(IServer))).Returns(null);
 
        var exception = Record.Exception(() => hostMock.Object.GetListenUris());
 
        Assert.IsType<InvalidOperationException>(exception);
    }
 
    [Fact]
    public void GetUri_NoHost_Throws()
    {
        var exception = Record.Exception(() => ((IHost)null!).GetListenUris());
        Assert.IsType<ArgumentNullException>(exception);
    }
 
    [Fact]
    public void GetListenUris_PassesAddresses()
    {
        var hostMock = CreateHostMock(_urlAddresses);
 
        Assert.Collection(hostMock.Object.GetListenUris(),
            address => Assert.Equal(address.AbsoluteUri, _urlAddresses[0]),
            address => Assert.Equal(address.AbsoluteUri, _urlAddresses[1]),
            address => Assert.Equal(address.AbsoluteUri, _urlAddresses[2]));
    }
 
    [Fact]
    public void GetListenUris_ReplacesEmptyAddressWithLocalhost()
    {
        var hostMock = CreateHostMock("https://[::]");
        Assert.StartsWith("https://localhost", hostMock.Object.GetListenUris().Single().AbsoluteUri);
    }
 
    private static Mock<IHost> CreateHostMock(params string[] addresses)
    {
        var addressesFeature = new ServerAddressesFeature();
 
        foreach (var address in addresses)
        {
            addressesFeature.Addresses.Add(address);
        }
 
        var features = new FeatureCollection { [typeof(IServerAddressesFeature)] = addressesFeature };
 
        var mockServer = new Mock<IServer>();
        mockServer.Setup(x => x.Features).Returns(features);
 
        var serviceProviderMock = new Mock<IServiceProvider>();
        serviceProviderMock.Setup(x => x.GetService(typeof(IServer))).Returns(mockServer.Object);
 
        var hostMock = new Mock<IHost>();
        hostMock.SetupGet(x => x.Services).Returns(serviceProviderMock.Object);
 
        return hostMock;
    }
}