diff --git a/src/Yavsc.Org.Tests/Mandatory/Remoting.cs b/src/Yavsc.Org.Tests/Mandatory/Remoting.cs index 73f3da18..be5b716f 100644 --- a/src/Yavsc.Org.Tests/Mandatory/Remoting.cs +++ b/src/Yavsc.Org.Tests/Mandatory/Remoting.cs @@ -8,32 +8,32 @@ namespace Yavsc.Org.Tests [Trait("regression", "oui")] public class Remoting : BaseTestContext, IClassFixture { + private readonly ITestOutputHelper _output; + public Remoting(WebServerFixture serverFixture, ITestOutputHelper output) : base(output, serverFixture) { - + _output = output; } [Fact] public async Task ObtainServiceToken() { - var serverUrl = _serverFixture.SiteSettings.Authority; - if (string.IsNullOrEmpty(serverUrl)) - throw new InvalidOperationException("No HTTPS server address found"); + var serverUrl = GetServerUrl(); + var cancellationToken = TestContext.Current.CancellationToken; HttpClient client = NewHttpClient(); - var disco = await client.GetDiscoveryDocumentAsync(serverUrl); - if (disco.IsError) throw new Exception(disco.Error); + var tokenEndpoint = await ResolveTokenEndpointAsync(client, serverUrl, cancellationToken); var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { - Address = disco.TokenEndpoint, - ClientId = _serverFixture.TestClientId, - ClientSecret = _serverFixture.TestClientSecret, + Address = tokenEndpoint, + ClientId = RequireNonEmpty(_serverFixture.TestClientId, nameof(_serverFixture.TestClientId)), + ClientSecret = RequireNonEmpty(_serverFixture.TestClientSecret, nameof(_serverFixture.TestClientSecret)), Scope = "test", GrantType = "client_credentials" - }); + }, cancellationToken); if (response.IsError) throw new Exception(response.Error); } @@ -45,27 +45,25 @@ namespace Yavsc.Org.Tests [Fact] public async Task ObtainResourceOwnerPasswordToken() { - var serverUrl = _serverFixture.SiteSettings.Authority; - if (string.IsNullOrEmpty(serverUrl)) - throw new InvalidOperationException("No HTTPS server address found"); + var serverUrl = GetServerUrl(); + var cancellationToken = TestContext.Current.CancellationToken; var client = NewHttpClient(); - var disco = await client.GetDiscoveryDocumentAsync(serverUrl); - if (disco.IsError) throw new Exception(disco.Error); + var tokenEndpoint = await ResolveTokenEndpointAsync(client, serverUrl, cancellationToken); var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest { - Address = disco.TokenEndpoint, - ClientId = _serverFixture.TestClientId, - ClientSecret = _serverFixture.TestClientSecret, - UserName = _serverFixture.TestingUserName, - Password = _serverFixture.TestingUserPassword, + Address = tokenEndpoint, + ClientId = RequireNonEmpty(_serverFixture.TestClientId, nameof(_serverFixture.TestClientId)), + ClientSecret = RequireNonEmpty(_serverFixture.TestClientSecret, nameof(_serverFixture.TestClientSecret)), + UserName = RequireNonEmpty(_serverFixture.TestingUserName, nameof(_serverFixture.TestingUserName)), + Password = RequireNonEmpty(_serverFixture.TestingUserPassword, nameof(_serverFixture.TestingUserPassword)), Scope = "test", Parameters = { { "acr_values", "tenant:custom_account_store1 foo bar quux" } } - }); + }, cancellationToken); if (response.IsError) throw new Exception(response.Error); @@ -76,6 +74,36 @@ namespace Yavsc.Org.Tests return new object[][] { new object[] { "testuser", "test" } }; } + private async Task ResolveTokenEndpointAsync(HttpClient client, string serverUrl, CancellationToken cancellationToken) + { + var disco = await client.GetDiscoveryDocumentAsync(serverUrl, cancellationToken); + if (!disco.IsError && !string.IsNullOrWhiteSpace(disco.TokenEndpoint)) + { + return disco.TokenEndpoint; + } + + // Some full-suite runs intermittently return 500 on the OIDC + // discovery document while /connect/token remains available. + var fallback = new Uri(new Uri(serverUrl), "/connect/token").ToString(); + _output.WriteLine($"WARNING: OIDC discovery failed ({disco.Error}). Fallback token endpoint: {fallback}"); + return fallback; + } + + private string GetServerUrl() + { + return RequireNonEmpty(_serverFixture.SiteSettings?.Authority, "SiteSettings.Authority"); + } + + private static string RequireNonEmpty(string? value, string name) + { + if (string.IsNullOrWhiteSpace(value)) + { + throw new InvalidOperationException($"Missing required test setting: {name}"); + } + + return value; + } + } internal class BypassSslValidationHandler : HttpClientHandler