139 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			139 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using System;
 | 
						|
using System.Net;
 | 
						|
using System.Net.Http;
 | 
						|
using System.Text;
 | 
						|
using System.Threading;
 | 
						|
using FluentAssertions;
 | 
						|
using Microsoft.Extensions.Caching.Memory;
 | 
						|
using Microsoft.Extensions.Logging.Abstractions;
 | 
						|
using StellaOps.Excititor.Connectors.SUSE.RancherVEXHub.Authentication;
 | 
						|
using StellaOps.Excititor.Connectors.SUSE.RancherVEXHub.Configuration;
 | 
						|
using Xunit;
 | 
						|
 | 
						|
namespace StellaOps.Excititor.Connectors.SUSE.RancherVEXHub.Tests.Authentication;
 | 
						|
 | 
						|
public sealed class RancherHubTokenProviderTests
 | 
						|
{
 | 
						|
    private const string TokenResponse = "{\"access_token\":\"abc123\",\"token_type\":\"Bearer\",\"expires_in\":3600}";
 | 
						|
 | 
						|
    [Fact]
 | 
						|
    public async Task GetAccessTokenAsync_RequestsAndCachesToken()
 | 
						|
    {
 | 
						|
        var handler = TestHttpMessageHandler.RespondWith(request =>
 | 
						|
        {
 | 
						|
            request.Headers.Authorization.Should().NotBeNull();
 | 
						|
            request.Content.Should().NotBeNull();
 | 
						|
 | 
						|
            return new HttpResponseMessage(HttpStatusCode.OK)
 | 
						|
            {
 | 
						|
                Content = new StringContent(TokenResponse, Encoding.UTF8, "application/json"),
 | 
						|
            };
 | 
						|
        });
 | 
						|
 | 
						|
        var client = new HttpClient(handler)
 | 
						|
        {
 | 
						|
            BaseAddress = new Uri("https://identity.suse.com"),
 | 
						|
        };
 | 
						|
 | 
						|
        var factory = new SingleClientHttpClientFactory(client);
 | 
						|
        var cache = new MemoryCache(new MemoryCacheOptions());
 | 
						|
        var provider = new RancherHubTokenProvider(factory, cache, NullLogger<RancherHubTokenProvider>.Instance);
 | 
						|
 | 
						|
        var options = new RancherHubConnectorOptions
 | 
						|
        {
 | 
						|
            ClientId = "client",
 | 
						|
            ClientSecret = "secret",
 | 
						|
            TokenEndpoint = new Uri("https://identity.suse.com/oauth/token"),
 | 
						|
            Audience = "https://vexhub.suse.com",
 | 
						|
        };
 | 
						|
        options.Scopes.Clear();
 | 
						|
        options.Scopes.Add("hub.read");
 | 
						|
        options.Scopes.Add("hub.events");
 | 
						|
 | 
						|
        var token = await provider.GetAccessTokenAsync(options, CancellationToken.None);
 | 
						|
        token.Should().NotBeNull();
 | 
						|
        token!.Value.Should().Be("abc123");
 | 
						|
 | 
						|
        var cached = await provider.GetAccessTokenAsync(options, CancellationToken.None);
 | 
						|
        cached.Should().NotBeNull();
 | 
						|
        handler.InvocationCount.Should().Be(1);
 | 
						|
    }
 | 
						|
 | 
						|
    [Fact]
 | 
						|
    public async Task GetAccessTokenAsync_ReturnsNullWhenOfflinePreferred()
 | 
						|
    {
 | 
						|
        var handler = TestHttpMessageHandler.RespondWith(_ => new HttpResponseMessage(HttpStatusCode.OK));
 | 
						|
        var client = new HttpClient(handler)
 | 
						|
        {
 | 
						|
            BaseAddress = new Uri("https://identity.suse.com"),
 | 
						|
        };
 | 
						|
 | 
						|
        var factory = new SingleClientHttpClientFactory(client);
 | 
						|
        var cache = new MemoryCache(new MemoryCacheOptions());
 | 
						|
        var provider = new RancherHubTokenProvider(factory, cache, NullLogger<RancherHubTokenProvider>.Instance);
 | 
						|
        var options = new RancherHubConnectorOptions
 | 
						|
        {
 | 
						|
            PreferOfflineSnapshot = true,
 | 
						|
            ClientId = "client",
 | 
						|
            ClientSecret = "secret",
 | 
						|
            TokenEndpoint = new Uri("https://identity.suse.com/oauth/token"),
 | 
						|
        };
 | 
						|
 | 
						|
        var token = await provider.GetAccessTokenAsync(options, CancellationToken.None);
 | 
						|
        token.Should().BeNull();
 | 
						|
        handler.InvocationCount.Should().Be(0);
 | 
						|
    }
 | 
						|
 | 
						|
    [Fact]
 | 
						|
    public async Task GetAccessTokenAsync_ReturnsNullWithoutCredentials()
 | 
						|
    {
 | 
						|
        var handler = TestHttpMessageHandler.RespondWith(_ => new HttpResponseMessage(HttpStatusCode.OK));
 | 
						|
        var client = new HttpClient(handler)
 | 
						|
        {
 | 
						|
            BaseAddress = new Uri("https://identity.suse.com"),
 | 
						|
        };
 | 
						|
 | 
						|
        var factory = new SingleClientHttpClientFactory(client);
 | 
						|
        var cache = new MemoryCache(new MemoryCacheOptions());
 | 
						|
        var provider = new RancherHubTokenProvider(factory, cache, NullLogger<RancherHubTokenProvider>.Instance);
 | 
						|
        var options = new RancherHubConnectorOptions();
 | 
						|
 | 
						|
        var token = await provider.GetAccessTokenAsync(options, CancellationToken.None);
 | 
						|
        token.Should().BeNull();
 | 
						|
        handler.InvocationCount.Should().Be(0);
 | 
						|
    }
 | 
						|
 | 
						|
    private sealed class SingleClientHttpClientFactory : IHttpClientFactory
 | 
						|
    {
 | 
						|
        private readonly HttpClient _client;
 | 
						|
 | 
						|
        public SingleClientHttpClientFactory(HttpClient client)
 | 
						|
        {
 | 
						|
            _client = client;
 | 
						|
        }
 | 
						|
 | 
						|
        public HttpClient CreateClient(string name) => _client;
 | 
						|
    }
 | 
						|
 | 
						|
    private sealed class TestHttpMessageHandler : HttpMessageHandler
 | 
						|
    {
 | 
						|
        private readonly Func<HttpRequestMessage, HttpResponseMessage> _responseFactory;
 | 
						|
 | 
						|
        private TestHttpMessageHandler(Func<HttpRequestMessage, HttpResponseMessage> responseFactory)
 | 
						|
        {
 | 
						|
            _responseFactory = responseFactory;
 | 
						|
        }
 | 
						|
 | 
						|
        public int InvocationCount { get; private set; }
 | 
						|
 | 
						|
        public static TestHttpMessageHandler RespondWith(Func<HttpRequestMessage, HttpResponseMessage> responseFactory)
 | 
						|
            => new(responseFactory);
 | 
						|
 | 
						|
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 | 
						|
        {
 | 
						|
            InvocationCount++;
 | 
						|
            return Task.FromResult(_responseFactory(request));
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |