feat(AuthClient): implementiert IHostedService.

- Aktualisiert um AuthClient zu den Diensten als Hosted Service hinzuzufügen.
 - Der zugehörige Unit-Test wurde aktualisiert, um IHost anstelle von IServiceProvider zu verwenden, um gehostete Dienste testen zu können.
This commit is contained in:
Developer 02 2025-03-11 15:53:37 +01:00
parent 4ccf7a20b3
commit 3b0428130a
4 changed files with 51 additions and 31 deletions

View File

@ -1,11 +1,12 @@
using DigitalData.Auth.Abstractions;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace DigitalData.Auth.Client;
public class AuthClient : IAuthClient, IAsyncDisposable
public class AuthClient : IAuthClient, IHostedService
{
private readonly HubConnection _connection;
@ -60,11 +61,4 @@ public class AuthClient : IAuthClient, IAsyncDisposable
foreach (var publicKey in PublicKeys)
await GetPublicKeyAsync(publicKey.Issuer, publicKey.Audience);
}
public virtual async ValueTask DisposeAsync()
{
await _connection.StopAsync();
await _connection.DisposeAsync();
GC.SuppressFinalize(this);
}
}

View File

@ -18,6 +18,14 @@ public static class DIExtensions
.AddSingleton<IAuthClient, AuthClient>()
.TryAddSingleton<HubConnectionBuilder>();
services.AddHostedService(sp =>
{
var client = sp.GetRequiredService<IAuthClient>() as AuthClient;
if (client is not null)
return client;
else throw new Exception();
});
return services;
}
}

View File

@ -29,6 +29,7 @@
<PackageReference Include="DigitalData.Core.Abstractions" Version="3.3.0" />
<PackageReference Include="DigitalData.Core.Security" Version="1.0.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
</ItemGroup>
<ItemGroup>

View File

@ -18,7 +18,7 @@ public class AuthClientTests
{
private string _hubUrl;
private Func<Action<ClientParams>, ServiceProvider> Build;
private Func<Action<ClientParams>, IHost> Build;
private WebApplication? _app;
@ -84,13 +84,18 @@ public class AuthClientTests
{
Build = options =>
{
var provider = new ServiceCollection()
.AddAuthHubClient(options: options)
.BuildServiceProvider();
var host = Host.CreateDefaultBuilder()
.ConfigureServices(services =>
{
services.AddAuthHubClient(options: options)
.BuildServiceProvider();
})
.Build();
_disposableAsync.Enqueue(provider);
if(host is IAsyncDisposable disposable)
_disposableAsync.Enqueue(disposable);
return provider;
return host;
};
// Create and run test server
@ -104,26 +109,38 @@ public class AuthClientTests
// Stop test server
if (_app is not null)
{
await _app.StopAsync();
await _app.DisposeAsync();
Console.WriteLine("Test server stopped.");
}
while (_disposableAsync.Count > 0)
await _disposableAsync.Dequeue().DisposeAsync();
{
var disposable = _disposableAsync.Dequeue();
if (disposable is IHost host)
await host.StopAsync();
await disposable.DisposeAsync();
}
}
[Test]
public async Task StartAsync_ShouldConnectSuccessfully()
[TestCase(true, false, true, TestName = "ShouldStart_WhenHostStartsEvenIfClientDoesNot")]
[TestCase(false, true, true, TestName = "ShouldStart_WhenClientStartsEvenIfHostDoesNot")]
public async Task StartAsync_ShouldConnectSuccessfully(bool startHost, bool startClient, bool expectedIsConnected)
{
// Arrange
var provider = Build(opt => opt.Url = _hubUrl);
var client = provider.GetRequiredService<IAuthClient>();
var host = Build(opt => opt.Url = _hubUrl);
var client = host.Services.GetRequiredService<IAuthClient>();
// Act
await client.StartAsync();
if (startHost)
await host.StartAsync();
if (startClient)
await client.StartAsync();
// Assert
Assert.That(client.IsConnected);
Assert.That(client.IsConnected, Is.EqualTo(expectedIsConnected));
}
[Test]
@ -135,12 +152,12 @@ public class AuthClientTests
string rcv_key = string.Empty;
// Sender client
var provider_sender = Build(opt => opt.Url = _hubUrl);
var sender_client = provider_sender.GetRequiredService<IAuthClient>();
var sender_host = Build(opt => opt.Url = _hubUrl);
var sender_client = sender_host.Services.GetRequiredService<IAuthClient>();
await sender_client.StartAsync();
// Receiver client
var provider_receiver = Build(opt =>
var receiver_host = Build(opt =>
{
opt.Url = _hubUrl;
opt.OnPublicKeyReceived += (client, issuer, audience, key, logger) =>
@ -150,7 +167,7 @@ public class AuthClientTests
rcv_key = key;
};
});
var client_receiver = provider_receiver.GetRequiredService<IAuthClient>();
var client_receiver = receiver_host.Services.GetRequiredService<IAuthClient>();
await client_receiver.StartAsync();
string issuer = "issuer";
@ -177,12 +194,12 @@ public class AuthClientTests
{
// Arrange
string? publicKey = null;
var provider = Build(opt =>
var host = Build(opt =>
{
opt.Url = _hubUrl;
opt.OnPublicKeyReceived += (client, issuer, audience, key, logger) => publicKey = key;
});
var client = provider.GetRequiredService<IAuthClient>();
var client = host.Services.GetRequiredService<IAuthClient>();
await client.StartAsync();
@ -207,12 +224,12 @@ public class AuthClientTests
{
// Arrange
var publicKey = new ClientPublicKey() { Issuer = "Foo", Audience = "Bar" };
var provider = Build(opt =>
var host = Build(opt =>
{
opt.Url = _hubUrl;
opt.PublicKeys.Add(publicKey);
});
var client = provider.GetRequiredService<IAuthClient>();
var client = host.Services.GetRequiredService<IAuthClient>();
await client.StartAsync();
// Act
@ -230,18 +247,18 @@ public class AuthClientTests
{
// Arrange
var publicKey = new ClientPublicKey() { Issuer = "Foo", Audience = "Bar" };
var provider = Build(opt =>
var host = Build(opt =>
{
opt.Url = _hubUrl;
opt.PublicKeys.Add(publicKey);
opt.RetryDelay = new TimeSpan(0, 0, 1);
});
var client = provider.GetRequiredService<IAuthClient>();
var client = host.Services.GetRequiredService<IAuthClient>();
await client.StartAsync();
// Act
CancellationToken cancellationToken = default;
await _app.StopAsync(cancellationToken);
await _app!.StopAsync(cancellationToken);
_app = null;
var newApp = CreateWebApplication(_port);