Skip to content

Commit

Permalink
Add Test MqttHostedServerStartup
Browse files Browse the repository at this point in the history
  • Loading branch information
beppemarazzi committed Jan 6, 2025
1 parent 672435c commit c4db2e9
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Source/MQTTnet.AspnetCore/MQTTnet.AspNetCore.csproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<!--TODO: remove before close https://github.com/dotnet/MQTTnet/pull/2102 WIP: Fix ignored exceptions in MqttHostedServer startup #2102 -->
<PropertyGroup>
<DefineConstants>$(DefineConstants);HOSTEDSERVER_WITHOUT_STARTUP_YIELD</DefineConstants>
</PropertyGroup>

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>MQTTnet.AspNetCore</AssemblyName>
Expand Down
56 changes: 56 additions & 0 deletions Source/MQTTnet.AspnetCore/MqttHostedServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

//TODO: remove before close https://github.com/dotnet/MQTTnet/pull/2102
////WIP: Fix ignored exceptions in MqttHostedServer startup #2102
#if HOSTEDSERVER_WITHOUT_STARTUP_YIELD

using System;
using System.Collections.Generic;
using System.Threading;
Expand Down Expand Up @@ -35,3 +39,55 @@ public override async Task StopAsync(CancellationToken cancellationToken)
await base.StopAsync(cancellationToken);
}
}
#else

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Server;

namespace MQTTnet.AspNetCore;

public sealed class MqttHostedServer : MqttServer, IHostedService
{
readonly IHostApplicationLifetime _hostApplicationLifetime;
readonly MqttServerFactory _mqttFactory;

public MqttHostedServer(
IHostApplicationLifetime hostApplicationLifetime,
MqttServerFactory mqttFactory,
MqttServerOptions options,
IEnumerable<IMqttServerAdapter> adapters,
IMqttNetLogger logger) : base(options, adapters, logger)
{
_mqttFactory = mqttFactory ?? throw new ArgumentNullException(nameof(mqttFactory));
_hostApplicationLifetime = hostApplicationLifetime;
}

public async Task StartAsync(CancellationToken cancellationToken)
{
// The yield makes sure that the hosted service is considered up and running.
await Task.Yield();

_hostApplicationLifetime.ApplicationStarted.Register(OnStarted);
}

public Task StopAsync(CancellationToken cancellationToken)
{
return StopAsync(_mqttFactory.CreateMqttServerStopOptionsBuilder().Build());
}

void OnStarted()
{
_ = StartAsync();
}
}

#endif
8 changes: 8 additions & 0 deletions Source/MQTTnet.AspnetCore/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,16 @@ public static void AddHostedMqttServer(this IServiceCollection services)
services.TryAddSingleton(new MqttServerFactory());

services.AddSingleton<MqttHostedServer>();
//TODO: remove before close https://github.com/dotnet/MQTTnet/pull/2102
////WIP: Fix ignored exceptions in MqttHostedServer startup #2102
#if HOSTEDSERVER_WITHOUT_STARTUP_YIELD
services.AddHostedService(s => s.GetService<MqttHostedServer>());
services.AddSingleton<MqttServer>(s => s.GetService<MqttHostedServer>().MqttServer);
#else
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
services.AddSingleton<MqttServer>(s => s.GetService<MqttHostedServer>());
#endif

}

public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
Expand Down
66 changes: 66 additions & 0 deletions Source/MQTTnet.Tests/ASP/MqttHostedServerStartup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.AspNetCore;
using System.Net.Sockets;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using MQTTnet.Server;
using Microsoft.AspNetCore.Hosting;

namespace MQTTnet.Tests.ASP
{
[TestClass]
public class MqttHostedServerStartup
{
private async Task TestStartup(bool useOccupiedPort)
{
using TcpListener l = new TcpListener(IPAddress.Any, 0);
l.Start();
int port = ((IPEndPoint)l.LocalEndpoint).Port;

if(!useOccupiedPort)
l.Stop();


var builder = WebApplication.CreateBuilder();
builder.WebHost.UseUrls("http://127.0.0.1:0");

builder.Services.AddMqttTcpServerAdapter();
builder.Services.AddHostedMqttServer(cfg =>
{
cfg
.WithDefaultEndpoint()
.WithDefaultEndpointPort(port);
});


var app = builder.Build();

if(!useOccupiedPort)
{
await app.StartAsync();
var server = app.Services.GetRequiredService<MqttServer>();
Assert.IsTrue(server.IsStarted);
await app.StopAsync();
}
else
{
await Assert.ThrowsExceptionAsync<SocketException>(() =>
app.StartAsync()
);
}
}

[TestMethod]
[DoNotParallelize]
public Task TestSuccessfullyStartup()
=> TestStartup(useOccupiedPort: false);

[TestMethod]
[DoNotParallelize]
public Task TestFailedStartup()
=> TestStartup(useOccupiedPort: true);

}
}

0 comments on commit c4db2e9

Please sign in to comment.