Skip to content

Commit 9466cb7

Browse files
committed
Add Test MqttHostedServerStartup
1 parent 672435c commit 9466cb7

File tree

4 files changed

+134
-0
lines changed

4 files changed

+134
-0
lines changed

Source/MQTTnet.AspnetCore/MQTTnet.AspNetCore.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

3+
<!--TODO: remove before close https://github.com/dotnet/MQTTnet/pull/2102 WIP: Fix ignored exceptions in MqttHostedServer startup #2102 -->
4+
<PropertyGroup>
5+
<DefineConstants>$(DefineConstants);HOSTEDSERVER_WITHOUT_STARTUP_YIELD</DefineConstants>
6+
</PropertyGroup>
7+
38
<PropertyGroup>
49
<TargetFramework>net8.0</TargetFramework>
510
<AssemblyName>MQTTnet.AspNetCore</AssemblyName>

Source/MQTTnet.AspnetCore/MqttHostedServer.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,56 @@
1+
//TODO: remove before close https://github.com/dotnet/MQTTnet/pull/2102
2+
////WIP: Fix ignored exceptions in MqttHostedServer startup #2102
3+
#if !HOSTEDSERVER_WITHOUT_STARTUP_YIELD
4+
// Licensed to the .NET Foundation under one or more agreements.
5+
// The .NET Foundation licenses this file to you under the MIT license.
6+
// See the LICENSE file in the project root for more information.
7+
8+
using System;
9+
using System.Collections.Generic;
10+
using System.Threading;
11+
using System.Threading.Tasks;
12+
using Microsoft.Extensions.Hosting;
13+
using MQTTnet.Diagnostics.Logger;
14+
using MQTTnet.Server;
15+
16+
namespace MQTTnet.AspNetCore;
17+
18+
public sealed class MqttHostedServer : MqttServer, IHostedService
19+
{
20+
readonly IHostApplicationLifetime _hostApplicationLifetime;
21+
readonly MqttServerFactory _mqttFactory;
22+
23+
public MqttHostedServer(
24+
IHostApplicationLifetime hostApplicationLifetime,
25+
MqttServerFactory mqttFactory,
26+
MqttServerOptions options,
27+
IEnumerable<IMqttServerAdapter> adapters,
28+
IMqttNetLogger logger) : base(options, adapters, logger)
29+
{
30+
_mqttFactory = mqttFactory ?? throw new ArgumentNullException(nameof(mqttFactory));
31+
_hostApplicationLifetime = hostApplicationLifetime;
32+
}
33+
34+
public async Task StartAsync(CancellationToken cancellationToken)
35+
{
36+
// The yield makes sure that the hosted service is considered up and running.
37+
await Task.Yield();
38+
39+
_hostApplicationLifetime.ApplicationStarted.Register(OnStarted);
40+
}
41+
42+
public Task StopAsync(CancellationToken cancellationToken)
43+
{
44+
return StopAsync(_mqttFactory.CreateMqttServerStopOptionsBuilder().Build());
45+
}
46+
47+
void OnStarted()
48+
{
49+
_ = StartAsync();
50+
}
51+
}
52+
53+
#else
154
// Licensed to the .NET Foundation under one or more agreements.
255
// The .NET Foundation licenses this file to you under the MIT license.
356
// See the LICENSE file in the project root for more information.
@@ -35,3 +88,5 @@ public override async Task StopAsync(CancellationToken cancellationToken)
3588
await base.StopAsync(cancellationToken);
3689
}
3790
}
91+
92+
#endif

Source/MQTTnet.AspnetCore/ServiceCollectionExtensions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,16 @@ public static void AddHostedMqttServer(this IServiceCollection services)
4545
services.TryAddSingleton(new MqttServerFactory());
4646

4747
services.AddSingleton<MqttHostedServer>();
48+
//TODO: remove before close https://github.com/dotnet/MQTTnet/pull/2102
49+
////WIP: Fix ignored exceptions in MqttHostedServer startup #2102
50+
#if !HOSTEDSERVER_WITHOUT_STARTUP_YIELD
51+
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
52+
services.AddSingleton<MqttServer>(s => s.GetService<MqttHostedServer>());
53+
#else
4854
services.AddHostedService(s => s.GetService<MqttHostedServer>());
4955
services.AddSingleton<MqttServer>(s => s.GetService<MqttHostedServer>().MqttServer);
56+
#endif
57+
5058
}
5159

5260
public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.VisualStudio.TestTools.UnitTesting;
3+
using MQTTnet.AspNetCore;
4+
using System.Net.Sockets;
5+
using System.Net;
6+
using System.Threading.Tasks;
7+
using Microsoft.Extensions.DependencyInjection;
8+
using MQTTnet.Server;
9+
using Microsoft.AspNetCore.Hosting;
10+
11+
namespace MQTTnet.Tests.ASP
12+
{
13+
[TestClass]
14+
public class MqttHostedServerStartup
15+
{
16+
private async Task TestStartup(bool useOccupiedPort)
17+
{
18+
using TcpListener l = new TcpListener(IPAddress.Any, 0);
19+
l.Start();
20+
int port = ((IPEndPoint)l.LocalEndpoint).Port;
21+
22+
if(!useOccupiedPort)
23+
l.Stop();
24+
25+
26+
var builder = WebApplication.CreateBuilder();
27+
builder.WebHost.UseUrls("http://127.0.0.1:0");
28+
29+
builder.Services.AddMqttTcpServerAdapter();
30+
builder.Services.AddHostedMqttServer(cfg =>
31+
{
32+
cfg
33+
.WithDefaultEndpoint()
34+
.WithDefaultEndpointPort(port);
35+
});
36+
37+
38+
var app = builder.Build();
39+
40+
if(!useOccupiedPort)
41+
{
42+
await app.StartAsync();
43+
var server = app.Services.GetRequiredService<MqttServer>();
44+
Assert.IsTrue(server.IsStarted);
45+
await app.StopAsync();
46+
}
47+
else
48+
{
49+
await Assert.ThrowsExceptionAsync<SocketException>(() =>
50+
app.StartAsync()
51+
);
52+
}
53+
}
54+
55+
[TestMethod]
56+
[DoNotParallelize]
57+
public Task TestSuccessfullyStartup()
58+
=> TestStartup(useOccupiedPort: false);
59+
60+
[TestMethod]
61+
[DoNotParallelize]
62+
public Task TestFailedStartup()
63+
=> TestStartup(useOccupiedPort: true);
64+
65+
}
66+
}

0 commit comments

Comments
 (0)