using DigitalData.Auth.Abstractions; using DigitalData.Auth.API.Hubs; using DigitalData.Auth.Client; using DigitalData.Core.Abstractions.Security; using DigitalData.Core.Security; using DigitalData.Core.Security.Config; using DigitalData.Core.Security.RSAKey; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; namespace DigitalData.Auth.Tests.Client; // TODO: The test checks if the services are working. Performance measurement is ignored. Update it to measure performance as well. [TestFixture] public class AuthClientTests { private string _hubUrl; private Func, ServiceProvider> Build; private WebApplication? _app; private int _port; private readonly Queue _disposableAsync = new(); private static int AvailablePort { get { using var listener = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Loopback, 0); listener.Start(); int port = ((System.Net.IPEndPoint)listener.LocalEndpoint).Port; listener.Stop(); return port; } } private static IEnumerable CreatetokenDescriptors() { return [ new() { Issuer = "Foo", Audience = "Bar", Lifetime = new TimeSpan(1, 0, 0), Content = Instance.RSAFactory.CreatePrivateKeyPem() } ]; } private WebApplication CreateWebApplication(int port) { // Create builder and add SignalR service var builder = WebApplication.CreateBuilder(); builder.Services.AddSignalR(); builder.Services.AddCryptoFactory(new CryptoFactoryParams() { PemDirectory = "/", Decryptors = [new RSADecryptor()], TokenDescriptors = CreatetokenDescriptors() }); builder.Services.AddMemoryCache(); // Listen AvailablePort and map hub var app = builder.Build(); var url = $"http://localhost:{port}"; var hubRoute = "/auth-hub"; _hubUrl = url + hubRoute; app.Urls.Add(url); app.MapHub(hubRoute); app.Start(); _disposableAsync.Enqueue(app); return app; } private static CryptoFactoryParams GetCryptoFactoryParamsOf(WebApplication application) => application .Services.GetRequiredService>().Value; [SetUp] public void Setup() { Build = options => { var provider = new ServiceCollection() .AddAuthHubClient(options) .BuildServiceProvider(); _disposableAsync.Enqueue(provider); return provider; }; // Create and run test server _port = AvailablePort; _app = CreateWebApplication(_port); } [TearDown] public async Task TearDown() { // Stop test server if (_app is not null) { await _app.StopAsync(); Console.WriteLine("Test server stopped."); } while (_disposableAsync.Count > 0) await _disposableAsync.Dequeue().DisposeAsync(); } [Test] public async Task StartAsync_ShouldConnectSuccessfully() { // Arrange var provider = Build(opt => opt.Url = _hubUrl); var client = provider.GetRequiredService(); // Act await client.StartAsync(); // Assert Assert.That(client.IsConnected); } [Test] public async Task ReceiveMessage_ShouldCallOnMessageReceived() { // Arrange string rcv_issuer = string.Empty; string rcv_audience = string.Empty; string rcv_key = string.Empty; // Sender client var provider_sender = Build(opt => opt.Url = _hubUrl); var sender_client = provider_sender.GetRequiredService(); await sender_client.StartAsync(); // Receiver client var provider_receiver = Build(opt => { opt.Url = _hubUrl; opt.OnMessageReceived += (client, issuer, audience, key, logger) => { rcv_issuer = issuer; rcv_audience = audience; rcv_key = key; }; }); var client_receiver = provider_receiver.GetRequiredService(); await client_receiver.StartAsync(); string issuer = "issuer"; string audience = "audience"; string key = "key"; // Act await sender_client.SendPublicKeyAsync(issuer, audience, key); // delay fort getting answer await Task.Delay(2000); // Assert Assert.Multiple(() => { Assert.That(rcv_issuer, Is.EqualTo(issuer)); Assert.That(rcv_audience, Is.EqualTo(audience)); Assert.That(rcv_key, Is.EqualTo(key)); }); } [Test] public async Task GetPublicKey_ShouldReturnExpectedPublicKey() { // Arrange string? publicKey = null; var provider = Build(opt => { opt.Url = _hubUrl; opt.OnMessageReceived += (client, issuer, audience, key, logger) => publicKey = key; }); var client = provider.GetRequiredService(); await client.StartAsync(); var expectedPublicKey = GetCryptoFactoryParamsOf(_app).TokenDescriptors.Get("Foo", "Bar").PublicKey.Content; // Act await client.GetPublicKeyAsync("Foo", "Bar"); // wait for network await Task.Delay(2000); // Assert Assert.Multiple(() => { Assert.That(publicKey, Is.Not.Null); Assert.That(publicKey, Is.EqualTo(expectedPublicKey)); }); } [Test] public async Task StartAsync_ShouldUpdateAllPublicKey() { // Arrange var publicKey = new AsymmetricPublicKey() { Issuer = "Foo", Audience = "Bar" }; var provider = Build(opt => { opt.Url = _hubUrl; opt.PublicKeys.Add(publicKey); }); var client = provider.GetRequiredService(); await client.StartAsync(); // Act var expectedPublicKey = GetCryptoFactoryParamsOf(_app).TokenDescriptors.Get("Foo", "Bar").PublicKey; // wait for network await Task.Delay(2000); // Assert Assert.That(publicKey.Content, Is.EqualTo(expectedPublicKey.Content)); } [Test] public async Task Reconnected_ShouldUpdateAllPublicKey() { // Arrange var publicKey = new AsymmetricPublicKey() { Issuer = "Foo", Audience = "Bar" }; var provider = Build(opt => { opt.Url = _hubUrl; opt.PublicKeys.Add(publicKey); opt.RetryDelay = new TimeSpan(0, 0, 1); }); var client = provider.GetRequiredService(); await client.StartAsync(); // Act CancellationToken cancellationToken = default; await _app.StopAsync(cancellationToken); _app = null; var newApp = CreateWebApplication(_port); var expectedPublicKey = GetCryptoFactoryParamsOf(newApp).TokenDescriptors.Get("Foo", "Bar").PublicKey; // wait for network await Task.Delay(5000); // Assert Assert.Multiple(() => { Assert.That(cancellationToken.IsCancellationRequested, Is.False); Assert.That(publicKey.Content, Is.EqualTo(expectedPublicKey.Content)); }); } }