File: HostingFakesExtensionsTests.cs
Web Access
Project: src\test\Libraries\Microsoft.Extensions.Hosting.Testing.Tests\Microsoft.Extensions.Hosting.Testing.Tests.csproj (Microsoft.Extensions.Hosting.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.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
 
namespace Microsoft.Extensions.Hosting.Testing.Test;
 
public class HostingFakesExtensionsTests
{
    [Fact]
    public async Task StartAndStop_NullGiven_Throws()
    {
        var exception = await Record.ExceptionAsync(() => ((IHostedService)null!).StartAndStopAsync());
        Assert.IsType<ArgumentNullException>(exception);
    }
 
    [Fact]
    public async Task StartAndStop_ServiceGiven_StartsAndStopsTheService()
    {
        using var tokenSource = new CancellationTokenSource();
 
        var serviceMock = new Mock<IHostedService>(MockBehavior.Strict);
        serviceMock.Setup(x => x.StartAsync(tokenSource.Token)).Returns(Task.CompletedTask);
        serviceMock.Setup(x => x.StopAsync(tokenSource.Token)).Returns(Task.CompletedTask);
 
        await serviceMock.Object.StartAndStopAsync(tokenSource.Token);
 
        serviceMock.VerifyAll();
    }
 
    [Fact]
    public async Task GetFakeLogCollector_FetchesGetFakeLogCollector()
    {
        using var host = await FakeHost.CreateBuilder().StartAsync();
        Assert.NotNull(host.GetFakeLogCollector());
    }
 
    [Fact]
    public void GetFakeLogCollector_FakeCollectorMissing_ThrowsException()
    {
        using var host = new HostBuilder().Build();
 
        var exception = Record.Exception(() => host.GetFakeLogCollector());
 
        Assert.NotNull(exception);
        Assert.IsType<InvalidOperationException>(exception);
        Assert.Equal("No fake log collector registered", exception.Message);
    }
 
    [Fact]
    public async Task GetFakeRedactionCollector_FetchesFakeRedactionCollector()
    {
        using var host = await FakeHost.CreateBuilder().StartAsync();
 
        var collector = host.GetFakeRedactionCollector();
 
        Assert.NotNull(collector);
    }
 
    [Fact]
    public void GetFakeRedactionCollector_FakeCollectorMissing_ThrowsException()
    {
        using var host = new HostBuilder().Build();
 
        var exception = Record.Exception(() => host.GetFakeRedactionCollector());
 
        Assert.NotNull(exception);
        Assert.IsType<InvalidOperationException>(exception);
    }
 
    [Fact]
    public void Configure_delegateUsed_ConfiguresGivenBuilder()
    {
        var builderMock = new Mock<IHostBuilder>();
 
        var returnedBuilder = builderMock.Object.Configure(builder => builder.Build());
 
        Assert.Equal(builderMock.Object, returnedBuilder);
        builderMock.Verify(mock => mock.Build(), Times.Once);
    }
 
    [Fact]
    public void Configure_BuilderIsNull_Throws()
    {
        var exception = Record.Exception(() => ((IHostBuilder)null!).Configure(_ => { }));
        Assert.IsType<ArgumentNullException>(exception);
    }
 
    [Fact]
    public void Configure_ConfigureDelegateIsNull_Throws()
    {
        var exception = Record.Exception(() => new HostBuilder().Configure(null!));
        Assert.IsType<ArgumentNullException>(exception);
    }
 
    [Fact]
    public void Configure_nullDelegate_Throws()
    {
        var builderMock = new Mock<IHostBuilder>();
 
        var exception = Record.Exception(() => builderMock.Object.Configure(null!));
        Assert.IsType<ArgumentNullException>(exception);
    }
 
    [Fact]
    public void ConfigureAppConfiguration_ReturnsBuilder()
    {
        var builderMock = new Mock<IHostBuilder>();
        builderMock
            .Setup(x => x.ConfigureAppConfiguration(It.IsAny<Action<HostBuilderContext, IConfigurationBuilder>>()))
            .Returns(builderMock.Object);
 
        var returnedBuilder = builderMock.Object.ConfigureAppConfiguration("testKey", "testValue");
 
        Assert.Equal(builderMock.Object, returnedBuilder);
    }
 
    [Fact]
    public void ConfigureAppConfiguration_KeyAndValueGiven_AddsToConfiguration()
    {
        var configBuilder = new ConfigurationBuilder();
        configBuilder.Sources.Add(new ChainedConfigurationSource());
 
        var builderMock = CreateHostBuilderMock(appConfigBuilder: configBuilder);
 
        _ = builderMock.Object.ConfigureAppConfiguration("testKey", "testValue");
 
        Assert.Collection(
            configBuilder.Sources,
            source => Assert.IsType<ChainedConfigurationSource>(source),
            source =>
            {
                Assert.IsType<FakeConfigurationSource>(source);
                Assert.Collection(
                    ((FakeConfigurationSource)source).InitialData!,
                    item =>
                    {
                        Assert.Equal("testKey", item.Key);
                        Assert.Equal("testValue", item.Value);
                    });
            });
    }
 
    [Fact]
    public void ConfigureAppConfiguration_MultipleKeyAndValueGiven_AddsToConfiguration()
    {
        var configBuilder = new ConfigurationBuilder();
        configBuilder.Sources.Add(new ChainedConfigurationSource());
 
        var builderMock = CreateHostBuilderMock(appConfigBuilder: configBuilder);
 
        _ = builderMock.Object.ConfigureAppConfiguration(("testKey1", "testValue1"), ("testKey2", "testValue2"));
 
        Assert.Collection(
            configBuilder.Sources,
            source => Assert.IsType<ChainedConfigurationSource>(source),
            source =>
            {
                Assert.IsType<FakeConfigurationSource>(source);
                Assert.Collection(
                    ((FakeConfigurationSource)source).InitialData!,
                    item =>
                    {
                        Assert.Equal("testKey1", item.Key);
                        Assert.Equal("testValue1", item.Value);
                    },
                    item =>
                    {
                        Assert.Equal("testKey2", item.Key);
                        Assert.Equal("testValue2", item.Value);
                    });
            });
    }
 
    [Fact]
    public void ConfigureAppConfiguration_MultipleKeyAndValueGiven_AddsOnlyOneConfigurationSource()
    {
        var configBuilder = new ConfigurationBuilder();
        var builderMock = CreateHostBuilderMock(appConfigBuilder: configBuilder);
 
        _ = builderMock.Object.ConfigureAppConfiguration("testKey", "testValue");
        _ = builderMock.Object.ConfigureAppConfiguration("anotherTestKey", "anotherTestValue");
 
        Assert.Collection(
            configBuilder.Sources,
            source =>
            {
                Assert.IsType<FakeConfigurationSource>(source);
                Assert.Collection(
                    ((FakeConfigurationSource)source).InitialData!,
                    item =>
                    {
                        Assert.Equal("testKey", item.Key);
                        Assert.Equal("testValue", item.Value);
                    },
                    item =>
                    {
                        Assert.Equal("anotherTestKey", item.Key);
                        Assert.Equal("anotherTestValue", item.Value);
                    });
            });
    }
 
    [Fact]
    public void ConfigureHostConfiguration_KeyAndValueGiven_AddsToConfiguration()
    {
        var configBuilder = new ConfigurationBuilder();
        configBuilder.Sources.Add(new ChainedConfigurationSource());
 
        var builderMock = CreateHostBuilderMock(hostConfigBuilder: configBuilder);
 
        _ = builderMock.Object.ConfigureHostConfiguration("testKey", "testValue");
 
        Assert.Collection(
            configBuilder.Sources,
            source => Assert.IsType<ChainedConfigurationSource>(source),
            source =>
            {
                Assert.IsType<FakeConfigurationSource>(source);
                Assert.Collection(
                    ((FakeConfigurationSource)source).InitialData!,
                    item =>
                    {
                        Assert.Equal("testKey", item.Key);
                        Assert.Equal("testValue", item.Value);
                    });
            });
    }
 
    [Fact]
    public void ConfigureHostConfiguration_MultipleEntriesGiven_AddsToConfiguration()
    {
        var configBuilder = new ConfigurationBuilder();
        configBuilder.Sources.Add(new ChainedConfigurationSource());
 
        var builderMock = CreateHostBuilderMock(hostConfigBuilder: configBuilder);
 
        _ = builderMock.Object.ConfigureHostConfiguration(("testKey1", "testValue1"), ("testKey2", "testValue2"));
 
        Assert.Collection(
            configBuilder.Sources,
            source => Assert.IsType<ChainedConfigurationSource>(source),
            source =>
            {
                Assert.IsType<FakeConfigurationSource>(source);
                Assert.Collection(
                    ((FakeConfigurationSource)source).InitialData!,
                    item =>
                    {
                        Assert.Equal("testKey1", item.Key);
                        Assert.Equal("testValue1", item.Value);
                    },
                    item =>
                    {
                        Assert.Equal("testKey2", item.Key);
                        Assert.Equal("testValue2", item.Value);
                    });
            });
    }
 
    [Fact]
    public void ConfigureHostConfiguration_MultipleKeyAndValueGiven_AddsOnlyOneConfigurationSource()
    {
        var configBuilder = new ConfigurationBuilder();
        var builderMock = CreateHostBuilderMock(hostConfigBuilder: configBuilder);
 
        _ = builderMock.Object.ConfigureHostConfiguration("testKey", "testValue");
        _ = builderMock.Object.ConfigureHostConfiguration("anotherTestKey", "anotherTestValue");
 
        Assert.Collection(
            configBuilder.Sources,
            source =>
            {
                Assert.IsType<FakeConfigurationSource>(source);
                Assert.Collection(
                    ((FakeConfigurationSource)source).InitialData!,
                    item =>
                    {
                        Assert.Equal("testKey", item.Key);
                        Assert.Equal("testValue", item.Value);
                    },
                    item =>
                    {
                        Assert.Equal("anotherTestKey", item.Key);
                        Assert.Equal("anotherTestValue", item.Value);
                    });
            });
    }
 
    [Fact]
    public void AddLoggingCallback_NullCallback_Throws()
    {
        Assert.Throws<ArgumentNullException>(() => FakeHost.CreateBuilder().AddFakeLoggingOutputSink(null!));
    }
 
    [Fact]
    public void AddLoggingCallback_NullHostBuilder_Throws()
    {
        Assert.Throws<ArgumentNullException>(() => ((IHostBuilder)null!).AddFakeLoggingOutputSink(_ => { }));
    }
 
    [Fact]
    public async Task AddLoggingCallback_CallbackUsed_AddsCallback()
    {
        var message = Guid.NewGuid().ToString();
        var firstCallbackTarget = new List<string>();
        var secondCallbackTarget = new List<string>();
        using var host = await FakeHost.CreateBuilder()
            .AddFakeLoggingOutputSink(firstCallbackTarget.Add)
            .AddFakeLoggingOutputSink(secondCallbackTarget.Add)
            .StartAsync();
 
        var logger = host.Services.GetRequiredService<ILogger>();
        logger.LogWarning(message);
 
        Assert.Contains(firstCallbackTarget, record => record.Contains(message));
        Assert.Contains(secondCallbackTarget, record => record.Contains(message));
    }
 
    private static Mock<IHostBuilder> CreateHostBuilderMock(
        IConfigurationBuilder? appConfigBuilder = null,
        IConfigurationBuilder? hostConfigBuilder = null)
    {
        var builderMock = new Mock<IHostBuilder>();
 
        if (appConfigBuilder is not null)
        {
            builderMock
                .Setup(x => x.ConfigureAppConfiguration(It.IsAny<Action<HostBuilderContext, IConfigurationBuilder>>()))
                .Returns(builderMock.Object)
                .Callback<Action<HostBuilderContext?, IConfigurationBuilder>>(configure => configure(null, appConfigBuilder));
        }
 
        if (hostConfigBuilder is not null)
        {
            builderMock
                .Setup(x => x.ConfigureHostConfiguration(It.IsAny<Action<IConfigurationBuilder>>()))
                .Returns(builderMock.Object)
                .Callback<Action<IConfigurationBuilder>>(configure => configure(hostConfigBuilder));
        }
 
        return builderMock;
    }
}