File: PassThroughServiceEndpointResolverTests.cs
Web Access
Project: src\tests\Microsoft.Extensions.ServiceDiscovery.Tests\Microsoft.Extensions.ServiceDiscovery.Tests.csproj (Microsoft.Extensions.ServiceDiscovery.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.Net;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.ServiceDiscovery.Internal;
using Microsoft.Extensions.ServiceDiscovery.PassThrough;
using Xunit;
 
namespace Microsoft.Extensions.ServiceDiscovery.Tests;
 
/// <summary>
/// Tests for <see cref="PassThroughServiceEndpointProviderFactory"/>.
/// These also cover <see cref="ServiceEndpointWatcher"/> and <see cref="ServiceEndpointWatcherFactory"/> by extension.
/// </summary>
public class PassThroughServiceEndpointResolverTests
{
    [Fact]
    public async Task ResolveServiceEndpoint_PassThrough()
    {
        var services = new ServiceCollection()
            .AddServiceDiscoveryCore()
            .AddPassThroughServiceEndpointProvider()
            .BuildServiceProvider();
        var watcherFactory = services.GetRequiredService<ServiceEndpointWatcherFactory>();
        ServiceEndpointWatcher watcher;
        await using ((watcher = watcherFactory.CreateWatcher("http://basket")).ConfigureAwait(false))
        {
            Assert.NotNull(watcher);
            var tcs = new TaskCompletionSource<ServiceEndpointResolverResult>();
            watcher.OnEndpointsUpdated = tcs.SetResult;
            watcher.Start();
            var initialResult = await tcs.Task;
            Assert.NotNull(initialResult);
            Assert.True(initialResult.ResolvedSuccessfully);
            var ep = Assert.Single(initialResult.EndpointSource.Endpoints);
            Assert.Equal(new DnsEndPoint("basket", 80), ep.EndPoint);
        }
    }
 
    [Fact]
    public async Task ResolveServiceEndpoint_Superseded()
    {
        var configSource = new MemoryConfigurationSource
        {
            InitialData = new Dictionary<string, string?>
            {
                ["services:basket:http:0"] = "http://localhost:8080",
            }
        };
        var config = new ConfigurationBuilder().Add(configSource);
        var services = new ServiceCollection()
            .AddSingleton<IConfiguration>(config.Build())
            .AddServiceDiscovery() // Adds the configuration and pass-through providers.
            .BuildServiceProvider();
        var watcherFactory = services.GetRequiredService<ServiceEndpointWatcherFactory>();
        ServiceEndpointWatcher watcher;
        await using ((watcher = watcherFactory.CreateWatcher("http://basket")).ConfigureAwait(false))
        {
            Assert.NotNull(watcher);
            var tcs = new TaskCompletionSource<ServiceEndpointResolverResult>();
            watcher.OnEndpointsUpdated = tcs.SetResult;
            watcher.Start();
            var initialResult = await tcs.Task;
            Assert.NotNull(initialResult);
            Assert.True(initialResult.ResolvedSuccessfully);
 
            // We expect the basket service to be resolved from Configuration, not the pass-through provider.
            Assert.Single(initialResult.EndpointSource.Endpoints);
            Assert.Equal(new UriEndPoint(new Uri("http://localhost:8080")), initialResult.EndpointSource.Endpoints[0].EndPoint);
        }
    }
 
    [Fact]
    public async Task ResolveServiceEndpoint_Fallback()
    {
        var configSource = new MemoryConfigurationSource
        {
            InitialData = new Dictionary<string, string?>
            {
                ["services:basket:default:0"] = "http://localhost:8080",
            }
        };
        var config = new ConfigurationBuilder().Add(configSource);
        var services = new ServiceCollection()
            .AddSingleton<IConfiguration>(config.Build())
            .AddServiceDiscovery() // Adds the configuration and pass-through providers.
            .BuildServiceProvider();
        var watcherFactory = services.GetRequiredService<ServiceEndpointWatcherFactory>();
        ServiceEndpointWatcher watcher;
        await using ((watcher = watcherFactory.CreateWatcher("http://catalog")).ConfigureAwait(false))
        {
            Assert.NotNull(watcher);
            var tcs = new TaskCompletionSource<ServiceEndpointResolverResult>();
            watcher.OnEndpointsUpdated = tcs.SetResult;
            watcher.Start();
            var initialResult = await tcs.Task;
            Assert.NotNull(initialResult);
            Assert.True(initialResult.ResolvedSuccessfully);
 
            // We expect the CATALOG service to be resolved from the pass-through provider.
            Assert.Single(initialResult.EndpointSource.Endpoints);
            Assert.Equal(new DnsEndPoint("catalog", 80), initialResult.EndpointSource.Endpoints[0].EndPoint);
        }
    }
 
    // Ensures that pass-through resolution succeeds in scenarios where no scheme is specified during resolution.
    [Fact]
    public async Task ResolveServiceEndpoint_Fallback_NoScheme()
    {
        var configSource = new MemoryConfigurationSource
        {
            InitialData = new Dictionary<string, string?>
            {
                ["services:basket:default:0"] = "http://localhost:8080",
            }
        };
        var config = new ConfigurationBuilder().Add(configSource);
        var services = new ServiceCollection()
            .AddSingleton<IConfiguration>(config.Build())
            .AddServiceDiscovery() // Adds the configuration and pass-through providers.
            .BuildServiceProvider();
 
        var resolver = services.GetRequiredService<ServiceEndpointResolver>();
        var result = await resolver.GetEndpointsAsync("catalog", default);
        Assert.Equal(new DnsEndPoint("catalog", 0), result.Endpoints[0].EndPoint);
    }
}