From f9d9762a20264b1e2550e00d4da4bb0848256471 Mon Sep 17 00:00:00 2001 From: Darthruneis Date: Fri, 19 Jun 2020 09:43:44 -0400 Subject: [PATCH] Port OpenIddictServerBuilderTests from OpenIddict 2.x --- OpenIddict.sln | 7 + test/OpenIddict.Server.Tests/Certificate.pfx | Bin 0 -> 2482 bytes .../OpenIddict.Server.Tests.csproj | 31 + .../OpenIddictServerBuilderTests.cs | 1838 +++++++++++++++++ 4 files changed, 1876 insertions(+) create mode 100644 test/OpenIddict.Server.Tests/Certificate.pfx create mode 100644 test/OpenIddict.Server.Tests/OpenIddict.Server.Tests.csproj create mode 100644 test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs diff --git a/OpenIddict.sln b/OpenIddict.sln index c491c42c..04bc4413 100644 --- a/OpenIddict.sln +++ b/OpenIddict.sln @@ -101,6 +101,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Server.AspNetCor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Server.Owin.IntegrationTests", "test\OpenIddict.Server.Owin.IntegrationTests\OpenIddict.Server.Owin.IntegrationTests.csproj", "{E62124D4-3660-4590-B4D1-787168BBBEDD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIddict.Server.Tests", "test\OpenIddict.Server.Tests\OpenIddict.Server.Tests.csproj", "{D94B10D3-3DD3-4829-B305-17C48833AB33}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -235,6 +237,10 @@ Global {E62124D4-3660-4590-B4D1-787168BBBEDD}.Debug|Any CPU.Build.0 = Debug|Any CPU {E62124D4-3660-4590-B4D1-787168BBBEDD}.Release|Any CPU.ActiveCfg = Release|Any CPU {E62124D4-3660-4590-B4D1-787168BBBEDD}.Release|Any CPU.Build.0 = Release|Any CPU + {D94B10D3-3DD3-4829-B305-17C48833AB33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D94B10D3-3DD3-4829-B305-17C48833AB33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D94B10D3-3DD3-4829-B305-17C48833AB33}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D94B10D3-3DD3-4829-B305-17C48833AB33}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -272,6 +278,7 @@ Global {0947C388-31DD-45C0-8DD2-61582C648F07} = {5FC71D6A-A994-4F62-977F-88A7D25379D7} {FBFDB9E2-4A44-4B90-B896-E094BFC05C03} = {5FC71D6A-A994-4F62-977F-88A7D25379D7} {E62124D4-3660-4590-B4D1-787168BBBEDD} = {5FC71D6A-A994-4F62-977F-88A7D25379D7} + {D94B10D3-3DD3-4829-B305-17C48833AB33} = {5FC71D6A-A994-4F62-977F-88A7D25379D7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A710059F-0466-4D48-9B3A-0EF4F840B616} diff --git a/test/OpenIddict.Server.Tests/Certificate.pfx b/test/OpenIddict.Server.Tests/Certificate.pfx new file mode 100644 index 0000000000000000000000000000000000000000..8c05f9d4d414d5036c38f34f8755b711d4f8811d GIT binary patch literal 2482 zcmY*adpOi-8~)9~jG=@X8WTe~hS6ZwsihX>Y>NC<>g0B7;H%2Ny|7*MT)LXl75H2CWrbxRg^g!5{B?SkmJ19R%Qs-hi}4MT4(21l<7_QTC; zhFRy@iAohZig$N!6`CqnNIX-OrcKMTbXD>*;`^6iMf9U}dzi`ET}#p0kiX zn_E1rxtYUy_&BM~tp_t8;H-q*<=GUVUmD{P%#_n9H?OTv{+@XM2xMHjW5uGKg=k~f z2amqit^(MKN6($FO@8qb&GXH6QDHUc+>>gj!9;Ki*6b?iJ(r zH$ZEI!bbNfD(YJuWL1T|K3soS0x?9m{LHl8*pM|f7COg9o59RbqmdSdK6}rtaRh9R zh{}vxI>PL>d)zjmv8lG`tf;if2r{wa$W8_EVo2ualvRc({_ry4P0H5&`!V`2R?x7n zECIoRGmhIk;mY^m$#nQpF|*J}ILgR|Dl*ACh6 zL!uIQ)(bJ6l*Wh8Uxz<1U+DP6qMhf7rZ3^W&FxmCsO=W3B3h(y1O7maO3mg8x!O4t zKh>*~vIt9#M3Jm3(tPFYugc5>Y8Tab566wYYRQ~>x{j^%@phz=B`d$Y94Vye7!1lm zzopGDR_vuVxEgH~4auUn9&@&8QJ0tb^4H8=1<);5biZd3-6yU|4q6N`N~-JIb>yM< zVktlMx6;*l+0X*qlbDdN-?+~l7>^DFYAZW?`HJ>)Z}YNMo*pE zNt~>i|Kvvc<-WJa%cdlR`>fD}F(Y~`{V>n}QjcNGzS-J#@`rY%vnDsh+@dO5en!(U zBuQnf(^`bUxn-`?1x6G7kq=!e>-s1wDH5P$Xw|O8MMP)D%|lPsblz)f_GG}M4?bft z#XHkP4<^QM~7@0#~Sb)_|tN`J76Lmi5c(cd#O9Qnt3${(~@3g)(|Ev(?M zyFX?&UT*HrAcuu!pB+n38XeYb6D#TdoQ{;|d}$5Ux4hGF_k?q+XwsA%srzbUd-ncY zHs{490^Vvv^0hSM$B5{EzDtyZCISwJ002OA`nQSEw8WbOdVmk`6QBjc6VL}i0GS>L zUyzYNMglnjI0HKL0WXkg0^T5WK_>~UI-sHpYQDrk1WFA7fkSAzVh~MtyenV>1b_i5 z5CVjQ@CD8Ta=CuvR={Zy(+J6mza>Mr6C7Vvxv6 z4lrB08stzB^NX~KaiDBm(~oLwK>5wBpDCpxp=^J2gt*jX%-HPSw7t&j8z~12-6E;F zuGMuD#gAP0;TvL4-+jalN_ZpJpl>Vd4O&>YtR2mghGh+@E(XL?>m@#BjV45z;K{sv z+E?{{8{VMDHV~k*>lYa%pY+7Ex8hEliD!J|>$%&BCm)83Ck^AZ9O6W7aOcl=M@HdFaVt)rR! zyK?k{iUuJENoGWb{Q(eu4`y(Tn+V3eoRcG(DE;G37Wt3uH!^aZI6Up0 zF7kD2SCx3NHcZ7k)zSI+l9YG0wo?wH7_!&t3!4a3&8iE~fbe2fhy zS$9U~H&Pkt&7&o*miw1n9P_^F$o!iz&-6P6=udX)0hsaC;;m`bydX)+;We?F-0{!x zH+}6=f3Gl5Tiz|K9*a|YT+57jPsq9A28Sa}H&!&n6hzuu`@Iy37LO1YrYWlj{5!9C zwtX#^$=Ix{w=0Qaa$Z;4CJs3FZSPTk9G+~FL^#}$O;E5OnIP&CWr;|HfSL#lii2a` uIJb^0)}Y-Z<4p2IzGW4c$JV97F}teVFIaxKqA_Or9lZuCV;KvZEdB|+V@&1% literal 0 HcmV?d00001 diff --git a/test/OpenIddict.Server.Tests/OpenIddict.Server.Tests.csproj b/test/OpenIddict.Server.Tests/OpenIddict.Server.Tests.csproj new file mode 100644 index 00000000..89a37682 --- /dev/null +++ b/test/OpenIddict.Server.Tests/OpenIddict.Server.Tests.csproj @@ -0,0 +1,31 @@ + + + + net461;net472;netcoreapp2.1;netcoreapp3.1 + false + + + + + + + + + + + + + + + + + + + + + $(DefineConstants);SUPPORTS_ECDSA + $(DefineConstants);SUPPORTS_CERTIFICATE_GENERATION + + + diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs new file mode 100644 index 00000000..b2b0369c --- /dev/null +++ b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs @@ -0,0 +1,1838 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; +using Moq; +using OpenIddict.Abstractions; +using Xunit; +using static OpenIddict.Server.OpenIddictServerEvents; + +namespace OpenIddict.Server.Tests +{ + public class OpenIddictServerBuilderTests + { + [Fact] + public void Constructor_ThrowsAnExceptionForNullServices() + { + // Arrange + var services = (IServiceCollection)null; + + // Act and assert + var exception = Assert.Throws(() => new OpenIddictServerBuilder(services)); + + Assert.Equal("services", exception.ParamName); + } + + [Fact] + public void AddEventHandler_ThrowsAnExceptionWhenConfigurationIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Action> configuration = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.AddEventHandler(configuration)); + Assert.Equal(nameof(configuration), exception.ParamName); + } + + [Fact] + public void AddEventHandler_ThrowsAnExceptionWhenDescriptorIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + OpenIddictServerHandlerDescriptor descriptor = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.AddEventHandler(descriptor)); + Assert.Equal(nameof(descriptor), exception.ParamName); + } + + [Fact] + public void AddEventHandler_HandlerIsAttached() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AddEventHandler(x => + { + x.UseSingletonHandler(); + }); + + // Assert + Assert.Contains(services, service => service.ServiceType == typeof(CustomHandler)); + } + + [Fact] + public void AddEventHandler_HandlerInstanceIsRegistered() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AddEventHandler(x => + { + x.UseSingletonHandler(new CustomHandler()); + }); + + // Assert + Assert.Contains(services, service => + service.ServiceType == typeof(CustomHandler) && + service.ImplementationInstance?.GetType() == typeof(CustomHandler) && + service.Lifetime == ServiceLifetime.Singleton); + } + + [Fact] + public void AddEventHandler_SingletonHandlerIsRegisteredAsASingleton() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AddEventHandler(x => + { + x.UseSingletonHandler(); + }); + + // Assert + Assert.Contains(services, service => + service.ServiceType == typeof(CustomHandler) && + service.Lifetime == ServiceLifetime.Singleton); + } + + [Fact] + public void AddEventHandler_ScopedHandlerIsRegisteredAsScoped() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AddEventHandler(x => + { + x.UseScopedHandler(); + }); + + // Assert + Assert.Contains(services, service => + service.ServiceType == typeof(CustomHandler) && + service.Lifetime == ServiceLifetime.Scoped); + } + + [Fact] + public void AddEncryptionCredentials_ThrowsExceptionWhenCredentialsAreNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + EncryptingCredentials credentials = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.AddEncryptionCredentials(credentials)); + Assert.Equal(nameof(credentials), exception.ParamName); + } + + [Fact] + public void AddEncryptionKey_ThrowsExceptionWhenKeyIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + SecurityKey key = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.AddEncryptionKey(key)); + Assert.Equal(nameof(key), exception.ParamName); + } + + [Fact] + public void AddEncryptionKey_ThrowsExceptionWhenAsymmetricKeyPrivateKeyIsMissing() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + var key = new Mock(); + key.SetupGet(x => x.PrivateKeyStatus).Returns(PrivateKeyStatus.DoesNotExist); + + // Act & Assert + var exception = Assert.Throws(() => builder.AddEncryptionKey(key.Object)); + Assert.Equal("The asymmetric encryption key doesn't contain the required private key.", exception.Message); + } + + [Fact] + public void RemoveEventHandler_ThrowsAnExceptionWhenDescriptorIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + OpenIddictServerHandlerDescriptor descriptor = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.RemoveEventHandler(descriptor)); + Assert.Equal(nameof(descriptor), exception.ParamName); + } + + [Fact] + public void RemoveEventHandler_RemovesService() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + OpenIddictServerHandlerDescriptor descriptor = OpenIddictServerHandlerDescriptor.CreateBuilder().UseSingletonHandler().Build(); + builder.AddEventHandler(descriptor); + + // Act + builder.RemoveEventHandler(descriptor); + var options = GetOptions(services); + + // Assert + Assert.DoesNotContain(services, x => x.ServiceType == descriptor.ServiceDescriptor.ServiceType); + Assert.DoesNotContain(options.CustomHandlers, x => x.ServiceDescriptor.ServiceType == descriptor.ServiceDescriptor.ServiceType); + Assert.DoesNotContain(options.DefaultHandlers, x => x.ServiceDescriptor.ServiceType == descriptor.ServiceDescriptor.ServiceType); + } + + [Fact] + public void Configure_OptionsAreCorrectlyAmended() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.Configure(configuration => configuration.AccessTokenLifetime = TimeSpan.FromDays(1)); + + var options = GetOptions(services); + + // Assert + Assert.Equal(TimeSpan.FromDays(1), options.AccessTokenLifetime); + } + + [Fact] + public void Configure_ThrowsAnExceptionWhenConfigurationIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Action configuration = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.Configure(configuration)); + Assert.Equal(nameof(configuration), exception.ParamName); + } + + [Fact] + public void AddDevelopmentSigningCertificate_ThrowsAnExceptionForNullSubject() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act and assert + var exception = Assert.Throws(delegate + { + builder.AddDevelopmentSigningCertificate(subject: null); + }); + + Assert.Equal("subject", exception.ParamName); + } + + [Fact] + public void AddDevelopmentEncryptionCertificate_ThrowsAnExceptionForNullSubject() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + X500DistinguishedName subject = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.AddDevelopmentEncryptionCertificate(subject)); + Assert.Equal(nameof(subject), exception.ParamName); + } + +#if SUPPORTS_CERTIFICATE_GENERATION + [Fact] + public void AddDevelopmentSigningCertificate_CanGenerateCertificate() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AddDevelopmentSigningCertificate(); + + var options = GetOptions(services); + + // Assert + Assert.Equal(1, options.SigningCredentials.Count); + Assert.Equal(SecurityAlgorithms.RsaSha256, options.SigningCredentials[0].Algorithm); + Assert.NotNull(options.SigningCredentials[0].Kid); + } +#else + [Fact] + public void AddDevelopmentSigningCertificate_ThrowsAnExceptionOnUnsupportedPlatforms() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act and assert + var exception = Assert.Throws(delegate + { + builder.AddDevelopmentSigningCertificate(); + return GetOptions(services); + }); + + Assert.Equal("X.509 certificate generation is not supported on this platform.", exception.Message); + } +#endif + + [Fact] + public void AddEphemeralSigningKey_SigningKeyIsCorrectlyAdded() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AddEphemeralSigningKey(); + + var options = GetOptions(services); + + // Assert + Assert.Equal(1, options.SigningCredentials.Count); + } + + [Theory] + [InlineData(SecurityAlgorithms.RsaSha256)] + [InlineData(SecurityAlgorithms.RsaSha384)] + [InlineData(SecurityAlgorithms.RsaSha512)] +#if SUPPORTS_ECDSA + [InlineData(SecurityAlgorithms.EcdsaSha256)] + [InlineData(SecurityAlgorithms.EcdsaSha384)] + [InlineData(SecurityAlgorithms.EcdsaSha512)] +#endif + public void AddEphemeralSigningKey_SigningCredentialsUseSpecifiedAlgorithm(string algorithm) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AddEphemeralSigningKey(algorithm); + + var options = GetOptions(services); + var credentials = options.SigningCredentials[0]; + + // Assert + Assert.Equal(algorithm, credentials.Algorithm); + } + + //[Fact] + //public void AddEncryptingKey_EncryptingKeyIsCorrectlyAdded() + //{ + // // Arrange + // var services = CreateServices(); + // var builder = CreateBuilder(services); + + // var factory = Mock.Of(mock => + // mock.IsSupportedAlgorithm(SecurityAlgorithms.Aes256KW, It.IsAny())); + + // var key = Mock.Of(mock => mock.CryptoProviderFactory == factory); + + // // Act + // builder.AddEncryptingKey(key); + + // var options = GetOptions(services); + + // // Assert + // Assert.Same(key, options.EncryptingCredentials[0].Key); + //} + + [Theory] + [InlineData(SecurityAlgorithms.HmacSha256)] + [InlineData(SecurityAlgorithms.RsaSha256)] +#if SUPPORTS_ECDSA + [InlineData(SecurityAlgorithms.EcdsaSha256)] + [InlineData(SecurityAlgorithms.EcdsaSha384)] + [InlineData(SecurityAlgorithms.EcdsaSha512)] +#endif + public void AddSigningKey_SigningKeyIsCorrectlyAdded(string algorithm) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + var factory = Mock.Of(mock => + mock.IsSupportedAlgorithm(algorithm, It.IsAny())); + + var key = Mock.Of(mock => mock.CryptoProviderFactory == factory); + + // Act + builder.AddSigningKey(key); + + var options = GetOptions(services); + + // Assert + Assert.Same(key, options.SigningCredentials[0].Key); + } + + [Fact] + public void AddSigningCertificate_SigningKeyIsCorrectlyAdded() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AddSigningCertificate( + assembly: typeof(OpenIddictServerBuilderTests).GetTypeInfo().Assembly, + resource: "OpenIddict.Server.Tests.Certificate.pfx", + password: "OpenIddict"); + + var options = GetOptions(services); + + // Assert + Assert.IsType(options.SigningCredentials[0].Key); + } + + [Fact] + public void AllowAuthorizationCodeFlow_CodeFlowIsAddedToGrantTypes() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AllowAuthorizationCodeFlow(); + + var options = GetOptions(services); + + // Assert + Assert.Contains(OpenIddictConstants.GrantTypes.AuthorizationCode, options.GrantTypes); + } + + [Fact] + public void AllowClientCredentialsFlow_ClientCredentialsFlowIsAddedToGrantTypes() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AllowClientCredentialsFlow(); + + var options = GetOptions(services); + + // Assert + Assert.Contains(OpenIddictConstants.GrantTypes.ClientCredentials, options.GrantTypes); + } + + [Fact] + public void AllowCustomFlow_CustomFlowIsAddedToGrantTypes() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AllowCustomFlow("urn:ietf:params:oauth:grant-type:custom_grant"); + + var options = GetOptions(services); + + // Assert + Assert.Contains("urn:ietf:params:oauth:grant-type:custom_grant", options.GrantTypes); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void AllowCustomFlow_ThrowsAnExceptionForType(string type) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act & Assert + var exception = Assert.Throws(() => builder.AllowCustomFlow(type)); + Assert.Equal(nameof(type), exception.ParamName); + Assert.Contains("The grant type cannot be null or empty.", exception.Message); + } + + [Fact] + public void AllowImplicitFlow_ImplicitFlowIsAddedToGrantTypes() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AllowImplicitFlow(); + + var options = GetOptions(services); + + // Assert + Assert.Contains(OpenIddictConstants.GrantTypes.Implicit, options.GrantTypes); + } + + [Fact] + public void AllowPasswordFlow_PasswordFlowIsAddedToGrantTypes() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AllowPasswordFlow(); + + var options = GetOptions(services); + + // Assert + Assert.Contains(OpenIddictConstants.GrantTypes.Password, options.GrantTypes); + } + + [Fact] + public void AllowRefreshTokenFlow_RefreshTokenFlowIsAddedToGrantTypes() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AllowRefreshTokenFlow(); + + var options = GetOptions(services); + + // Assert + Assert.Contains(OpenIddictConstants.GrantTypes.RefreshToken, options.GrantTypes); + } + + [Fact] + public void DisableAuthorizationStorage_AuthorizationStorageIsDisabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.DisableAuthorizationStorage(); + + var options = GetOptions(services); + + // Assert + Assert.True(options.DisableAuthorizationStorage); + } + + [Fact] + public void SetConfigurationEndpointUris_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetConfigurationEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Fact] + public void SetConfigurationEndpointUris_Strings_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetConfigurationEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + public const string InvalidUriString = @"C:\"; + + [Theory] + [InlineData(InvalidUriString)] + public void SetConfigurationEndpointUris_ThrowsExceptionForUri(string uri) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = { new Uri(uri), }; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetConfigurationEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + Assert.Contains("One of the specified addresses is not valid.", exception.Message); + } + + [Fact] + public void SetConfigurationEndpointUris_ClearsUris() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetConfigurationEndpointUris(Array.Empty()); + + var options = GetOptions(services); + + // Assert + Assert.Empty(options.ConfigurationEndpointUris); + } + + [Fact] + public void SetConfigurationEndpointUris_AddsUri() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetConfigurationEndpointUris("http://localhost/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Contains(new Uri("http://localhost/endpoint-path"), options.ConfigurationEndpointUris); + } + + [Fact] + public void SetDeviceEndpointUris_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetDeviceEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Fact] + public void SetDeviceEndpointUris_Strings_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetDeviceEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Theory] + [InlineData(InvalidUriString)] + public void SetDeviceEndpointUris_ThrowsExceptionForUri(string uri) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = { new Uri(uri), }; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetDeviceEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + Assert.Contains("One of the specified addresses is not valid.", exception.Message); + } + + [Fact] + public void SetDeviceEndpointUris_ClearsUris() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetDeviceEndpointUris(Array.Empty()); + + var options = GetOptions(services); + + // Assert + Assert.Empty(options.DeviceEndpointUris); + } + + [Fact] + public void SetDeviceEndpointUris_AddsUri() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetDeviceEndpointUris("http://localhost/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Contains(new Uri("http://localhost/endpoint-path"), options.DeviceEndpointUris); + } + + [Fact] + public void AddDeviceCodeFlow_AddsDeviceCodeGrantType() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AllowDeviceCodeFlow(); + + var options = GetOptions(services); + + // Assert + Assert.Contains(OpenIddictConstants.GrantTypes.DeviceCode, options.GrantTypes); + } + + [Fact] + public void SetCryptographyEndpointUris_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetCryptographyEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Fact] + public void SetCryptographyEndpointUris_Strings_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetCryptographyEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Theory] + [InlineData(InvalidUriString)] + public void SetCryptographyEndpointUris_ThrowsExceptionForUri(string uri) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = { new Uri(uri), }; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetCryptographyEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + Assert.Contains("One of the specified addresses is not valid.", exception.Message); + } + + [Fact] + public void SetCryptographyEndpointUris_ClearsUris() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetCryptographyEndpointUris(Array.Empty()); + + var options = GetOptions(services); + + // Assert + Assert.Empty(options.CryptographyEndpointUris); + } + + [Fact] + public void SetCryptographyEndpointUris_AddsUri() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetCryptographyEndpointUris("http://localhost/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Contains(new Uri("http://localhost/endpoint-path"), options.CryptographyEndpointUris); + } + + [Fact] + public void DisableSlidingExpiration_SlidingExpirationIsDisabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.DisableSlidingExpiration(); + + var options = GetOptions(services); + + // Assert + Assert.False(options.UseSlidingExpiration); + } + + [Fact] + public void DisableTokenStorage_TokenStorageIsDisabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.DisableTokenStorage(); + + var options = GetOptions(services); + + // Assert + Assert.True(options.DisableTokenStorage); + } + + [Fact] + public void DisableAccessTokenEncryption_AccessTokenEncryptionIsDisabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.DisableAccessTokenEncryption(); + + var options = GetOptions(services); + + // Assert + Assert.True(options.DisableAccessTokenEncryption); + } + + [Fact] + public void SetAuthorizationEndpointUris_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetAuthorizationEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Fact] + public void SetAuthorizationEndpointUris_Strings_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetAuthorizationEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Theory] + [InlineData(InvalidUriString)] + public void SetAuthorizationEndpointUris_ThrowsExceptionForUri(string uri) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = { new Uri(uri), }; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetAuthorizationEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + Assert.Contains("One of the specified addresses is not valid.", exception.Message); + } + + [Fact] + public void SetAuthorizationEndpointUris_ClearsUris() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetAuthorizationEndpointUris(Array.Empty()); + + var options = GetOptions(services); + + // Assert + Assert.Empty(options.AuthorizationEndpointUris); + } + + [Fact] + public void SetAuthorizationEndpointUris_AddsUri() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetAuthorizationEndpointUris("http://localhost/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Contains(new Uri("http://localhost/endpoint-path"), options.AuthorizationEndpointUris); + } + + [Fact] + public void SetIntrospectionEndpointUris_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetIntrospectionEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Fact] + public void SetIntrospectionEndpointUris_Strings_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetIntrospectionEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Theory] + [InlineData(InvalidUriString)] + public void SetIntrospectionEndpointUris_ThrowsExceptionForUri(string uri) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = { new Uri(uri), }; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetIntrospectionEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + Assert.Contains("One of the specified addresses is not valid.", exception.Message); + } + + [Fact] + public void SetIntrospectionEndpointUris_ClearsUris() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetIntrospectionEndpointUris(Array.Empty()); + + var options = GetOptions(services); + + // Assert + Assert.Empty(options.IntrospectionEndpointUris); + } + + [Fact] + public void SetIntrospectionEndpointUris_AddsUri() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetIntrospectionEndpointUris("http://localhost/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Contains(new Uri("http://localhost/endpoint-path"), options.IntrospectionEndpointUris); + } + + [Fact] + public void SetLogoutEndpointUris_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetLogoutEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Fact] + public void SetLogoutEndpointUris_Strings_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetLogoutEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Theory] + [InlineData(InvalidUriString)] + public void SetLogoutEndpointUris_ThrowsExceptionForUri(string uri) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = { new Uri(uri), }; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetLogoutEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + Assert.Contains("One of the specified addresses is not valid.", exception.Message); + } + + [Fact] + public void SetLogoutEndpointUris_ClearsUris() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetLogoutEndpointUris(Array.Empty()); + + var options = GetOptions(services); + + // Assert + Assert.Empty(options.LogoutEndpointUris); + } + + [Fact] + public void SetLogoutEndpointUris_AddsUri() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetLogoutEndpointUris("http://localhost/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Contains(new Uri("http://localhost/endpoint-path"), options.LogoutEndpointUris); + } + + //[Fact] + //public void EnableRequestCaching_RequestCachingIsEnabled() + //{ + // // Arrange + // var services = CreateServices(); + // var builder = CreateBuilder(services); + + // // Act + // builder.EnableRequestCaching(); + + // var options = GetOptions(services); + + // // Assert + // Assert.True(options.EnableRequestCaching); + //} + + [Fact] + public void SetRevocationEndpointUris_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetRevocationEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Fact] + public void SetRevocationEndpointUris_Strings_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetRevocationEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Theory] + [InlineData(InvalidUriString)] + public void SetRevocationEndpointUris_ThrowsExceptionForUri(string uri) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = { new Uri(uri), }; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetRevocationEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + Assert.Contains("One of the specified addresses is not valid.", exception.Message); + } + + [Fact] + public void SetRevocationEndpointUris_ClearsUris() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetRevocationEndpointUris(Array.Empty()); + + var options = GetOptions(services); + + // Assert + Assert.Empty(options.RevocationEndpointUris); + } + + [Fact] + public void SetRevocationEndpointUris_AddsUri() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetRevocationEndpointUris("http://localhost/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Contains(new Uri("http://localhost/endpoint-path"), options.RevocationEndpointUris); + } + + [Fact] + public void DisableScopeValidation_ScopeValidationIsDisabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.DisableScopeValidation(); + + var options = GetOptions(services); + + // Assert + Assert.True(options.DisableScopeValidation); + } + + [Fact] + public void SetTokenEndpointUris_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetTokenEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Fact] + public void SetTokenEndpointUris_Strings_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetTokenEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Theory] + [InlineData(InvalidUriString)] + public void SetTokenEndpointUris_ThrowsExceptionForUri(string uri) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = { new Uri(uri), }; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetTokenEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + Assert.Contains("One of the specified addresses is not valid.", exception.Message); + } + + [Fact] + public void SetTokenEndpointUris_ClearsUris() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetTokenEndpointUris(Array.Empty()); + + var options = GetOptions(services); + + // Assert + Assert.Empty(options.TokenEndpointUris); + } + + [Fact] + public void SetTokenEndpointUris_AddsUri() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetTokenEndpointUris("http://localhost/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Contains(new Uri("http://localhost/endpoint-path"), options.TokenEndpointUris); + } + + [Fact] + public void SetUserinfoEndpointUris_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetUserinfoEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Fact] + public void SetUserinfoEndpointUris_Strings_ThrowsExceptionWhenAddressesIsNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetUserinfoEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Theory] + [InlineData(InvalidUriString)] + public void SetUserinfoEndpointUris_ThrowsExceptionForUri(string uri) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = {new Uri(uri), }; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetUserinfoEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + Assert.Contains("One of the specified addresses is not valid.", exception.Message); + } + + [Fact] + public void SetUserinfoEndpointUris_ClearsUris() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetUserinfoEndpointUris(Array.Empty()); + + var options = GetOptions(services); + + // Assert + Assert.Empty(options.UserinfoEndpointUris); + } + + [Fact] + public void SetUserinfoEndpointUris_AddsUri() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetUserinfoEndpointUris("http://localhost/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Contains(new Uri("http://localhost/endpoint-path"), options.UserinfoEndpointUris); + } + + [Fact] + public void AcceptAnonymousClients_ClientIdentificationIsOptional() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AcceptAnonymousClients(); + + var options = GetOptions(services); + + // Assert + Assert.True(options.AcceptAnonymousClients); + } + + [Fact] + public void SetAccessTokenLifetime_DefaultAccessTokenLifetimeIsReplaced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetAccessTokenLifetime(TimeSpan.FromMinutes(42)); + + var options = GetOptions(services); + + // Assert + Assert.Equal(TimeSpan.FromMinutes(42), options.AccessTokenLifetime); + } + + [Fact] + public void SetAccessTokenLifetime_AccessTokenLifetimeCanBeSetToNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetAccessTokenLifetime(null); + + var options = GetOptions(services); + + // Assert + Assert.Null(options.AccessTokenLifetime); + } + + [Fact] + public void SetAuthorizationCodeLifetime_DefaultAuthorizationCodeLifetimeIsReplaced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetAuthorizationCodeLifetime(TimeSpan.FromMinutes(42)); + + var options = GetOptions(services); + + // Assert + Assert.Equal(TimeSpan.FromMinutes(42), options.AuthorizationCodeLifetime); + } + + [Fact] + public void SetAuthorizationCodeLifetime_AuthorizationCodeLifetimeCanBeSetToNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetAuthorizationCodeLifetime(null); + + var options = GetOptions(services); + + // Assert + Assert.Null(options.AuthorizationCodeLifetime); + } + + [Fact] + public void SetIdentityTokenLifetime_DefaultIdentityTokenLifetimeIsReplaced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetIdentityTokenLifetime(TimeSpan.FromMinutes(42)); + + var options = GetOptions(services); + + // Assert + Assert.Equal(TimeSpan.FromMinutes(42), options.IdentityTokenLifetime); + } + + [Fact] + public void SetIdentityTokenLifetime_IdentityTokenLifetimeCanBeSetToNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetIdentityTokenLifetime(null); + + var options = GetOptions(services); + + // Assert + Assert.Null(options.IdentityTokenLifetime); + } + + [Fact] + public void SetDeviceCodeLifetimeLifetime_DefaultDeviceCodeLifetimeIsReplaced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetDeviceCodeLifetime(TimeSpan.FromMinutes(42)); + + var options = GetOptions(services); + + // Assert + Assert.Equal(TimeSpan.FromMinutes(42), options.DeviceCodeLifetime); + } + + [Fact] + public void SetDeviceCodeLifetimeLifetime_DeviceCodeLifetimeCanBeSetToNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetDeviceCodeLifetime(null); + + var options = GetOptions(services); + + // Assert + Assert.Null(options.DeviceCodeLifetime); + } + + [Fact] + public void SetUserCodeLifetime_DefaultUserCodeLifetimeIsReplaced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetUserCodeLifetime(TimeSpan.FromMinutes(42)); + + var options = GetOptions(services); + + // Assert + Assert.Equal(TimeSpan.FromMinutes(42), options.UserCodeLifetime); + } + + [Fact] + public void SetUserCodeLifetime_UserLifetimeCanBeSetToNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetUserCodeLifetime(null); + + var options = GetOptions(services); + + // Assert + Assert.Null(options.UserCodeLifetime); + } + + [Fact] + public void SetRefreshTokenLifetime_DefaultRefreshTokenLifetimeIsReplaced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetRefreshTokenLifetime(TimeSpan.FromMinutes(42)); + + var options = GetOptions(services); + + // Assert + Assert.Equal(TimeSpan.FromMinutes(42), options.RefreshTokenLifetime); + } + + [Fact] + public void SetRefreshTokenLifetime_RefreshTokenLifetimeCanBeSetToNull() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetRefreshTokenLifetime(null); + + var options = GetOptions(services); + + // Assert + Assert.Null(options.RefreshTokenLifetime); + } + + //[Fact] + //public void SetRequestCachingPolicy_ThrowsAnExceptionForNullPolicy() + //{ + // // Arrange + // var services = CreateServices(); + // var builder = CreateBuilder(services); + + // // Act and assert + // var exception = Assert.Throws(() => builder.SetRequestCachingPolicy(null)); + + // Assert.Equal("policy", exception.ParamName); + //} + + //[Fact] + //public void SetRequestCachingPolicy_PolicyIsUpdated() + //{ + // // Arrange + // var services = CreateServices(); + // var builder = CreateBuilder(services); + + // var policy = new DistributedCacheEntryOptions + // { + // AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(42), + // SlidingExpiration = TimeSpan.FromSeconds(42) + // }; + + // // Act + // builder.SetRequestCachingPolicy(policy); + + // var options = GetOptions(services); + + // // Assert + // Assert.Same(policy, options.RequestCachingPolicy); + //} + + //[Fact] + //public void UseDataProtectionProvider_DefaultProviderIsReplaced() + //{ + // // Arrange + // var services = CreateServices(); + // var builder = CreateBuilder(services); + + // // Act + // builder.UseDataProtectionProvider(new EphemeralDataProtectionProvider()); + + // var options = GetOptions(services); + + // // Assert + // Assert.IsType(options.DataProtectionProvider); + //} + + //[Fact] + //public void UseJsonWebTokens_AccessTokenHandlerIsCorrectlySet() + //{ + // // Arrange + // var services = CreateServices(); + // var builder = CreateBuilder(services); + + // // Act + // builder.UseJsonWebTokens(); + + // var options = GetOptions(services); + + // // Assert + // Assert.IsType(options.AccessTokenHandler); + //} + + [Fact] + public void SetIssuer_ThrowsAnExceptionForNullIssuer() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act and assert + var exception = Assert.Throws(() => builder.SetIssuer(null)); + + Assert.Equal("address", exception.ParamName); + } + + [Fact] + public void SetIssuer_AddressIsReplaced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetIssuer(new Uri("http://www.fabrikam.com/")); + + var options = GetOptions(services); + + // Assert + Assert.Equal(new Uri("http://www.fabrikam.com/"), options.Issuer); + } + + [Fact] + public void RegisterClaims_ClaimsAreAdded() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.RegisterClaims("custom_claim_1", "custom_claim_2"); + + var options = GetOptions(services); + + // Assert + Assert.Contains("custom_claim_1", options.Claims); + Assert.Contains("custom_claim_2", options.Claims); + } + + [Fact] + public void RegisterClaims_ThrowsAnExceptionForNullClaims() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] claims = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.RegisterClaims(claims)); + Assert.Equal(nameof(claims), exception.ParamName); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void RegisterClaims_ThrowsAnExceptionForClaim(string claim) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] claims = { claim }; + + // Act & Assert + var exception = Assert.Throws(() => builder.RegisterClaims(claims)); + Assert.Equal(nameof(claims), exception.ParamName); + Assert.Contains("Claims cannot be null or empty.", exception.Message); + } + + [Fact] + public void RegisterScopes_ScopesAreAdded() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.RegisterScopes("custom_scope_1", "custom_scope_2"); + + var options = GetOptions(services); + + // Assert + Assert.Contains("custom_scope_1", options.Scopes); + Assert.Contains("custom_scope_2", options.Scopes); + } + + [Fact] + public void RegisterScopes_ThrowsAnExceptionForNullScopes() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] scopes = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.RegisterScopes(scopes)); + Assert.Equal(nameof(scopes), exception.ParamName); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void RegisterScopes_ThrowsAnExceptionForScope(string scope) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] scopes = { scope }; + + // Act & Assert + var exception = Assert.Throws(() => builder.RegisterScopes(scopes)); + Assert.Equal(nameof(scopes), exception.ParamName); + Assert.Contains("Scopes cannot be null or empty.", exception.Message); + } + + [Fact] + public void UseReferenceTokens_ReferenceTokensAreEnabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.UseReferenceTokens(); + + var options = GetOptions(services); + + // Assert + Assert.True(options.UseReferenceTokens); + } + + [Fact] + public void SetVerificationEndpointUris_ThrowsExceptionWhenNullAddresses() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetVerificationEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Fact] + public void SetVerificationEndpointUris_Strings_ThrowsExceptionWhenNullAddresses() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] addresses = null; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetVerificationEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + } + + [Fact] + public void SetVerificationEndpointUris_Strings_AddedUriIsRelativeOrAbsoluteUriKind() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + string[] addresses = {"http://localhost/verify"}; + + // Act + builder.SetVerificationEndpointUris(addresses); + + var options = GetOptions(services); + + // Assert + Assert.True(options.VerificationEndpointUris[0].IsAbsoluteUri); + } + + [Theory] + [InlineData(InvalidUriString)] + public void SetVerificationEndpointUris_ThrowsExceptionForUri(string uri) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = { new Uri(uri)}; + + // Act & Assert + var exception = Assert.Throws(() => builder.SetVerificationEndpointUris(addresses)); + Assert.Equal(nameof(addresses), exception.ParamName); + Assert.Contains("One of the specified addresses is not valid.", exception.Message); + } + + [Fact] + public void SetVerificationEndpointUris_ClearsExistingUris() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = Array.Empty(); + + // Act + builder.SetVerificationEndpointUris(addresses); + + var options = GetOptions(services); + + // Assert + Assert.Empty(options.VerificationEndpointUris); + } + + [Theory] + [InlineData("http://localhost/verify")] + [InlineData("http://localhost/verify-1")] + [InlineData("http://localhost/verification")] + [InlineData("http://localhost/verification-1")] + public void SetVerificationEndpointUris_AddsUri(string uri) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + Uri[] addresses = { new Uri(uri), }; + + // Act + builder.SetVerificationEndpointUris(addresses); + + var options = GetOptions(services); + + // Assert + Assert.Contains(addresses[0], options.VerificationEndpointUris); + } + + private static IServiceCollection CreateServices() + { + return new ServiceCollection().AddOptions(); + } + + private static OpenIddictServerBuilder CreateBuilder(IServiceCollection services) + => new OpenIddictServerBuilder(services); + + private static OpenIddictServerOptions GetOptions(IServiceCollection services) + { + var provider = services.BuildServiceProvider(); + + var options = provider.GetRequiredService>(); + //return options.Get(OpenIddictServerDefaults.AuthenticationScheme); + return options.Value; + } + + private class CustomContext : BaseContext + { + /// + /// Creates a new instance of the class. + /// + public CustomContext(OpenIddictServerTransaction transaction) : base(transaction) { } + } + + private class CustomHandler : IOpenIddictServerHandler + { + /// + /// Processes the event. + /// + /// The context associated with the event to process. + /// + /// A that can be used to monitor the asynchronous operation. + /// + public ValueTask HandleAsync(CustomContext context) + { + return new ValueTask(); + } + } + } +} \ No newline at end of file