From 3b0428130ac18cd01626f02aa79cc0d2deb01657 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 11 Mar 2025 15:53:37 +0100 Subject: [PATCH] =?UTF-8?q?feat(AuthClient):=20implementiert=20IHostedServ?= =?UTF-8?q?ice.=20=20-=20Aktualisiert=20um=20AuthClient=20zu=20den=20Diens?= =?UTF-8?q?ten=20als=20Hosted=20Service=20hinzuzuf=C3=BCgen.=20=20-=20Der?= =?UTF-8?q?=20zugeh=C3=B6rige=20Unit-Test=20wurde=20aktualisiert,=20um=20I?= =?UTF-8?q?Host=20anstelle=20von=20IServiceProvider=20zu=20verwenden,=20um?= =?UTF-8?q?=20gehostete=20Dienste=20testen=20zu=20k=C3=B6nnen.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DigitalData.Auth.Client/AuthClient.cs | 10 +-- DigitalData.Auth.Client/DIExtensions.cs | 8 +++ .../DigitalData.Auth.Client.csproj | 1 + .../Client/AuthClientTests.cs | 67 ++++++++++++------- 4 files changed, 53 insertions(+), 33 deletions(-) diff --git a/DigitalData.Auth.Client/AuthClient.cs b/DigitalData.Auth.Client/AuthClient.cs index 3ce284b..2e69171 100644 --- a/DigitalData.Auth.Client/AuthClient.cs +++ b/DigitalData.Auth.Client/AuthClient.cs @@ -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); - } } \ No newline at end of file diff --git a/DigitalData.Auth.Client/DIExtensions.cs b/DigitalData.Auth.Client/DIExtensions.cs index 50b718a..c9114cf 100644 --- a/DigitalData.Auth.Client/DIExtensions.cs +++ b/DigitalData.Auth.Client/DIExtensions.cs @@ -18,6 +18,14 @@ public static class DIExtensions .AddSingleton() .TryAddSingleton(); + services.AddHostedService(sp => + { + var client = sp.GetRequiredService() as AuthClient; + if (client is not null) + return client; + else throw new Exception(); + }); + return services; } } \ No newline at end of file diff --git a/DigitalData.Auth.Client/DigitalData.Auth.Client.csproj b/DigitalData.Auth.Client/DigitalData.Auth.Client.csproj index beb9f1b..46116b3 100644 --- a/DigitalData.Auth.Client/DigitalData.Auth.Client.csproj +++ b/DigitalData.Auth.Client/DigitalData.Auth.Client.csproj @@ -29,6 +29,7 @@ + diff --git a/DigitalData.Auth.Tests/Client/AuthClientTests.cs b/DigitalData.Auth.Tests/Client/AuthClientTests.cs index bbd2c1d..eb3dbdd 100644 --- a/DigitalData.Auth.Tests/Client/AuthClientTests.cs +++ b/DigitalData.Auth.Tests/Client/AuthClientTests.cs @@ -18,7 +18,7 @@ public class AuthClientTests { private string _hubUrl; - private Func, ServiceProvider> Build; + private Func, IHost> Build; private WebApplication? _app; @@ -84,13 +84,18 @@ public class AuthClientTests { Build = options => { - var provider = new ServiceCollection() - .AddAuthHubClient(options: options) - .BuildServiceProvider(); - - _disposableAsync.Enqueue(provider); - - return provider; + var host = Host.CreateDefaultBuilder() + .ConfigureServices(services => + { + services.AddAuthHubClient(options: options) + .BuildServiceProvider(); + }) + .Build(); + + if(host is IAsyncDisposable disposable) + _disposableAsync.Enqueue(disposable); + + 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(); + var host = Build(opt => opt.Url = _hubUrl); + var client = host.Services.GetRequiredService(); // 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(); + var sender_host = Build(opt => opt.Url = _hubUrl); + var sender_client = sender_host.Services.GetRequiredService(); 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(); + var client_receiver = receiver_host.Services.GetRequiredService(); 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(); + var client = host.Services.GetRequiredService(); 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(); + var client = host.Services.GetRequiredService(); 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(); + var client = host.Services.GetRequiredService(); await client.StartAsync(); // Act CancellationToken cancellationToken = default; - await _app.StopAsync(cancellationToken); + await _app!.StopAsync(cancellationToken); _app = null; var newApp = CreateWebApplication(_port);