From 6ae2ec4ce3df64c1267ab67ed58b5fb397047763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Wed, 29 May 2024 13:40:11 +0200 Subject: [PATCH 1/5] Remove dependency on Microsoft.Extensions.PlatformAbstractions Use [CallerFilePath] in order to get the path to the test documents, making it unnecessary to copy the files from the `documents` directory to the output directory. Also disable deterministic source paths so that the caller file path is never substituted with /_/ in case `ContinuousIntegrationBuild` is enabled. --- test/UnitTests/Infrastructure/FileName.cs | 12 +++--------- test/UnitTests/UnitTests.csproj | 9 ++------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/test/UnitTests/Infrastructure/FileName.cs b/test/UnitTests/Infrastructure/FileName.cs index 09b32800..7e2338ff 100644 --- a/test/UnitTests/Infrastructure/FileName.cs +++ b/test/UnitTests/Infrastructure/FileName.cs @@ -2,20 +2,14 @@ // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. using System.IO; +using System.Runtime.CompilerServices; namespace IdentityModel.UnitTests { internal static class FileName { - public static string Create(string name) - { -#if NETCOREAPP2_1 || NETCOREAPP3_1 || NET5_0 || NET6_0 || NET7_0 || NET8_0 - var fullName = Path.Combine(System.AppContext.BaseDirectory, "documents", name); -#else - var fullName = Path.Combine(Microsoft.Extensions.PlatformAbstractions.PlatformServices.Default.Application.ApplicationBasePath, "documents", name); -#endif + public static string Create(string name) => Path.Combine(UnitTestsPath(), "documents", name); - return fullName; - } + private static string UnitTestsPath([CallerFilePath] string path = "") => Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), "..")); } } diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index 67c33ec8..9d3771eb 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -3,6 +3,7 @@ false ../../key.snk true + false @@ -18,12 +19,6 @@ net6.0 - - - PreserveNewest - - - @@ -40,7 +35,7 @@ - + \ No newline at end of file From ffdb23044d89af86a3be94fdd37fe77233a6b78f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Wed, 29 May 2024 13:43:37 +0200 Subject: [PATCH 2/5] Remove conditions around Microsoft.AspNetCore.WebUtilities Microsoft.AspNetCore.WebUtilities 2.2.0 is used on all target frameworks so the conditions were unnecessary. --- test/UnitTests/UnitTests.csproj | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index 9d3771eb..0688ed66 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -24,18 +24,14 @@ + - - - - - \ No newline at end of file From fa65211cb59a9cf7c0b7cc9c7eca02a4f40b5f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Wed, 29 May 2024 14:12:27 +0200 Subject: [PATCH 3/5] Update all test dependencies Adapt the tests for FluentAssertions 6 breaking changes. --- .../PushedAuthorizationTests.cs | 8 +++--- .../TokenIntrospectionTests.cs | 4 +-- .../TokenRequestExtensionsRequestTests.cs | 28 +++++++++---------- test/UnitTests/TokenClientRequestTests.cs | 24 ++++++++-------- test/UnitTests/UnitTests.csproj | 8 +++--- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/test/UnitTests/HttpClientExtensions/PushedAuthorizationTests.cs b/test/UnitTests/HttpClientExtensions/PushedAuthorizationTests.cs index 2c8c35da..c4260aa4 100644 --- a/test/UnitTests/HttpClientExtensions/PushedAuthorizationTests.cs +++ b/test/UnitTests/HttpClientExtensions/PushedAuthorizationTests.cs @@ -188,7 +188,7 @@ public async Task Additional_request_parameters_should_be_handled_correctly() } [Fact] - public void Pushed_authorization_without_response_type_should_fail() + public async Task Pushed_authorization_without_response_type_should_fail() { var document = File.ReadAllText(FileName.Create("success_par_response.json")); var handler = new NetworkHandler(document, HttpStatusCode.OK); @@ -198,11 +198,11 @@ public void Pushed_authorization_without_response_type_should_fail() Func act = async () => await client.PushAuthorizationAsync(Request); - act.Should().Throw().And.ParamName.Should().Be("response_type"); + (await act.Should().ThrowAsync()).WithParameterName("response_type"); } [Fact] - public void Pushed_authorization_with_request_uri_should_fail() + public async Task Pushed_authorization_with_request_uri_should_fail() { var document = File.ReadAllText(FileName.Create("success_par_response.json")); var handler = new NetworkHandler(document, HttpStatusCode.OK); @@ -213,7 +213,7 @@ public void Pushed_authorization_with_request_uri_should_fail() Func act = async () => await client.PushAuthorizationAsync(Request); - act.Should().Throw().And.ParamName.Should().Be("request_uri"); + (await act.Should().ThrowAsync()).WithParameterName("request_uri"); } } } \ No newline at end of file diff --git a/test/UnitTests/HttpClientExtensions/TokenIntrospectionTests.cs b/test/UnitTests/HttpClientExtensions/TokenIntrospectionTests.cs index 68991fd9..fea9ef67 100644 --- a/test/UnitTests/HttpClientExtensions/TokenIntrospectionTests.cs +++ b/test/UnitTests/HttpClientExtensions/TokenIntrospectionTests.cs @@ -219,7 +219,7 @@ public async Task Repeating_a_request_should_succeed() } [Fact] - public void Request_without_token_should_fail() + public async Task Request_without_token_should_fail() { var document = File.ReadAllText(FileName.Create("success_introspection_response.json")); var handler = new NetworkHandler(document, HttpStatusCode.OK); @@ -231,7 +231,7 @@ public void Request_without_token_should_fail() Func act = async () => await client.IntrospectTokenAsync(new TokenIntrospectionRequest()); - act.Should().Throw().And.ParamName.Should().Be("token"); + (await act.Should().ThrowAsync()).WithParameterName("token"); } [Fact] diff --git a/test/UnitTests/HttpClientExtensions/TokenRequestExtensionsRequestTests.cs b/test/UnitTests/HttpClientExtensions/TokenRequestExtensionsRequestTests.cs index 1c6a2fd8..53659c0f 100644 --- a/test/UnitTests/HttpClientExtensions/TokenRequestExtensionsRequestTests.cs +++ b/test/UnitTests/HttpClientExtensions/TokenRequestExtensionsRequestTests.cs @@ -221,13 +221,13 @@ public async Task dpop_nonce_should_be_returned() [Fact] - public void Explicit_null_parameters_should_not_fail_() + public async Task Explicit_null_parameters_should_not_fail_() { Func act = async () => await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { ClientId = "client", Parameters = null }); - act.Should().NotThrow(); + await act.Should().NotThrowAsync(); } [Fact] @@ -250,12 +250,12 @@ public async Task Device_request_should_have_correct_format() } [Fact] - public void Device_request_without_device_code_should_fail() + public async Task Device_request_without_device_code_should_fail() { Func act = async () => await _client.RequestDeviceTokenAsync(new DeviceTokenRequest { ClientId = "device" }); - act.Should().Throw().And.ParamName.Should().Be("device_code"); + (await act.Should().ThrowAsync()).WithParameterName("device_code"); } [Fact] @@ -314,11 +314,11 @@ public async Task Password_request_without_password_should_have_correct_format() } [Fact] - public void Password_request_without_username_should_fail() + public async Task Password_request_without_username_should_fail() { Func act = async () => await _client.RequestPasswordTokenAsync(new PasswordTokenRequest()); - act.Should().Throw().And.ParamName.Should().Be("username"); + (await act.Should().ThrowAsync()).WithParameterName("username"); } [Fact] @@ -355,7 +355,7 @@ public async Task Code_request_should_have_correct_format() } [Fact] - public void Code_request_without_code_should_fail() + public async Task Code_request_without_code_should_fail() { Func act = async () => await _client.RequestAuthorizationCodeTokenAsync( new AuthorizationCodeTokenRequest @@ -363,11 +363,11 @@ public void Code_request_without_code_should_fail() RedirectUri = "uri" }); - act.Should().Throw().And.ParamName.Should().Be("code"); + (await act.Should().ThrowAsync()).WithParameterName("code"); } [Fact] - public void Code_request_without_redirect_uri_should_fail() + public async Task Code_request_without_redirect_uri_should_fail() { Func act = async () => await _client.RequestAuthorizationCodeTokenAsync( new AuthorizationCodeTokenRequest @@ -375,7 +375,7 @@ public void Code_request_without_redirect_uri_should_fail() Code = "code" }); - act.Should().Throw().And.ParamName.Should().Be("redirect_uri"); + (await act.Should().ThrowAsync()).WithParameterName("redirect_uri"); } [Fact] @@ -408,11 +408,11 @@ public async Task Refresh_request_should_have_correct_format() } [Fact] - public void Refresh_request_without_refresh_token_should_fail() + public async Task Refresh_request_without_refresh_token_should_fail() { Func act = async () => await _client.RequestRefreshTokenAsync(new RefreshTokenRequest()); - act.Should().Throw().And.ParamName.Should().Be("refresh_token"); + (await act.Should().ThrowAsync()).WithParameterName("refresh_token"); } [Fact] @@ -492,11 +492,11 @@ public async Task Backchannel_authentication_request_should_have_correct_format( } [Fact] - public void Setting_no_grant_type_should_fail() + public async Task Setting_no_grant_type_should_fail() { Func act = async () => await _client.RequestTokenAsync(new TokenRequest()); - act.Should().Throw().And.ParamName.Should().Be("grant_type"); + (await act.Should().ThrowAsync()).WithParameterName("grant_type"); } [Fact] diff --git a/test/UnitTests/TokenClientRequestTests.cs b/test/UnitTests/TokenClientRequestTests.cs index 5b873afa..777d68dd 100644 --- a/test/UnitTests/TokenClientRequestTests.cs +++ b/test/UnitTests/TokenClientRequestTests.cs @@ -78,13 +78,13 @@ public async Task Device_request_should_have_correct_format() } [Fact] - public void Device_request_without_device_code_should_fail() + public async Task Device_request_without_device_code_should_fail() { var tokenClient = new TokenClient(_client, new TokenClientOptions { ClientId = "device" }); Func act = async () => await tokenClient.RequestDeviceTokenAsync(null); - act.Should().Throw().And.ParamName.Should().Be("device_code"); + (await act.Should().ThrowAsync()).WithParameterName("device_code"); } [Fact] @@ -131,13 +131,13 @@ public async Task Password_request_without_password_should_have_correct_format() } [Fact] - public void Password_request_without_username_should_fail() + public async Task Password_request_without_username_should_fail() { var tokenClient = new TokenClient(_client, new TokenClientOptions()); Func act = async () => await tokenClient.RequestPasswordTokenAsync(userName: null); - act.Should().Throw().And.ParamName.Should().Be("username"); + (await act.Should().ThrowAsync()).WithParameterName("username"); } [Fact] @@ -164,23 +164,23 @@ public async Task Code_request_should_have_correct_format() } [Fact] - public void Code_request_without_code_should_fail() + public async Task Code_request_without_code_should_fail() { var tokenClient = new TokenClient(_client, new TokenClientOptions()); Func act = async () => await tokenClient.RequestAuthorizationCodeTokenAsync(code: null, redirectUri: "uri", codeVerifier: "verifier"); - act.Should().Throw().And.ParamName.Should().Be("code"); + (await act.Should().ThrowAsync()).WithParameterName("code"); } [Fact] - public void Code_request_without_redirect_uri_should_fail() + public async Task Code_request_without_redirect_uri_should_fail() { var tokenClient = new TokenClient(_client, new TokenClientOptions()); Func act = async () => await tokenClient.RequestAuthorizationCodeTokenAsync(code: "code", redirectUri: null, codeVerifier: "verifier"); - act.Should().Throw().And.ParamName.Should().Be("redirect_uri"); + (await act.Should().ThrowAsync()).WithParameterName("redirect_uri"); } [Fact] @@ -204,23 +204,23 @@ public async Task Refresh_request_should_have_correct_format() } [Fact] - public void Refresh_request_without_refresh_token_should_fail() + public async Task Refresh_request_without_refresh_token_should_fail() { var tokenClient = new TokenClient(_client, new TokenClientOptions()); Func act = async () => await tokenClient.RequestRefreshTokenAsync(refreshToken: null, scope: "scope"); - act.Should().Throw().And.ParamName.Should().Be("refresh_token"); + (await act.Should().ThrowAsync()).WithParameterName("refresh_token"); } [Fact] - public void Setting_no_grant_type_should_fail() + public async Task Setting_no_grant_type_should_fail() { var tokenClient = new TokenClient(_client, new TokenClientOptions()); Func act = async () => await tokenClient.RequestTokenAsync(grantType: null); - act.Should().Throw().And.ParamName.Should().Be("grant_type"); + (await act.Should().ThrowAsync()).WithParameterName("grant_type"); } [Fact] diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index 0688ed66..704debc8 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -25,10 +25,10 @@ - - - - + + + + From 77a2341de7796fa770abef8b05c409d9a206adbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Wed, 29 May 2024 13:57:35 +0200 Subject: [PATCH 4/5] Simplify the definition of the target frameworks Remove conditional compilation around osx-arm64 which was introduced in #428 but is not necessary anymore now that the library is targeting .NET 6 onwards. Also avoid duplicating the .NET target framework but add net462 and net472 when running on Windows (on both the main and unit tests projects). --- src/IdentityModel.csproj | 8 ++++---- test/UnitTests/UnitTests.csproj | 11 +++-------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/IdentityModel.csproj b/src/IdentityModel.csproj index 9acda323..6121e7e4 100644 --- a/src/IdentityModel.csproj +++ b/src/IdentityModel.csproj @@ -36,12 +36,12 @@ - - net462;net472;netstandard2.0;net6.0 - - + netstandard2.0;net6.0 + + net462;net472;$(TargetFrameworks) + diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index 704debc8..71db5a1d 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -7,16 +7,11 @@ - - net462;net472;net6.0;net7.0;net8.0 - - - + net6.0;net7.0;net8.0 - - - net6.0 + + net462;net472;$(TargetFrameworks) From ad5abeb624c23b256d5f84e89e483593257344ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Wed, 29 May 2024 14:57:50 +0200 Subject: [PATCH 5/5] Improve the assertions in TokenIntrospectionTests The `Should().BeEquivalentTo()` versions are much easier to read, actually test all the properties and in case of failure the error messages are excellent. --- .../TokenIntrospectionTests.cs | 237 ++++++++---------- 1 file changed, 100 insertions(+), 137 deletions(-) diff --git a/test/UnitTests/HttpClientExtensions/TokenIntrospectionTests.cs b/test/UnitTests/HttpClientExtensions/TokenIntrospectionTests.cs index fea9ef67..cf8aa6a7 100644 --- a/test/UnitTests/HttpClientExtensions/TokenIntrospectionTests.cs +++ b/test/UnitTests/HttpClientExtensions/TokenIntrospectionTests.cs @@ -3,12 +3,13 @@ using FluentAssertions; using IdentityModel.Client; -using Microsoft.AspNetCore.WebUtilities; using System; +using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; using System.Net.Http; +using System.Security.Claims; +using System.Text.Json; using System.Threading.Tasks; using Xunit; @@ -33,24 +34,22 @@ public async Task Http_request_should_have_correct_format() request.Headers.Add("custom", "custom"); request.Properties.Add("custom", "custom"); - var response = await client.IntrospectTokenAsync(request); + _ = await client.IntrospectTokenAsync(request); var httpRequest = handler.Request; httpRequest.Method.Should().Be(HttpMethod.Post); httpRequest.RequestUri.Should().Be(new Uri(Endpoint)); httpRequest.Content.Should().NotBeNull(); - - var headers = httpRequest.Headers; - headers.Count().Should().Be(2); - headers.Should().Contain(h => h.Key == "custom" && h.Value.First() == "custom"); - - var properties = httpRequest.Properties; - properties.Count.Should().Be(1); - - var prop = properties.First(); - prop.Key.Should().Be("custom"); - ((string)prop.Value).Should().Be("custom"); + httpRequest.Headers.Should().BeEquivalentTo(new Dictionary + { + ["Accept"] = new[] { "application/json" }, + ["custom"] = new[] { "custom" }, + }); + httpRequest.Properties.Should().BeEquivalentTo(new Dictionary + { + ["custom"] = "custom", + }); } [Fact] @@ -73,29 +72,22 @@ public async Task Success_protocol_response_should_be_handled_correctly() response.ErrorType.Should().Be(ResponseErrorType.None); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.IsActive.Should().BeTrue(); - response.Claims.Should().NotBeEmpty(); - - var audiences = response.Claims.Where(c => c.Type == "aud").ToList(); - audiences.Count().Should().Be(2); - audiences.First().Value.Should().Be("https://idsvr4/resources"); - audiences.Skip(1).First().Value.Should().Be("api1"); - - response.Claims.First(c => c.Type == "iss").Value.Should().Be("https://idsvr4"); - response.Claims.First(c => c.Type == "nbf").Value.Should().Be("1475824871"); - response.Claims.First(c => c.Type == "exp").Value.Should().Be("1475828471"); - response.Claims.First(c => c.Type == "client_id").Value.Should().Be("client"); - response.Claims.First(c => c.Type == "sub").Value.Should().Be("1"); - response.Claims.First(c => c.Type == "auth_time").Value.Should().Be("1475824871"); - response.Claims.First(c => c.Type == "idp").Value.Should().Be("local"); - response.Claims.First(c => c.Type == "amr").Value.Should().Be("password"); - response.Claims.First(c => c.Type == "active").Value.Should().Be("true"); - - var scopes = response.Claims.Where(c => c.Type == "scope").ToList(); - scopes.Count().Should().Be(2); - scopes.First().Value.Should().Be("api1"); - scopes.First().Issuer.Should().Be("https://idsvr4"); - scopes.Skip(1).First().Value.Should().Be("api2"); - scopes.Skip(1).First().Issuer.Should().Be("https://idsvr4"); + response.Claims.Should().BeEquivalentTo(new[] + { + new Claim("aud", "https://idsvr4/resources", ClaimValueTypes.String, "https://idsvr4"), + new Claim("aud", "api1", ClaimValueTypes.String, "https://idsvr4"), + new Claim("iss", "https://idsvr4", ClaimValueTypes.String, "https://idsvr4"), + new Claim("nbf", "1475824871", ClaimValueTypes.String, "https://idsvr4"), + new Claim("exp", "1475828471", ClaimValueTypes.String, "https://idsvr4"), + new Claim("client_id", "client", ClaimValueTypes.String, "https://idsvr4"), + new Claim("sub", "1", ClaimValueTypes.String, "https://idsvr4"), + new Claim("auth_time", "1475824871", ClaimValueTypes.String, "https://idsvr4"), + new Claim("idp", "local", ClaimValueTypes.String, "https://idsvr4"), + new Claim("amr", "password", ClaimValueTypes.String, "https://idsvr4"), + new Claim("active", "true", ClaimValueTypes.String, "https://idsvr4"), + new Claim("scope", "api1", ClaimValueTypes.String, "https://idsvr4"), + new Claim("scope", "api2", ClaimValueTypes.String, "https://idsvr4"), + }); } [Fact] @@ -118,26 +110,21 @@ public async Task Success_protocol_response_without_issuer_should_be_handled_cor response.ErrorType.Should().Be(ResponseErrorType.None); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.IsActive.Should().BeTrue(); - response.Claims.Should().NotBeEmpty(); - - var audiences = response.Claims.Where(c => c.Type == "aud").ToList(); - audiences.Count().Should().Be(2); - audiences.First().Value.Should().Be("https://idsvr4/resources"); - audiences.Skip(1).First().Value.Should().Be("api1"); - - response.Claims.First(c => c.Type == "nbf").Value.Should().Be("1475824871"); - response.Claims.First(c => c.Type == "exp").Value.Should().Be("1475828471"); - response.Claims.First(c => c.Type == "client_id").Value.Should().Be("client"); - response.Claims.First(c => c.Type == "sub").Value.Should().Be("1"); - response.Claims.First(c => c.Type == "auth_time").Value.Should().Be("1475824871"); - response.Claims.First(c => c.Type == "idp").Value.Should().Be("local"); - response.Claims.First(c => c.Type == "amr").Value.Should().Be("password"); - response.Claims.First(c => c.Type == "active").Value.Should().Be("true"); - - var scopes = response.Claims.Where(c => c.Type == "scope").ToList(); - scopes.Count().Should().Be(2); - scopes.First().Value.Should().Be("api1"); - scopes.Skip(1).First().Value.Should().Be("api2"); + response.Claims.Should().BeEquivalentTo(new[] + { + new Claim("aud", "https://idsvr4/resources", ClaimValueTypes.String, "LOCAL AUTHORITY"), + new Claim("aud", "api1", ClaimValueTypes.String, "LOCAL AUTHORITY"), + new Claim("nbf", "1475824871", ClaimValueTypes.String, "LOCAL AUTHORITY"), + new Claim("exp", "1475828471", ClaimValueTypes.String, "LOCAL AUTHORITY"), + new Claim("client_id", "client", ClaimValueTypes.String, "LOCAL AUTHORITY"), + new Claim("sub", "1", ClaimValueTypes.String, "LOCAL AUTHORITY"), + new Claim("auth_time", "1475824871", ClaimValueTypes.String, "LOCAL AUTHORITY"), + new Claim("idp", "local", ClaimValueTypes.String, "LOCAL AUTHORITY"), + new Claim("amr", "password", ClaimValueTypes.String, "LOCAL AUTHORITY"), + new Claim("active", "true", ClaimValueTypes.String, "LOCAL AUTHORITY"), + new Claim("scope", "api1", ClaimValueTypes.String, "LOCAL AUTHORITY"), + new Claim("scope", "api2", ClaimValueTypes.String, "LOCAL AUTHORITY"), + }); } [Fact] @@ -162,29 +149,22 @@ public async Task Repeating_a_request_should_succeed() response.ErrorType.Should().Be(ResponseErrorType.None); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.IsActive.Should().BeTrue(); - response.Claims.Should().NotBeEmpty(); - - var audiences = response.Claims.Where(c => c.Type == "aud").ToList(); - audiences.Count().Should().Be(2); - audiences.First().Value.Should().Be("https://idsvr4/resources"); - audiences.Skip(1).First().Value.Should().Be("api1"); - - response.Claims.First(c => c.Type == "iss").Value.Should().Be("https://idsvr4"); - response.Claims.First(c => c.Type == "nbf").Value.Should().Be("1475824871"); - response.Claims.First(c => c.Type == "exp").Value.Should().Be("1475828471"); - response.Claims.First(c => c.Type == "client_id").Value.Should().Be("client"); - response.Claims.First(c => c.Type == "sub").Value.Should().Be("1"); - response.Claims.First(c => c.Type == "auth_time").Value.Should().Be("1475824871"); - response.Claims.First(c => c.Type == "idp").Value.Should().Be("local"); - response.Claims.First(c => c.Type == "amr").Value.Should().Be("password"); - response.Claims.First(c => c.Type == "active").Value.Should().Be("true"); - - var scopes = response.Claims.Where(c => c.Type == "scope").ToList(); - scopes.Count().Should().Be(2); - scopes.First().Value.Should().Be("api1"); - scopes.First().Issuer.Should().Be("https://idsvr4"); - scopes.Skip(1).First().Value.Should().Be("api2"); - scopes.Skip(1).First().Issuer.Should().Be("https://idsvr4"); + response.Claims.Should().BeEquivalentTo(new[] + { + new Claim("aud", "https://idsvr4/resources", ClaimValueTypes.String, "https://idsvr4"), + new Claim("aud", "api1", ClaimValueTypes.String, "https://idsvr4"), + new Claim("iss", "https://idsvr4", ClaimValueTypes.String, "https://idsvr4"), + new Claim("nbf", "1475824871", ClaimValueTypes.String, "https://idsvr4"), + new Claim("exp", "1475828471", ClaimValueTypes.String, "https://idsvr4"), + new Claim("client_id", "client", ClaimValueTypes.String, "https://idsvr4"), + new Claim("sub", "1", ClaimValueTypes.String, "https://idsvr4"), + new Claim("auth_time", "1475824871", ClaimValueTypes.String, "https://idsvr4"), + new Claim("idp", "local", ClaimValueTypes.String, "https://idsvr4"), + new Claim("amr", "password", ClaimValueTypes.String, "https://idsvr4"), + new Claim("active", "true", ClaimValueTypes.String, "https://idsvr4"), + new Claim("scope", "api1", ClaimValueTypes.String, "https://idsvr4"), + new Claim("scope", "api2", ClaimValueTypes.String, "https://idsvr4"), + }); // repeat response = await client.IntrospectTokenAsync(request); @@ -193,29 +173,22 @@ public async Task Repeating_a_request_should_succeed() response.ErrorType.Should().Be(ResponseErrorType.None); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.IsActive.Should().BeTrue(); - response.Claims.Should().NotBeEmpty(); - - audiences = response.Claims.Where(c => c.Type == "aud").ToList(); - audiences.Count().Should().Be(2); - audiences.First().Value.Should().Be("https://idsvr4/resources"); - audiences.Skip(1).First().Value.Should().Be("api1"); - - response.Claims.First(c => c.Type == "iss").Value.Should().Be("https://idsvr4"); - response.Claims.First(c => c.Type == "nbf").Value.Should().Be("1475824871"); - response.Claims.First(c => c.Type == "exp").Value.Should().Be("1475828471"); - response.Claims.First(c => c.Type == "client_id").Value.Should().Be("client"); - response.Claims.First(c => c.Type == "sub").Value.Should().Be("1"); - response.Claims.First(c => c.Type == "auth_time").Value.Should().Be("1475824871"); - response.Claims.First(c => c.Type == "idp").Value.Should().Be("local"); - response.Claims.First(c => c.Type == "amr").Value.Should().Be("password"); - response.Claims.First(c => c.Type == "active").Value.Should().Be("true"); - - scopes = response.Claims.Where(c => c.Type == "scope").ToList(); - scopes.Count().Should().Be(2); - scopes.First().Value.Should().Be("api1"); - scopes.First().Issuer.Should().Be("https://idsvr4"); - scopes.Skip(1).First().Value.Should().Be("api2"); - scopes.Skip(1).First().Issuer.Should().Be("https://idsvr4"); + response.Claims.Should().BeEquivalentTo(new[] + { + new Claim("aud", "https://idsvr4/resources", ClaimValueTypes.String, "https://idsvr4"), + new Claim("aud", "api1", ClaimValueTypes.String, "https://idsvr4"), + new Claim("iss", "https://idsvr4", ClaimValueTypes.String, "https://idsvr4"), + new Claim("nbf", "1475824871", ClaimValueTypes.String, "https://idsvr4"), + new Claim("exp", "1475828471", ClaimValueTypes.String, "https://idsvr4"), + new Claim("client_id", "client", ClaimValueTypes.String, "https://idsvr4"), + new Claim("sub", "1", ClaimValueTypes.String, "https://idsvr4"), + new Claim("auth_time", "1475824871", ClaimValueTypes.String, "https://idsvr4"), + new Claim("idp", "local", ClaimValueTypes.String, "https://idsvr4"), + new Claim("amr", "password", ClaimValueTypes.String, "https://idsvr4"), + new Claim("active", "true", ClaimValueTypes.String, "https://idsvr4"), + new Claim("scope", "api1", ClaimValueTypes.String, "https://idsvr4"), + new Claim("scope", "api2", ClaimValueTypes.String, "https://idsvr4"), + }); } [Fact] @@ -250,13 +223,14 @@ public async Task Malformed_response_document_should_be_handled_correctly() response.IsError.Should().BeTrue(); response.ErrorType.Should().Be(ResponseErrorType.Exception); response.Raw.Should().Be("invalid"); - response.Exception.Should().NotBeNull(); + response.Exception.Should().BeAssignableTo(); } [Fact] public async Task Exception_should_be_handled_correctly() { - var handler = new NetworkHandler(new Exception("exception")); + var exception = new Exception("exception"); + var handler = new NetworkHandler(exception); var client = new HttpClient(handler); var response = await client.IntrospectTokenAsync(new TokenIntrospectionRequest @@ -268,7 +242,7 @@ public async Task Exception_should_be_handled_correctly() response.IsError.Should().BeTrue(); response.ErrorType.Should().Be(ResponseErrorType.Exception); response.Error.Should().Be("exception"); - response.Exception.Should().NotBeNull(); + response.Exception.Should().BeSameAs(exception); } [Fact] @@ -306,29 +280,22 @@ public async Task Legacy_protocol_response_should_be_handled_correctly() response.ErrorType.Should().Be(ResponseErrorType.None); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.IsActive.Should().BeTrue(); - response.Claims.Should().NotBeEmpty(); - - var audiences = response.Claims.Where(c => c.Type == "aud").ToList(); - audiences.Count().Should().Be(2); - audiences.First().Value.Should().Be("https://idsvr4/resources"); - audiences.Skip(1).First().Value.Should().Be("api1"); - - response.Claims.First(c => c.Type == "iss").Value.Should().Be("https://idsvr4"); - response.Claims.First(c => c.Type == "nbf").Value.Should().Be("1475824871"); - response.Claims.First(c => c.Type == "exp").Value.Should().Be("1475828471"); - response.Claims.First(c => c.Type == "client_id").Value.Should().Be("client"); - response.Claims.First(c => c.Type == "sub").Value.Should().Be("1"); - response.Claims.First(c => c.Type == "auth_time").Value.Should().Be("1475824871"); - response.Claims.First(c => c.Type == "idp").Value.Should().Be("local"); - response.Claims.First(c => c.Type == "amr").Value.Should().Be("password"); - response.Claims.First(c => c.Type == "active").Value.Should().Be("true"); - - var scopes = response.Claims.Where(c => c.Type == "scope").ToList(); - scopes.Count().Should().Be(2); - scopes.First().Value.Should().Be("api1"); - scopes.First().Issuer.Should().Be("https://idsvr4"); - scopes.Skip(1).First().Value.Should().Be("api2"); - scopes.Skip(1).First().Issuer.Should().Be("https://idsvr4"); + response.Claims.Should().BeEquivalentTo(new[] + { + new Claim("aud", "https://idsvr4/resources", ClaimValueTypes.String, "https://idsvr4"), + new Claim("aud", "api1", ClaimValueTypes.String, "https://idsvr4"), + new Claim("iss", "https://idsvr4", ClaimValueTypes.String, "https://idsvr4"), + new Claim("nbf", "1475824871", ClaimValueTypes.String, "https://idsvr4"), + new Claim("exp", "1475828471", ClaimValueTypes.String, "https://idsvr4"), + new Claim("client_id", "client", ClaimValueTypes.String, "https://idsvr4"), + new Claim("sub", "1", ClaimValueTypes.String, "https://idsvr4"), + new Claim("auth_time", "1475824871", ClaimValueTypes.String, "https://idsvr4"), + new Claim("idp", "local", ClaimValueTypes.String, "https://idsvr4"), + new Claim("amr", "password", ClaimValueTypes.String, "https://idsvr4"), + new Claim("active", "true", ClaimValueTypes.String, "https://idsvr4"), + new Claim("scope", "api1", ClaimValueTypes.String, "https://idsvr4"), + new Claim("scope", "api2", ClaimValueTypes.String, "https://idsvr4"), + }); } [Fact] @@ -345,18 +312,14 @@ public async Task Additional_request_parameters_should_be_handled_correctly() Token = "token", Parameters = { - { "scope", "scope1 scope2" }, - { "foo", "bar" } + { "scope", "scope1" }, + { "scope", "scope2" }, + { "foo", "bar baz" } } }); // check request - var fields = QueryHelpers.ParseQuery(handler.Body); - fields.Count.Should().Be(3); - - fields["token"].First().Should().Be("token"); - fields["scope"].First().Should().Be("scope1 scope2"); - fields["foo"].First().Should().Be("bar"); + handler.Body.Should().Be("scope=scope1&scope=scope2&foo=bar+baz&token=token"); // check response response.IsError.Should().BeFalse();