25 changed files with 9834 additions and 142 deletions
@ -0,0 +1,437 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using Microsoft.Extensions.Options; |
|||
using Moq; |
|||
using Xunit; |
|||
|
|||
namespace OpenIddict.Core.Tests; |
|||
|
|||
public class OpenIddictApplicationCacheTests |
|||
{ |
|||
[Fact] |
|||
public void Constructor_ThrowsAnExceptionForNullOptions() |
|||
{ |
|||
// Arrange
|
|||
var options = (IOptionsMonitor<OpenIddictCoreOptions>) null!; |
|||
var store = Mock.Of<IOpenIddictApplicationStore<object>>(); |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>(() => new OpenIddictApplicationCache<object>(options, store)); |
|||
|
|||
Assert.Equal("options", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Constructor_ThrowsAnExceptionForNullStore() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = (IOpenIddictApplicationStore<object>) null!; |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>(() => new OpenIddictApplicationCache<object>(options, store)); |
|||
|
|||
Assert.Equal("store", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AddAsync_ThrowsAnExceptionForNullApplication() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.AddAsync(application: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("application", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Dispose_CanBeCalledMultipleTimes() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store); |
|||
|
|||
// Act and assert
|
|||
cache.Dispose(); |
|||
cache.Dispose(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByClientIdAsync_ThrowsAnExceptionForNullIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.FindByClientIdAsync(identifier: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByClientIdAsync_ThrowsAnExceptionForEmptyIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentException>( |
|||
() => cache.FindByClientIdAsync(identifier: string.Empty, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByClientIdAsync_ReturnsCachedApplicationOnCacheHit() |
|||
{ |
|||
// Arrange
|
|||
var application = new OpenIddictApplication(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id"); |
|||
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("client-id"); |
|||
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store.Object); |
|||
|
|||
await cache.AddAsync(application, CancellationToken.None); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByClientIdAsync("client-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(application, result); |
|||
store.Verify(store => store.FindByClientIdAsync("client-id", It.IsAny<CancellationToken>()), Times.Never()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByClientIdAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var application = new OpenIddictApplication(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
|
|||
store.Setup(store => store.FindByClientIdAsync("client-id", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(application); |
|||
store.Setup(store => store.GetIdAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id"); |
|||
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("client-id"); |
|||
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store.Object); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByClientIdAsync("client-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(application, result); |
|||
store.Verify(store => store.FindByClientIdAsync("client-id", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ThrowsAnExceptionForNullIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.FindByIdAsync(identifier: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ThrowsAnExceptionForEmptyIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentException>( |
|||
() => cache.FindByIdAsync(identifier: string.Empty, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ReturnsCachedApplicationOnCacheHit() |
|||
{ |
|||
// Arrange
|
|||
var application = new OpenIddictApplication(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id"); |
|||
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("client-id"); |
|||
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store.Object); |
|||
|
|||
await cache.AddAsync(application, CancellationToken.None); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByIdAsync("application-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(application, result); |
|||
store.Verify(store => store.FindByIdAsync("application-id", It.IsAny<CancellationToken>()), Times.Never()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var application = new OpenIddictApplication(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
|
|||
store.Setup(store => store.FindByIdAsync("application-id", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(application); |
|||
store.Setup(store => store.GetIdAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id"); |
|||
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("client-id"); |
|||
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store.Object); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByIdAsync("application-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(application, result); |
|||
store.Verify(store => store.FindByIdAsync("application-id", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ReturnsNullWhenApplicationNotFound() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
|
|||
store.Setup(store => store.FindByIdAsync("application-id", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((OpenIddictApplication?) null); |
|||
|
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store.Object); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByIdAsync("application-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Null(result); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByPostLogoutRedirectUriAsync_ReturnsCachedApplicationsOnCacheHit() |
|||
{ |
|||
// Arrange
|
|||
var applications = new[] |
|||
{ |
|||
new OpenIddictApplication(), |
|||
new OpenIddictApplication() |
|||
}; |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(applications[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id-1"); |
|||
store.Setup(store => store.GetClientIdAsync(applications[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("client-id-1"); |
|||
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(applications[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
store.Setup(store => store.GetRedirectUrisAsync(applications[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
store.Setup(store => store.GetIdAsync(applications[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id-2"); |
|||
store.Setup(store => store.GetClientIdAsync(applications[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("client-id-2"); |
|||
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(applications[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
store.Setup(store => store.GetRedirectUrisAsync(applications[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
store.Setup(store => store.FindByPostLogoutRedirectUriAsync("https://localhost/signout-callback", It.IsAny<CancellationToken>())) |
|||
.Returns(applications.ToAsyncEnumerable()); |
|||
|
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store.Object); |
|||
|
|||
await cache.FindByPostLogoutRedirectUriAsync("https://localhost/signout-callback", CancellationToken.None).ToListAsync(); |
|||
|
|||
// Act
|
|||
var results = await cache.FindByPostLogoutRedirectUriAsync("https://localhost/signout-callback", CancellationToken.None).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Equal(2, results.Count); |
|||
Assert.Contains(applications[0], results); |
|||
Assert.Contains(applications[1], results); |
|||
store.Verify(store => store.FindByPostLogoutRedirectUriAsync("https://localhost/signout-callback", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByRedirectUriAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var applications = new[] |
|||
{ |
|||
new OpenIddictApplication(), |
|||
new OpenIddictApplication() |
|||
}; |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(applications[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id-1"); |
|||
store.Setup(store => store.GetClientIdAsync(applications[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("client-id-1"); |
|||
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(applications[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
store.Setup(store => store.GetRedirectUrisAsync(applications[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
store.Setup(store => store.GetIdAsync(applications[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id-2"); |
|||
store.Setup(store => store.GetClientIdAsync(applications[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("client-id-2"); |
|||
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(applications[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
store.Setup(store => store.GetRedirectUrisAsync(applications[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
store.Setup(store => store.FindByRedirectUriAsync("https://localhost/callback", It.IsAny<CancellationToken>())) |
|||
.Returns(applications.ToAsyncEnumerable()); |
|||
|
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store.Object); |
|||
|
|||
// Act
|
|||
var results = await cache.FindByRedirectUriAsync("https://localhost/callback", CancellationToken.None).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Equal(2, results.Count); |
|||
Assert.Contains(applications[0], results); |
|||
Assert.Contains(applications[1], results); |
|||
store.Verify(store => store.FindByRedirectUriAsync("https://localhost/callback", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveAsync_ThrowsAnExceptionForNullApplication() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.RemoveAsync(application: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("application", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveAsync_InvalidatesCachedEntries() |
|||
{ |
|||
// Arrange
|
|||
var application = new OpenIddictApplication(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id"); |
|||
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("client-id"); |
|||
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store.Object); |
|||
|
|||
await cache.AddAsync(application, CancellationToken.None); |
|||
|
|||
// Act
|
|||
await cache.RemoveAsync(application, CancellationToken.None); |
|||
|
|||
var result = await cache.FindByIdAsync("application-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Null(result); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveAsync_ThrowsForApplicationWithoutId() |
|||
{ |
|||
// Arrange
|
|||
var application = new OpenIddictApplication(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictApplicationStore<OpenIddictApplication>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(application, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
|
|||
var cache = new OpenIddictApplicationCache<OpenIddictApplication>(options, store.Object); |
|||
|
|||
// Act and assert
|
|||
await Assert.ThrowsAsync<InvalidOperationException>( |
|||
() => cache.RemoveAsync(application, CancellationToken.None).AsTask()); |
|||
} |
|||
|
|||
public sealed class OpenIddictApplication { } |
|||
} |
|||
@ -0,0 +1,330 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using Microsoft.Extensions.Options; |
|||
using Moq; |
|||
using Xunit; |
|||
|
|||
namespace OpenIddict.Core.Tests; |
|||
|
|||
public class OpenIddictAuthorizationCacheTests |
|||
{ |
|||
[Fact] |
|||
public void Constructor_ThrowsAnExceptionForNullOptions() |
|||
{ |
|||
// Arrange
|
|||
var options = (IOptionsMonitor<OpenIddictCoreOptions>) null!; |
|||
var store = Mock.Of<IOpenIddictAuthorizationStore<object>>(); |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>(() => new OpenIddictAuthorizationCache<object>(options, store)); |
|||
|
|||
Assert.Equal("options", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Constructor_ThrowsAnExceptionForNullStore() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = (IOpenIddictAuthorizationStore<object>) null!; |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>(() => new OpenIddictAuthorizationCache<object>(options, store)); |
|||
|
|||
Assert.Equal("store", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AddAsync_ThrowsAnExceptionForNullAuthorization() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictAuthorizationStore<OpenIddictAuthorization>>(); |
|||
var cache = new OpenIddictAuthorizationCache<OpenIddictAuthorization>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.AddAsync(authorization: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("authorization", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Dispose_CanBeCalledMultipleTimes() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictAuthorizationStore<OpenIddictAuthorization>>(); |
|||
var cache = new OpenIddictAuthorizationCache<OpenIddictAuthorization>(options, store); |
|||
|
|||
// Act and assert
|
|||
cache.Dispose(); |
|||
cache.Dispose(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByApplicationIdAsync_ReturnsCachedAuthorizationsOnCacheHit() |
|||
{ |
|||
// Arrange
|
|||
var authorizations = new[] |
|||
{ |
|||
new OpenIddictAuthorization(), |
|||
new OpenIddictAuthorization() |
|||
}; |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictAuthorizationStore<OpenIddictAuthorization>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(authorizations[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("authorization-id-1"); |
|||
store.Setup(store => store.GetApplicationIdAsync(authorizations[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id"); |
|||
store.Setup(store => store.GetSubjectAsync(authorizations[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("subject-1"); |
|||
|
|||
store.Setup(store => store.GetIdAsync(authorizations[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("authorization-id-2"); |
|||
store.Setup(store => store.GetApplicationIdAsync(authorizations[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id"); |
|||
store.Setup(store => store.GetSubjectAsync(authorizations[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("subject-2"); |
|||
|
|||
store.Setup(store => store.FindByApplicationIdAsync("application-id", It.IsAny<CancellationToken>())) |
|||
.Returns(authorizations.ToAsyncEnumerable()); |
|||
|
|||
var cache = new OpenIddictAuthorizationCache<OpenIddictAuthorization>(options, store.Object); |
|||
await cache.FindByApplicationIdAsync("application-id", CancellationToken.None).ToListAsync(); |
|||
|
|||
// Act
|
|||
var results = await cache.FindByApplicationIdAsync("application-id", CancellationToken.None).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Equal(2, results.Count); |
|||
Assert.Contains(authorizations[0], results); |
|||
Assert.Contains(authorizations[1], results); |
|||
store.Verify(store => store.FindByApplicationIdAsync("application-id", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ThrowsAnExceptionForNullIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictAuthorizationStore<OpenIddictAuthorization>>(); |
|||
var cache = new OpenIddictAuthorizationCache<OpenIddictAuthorization>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.FindByIdAsync(identifier: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ThrowsAnExceptionForEmptyIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictAuthorizationStore<OpenIddictAuthorization>>(); |
|||
var cache = new OpenIddictAuthorizationCache<OpenIddictAuthorization>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentException>( |
|||
() => cache.FindByIdAsync(identifier: string.Empty, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ReturnsCachedAuthorizationOnCacheHit() |
|||
{ |
|||
// Arrange
|
|||
var authorization = new OpenIddictAuthorization(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictAuthorizationStore<OpenIddictAuthorization>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(authorization, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("authorization-id"); |
|||
store.Setup(store => store.GetApplicationIdAsync(authorization, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id"); |
|||
store.Setup(store => store.GetSubjectAsync(authorization, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("subject"); |
|||
|
|||
var cache = new OpenIddictAuthorizationCache<OpenIddictAuthorization>(options, store.Object); |
|||
|
|||
await cache.AddAsync(authorization, CancellationToken.None); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByIdAsync("authorization-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(authorization, result); |
|||
store.Verify(store => store.FindByIdAsync("authorization-id", It.IsAny<CancellationToken>()), Times.Never()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var authorization = new OpenIddictAuthorization(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictAuthorizationStore<OpenIddictAuthorization>>(); |
|||
|
|||
store.Setup(store => store.FindByIdAsync("authorization-id", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(authorization); |
|||
store.Setup(store => store.GetIdAsync(authorization, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("authorization-id"); |
|||
store.Setup(store => store.GetApplicationIdAsync(authorization, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id"); |
|||
store.Setup(store => store.GetSubjectAsync(authorization, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("subject"); |
|||
|
|||
var cache = new OpenIddictAuthorizationCache<OpenIddictAuthorization>(options, store.Object); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByIdAsync("authorization-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(authorization, result); |
|||
store.Verify(store => store.FindByIdAsync("authorization-id", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ReturnsNullWhenAuthorizationNotFound() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictAuthorizationStore<OpenIddictAuthorization>>(); |
|||
|
|||
store.Setup(store => store.FindByIdAsync("authorization-id", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((OpenIddictAuthorization?) null); |
|||
|
|||
var cache = new OpenIddictAuthorizationCache<OpenIddictAuthorization>(options, store.Object); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByIdAsync("authorization-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Null(result); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindBySubjectAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var authorizations = new[] |
|||
{ |
|||
new OpenIddictAuthorization(), |
|||
new OpenIddictAuthorization() |
|||
}; |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictAuthorizationStore<OpenIddictAuthorization>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(authorizations[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("authorization-id-1"); |
|||
store.Setup(store => store.GetApplicationIdAsync(authorizations[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id-1"); |
|||
store.Setup(store => store.GetSubjectAsync(authorizations[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("subject"); |
|||
|
|||
store.Setup(store => store.GetIdAsync(authorizations[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("authorization-id-2"); |
|||
store.Setup(store => store.GetApplicationIdAsync(authorizations[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id-2"); |
|||
store.Setup(store => store.GetSubjectAsync(authorizations[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("subject"); |
|||
|
|||
store.Setup(store => store.FindBySubjectAsync("subject", It.IsAny<CancellationToken>())) |
|||
.Returns(authorizations.ToAsyncEnumerable()); |
|||
|
|||
var cache = new OpenIddictAuthorizationCache<OpenIddictAuthorization>(options, store.Object); |
|||
|
|||
// Act
|
|||
var results = await cache.FindBySubjectAsync("subject", CancellationToken.None).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Equal(2, results.Count); |
|||
Assert.Contains(authorizations[0], results); |
|||
Assert.Contains(authorizations[1], results); |
|||
store.Verify(store => store.FindBySubjectAsync("subject", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveAsync_ThrowsAnExceptionForNullAuthorization() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictAuthorizationStore<OpenIddictAuthorization>>(); |
|||
var cache = new OpenIddictAuthorizationCache<OpenIddictAuthorization>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.RemoveAsync(authorization: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("authorization", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveAsync_InvalidatesCachedEntries() |
|||
{ |
|||
// Arrange
|
|||
var authorization = new OpenIddictAuthorization(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictAuthorizationStore<OpenIddictAuthorization>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(authorization, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("authorization-id"); |
|||
store.Setup(store => store.GetApplicationIdAsync(authorization, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id"); |
|||
store.Setup(store => store.GetSubjectAsync(authorization, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("subject"); |
|||
|
|||
var cache = new OpenIddictAuthorizationCache<OpenIddictAuthorization>(options, store.Object); |
|||
|
|||
await cache.AddAsync(authorization, CancellationToken.None); |
|||
|
|||
// Act
|
|||
await cache.RemoveAsync(authorization, CancellationToken.None); |
|||
|
|||
var result = await cache.FindByIdAsync("authorization-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Null(result); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveAsync_ThrowsForAuthorizationWithoutId() |
|||
{ |
|||
// Arrange
|
|||
var authorization = new OpenIddictAuthorization(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictAuthorizationStore<OpenIddictAuthorization>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(authorization, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
|
|||
var cache = new OpenIddictAuthorizationCache<OpenIddictAuthorization>(options, store.Object); |
|||
|
|||
// Act and assert
|
|||
await Assert.ThrowsAsync<InvalidOperationException>( |
|||
() => cache.RemoveAsync(authorization, CancellationToken.None).AsTask()); |
|||
} |
|||
|
|||
public sealed class OpenIddictAuthorization { } |
|||
} |
|||
@ -0,0 +1,417 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System.Collections.Immutable; |
|||
using Microsoft.Extensions.Options; |
|||
using Moq; |
|||
using Xunit; |
|||
|
|||
namespace OpenIddict.Core.Tests; |
|||
|
|||
public class OpenIddictScopeCacheTests |
|||
{ |
|||
[Fact] |
|||
public void Constructor_ThrowsAnExceptionForNullOptions() |
|||
{ |
|||
// Arrange
|
|||
var options = (IOptionsMonitor<OpenIddictCoreOptions>) null!; |
|||
var store = Mock.Of<IOpenIddictScopeStore<object>>(); |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>(() => new OpenIddictScopeCache<object>(options, store)); |
|||
|
|||
Assert.Equal("options", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Constructor_ThrowsAnExceptionForNullStore() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = (IOpenIddictScopeStore<object>) null!; |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>(() => new OpenIddictScopeCache<object>(options, store)); |
|||
|
|||
Assert.Equal("store", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AddAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.AddAsync(scope: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Dispose_CanBeCalledMultipleTimes() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store); |
|||
|
|||
// Act and assert
|
|||
cache.Dispose(); |
|||
cache.Dispose(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ThrowsAnExceptionForNullIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.FindByIdAsync(identifier: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ThrowsAnExceptionForEmptyIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentException>( |
|||
() => cache.FindByIdAsync(identifier: string.Empty, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ReturnsCachedScopeOnCacheHit() |
|||
{ |
|||
// Arrange
|
|||
var scope = new OpenIddictScope(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-id"); |
|||
store.Setup(store => store.GetNameAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-name"); |
|||
store.Setup(store => store.GetResourcesAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store.Object); |
|||
|
|||
await cache.AddAsync(scope, CancellationToken.None); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByIdAsync("scope-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(scope, result); |
|||
store.Verify(store => store.FindByIdAsync("scope-id", It.IsAny<CancellationToken>()), Times.Never()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var scope = new OpenIddictScope(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
|
|||
store.Setup(store => store.FindByIdAsync("scope-id", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(scope); |
|||
store.Setup(store => store.GetIdAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-id"); |
|||
store.Setup(store => store.GetNameAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-name"); |
|||
store.Setup(store => store.GetResourcesAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store.Object); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByIdAsync("scope-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(scope, result); |
|||
store.Verify(store => store.FindByIdAsync("scope-id", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ReturnsNullWhenScopeNotFound() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
|
|||
store.Setup(store => store.FindByIdAsync("scope-id", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((OpenIddictScope?) null); |
|||
|
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store.Object); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByIdAsync("scope-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Null(result); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByNameAsync_ThrowsAnExceptionForNullName() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.FindByNameAsync(name: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("name", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByNameAsync_ThrowsAnExceptionForEmptyName() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentException>( |
|||
() => cache.FindByNameAsync(name: string.Empty, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("name", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByNameAsync_ReturnsCachedScopeOnCacheHit() |
|||
{ |
|||
// Arrange
|
|||
var scope = new OpenIddictScope(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-id"); |
|||
store.Setup(store => store.GetNameAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-name"); |
|||
store.Setup(store => store.GetResourcesAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store.Object); |
|||
|
|||
await cache.AddAsync(scope, CancellationToken.None); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByNameAsync("scope-name", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(scope, result); |
|||
store.Verify(store => store.FindByNameAsync("scope-name", It.IsAny<CancellationToken>()), Times.Never()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByNameAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var scope = new OpenIddictScope(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
|
|||
store.Setup(store => store.FindByNameAsync("scope-name", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(scope); |
|||
store.Setup(store => store.GetIdAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-id"); |
|||
store.Setup(store => store.GetNameAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-name"); |
|||
store.Setup(store => store.GetResourcesAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store.Object); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByNameAsync("scope-name", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(scope, result); |
|||
store.Verify(store => store.FindByNameAsync("scope-name", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByNamesAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var scopes = new[] |
|||
{ |
|||
new OpenIddictScope(), |
|||
new OpenIddictScope() |
|||
}; |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(scopes[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-id-1"); |
|||
store.Setup(store => store.GetNameAsync(scopes[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-name-1"); |
|||
store.Setup(store => store.GetResourcesAsync(scopes[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
store.Setup(store => store.GetIdAsync(scopes[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-id-2"); |
|||
store.Setup(store => store.GetNameAsync(scopes[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-name-2"); |
|||
store.Setup(store => store.GetResourcesAsync(scopes[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
store.Setup(store => store.FindByNamesAsync(It.IsAny<ImmutableArray<string>>(), It.IsAny<CancellationToken>())) |
|||
.Returns(scopes.ToAsyncEnumerable()); |
|||
|
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store.Object); |
|||
|
|||
// Act
|
|||
var results = await cache.FindByNamesAsync(["scope-name-1", "scope-name-2"], CancellationToken.None).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Equal(2, results.Count); |
|||
Assert.Contains(scopes[0], results); |
|||
Assert.Contains(scopes[1], results); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByResourceAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var scopes = new[] |
|||
{ |
|||
new OpenIddictScope(), |
|||
new OpenIddictScope() |
|||
}; |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(scopes[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-id-1"); |
|||
store.Setup(store => store.GetNameAsync(scopes[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-name-1"); |
|||
store.Setup(store => store.GetResourcesAsync(scopes[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
store.Setup(store => store.GetIdAsync(scopes[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-id-2"); |
|||
store.Setup(store => store.GetNameAsync(scopes[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-name-2"); |
|||
store.Setup(store => store.GetResourcesAsync(scopes[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
store.Setup(store => store.FindByResourceAsync("resource", It.IsAny<CancellationToken>())) |
|||
.Returns(scopes.ToAsyncEnumerable()); |
|||
|
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store.Object); |
|||
|
|||
// Act
|
|||
var results = await cache.FindByResourceAsync("resource", CancellationToken.None).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Equal(2, results.Count); |
|||
Assert.Contains(scopes[0], results); |
|||
Assert.Contains(scopes[1], results); |
|||
store.Verify(store => store.FindByResourceAsync("resource", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.RemoveAsync(scope: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveAsync_InvalidatesCachedEntries() |
|||
{ |
|||
// Arrange
|
|||
var scope = new OpenIddictScope(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-id"); |
|||
store.Setup(store => store.GetNameAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-name"); |
|||
store.Setup(store => store.GetResourcesAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync([]); |
|||
|
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store.Object); |
|||
|
|||
await cache.AddAsync(scope, CancellationToken.None); |
|||
|
|||
// Act
|
|||
await cache.RemoveAsync(scope, CancellationToken.None); |
|||
|
|||
var result = await cache.FindByIdAsync("scope-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Null(result); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveAsync_ThrowsForScopeWithoutId() |
|||
{ |
|||
// Arrange
|
|||
var scope = new OpenIddictScope(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<OpenIddictScope>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
|
|||
var cache = new OpenIddictScopeCache<OpenIddictScope>(options, store.Object); |
|||
|
|||
// Act and assert
|
|||
await Assert.ThrowsAsync<InvalidOperationException>( |
|||
() => cache.RemoveAsync(scope, CancellationToken.None).AsTask()); |
|||
} |
|||
|
|||
public sealed class OpenIddictScope { } |
|||
} |
|||
@ -0,0 +1,503 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using Microsoft.Extensions.Options; |
|||
using Moq; |
|||
using Xunit; |
|||
|
|||
namespace OpenIddict.Core.Tests; |
|||
|
|||
public class OpenIddictTokenCacheTests |
|||
{ |
|||
[Fact] |
|||
public void Constructor_ThrowsAnExceptionForNullOptions() |
|||
{ |
|||
// Arrange
|
|||
var options = (IOptionsMonitor<OpenIddictCoreOptions>) null!; |
|||
var store = Mock.Of<IOpenIddictTokenStore<object>>(); |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>(() => new OpenIddictTokenCache<object>(options, store)); |
|||
|
|||
Assert.Equal("options", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Constructor_ThrowsAnExceptionForNullStore() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = (IOpenIddictTokenStore<object>) null!; |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>(() => new OpenIddictTokenCache<object>(options, store)); |
|||
|
|||
Assert.Equal("store", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AddAsync_ThrowsAnExceptionForNullToken() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.AddAsync(token: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("token", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Dispose_CanBeCalledMultipleTimes() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store); |
|||
|
|||
// Act and assert
|
|||
cache.Dispose(); |
|||
cache.Dispose(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByApplicationIdAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var tokens = new[] |
|||
{ |
|||
new OpenIddictToken(), |
|||
new OpenIddictToken() |
|||
}; |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("token-id-1"); |
|||
store.Setup(store => store.GetReferenceIdAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetApplicationIdAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id"); |
|||
store.Setup(store => store.GetAuthorizationIdAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetSubjectAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
|
|||
store.Setup(store => store.GetIdAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("token-id-2"); |
|||
store.Setup(store => store.GetReferenceIdAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetApplicationIdAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("application-id"); |
|||
store.Setup(store => store.GetAuthorizationIdAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetSubjectAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
|
|||
store.Setup(store => store.FindByApplicationIdAsync("application-id", It.IsAny<CancellationToken>())) |
|||
.Returns(tokens.ToAsyncEnumerable()); |
|||
|
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store.Object); |
|||
|
|||
// Act
|
|||
var results = await cache.FindByApplicationIdAsync("application-id", CancellationToken.None).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Equal(2, results.Count); |
|||
Assert.Contains(tokens[0], results); |
|||
Assert.Contains(tokens[1], results); |
|||
store.Verify(store => store.FindByApplicationIdAsync("application-id", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByAuthorizationIdAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var tokens = new[] |
|||
{ |
|||
new OpenIddictToken(), |
|||
new OpenIddictToken() |
|||
}; |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("token-id-1"); |
|||
store.Setup(store => store.GetReferenceIdAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetApplicationIdAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetAuthorizationIdAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("authorization-id"); |
|||
store.Setup(store => store.GetSubjectAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
|
|||
store.Setup(store => store.GetIdAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("token-id-2"); |
|||
store.Setup(store => store.GetReferenceIdAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetApplicationIdAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetAuthorizationIdAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("authorization-id"); |
|||
store.Setup(store => store.GetSubjectAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
|
|||
store.Setup(store => store.FindByAuthorizationIdAsync("authorization-id", It.IsAny<CancellationToken>())) |
|||
.Returns(tokens.ToAsyncEnumerable()); |
|||
|
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store.Object); |
|||
|
|||
// Act
|
|||
var results = await cache.FindByAuthorizationIdAsync("authorization-id", CancellationToken.None).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Equal(2, results.Count); |
|||
Assert.Contains(tokens[0], results); |
|||
Assert.Contains(tokens[1], results); |
|||
store.Verify(store => store.FindByAuthorizationIdAsync("authorization-id", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ThrowsAnExceptionForNullIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.FindByIdAsync(identifier: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ThrowsAnExceptionForEmptyIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentException>( |
|||
() => cache.FindByIdAsync(identifier: string.Empty, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ReturnsCachedTokenOnCacheHit() |
|||
{ |
|||
// Arrange
|
|||
var token = new OpenIddictToken(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("token-id"); |
|||
store.Setup(store => store.GetReferenceIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetApplicationIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetAuthorizationIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetSubjectAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
|
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store.Object); |
|||
|
|||
await cache.AddAsync(token, CancellationToken.None); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByIdAsync("token-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(token, result); |
|||
store.Verify(store => store.FindByIdAsync("token-id", It.IsAny<CancellationToken>()), Times.Never()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var token = new OpenIddictToken(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
|
|||
store.Setup(store => store.FindByIdAsync("token-id", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(token); |
|||
store.Setup(store => store.GetIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("token-id"); |
|||
store.Setup(store => store.GetReferenceIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetApplicationIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetAuthorizationIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetSubjectAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
|
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store.Object); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByIdAsync("token-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(token, result); |
|||
store.Verify(store => store.FindByIdAsync("token-id", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ReturnsNullWhenTokenNotFound() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
|
|||
store.Setup(store => store.FindByIdAsync("token-id", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((OpenIddictToken?) null); |
|||
|
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store.Object); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByIdAsync("token-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Null(result); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByReferenceIdAsync_ThrowsAnExceptionForNullIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.FindByReferenceIdAsync(identifier: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByReferenceIdAsync_ThrowsAnExceptionForEmptyIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentException>( |
|||
() => cache.FindByReferenceIdAsync(identifier: string.Empty, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByReferenceIdAsync_ReturnsCachedTokenOnCacheHit() |
|||
{ |
|||
// Arrange
|
|||
var token = new OpenIddictToken(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("token-id"); |
|||
store.Setup(store => store.GetReferenceIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("reference-id"); |
|||
store.Setup(store => store.GetApplicationIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetAuthorizationIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetSubjectAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
|
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store.Object); |
|||
|
|||
await cache.AddAsync(token, CancellationToken.None); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByReferenceIdAsync("reference-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(token, result); |
|||
store.Verify(store => store.FindByReferenceIdAsync("reference-id", It.IsAny<CancellationToken>()), Times.Never()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByReferenceIdAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var token = new OpenIddictToken(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
|
|||
store.Setup(store => store.FindByReferenceIdAsync("reference-id", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(token); |
|||
store.Setup(store => store.GetIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("token-id"); |
|||
store.Setup(store => store.GetReferenceIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("reference-id"); |
|||
store.Setup(store => store.GetApplicationIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetAuthorizationIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetSubjectAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
|
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store.Object); |
|||
|
|||
// Act
|
|||
var result = await cache.FindByReferenceIdAsync("reference-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Same(token, result); |
|||
store.Verify(store => store.FindByReferenceIdAsync("reference-id", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindBySubjectAsync_QueriesStoreOnCacheMiss() |
|||
{ |
|||
// Arrange
|
|||
var tokens = new[] |
|||
{ |
|||
new OpenIddictToken(), |
|||
new OpenIddictToken() |
|||
}; |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("token-id-1"); |
|||
store.Setup(store => store.GetReferenceIdAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetApplicationIdAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetAuthorizationIdAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetSubjectAsync(tokens[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("subject"); |
|||
|
|||
store.Setup(store => store.GetIdAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("token-id-2"); |
|||
store.Setup(store => store.GetReferenceIdAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetApplicationIdAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetAuthorizationIdAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetSubjectAsync(tokens[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("subject"); |
|||
|
|||
store.Setup(store => store.FindBySubjectAsync("subject", It.IsAny<CancellationToken>())) |
|||
.Returns(tokens.ToAsyncEnumerable()); |
|||
|
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store.Object); |
|||
|
|||
// Act
|
|||
var results = await cache.FindBySubjectAsync("subject", CancellationToken.None).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Equal(2, results.Count); |
|||
Assert.Contains(tokens[0], results); |
|||
Assert.Contains(tokens[1], results); |
|||
store.Verify(store => store.FindBySubjectAsync("subject", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveAsync_ThrowsAnExceptionForNullToken() |
|||
{ |
|||
// Arrange
|
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => cache.RemoveAsync(token: null!, CancellationToken.None).AsTask()); |
|||
|
|||
Assert.Equal("token", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveAsync_InvalidatesCachedEntries() |
|||
{ |
|||
// Arrange
|
|||
var token = new OpenIddictToken(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("token-id"); |
|||
store.Setup(store => store.GetReferenceIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetApplicationIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetAuthorizationIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
store.Setup(store => store.GetSubjectAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
|
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store.Object); |
|||
|
|||
await cache.AddAsync(token, CancellationToken.None); |
|||
|
|||
// Act
|
|||
await cache.RemoveAsync(token, CancellationToken.None); |
|||
|
|||
var result = await cache.FindByIdAsync("token-id", CancellationToken.None); |
|||
|
|||
// Assert
|
|||
Assert.Null(result); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveAsync_ThrowsForTokenWithoutId() |
|||
{ |
|||
// Arrange
|
|||
var token = new OpenIddictToken(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictTokenStore<OpenIddictToken>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(token, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((string?) null); |
|||
|
|||
var cache = new OpenIddictTokenCache<OpenIddictToken>(options, store.Object); |
|||
|
|||
// Act and assert
|
|||
await Assert.ThrowsAsync<InvalidOperationException>( |
|||
() => cache.RemoveAsync(token, CancellationToken.None).AsTask()); |
|||
} |
|||
|
|||
public sealed class OpenIddictToken { } |
|||
} |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,977 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System.Collections.Immutable; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using System.Globalization; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Options; |
|||
using Moq; |
|||
using Xunit; |
|||
|
|||
namespace OpenIddict.Core.Tests; |
|||
|
|||
public class OpenIddictScopeManagerTests |
|||
{ |
|||
[Fact] |
|||
public void Constructor_ThrowsAnExceptionForNullCache() |
|||
{ |
|||
// Arrange
|
|||
var cache = (IOpenIddictScopeCache<CustomScope>) null!; |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>( |
|||
() => new OpenIddictScopeManager<CustomScope>(cache, logger, options, store)); |
|||
|
|||
Assert.Equal("cache", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Constructor_ThrowsAnExceptionForNullLogger() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = (ILogger<OpenIddictScopeManager<CustomScope>>) null!; |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>( |
|||
() => new OpenIddictScopeManager<CustomScope>(cache, logger, options, store)); |
|||
|
|||
Assert.Equal("logger", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Constructor_ThrowsAnExceptionForNullOptions() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = (IOptionsMonitor<OpenIddictCoreOptions>) null!; |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>( |
|||
() => new OpenIddictScopeManager<CustomScope>(cache, logger, options, store)); |
|||
|
|||
Assert.Equal("options", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Constructor_ThrowsAnExceptionForNullStore() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = (IOpenIddictScopeStore<CustomScope>) null!; |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>( |
|||
() => new OpenIddictScopeManager<CustomScope>(cache, logger, options, store)); |
|||
|
|||
Assert.Equal("store", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task CountAsync_CallsStoreMethod() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
store.Setup(store => store.CountAsync(It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(42); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
var count = await manager.CountAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Equal(42, count); |
|||
store.Verify(store => store.CountAsync(It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task CountAsync_WithQuery_ThrowsAnExceptionForNullQuery() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.CountAsync<CustomScope>(query: null!).AsTask()); |
|||
|
|||
Assert.Equal("query", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task CreateAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.CreateAsync(scope: null!).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task DeleteAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.DeleteAsync(scope: null!).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task DeleteAsync_RemovesScopeFromCache_WhenCachingIsEnabled() |
|||
{ |
|||
// Arrange
|
|||
var cache = new Mock<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = false }); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
var scope = new CustomScope(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache.Object, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
await manager.DeleteAsync(scope); |
|||
|
|||
// Assert
|
|||
cache.Verify(cache => cache.RemoveAsync(scope, It.IsAny<CancellationToken>()), Times.Once()); |
|||
store.Verify(store => store.DeleteAsync(scope, It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task DeleteAsync_DoesNotRemoveFromCache_WhenCachingIsDisabled() |
|||
{ |
|||
// Arrange
|
|||
var cache = new Mock<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = true }); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
var scope = new CustomScope(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache.Object, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
await manager.DeleteAsync(scope); |
|||
|
|||
// Assert
|
|||
cache.Verify(cache => cache.RemoveAsync(It.IsAny<CustomScope>(), It.IsAny<CancellationToken>()), Times.Never()); |
|||
store.Verify(store => store.DeleteAsync(scope, It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ThrowsAnExceptionForNullIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.FindByIdAsync(identifier: null!).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_ThrowsAnExceptionForEmptyIdentifier() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentException>( |
|||
() => manager.FindByIdAsync(identifier: string.Empty).AsTask()); |
|||
|
|||
Assert.Equal("identifier", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_UsesCache_WhenCachingIsEnabled() |
|||
{ |
|||
// Arrange
|
|||
var scope = new CustomScope(); |
|||
var cache = new Mock<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = false }); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
cache.Setup(cache => cache.FindByIdAsync("id", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(scope); |
|||
|
|||
store.Setup(store => store.GetIdAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("id"); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache.Object, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
var result = await manager.FindByIdAsync("id"); |
|||
|
|||
// Assert
|
|||
Assert.Same(scope, result); |
|||
cache.Verify(cache => cache.FindByIdAsync("id", It.IsAny<CancellationToken>()), Times.Once()); |
|||
store.Verify(store => store.FindByIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByIdAsync_UsesStore_WhenCachingIsDisabled() |
|||
{ |
|||
// Arrange
|
|||
var scope = new CustomScope(); |
|||
var cache = new Mock<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = true }); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
store.Setup(store => store.FindByIdAsync("id", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(scope); |
|||
|
|||
store.Setup(store => store.GetIdAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("id"); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache.Object, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
var result = await manager.FindByIdAsync("id"); |
|||
|
|||
// Assert
|
|||
Assert.Same(scope, result); |
|||
cache.Verify(cache => cache.FindByIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never()); |
|||
store.Verify(store => store.FindByIdAsync("id", It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByNameAsync_ThrowsAnExceptionForNullName() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.FindByNameAsync(name: null!).AsTask()); |
|||
|
|||
Assert.Equal("name", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByNameAsync_ThrowsAnExceptionForEmptyName() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentException>( |
|||
() => manager.FindByNameAsync(name: string.Empty).AsTask()); |
|||
|
|||
Assert.Equal("name", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindByNamesAsync_ReturnsEmptyWhenNoScopesMatch() |
|||
{ |
|||
// Arrange
|
|||
var cache = new Mock<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
cache.Setup(cache => cache.FindByNamesAsync(It.IsAny<ImmutableArray<string>>(), It.IsAny<CancellationToken>())) |
|||
.Returns((ImmutableArray<string> names, CancellationToken cancellationToken) => |
|||
{ |
|||
return Enumerable.Empty<CustomScope>().ToAsyncEnumerable(); |
|||
}); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache.Object, logger, options, store); |
|||
|
|||
// Act
|
|||
var results = new List<CustomScope>(); |
|||
await foreach (var scope in manager.FindByNamesAsync(["scope1", "scope2"])) |
|||
{ |
|||
results.Add(scope); |
|||
} |
|||
|
|||
// Assert
|
|||
Assert.Empty(results); |
|||
} |
|||
|
|||
[Fact] |
|||
public void FindByResourceAsync_ThrowsAnExceptionForNullResource() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>( |
|||
() => manager.FindByResourceAsync(resource: null!)); |
|||
|
|||
Assert.Equal("resource", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void FindByResourceAsync_ThrowsAnExceptionForEmptyResource() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentException>( |
|||
() => manager.FindByResourceAsync(resource: string.Empty)); |
|||
|
|||
Assert.Equal("resource", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetAsync_WithQuery_ThrowsAnExceptionForNullQuery() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.GetAsync<CustomScope>(query: null!).AsTask()); |
|||
|
|||
Assert.Equal("query", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetAsync_WithQueryAndState_ThrowsAnExceptionForNullQuery() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.GetAsync<object, CustomScope>(query: null!, state: null!).AsTask()); |
|||
|
|||
Assert.Equal("query", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetDescriptionAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.GetDescriptionAsync(scope: null!).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetDescriptionsAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.GetDescriptionsAsync(scope: null!).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetDisplayNameAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.GetDisplayNameAsync(scope: null!).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetDisplayNamesAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.GetDisplayNamesAsync(scope: null!).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetIdAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.GetIdAsync(scope: null!).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetIdAsync_ReturnsIdentifierFromStore() |
|||
{ |
|||
// Arrange
|
|||
var scope = new CustomScope(); |
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
store.Setup(store => store.GetIdAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("unique-scope-id"); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
var id = await manager.GetIdAsync(scope); |
|||
|
|||
// Assert
|
|||
Assert.Equal("unique-scope-id", id); |
|||
store.Verify(store => store.GetIdAsync(scope, It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetLocalizedDescriptionAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.GetLocalizedDescriptionAsync(scope: null!, CultureInfo.InvariantCulture).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetLocalizedDescriptionAsync_ReturnsDescriptionForMatchingCulture() |
|||
{ |
|||
// Arrange
|
|||
var scope = new CustomScope(); |
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
store.Setup(store => store.GetDescriptionsAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(ImmutableDictionary.Create<CultureInfo, string>() |
|||
.Add(CultureInfo.GetCultureInfo("en-US"), "English description") |
|||
.Add(CultureInfo.GetCultureInfo("fr-FR"), "Description fran�aise")); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
var description = await manager.GetLocalizedDescriptionAsync(scope, CultureInfo.GetCultureInfo("fr-FR")); |
|||
|
|||
// Assert
|
|||
Assert.Equal("Description fran�aise", description); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetLocalizedDescriptionAsync_FallsBackToNonLocalizedDescriptionWhenNoCultureMatches() |
|||
{ |
|||
// Arrange
|
|||
var scope = new CustomScope(); |
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
store.Setup(store => store.GetDescriptionsAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(ImmutableDictionary.Create<CultureInfo, string>() |
|||
.Add(CultureInfo.GetCultureInfo("en-US"), "English description")); |
|||
|
|||
store.Setup(store => store.GetDescriptionAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("Default description"); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
var description = await manager.GetLocalizedDescriptionAsync(scope, CultureInfo.GetCultureInfo("ja-JP")); |
|||
|
|||
// Assert
|
|||
Assert.Equal("Default description", description); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetLocalizedDisplayNameAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.GetLocalizedDisplayNameAsync(scope: null!, CultureInfo.InvariantCulture).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetLocalizedDisplayNameAsync_ReturnsDisplayNameForMatchingCulture() |
|||
{ |
|||
// Arrange
|
|||
var scope = new CustomScope(); |
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
store.Setup(store => store.GetDisplayNamesAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(ImmutableDictionary.Create<CultureInfo, string>() |
|||
.Add(CultureInfo.GetCultureInfo("en-US"), "English name") |
|||
.Add(CultureInfo.GetCultureInfo("fr-FR"), "Nom fran�ais")); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
var name = await manager.GetLocalizedDisplayNameAsync(scope, CultureInfo.GetCultureInfo("fr-FR")); |
|||
|
|||
// Assert
|
|||
Assert.Equal("Nom fran�ais", name); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetNameAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.GetNameAsync(scope: null!).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetPropertiesAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.GetPropertiesAsync(scope: null!).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetResourcesAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.GetResourcesAsync(scope: null!).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ListAsync_ReturnsAllScopes() |
|||
{ |
|||
// Arrange
|
|||
var scopes = new[] { new CustomScope(), new CustomScope() }; |
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
store.Setup(store => store.ListAsync(It.IsAny<int?>(), It.IsAny<int?>(), It.IsAny<CancellationToken>())) |
|||
.Returns(scopes.ToAsyncEnumerable()); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
var results = new List<CustomScope>(); |
|||
await foreach (var scp in manager.ListAsync()) |
|||
{ |
|||
results.Add(scp); |
|||
} |
|||
|
|||
// Assert
|
|||
Assert.Equal(2, results.Count); |
|||
store.Verify(store => store.ListAsync(It.IsAny<int?>(), It.IsAny<int?>(), It.IsAny<CancellationToken>()), Times.Once()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ListResourcesAsync_ReturnsDeduplicatedResourcesFromMatchingScopes() |
|||
{ |
|||
// Arrange
|
|||
var scopes = new[] { new CustomScope(), new CustomScope() }; |
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = true }); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
store.Setup(store => store.FindByNamesAsync(It.IsAny<ImmutableArray<string>>(), It.IsAny<CancellationToken>())) |
|||
.Returns(scopes.ToAsyncEnumerable()); |
|||
|
|||
store.Setup(store => store.GetNameAsync(scopes[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("openid"); |
|||
|
|||
store.Setup(store => store.GetNameAsync(scopes[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("profile"); |
|||
|
|||
store.Setup(store => store.GetResourcesAsync(scopes[0], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(["resource1", "resource2"]); |
|||
|
|||
store.Setup(store => store.GetResourcesAsync(scopes[1], It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(["resource2", "resource3"]); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
var resources = await manager.ListResourcesAsync(["openid", "profile"]).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Equal(3, resources.Count); |
|||
Assert.Contains("resource1", resources); |
|||
Assert.Contains("resource2", resources); |
|||
Assert.Contains("resource3", resources); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task PopulateAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
var descriptor = new OpenIddictScopeDescriptor(); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.PopulateAsync(scope: null!, descriptor).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task PopulateAsync_ThrowsAnExceptionForNullDescriptor() |
|||
{ |
|||
// Arrange
|
|||
var scope = new CustomScope(); |
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.PopulateAsync(scope, descriptor: null!).AsTask()); |
|||
|
|||
Assert.Equal("descriptor", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdateAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.UpdateAsync(scope: null!).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdateAsync_WithDescriptor_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
var descriptor = new OpenIddictScopeDescriptor(); |
|||
|
|||
// Act and assert
|
|||
var exception = await Assert.ThrowsAsync<ArgumentNullException>( |
|||
() => manager.UpdateAsync(scope: null!, descriptor).AsTask()); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ValidateAsync_ThrowsAnExceptionForNullScope() |
|||
{ |
|||
// Arrange
|
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(); |
|||
var store = Mock.Of<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store); |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>( |
|||
() => manager.ValidateAsync(scope: null!)); |
|||
|
|||
Assert.Equal("scope", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ValidateAsync_ReturnsErrorWhenNameIsEmpty() |
|||
{ |
|||
// Arrange
|
|||
var scope = new CustomScope(); |
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
store.Setup(store => store.GetNameAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(string.Empty); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
var results = await manager.ValidateAsync(scope).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2044)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ValidateAsync_ReturnsErrorWhenNameContainsSpace() |
|||
{ |
|||
// Arrange
|
|||
var scope = new CustomScope(); |
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
store.Setup(store => store.GetNameAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope name with space"); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
var results = await manager.ValidateAsync(scope).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2045)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ValidateAsync_ReturnsErrorWhenNameIsAlreadyUsed() |
|||
{ |
|||
// Arrange
|
|||
var scope = new CustomScope(); |
|||
var other = new CustomScope(); |
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
store.Setup(store => store.GetNameAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("openid"); |
|||
|
|||
store.Setup(store => store.FindByNameAsync("openid", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync(other); |
|||
|
|||
store.Setup(store => store.GetIdAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("scope-id"); |
|||
|
|||
store.Setup(store => store.GetIdAsync(other, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("other-scope-id"); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
var results = await manager.ValidateAsync(scope).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2060)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ValidateAsync_ReturnsNoErrorsForValidScope() |
|||
{ |
|||
// Arrange
|
|||
var scope = new CustomScope(); |
|||
var cache = Mock.Of<IOpenIddictScopeCache<CustomScope>>(); |
|||
var logger = Mock.Of<ILogger<OpenIddictScopeManager<CustomScope>>>(); |
|||
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>( |
|||
mock => mock.CurrentValue == new OpenIddictCoreOptions()); |
|||
var store = new Mock<IOpenIddictScopeStore<CustomScope>>(); |
|||
|
|||
store.Setup(store => store.GetNameAsync(scope, It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync("openid"); |
|||
|
|||
store.Setup(store => store.FindByNameAsync("openid", It.IsAny<CancellationToken>())) |
|||
.ReturnsAsync((CustomScope?) null); |
|||
|
|||
var manager = new OpenIddictScopeManager<CustomScope>(cache, logger, options, store.Object); |
|||
|
|||
// Act
|
|||
var results = await manager.ValidateAsync(scope).ToListAsync(); |
|||
|
|||
// Assert
|
|||
Assert.DoesNotContain(results, static result => result != ValidationResult.Success); |
|||
} |
|||
|
|||
public class CustomScope { } |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,303 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System.Security.Cryptography; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Xunit; |
|||
|
|||
namespace OpenIddict.Core.Tests; |
|||
|
|||
public class OpenIddictCoreConfigurationTests |
|||
{ |
|||
[Fact] |
|||
public void Constructor_ThrowsAnExceptionForNullProvider() |
|||
{ |
|||
// Arrange
|
|||
var provider = (IServiceProvider) null!; |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>(() => new OpenIddictCoreConfiguration(provider)); |
|||
|
|||
Assert.Equal("provider", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void PostConfigure_ThrowsAnExceptionForNullOptions() |
|||
{ |
|||
// Arrange
|
|||
var services = new ServiceCollection(); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>(() => configuration.PostConfigure(null, null!)); |
|||
|
|||
Assert.Equal("options", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void PostConfigure_SetsTimeProviderToSystemWhenNotRegistered() |
|||
{ |
|||
// Arrange
|
|||
var services = new ServiceCollection(); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
var options = new OpenIddictCoreOptions(); |
|||
|
|||
// Act
|
|||
configuration.PostConfigure(null, options); |
|||
|
|||
// Assert
|
|||
Assert.Same(TimeProvider.System, options.TimeProvider); |
|||
} |
|||
|
|||
[Fact] |
|||
public void PostConfigure_UsesRegisteredTimeProvider() |
|||
{ |
|||
// Arrange
|
|||
var customTimeProvider = new CustomTimeProvider(); |
|||
var services = new ServiceCollection(); |
|||
services.AddSingleton<TimeProvider>(customTimeProvider); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
var options = new OpenIddictCoreOptions(); |
|||
|
|||
// Act
|
|||
configuration.PostConfigure(null, options); |
|||
|
|||
// Assert
|
|||
Assert.Same(customTimeProvider, options.TimeProvider); |
|||
} |
|||
|
|||
[Fact] |
|||
public void PostConfigure_DoesNotOverrideExplicitlySetTimeProvider() |
|||
{ |
|||
// Arrange
|
|||
var explicitTimeProvider = new CustomTimeProvider(); |
|||
var registeredTimeProvider = new CustomTimeProvider(); |
|||
var services = new ServiceCollection(); |
|||
services.AddSingleton<TimeProvider>(registeredTimeProvider); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
var options = new OpenIddictCoreOptions |
|||
{ |
|||
TimeProvider = explicitTimeProvider |
|||
}; |
|||
|
|||
// Act
|
|||
configuration.PostConfigure(null, options); |
|||
|
|||
// Assert
|
|||
Assert.Same(explicitTimeProvider, options.TimeProvider); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Validate_ThrowsAnExceptionForNullOptions() |
|||
{ |
|||
// Arrange
|
|||
var services = new ServiceCollection(); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
|
|||
// Act and assert
|
|||
var exception = Assert.Throws<ArgumentNullException>(() => configuration.Validate(null, null!)); |
|||
|
|||
Assert.Equal("options", exception.ParamName); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Validate_SucceedsForDefaultOptions() |
|||
{ |
|||
// Arrange
|
|||
var services = new ServiceCollection(); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
var options = new OpenIddictCoreOptions(); |
|||
|
|||
// Act
|
|||
var result = configuration.Validate(null, options); |
|||
|
|||
// Assert
|
|||
Assert.True(result.Succeeded); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(nameof(HashAlgorithmName.SHA1))] |
|||
[InlineData(nameof(HashAlgorithmName.SHA256))] |
|||
[InlineData(nameof(HashAlgorithmName.SHA512))] |
|||
public void Validate_SucceedsForValidHashAlgorithm(string algorithmName) |
|||
{ |
|||
// Arrange
|
|||
var services = new ServiceCollection(); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
var options = new OpenIddictCoreOptions |
|||
{ |
|||
ClientSecretKeyDerivationHashAlgorithm = new HashAlgorithmName(algorithmName) |
|||
}; |
|||
|
|||
// Act
|
|||
var result = configuration.Validate(null, options); |
|||
|
|||
// Assert
|
|||
Assert.True(result.Succeeded); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Validate_FailsForInvalidHashAlgorithm() |
|||
{ |
|||
// Arrange
|
|||
var services = new ServiceCollection(); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
var options = new OpenIddictCoreOptions |
|||
{ |
|||
ClientSecretKeyDerivationHashAlgorithm = new HashAlgorithmName("MD5") |
|||
}; |
|||
|
|||
// Act
|
|||
var result = configuration.Validate(null, options); |
|||
|
|||
// Assert
|
|||
Assert.True(result.Failed); |
|||
Assert.Contains(SR.FormatID0217("MD5"), result.Failures); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(9_999)] |
|||
[InlineData(10_000_001)] |
|||
public void Validate_FailsForInvalidIterationCount(int iterations) |
|||
{ |
|||
// Arrange
|
|||
var services = new ServiceCollection(); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
var options = new OpenIddictCoreOptions |
|||
{ |
|||
ClientSecretKeyDerivationIterations = iterations |
|||
}; |
|||
|
|||
// Act
|
|||
var result = configuration.Validate(null, options); |
|||
|
|||
// Assert
|
|||
Assert.True(result.Failed); |
|||
Assert.Contains(SR.FormatID0518(10_000, 10_000_000), result.Failures); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(10_000)] |
|||
[InlineData(100_000)] |
|||
[InlineData(10_000_000)] |
|||
public void Validate_SucceedsForValidIterationCount(int iterations) |
|||
{ |
|||
// Arrange
|
|||
var services = new ServiceCollection(); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
var options = new OpenIddictCoreOptions |
|||
{ |
|||
ClientSecretKeyDerivationIterations = iterations |
|||
}; |
|||
|
|||
// Act
|
|||
var result = configuration.Validate(null, options); |
|||
|
|||
// Assert
|
|||
Assert.True(result.Succeeded); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(127)] |
|||
[InlineData(1025)] |
|||
public void Validate_FailsForInvalidSaltLength(int saltLength) |
|||
{ |
|||
// Arrange
|
|||
var services = new ServiceCollection(); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
var options = new OpenIddictCoreOptions |
|||
{ |
|||
ClientSecretKeyDerivationSaltLength = saltLength |
|||
}; |
|||
|
|||
// Act
|
|||
var result = configuration.Validate(null, options); |
|||
|
|||
// Assert
|
|||
Assert.True(result.Failed); |
|||
Assert.Contains(SR.FormatID0519(128, 1024), result.Failures); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(128)] |
|||
[InlineData(256)] |
|||
[InlineData(1024)] |
|||
public void Validate_SucceedsForValidSaltLength(int saltLength) |
|||
{ |
|||
// Arrange
|
|||
var services = new ServiceCollection(); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
var options = new OpenIddictCoreOptions |
|||
{ |
|||
ClientSecretKeyDerivationSaltLength = saltLength |
|||
}; |
|||
|
|||
// Act
|
|||
var result = configuration.Validate(null, options); |
|||
|
|||
// Assert
|
|||
Assert.True(result.Succeeded); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(255)] |
|||
[InlineData(2049)] |
|||
public void Validate_FailsForInvalidOutputLength(int outputLength) |
|||
{ |
|||
// Arrange
|
|||
var services = new ServiceCollection(); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
var options = new OpenIddictCoreOptions |
|||
{ |
|||
ClientSecretKeyDerivationOutputLength = outputLength |
|||
}; |
|||
|
|||
// Act
|
|||
var result = configuration.Validate(null, options); |
|||
|
|||
// Assert
|
|||
Assert.True(result.Failed); |
|||
Assert.Contains(SR.FormatID0520(256, 2048), result.Failures); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(256)] |
|||
[InlineData(512)] |
|||
[InlineData(2048)] |
|||
public void Validate_SucceedsForValidOutputLength(int outputLength) |
|||
{ |
|||
// Arrange
|
|||
var services = new ServiceCollection(); |
|||
var provider = services.BuildServiceProvider(); |
|||
var configuration = new OpenIddictCoreConfiguration(provider); |
|||
var options = new OpenIddictCoreOptions |
|||
{ |
|||
ClientSecretKeyDerivationOutputLength = outputLength |
|||
}; |
|||
|
|||
// Act
|
|||
var result = configuration.Validate(null, options); |
|||
|
|||
// Assert
|
|||
Assert.True(result.Succeeded); |
|||
} |
|||
private class CustomTimeProvider : TimeProvider |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,152 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System.Security.Cryptography; |
|||
using Xunit; |
|||
|
|||
namespace OpenIddict.Core.Tests; |
|||
|
|||
public class OpenIddictCoreOptionsTests |
|||
{ |
|||
[Fact] |
|||
public void Constructor_SetsDefaultValues() |
|||
{ |
|||
// Act
|
|||
var options = new OpenIddictCoreOptions(); |
|||
|
|||
// Assert
|
|||
Assert.Equal(HashAlgorithmName.SHA512, options.ClientSecretKeyDerivationHashAlgorithm); |
|||
Assert.Equal(100_000, options.ClientSecretKeyDerivationIterations); |
|||
Assert.Equal(512, options.ClientSecretKeyDerivationOutputLength); |
|||
Assert.Equal(256, options.ClientSecretKeyDerivationSaltLength); |
|||
Assert.False(options.DisableAdditionalFiltering); |
|||
Assert.False(options.DisableAutomaticClientSecretRehashing); |
|||
Assert.False(options.DisableEntityCaching); |
|||
Assert.Equal(250, options.EntityCacheLimit); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ClientSecretKeyDerivationHashAlgorithm_CanBeSet() |
|||
{ |
|||
// Arrange
|
|||
var options = new OpenIddictCoreOptions(); |
|||
|
|||
// Act
|
|||
options.ClientSecretKeyDerivationHashAlgorithm = HashAlgorithmName.SHA256; |
|||
|
|||
// Assert
|
|||
Assert.Equal(HashAlgorithmName.SHA256, options.ClientSecretKeyDerivationHashAlgorithm); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ClientSecretKeyDerivationIterations_CanBeSet() |
|||
{ |
|||
// Arrange
|
|||
var options = new OpenIddictCoreOptions(); |
|||
|
|||
// Act
|
|||
options.ClientSecretKeyDerivationIterations = 50_000; |
|||
|
|||
// Assert
|
|||
Assert.Equal(50_000, options.ClientSecretKeyDerivationIterations); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ClientSecretKeyDerivationOutputLength_CanBeSet() |
|||
{ |
|||
// Arrange
|
|||
var options = new OpenIddictCoreOptions(); |
|||
|
|||
// Act
|
|||
options.ClientSecretKeyDerivationOutputLength = 1024; |
|||
|
|||
// Assert
|
|||
Assert.Equal(1024, options.ClientSecretKeyDerivationOutputLength); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ClientSecretKeyDerivationSaltLength_CanBeSet() |
|||
{ |
|||
// Arrange
|
|||
var options = new OpenIddictCoreOptions(); |
|||
|
|||
// Act
|
|||
options.ClientSecretKeyDerivationSaltLength = 512; |
|||
|
|||
// Assert
|
|||
Assert.Equal(512, options.ClientSecretKeyDerivationSaltLength); |
|||
} |
|||
|
|||
[Fact] |
|||
public void DisableAdditionalFiltering_CanBeSet() |
|||
{ |
|||
// Arrange
|
|||
var options = new OpenIddictCoreOptions(); |
|||
|
|||
// Act
|
|||
options.DisableAdditionalFiltering = true; |
|||
|
|||
// Assert
|
|||
Assert.True(options.DisableAdditionalFiltering); |
|||
} |
|||
|
|||
[Fact] |
|||
public void DisableAutomaticClientSecretRehashing_CanBeSet() |
|||
{ |
|||
// Arrange
|
|||
var options = new OpenIddictCoreOptions(); |
|||
|
|||
// Act
|
|||
options.DisableAutomaticClientSecretRehashing = true; |
|||
|
|||
// Assert
|
|||
Assert.True(options.DisableAutomaticClientSecretRehashing); |
|||
} |
|||
|
|||
[Fact] |
|||
public void DisableEntityCaching_CanBeSet() |
|||
{ |
|||
// Arrange
|
|||
var options = new OpenIddictCoreOptions(); |
|||
|
|||
// Act
|
|||
options.DisableEntityCaching = true; |
|||
|
|||
// Assert
|
|||
Assert.True(options.DisableEntityCaching); |
|||
} |
|||
|
|||
[Fact] |
|||
public void EntityCacheLimit_CanBeSet() |
|||
{ |
|||
// Arrange
|
|||
var options = new OpenIddictCoreOptions(); |
|||
|
|||
// Act
|
|||
options.EntityCacheLimit = 500; |
|||
|
|||
// Assert
|
|||
Assert.Equal(500, options.EntityCacheLimit); |
|||
} |
|||
|
|||
[Fact] |
|||
public void TimeProvider_CanBeSet() |
|||
{ |
|||
// Arrange
|
|||
var options = new OpenIddictCoreOptions(); |
|||
var customTimeProvider = new CustomTimeProvider(); |
|||
|
|||
// Act
|
|||
options.TimeProvider = customTimeProvider; |
|||
|
|||
// Assert
|
|||
Assert.Same(customTimeProvider, options.TimeProvider); |
|||
} |
|||
|
|||
private class CustomTimeProvider : TimeProvider |
|||
{ |
|||
} |
|||
} |
|||
Loading…
Reference in new issue