Skip to content

Commit 39fa43e

Browse files
committed
Add integration test
1 parent bd7eb0f commit 39fa43e

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed

src/Servers/Kestrel/Core/test/KestrelServerTests.cs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
1515
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
1616
using Microsoft.AspNetCore.Testing;
17+
using Microsoft.Extensions.Configuration;
1718
using Microsoft.Extensions.DependencyInjection;
1819
using Microsoft.Extensions.Logging;
1920
using Microsoft.Extensions.Options;
21+
using Microsoft.Extensions.Primitives;
2022
using Microsoft.Net.Http.Headers;
2123
using Moq;
2224
using Xunit;
@@ -455,6 +457,150 @@ public void StartingServerInitializesHeartbeat()
455457
}
456458
}
457459

460+
[Fact]
461+
public async Task ReloadsOnConfigurationChangeByDefault()
462+
{
463+
var currentConfig = new ConfigurationBuilder().AddInMemoryCollection(new[]
464+
{
465+
new KeyValuePair<string, string>("Endpoints:A:Url", "http://*:5000"),
466+
new KeyValuePair<string, string>("Endpoints:B:Url", "http://*:5001"),
467+
}).Build();
468+
469+
Action changeCallback = null;
470+
471+
var mockChangeToken = new Mock<IChangeToken>();
472+
mockChangeToken.Setup(t => t.RegisterChangeCallback(It.IsAny<Action<object>>(), It.IsAny<object>())).Returns<Action<object>, object>((callback, state) =>
473+
{
474+
changeCallback = () => callback(state);
475+
return Mock.Of<IDisposable>();
476+
});
477+
478+
var mockConfig = new Mock<IConfiguration>();
479+
mockConfig.Setup(c => c.GetSection(It.IsAny<string>())).Returns<string>(name => currentConfig.GetSection(name));
480+
mockConfig.Setup(c => c.GetChildren()).Returns(() => currentConfig.GetChildren());
481+
mockConfig.Setup(c => c.GetReloadToken()).Returns(() => mockChangeToken.Object);
482+
483+
var mockLoggerFactory = new Mock<ILoggerFactory>();
484+
mockLoggerFactory.Setup(m => m.CreateLogger(It.IsAny<string>())).Returns(Mock.Of<ILogger>());
485+
486+
var serviceCollection = new ServiceCollection();
487+
serviceCollection.AddSingleton(mockLoggerFactory.Object);
488+
serviceCollection.AddSingleton(Mock.Of<ILogger<KestrelServer>>());
489+
490+
var options = new KestrelServerOptions
491+
{
492+
ApplicationServices = serviceCollection.BuildServiceProvider(),
493+
};
494+
495+
options.Configure(mockConfig.Object);
496+
497+
var mockTransports = new List<Mock<IConnectionListener>>();
498+
var mockTransportFactory = new Mock<IConnectionListenerFactory>();
499+
mockTransportFactory
500+
.Setup(transportFactory => transportFactory.BindAsync(It.IsAny<EndPoint>(), It.IsAny<CancellationToken>()))
501+
.Returns<EndPoint, CancellationToken>((e, token) =>
502+
{
503+
var mockTransport = new Mock<IConnectionListener>();
504+
mockTransport
505+
.Setup(transport => transport.AcceptAsync(It.IsAny<CancellationToken>()))
506+
.Returns(new ValueTask<ConnectionContext>(result: null));
507+
mockTransport
508+
.Setup(transport => transport.EndPoint).Returns(e);
509+
510+
mockTransports.Add(mockTransport);
511+
512+
return new ValueTask<IConnectionListener>(mockTransport.Object);
513+
});
514+
515+
// Don't use "using". Dispose() could hang if test fails.
516+
var server = new KestrelServer(Options.Create(options), new List<IConnectionListenerFactory>() { mockTransportFactory.Object }, mockLoggerFactory.Object);
517+
518+
await server.StartAsync(new DummyApplication(), CancellationToken.None).DefaultTimeout();
519+
520+
mockTransportFactory.Verify(f => f.BindAsync(new IPEndPoint(IPAddress.IPv6Any, 5000), It.IsAny<CancellationToken>()), Times.Once);
521+
mockTransportFactory.Verify(f => f.BindAsync(new IPEndPoint(IPAddress.IPv6Any, 5001), It.IsAny<CancellationToken>()), Times.Once);
522+
mockTransportFactory.Verify(f => f.BindAsync(new IPEndPoint(IPAddress.IPv6Any, 5002), It.IsAny<CancellationToken>()), Times.Never);
523+
mockTransportFactory.Verify(f => f.BindAsync(new IPEndPoint(IPAddress.IPv6Any, 5003), It.IsAny<CancellationToken>()), Times.Never);
524+
525+
Assert.Equal(2, mockTransports.Count);
526+
527+
foreach (var mockTransport in mockTransports)
528+
{
529+
mockTransport.Verify(t => t.UnbindAsync(It.IsAny<CancellationToken>()), Times.Never);
530+
}
531+
532+
currentConfig = new ConfigurationBuilder().AddInMemoryCollection(new[]
533+
{
534+
new KeyValuePair<string, string>("Endpoints:A:Url", "http://*:5000"),
535+
new KeyValuePair<string, string>("Endpoints:B:Url", "http://*:5002"),
536+
new KeyValuePair<string, string>("Endpoints:C:Url", "http://*:5003"),
537+
}).Build();
538+
539+
changeCallback();
540+
541+
mockTransportFactory.Verify(f => f.BindAsync(new IPEndPoint(IPAddress.IPv6Any, 5000), It.IsAny<CancellationToken>()), Times.Once);
542+
mockTransportFactory.Verify(f => f.BindAsync(new IPEndPoint(IPAddress.IPv6Any, 5001), It.IsAny<CancellationToken>()), Times.Once);
543+
mockTransportFactory.Verify(f => f.BindAsync(new IPEndPoint(IPAddress.IPv6Any, 5002), It.IsAny<CancellationToken>()), Times.Once);
544+
mockTransportFactory.Verify(f => f.BindAsync(new IPEndPoint(IPAddress.IPv6Any, 5003), It.IsAny<CancellationToken>()), Times.Once);
545+
546+
Assert.Equal(4, mockTransports.Count);
547+
548+
foreach (var mockTransport in mockTransports)
549+
{
550+
if (((IPEndPoint)mockTransport.Object.EndPoint).Port == 5001)
551+
{
552+
mockTransport.Verify(t => t.UnbindAsync(It.IsAny<CancellationToken>()), Times.Once);
553+
}
554+
else
555+
{
556+
mockTransport.Verify(t => t.UnbindAsync(It.IsAny<CancellationToken>()), Times.Never);
557+
}
558+
}
559+
560+
currentConfig = new ConfigurationBuilder().AddInMemoryCollection(new[]
561+
{
562+
new KeyValuePair<string, string>("Endpoints:A:Url", "http://*:5000"),
563+
new KeyValuePair<string, string>("Endpoints:B:Url", "http://*:5002"),
564+
new KeyValuePair<string, string>("Endpoints:C:Url", "https://*:5003"),
565+
}).Build();
566+
567+
changeCallback();
568+
569+
mockTransportFactory.Verify(f => f.BindAsync(new IPEndPoint(IPAddress.IPv6Any, 5000), It.IsAny<CancellationToken>()), Times.Once);
570+
mockTransportFactory.Verify(f => f.BindAsync(new IPEndPoint(IPAddress.IPv6Any, 5001), It.IsAny<CancellationToken>()), Times.Once);
571+
mockTransportFactory.Verify(f => f.BindAsync(new IPEndPoint(IPAddress.IPv6Any, 5002), It.IsAny<CancellationToken>()), Times.Once);
572+
mockTransportFactory.Verify(f => f.BindAsync(new IPEndPoint(IPAddress.IPv6Any, 5003), It.IsAny<CancellationToken>()), Times.Exactly(2));
573+
574+
Assert.Equal(5, mockTransports.Count);
575+
576+
var firstPort5003TransportChecked = false;
577+
578+
foreach (var mockTransport in mockTransports)
579+
{
580+
var port = ((IPEndPoint)mockTransport.Object.EndPoint).Port;
581+
if (port == 5001)
582+
{
583+
mockTransport.Verify(t => t.UnbindAsync(It.IsAny<CancellationToken>()), Times.Once);
584+
}
585+
else if (port == 5003 && !firstPort5003TransportChecked)
586+
{
587+
mockTransport.Verify(t => t.UnbindAsync(It.IsAny<CancellationToken>()), Times.Once);
588+
firstPort5003TransportChecked = true;
589+
}
590+
else
591+
{
592+
mockTransport.Verify(t => t.UnbindAsync(It.IsAny<CancellationToken>()), Times.Never);
593+
}
594+
}
595+
596+
await server.StopAsync(CancellationToken.None).DefaultTimeout();
597+
598+
foreach (var mockTransport in mockTransports)
599+
{
600+
mockTransport.Verify(t => t.UnbindAsync(It.IsAny<CancellationToken>()), Times.Once);
601+
}
602+
}
603+
458604
private static KestrelServer CreateServer(KestrelServerOptions options, ILogger testLogger)
459605
{
460606
return new KestrelServer(Options.Create(options), new List<IConnectionListenerFactory>() { new MockTransportFactory() }, new LoggerFactory(new[] { new KestrelTestLoggerProvider(testLogger) }));

0 commit comments

Comments
 (0)