/* * 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 OpenIddictResourceManagerTests { [Fact] public void Constructor_ThrowsAnExceptionForNullCache() { // Arrange var cache = (IOpenIddictResourceCache) null!; var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); // Act and assert var exception = Assert.Throws( () => new OpenIddictResourceManager(cache, logger, options, store)); Assert.Equal("cache", exception.ParamName); } [Fact] public void Constructor_ThrowsAnExceptionForNullLogger() { // Arrange var cache = Mock.Of>(); var logger = (ILogger>) null!; var options = Mock.Of>(); var store = Mock.Of>(); // Act and assert var exception = Assert.Throws( () => new OpenIddictResourceManager(cache, logger, options, store)); Assert.Equal("logger", exception.ParamName); } [Fact] public void Constructor_ThrowsAnExceptionForNullOptions() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = (IOptionsMonitor) null!; var store = Mock.Of>(); // Act and assert var exception = Assert.Throws( () => new OpenIddictResourceManager(cache, logger, options, store)); Assert.Equal("options", exception.ParamName); } [Fact] public void Constructor_ThrowsAnExceptionForNullStore() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = (IOpenIddictResourceStore) null!; // Act and assert var exception = Assert.Throws( () => new OpenIddictResourceManager(cache, logger, options, store)); Assert.Equal("store", exception.ParamName); } [Fact] public async Task CountAsync_CallsStoreMethod() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions()); var store = new Mock>(); store.Setup(store => store.CountAsync(It.IsAny())) .ReturnsAsync(42); var manager = new OpenIddictResourceManager(cache, logger, options, store.Object); // Act var count = await manager.CountAsync(); // Assert Assert.Equal(42, count); store.Verify(store => store.CountAsync(It.IsAny()), Times.Once()); } [Fact] public async Task CountAsync_WithQuery_ThrowsAnExceptionForNullQuery() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.CountAsync(query: null!).AsTask()); Assert.Equal("query", exception.ParamName); } [Fact] public async Task CreateAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions()); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.CreateAsync(resource: null!).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task DeleteAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions()); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.DeleteAsync(resource: null!).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task DeleteAsync_RemovesResourceFromCache_WhenCachingIsEnabled() { // Arrange var cache = new Mock>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = false }); var store = new Mock>(); var resource = new CustomResource(); var manager = new OpenIddictResourceManager(cache.Object, logger, options, store.Object); // Act await manager.DeleteAsync(resource); // Assert cache.Verify(cache => cache.RemoveAsync(resource, It.IsAny()), Times.Once()); store.Verify(store => store.DeleteAsync(resource, It.IsAny()), Times.Once()); } [Fact] public async Task DeleteAsync_DoesNotRemoveFromCache_WhenCachingIsDisabled() { // Arrange var cache = new Mock>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = true }); var store = new Mock>(); var resource = new CustomResource(); var manager = new OpenIddictResourceManager(cache.Object, logger, options, store.Object); // Act await manager.DeleteAsync(resource); // Assert cache.Verify(cache => cache.RemoveAsync(It.IsAny(), It.IsAny()), Times.Never()); store.Verify(store => store.DeleteAsync(resource, It.IsAny()), Times.Once()); } [Fact] public async Task FindByIdAsync_ThrowsAnExceptionForNullIdentifier() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.FindByIdAsync(identifier: null!).AsTask()); Assert.Equal("identifier", exception.ParamName); } [Fact] public async Task FindByIdAsync_ThrowsAnExceptionForEmptyIdentifier() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.FindByIdAsync(identifier: string.Empty).AsTask()); Assert.Equal("identifier", exception.ParamName); } [Fact] public async Task FindByIdAsync_UsesCache_WhenCachingIsEnabled() { // Arrange var resource = new CustomResource(); var cache = new Mock>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = false }); var store = new Mock>(); cache.Setup(cache => cache.FindByIdAsync("id", It.IsAny())) .ReturnsAsync(resource); store.Setup(store => store.GetIdAsync(resource, It.IsAny())) .ReturnsAsync("id"); var manager = new OpenIddictResourceManager(cache.Object, logger, options, store.Object); // Act var result = await manager.FindByIdAsync("id"); // Assert Assert.Same(resource, result); cache.Verify(cache => cache.FindByIdAsync("id", It.IsAny()), Times.Once()); store.Verify(store => store.FindByIdAsync(It.IsAny(), It.IsAny()), Times.Never()); } [Fact] public async Task FindByIdAsync_UsesStore_WhenCachingIsDisabled() { // Arrange var resource = new CustomResource(); var cache = new Mock>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = true }); var store = new Mock>(); store.Setup(store => store.FindByIdAsync("id", It.IsAny())) .ReturnsAsync(resource); store.Setup(store => store.GetIdAsync(resource, It.IsAny())) .ReturnsAsync("id"); var manager = new OpenIddictResourceManager(cache.Object, logger, options, store.Object); // Act var result = await manager.FindByIdAsync("id"); // Assert Assert.Same(resource, result); cache.Verify(cache => cache.FindByIdAsync(It.IsAny(), It.IsAny()), Times.Never()); store.Verify(store => store.FindByIdAsync("id", It.IsAny()), Times.Once()); } [Fact] public async Task FindByNameAsync_ThrowsAnExceptionForNullName() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.FindByNameAsync(name: null!).AsTask()); Assert.Equal("name", exception.ParamName); } [Fact] public async Task FindByNameAsync_ThrowsAnExceptionForEmptyName() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.FindByNameAsync(name: string.Empty).AsTask()); Assert.Equal("name", exception.ParamName); } [Fact] public async Task FindByNamesAsync_ReturnsEmptyWhenNoResourcesMatch() { // Arrange var cache = new Mock>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions()); var store = Mock.Of>(); cache.Setup(cache => cache.FindByNamesAsync(It.IsAny>(), It.IsAny())) .Returns((ImmutableArray names, CancellationToken cancellationToken) => { return Enumerable.Empty().ToAsyncEnumerable(); }); var manager = new OpenIddictResourceManager(cache.Object, logger, options, store); // Act var results = new List(); await foreach (var resource in manager.FindByNamesAsync(["resource1", "resource2"])) { results.Add(resource); } // Assert Assert.Empty(results); } [Fact] public async Task GetAsync_WithQuery_ThrowsAnExceptionForNullQuery() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.GetAsync(query: null!).AsTask()); Assert.Equal("query", exception.ParamName); } [Fact] public async Task GetAsync_WithQueryAndState_ThrowsAnExceptionForNullQuery() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.GetAsync(query: null!, state: null!).AsTask()); Assert.Equal("query", exception.ParamName); } [Fact] public async Task GetDescriptionAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.GetDescriptionAsync(resource: null!).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task GetDescriptionsAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.GetDescriptionsAsync(resource: null!).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task GetDisplayNameAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.GetDisplayNameAsync(resource: null!).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task GetDisplayNamesAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.GetDisplayNamesAsync(resource: null!).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task GetIdAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.GetIdAsync(resource: null!).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task GetIdAsync_ReturnsIdentifierFromStore() { // Arrange var resource = new CustomResource(); var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = new Mock>(); store.Setup(store => store.GetIdAsync(resource, It.IsAny())) .ReturnsAsync("unique-resource-id"); var manager = new OpenIddictResourceManager(cache, logger, options, store.Object); // Act var id = await manager.GetIdAsync(resource); // Assert Assert.Equal("unique-resource-id", id); store.Verify(store => store.GetIdAsync(resource, It.IsAny()), Times.Once()); } [Fact] public async Task GetLocalizedDescriptionAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.GetLocalizedDescriptionAsync(resource: null!, CultureInfo.InvariantCulture).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task GetLocalizedDescriptionAsync_ReturnsDescriptionForMatchingCulture() { // Arrange var resource = new CustomResource(); var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = new Mock>(); store.Setup(store => store.GetDescriptionsAsync(resource, It.IsAny())) .ReturnsAsync(ImmutableDictionary.Create() .Add(CultureInfo.GetCultureInfo("en-US"), "English description") .Add(CultureInfo.GetCultureInfo("fr-FR"), "Description française")); var manager = new OpenIddictResourceManager(cache, logger, options, store.Object); // Act var description = await manager.GetLocalizedDescriptionAsync(resource, CultureInfo.GetCultureInfo("fr-FR")); // Assert Assert.Equal("Description française", description); } [Fact] public async Task GetLocalizedDescriptionAsync_FallsBackToNonLocalizedDescriptionWhenNoCultureMatches() { // Arrange var resource = new CustomResource(); var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = new Mock>(); store.Setup(store => store.GetDescriptionsAsync(resource, It.IsAny())) .ReturnsAsync(ImmutableDictionary.Create() .Add(CultureInfo.GetCultureInfo("en-US"), "English description")); store.Setup(store => store.GetDescriptionAsync(resource, It.IsAny())) .ReturnsAsync("Default description"); var manager = new OpenIddictResourceManager(cache, logger, options, store.Object); // Act var description = await manager.GetLocalizedDescriptionAsync(resource, CultureInfo.GetCultureInfo("ja-JP")); // Assert Assert.Equal("Default description", description); } [Fact] public async Task GetLocalizedDisplayNameAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.GetLocalizedDisplayNameAsync(resource: null!, CultureInfo.InvariantCulture).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task GetLocalizedDisplayNameAsync_ReturnsDisplayNameForMatchingCulture() { // Arrange var resource = new CustomResource(); var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = new Mock>(); store.Setup(store => store.GetDisplayNamesAsync(resource, It.IsAny())) .ReturnsAsync(ImmutableDictionary.Create() .Add(CultureInfo.GetCultureInfo("en-US"), "English name") .Add(CultureInfo.GetCultureInfo("fr-FR"), "Nom fran�ais")); var manager = new OpenIddictResourceManager(cache, logger, options, store.Object); // Act var name = await manager.GetLocalizedDisplayNameAsync(resource, CultureInfo.GetCultureInfo("fr-FR")); // Assert Assert.Equal("Nom fran�ais", name); } [Fact] public async Task GetNameAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.GetNameAsync(resource: null!).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task GetPropertiesAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.GetPropertiesAsync(resource: null!).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task ListAsync_ReturnsAllResources() { // Arrange var resources = new[] { new CustomResource(), new CustomResource() }; var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions()); var store = new Mock>(); store.Setup(store => store.ListAsync(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(resources.ToAsyncEnumerable()); var manager = new OpenIddictResourceManager(cache, logger, options, store.Object); // Act var results = new List(); await foreach (var scp in manager.ListAsync()) { results.Add(scp); } // Assert Assert.Equal(2, results.Count); store.Verify(store => store.ListAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); } [Fact] public async Task PopulateAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); var descriptor = new OpenIddictResourceDescriptor(); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.PopulateAsync(resource: null!, descriptor).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task PopulateAsync_ThrowsAnExceptionForNullDescriptor() { // Arrange var resource = new CustomResource(); var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.PopulateAsync(resource, descriptor: null!).AsTask()); Assert.Equal("descriptor", exception.ParamName); } [Fact] public async Task UpdateAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions()); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.UpdateAsync(resource: null!).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task UpdateAsync_WithDescriptor_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions()); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); var descriptor = new OpenIddictResourceDescriptor(); // Act and assert var exception = await Assert.ThrowsAsync( () => manager.UpdateAsync(resource: null!, descriptor).AsTask()); Assert.Equal("resource", exception.ParamName); } [Fact] public void ValidateAsync_ThrowsAnExceptionForNullResource() { // Arrange var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>(); var store = Mock.Of>(); var manager = new OpenIddictResourceManager(cache, logger, options, store); // Act and assert var exception = Assert.Throws( () => manager.ValidateAsync(resource: null!)); Assert.Equal("resource", exception.ParamName); } [Fact] public async Task ValidateAsync_ReturnsErrorWhenNameIsEmpty() { // Arrange var resource = new CustomResource(); var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions()); var store = new Mock>(); store.Setup(store => store.GetNameAsync(resource, It.IsAny())) .ReturnsAsync(string.Empty); var manager = new OpenIddictResourceManager(cache, logger, options, store.Object); // Act var results = await manager.ValidateAsync(resource).ToListAsync(); // Assert Assert.Contains(results, result => string.Equals(result.ErrorMessage, SR.GetResourceString(SR.ID2206), StringComparison.Ordinal)); } [Theory] [InlineData("resource")] [InlineData("/resource")] [InlineData("urn:resource#fragment")] public async Task ValidateAsync_ReturnsErrorWhenNameIsNotValidUri(string name) { // Arrange var resource = new CustomResource(); var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions()); var store = new Mock>(); store.Setup(store => store.GetNameAsync(resource, It.IsAny())) .ReturnsAsync(name); var manager = new OpenIddictResourceManager(cache, logger, options, store.Object); // Act var results = await manager.ValidateAsync(resource).ToListAsync(); // Assert Assert.Contains(results, result => string.Equals(result.ErrorMessage, SR.GetResourceString(SR.ID2207), StringComparison.Ordinal)); } [Fact] public async Task ValidateAsync_ReturnsErrorWhenNameIsAlreadyUsed() { // Arrange var resource = new CustomResource(); var other = new CustomResource(); var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions()); var store = new Mock>(); store.Setup(store => store.GetNameAsync(resource, It.IsAny())) .ReturnsAsync("urn:resource"); store.Setup(store => store.FindByNameAsync("urn:resource", It.IsAny())) .ReturnsAsync(other); store.Setup(store => store.GetIdAsync(resource, It.IsAny())) .ReturnsAsync("urn:resource-id"); store.Setup(store => store.GetIdAsync(other, It.IsAny())) .ReturnsAsync("urn:other-resource-id"); var manager = new OpenIddictResourceManager(cache, logger, options, store.Object); // Act var results = await manager.ValidateAsync(resource).ToListAsync(); // Assert Assert.Contains(results, result => string.Equals(result.ErrorMessage, SR.GetResourceString(SR.ID2208), StringComparison.Ordinal)); } [Fact] public async Task ValidateAsync_ReturnsNoErrorsForValidResource() { // Arrange var resource = new CustomResource(); var cache = Mock.Of>(); var logger = Mock.Of>>(); var options = Mock.Of>( mock => mock.CurrentValue == new OpenIddictCoreOptions()); var store = new Mock>(); store.Setup(store => store.GetNameAsync(resource, It.IsAny())) .ReturnsAsync("urn:resource"); store.Setup(store => store.FindByNameAsync("urn:resource", It.IsAny())) .ReturnsAsync((CustomResource?) null); var manager = new OpenIddictResourceManager(cache, logger, options, store.Object); // Act var results = await manager.ValidateAsync(resource).ToListAsync(); // Assert Assert.DoesNotContain(results, static result => result != ValidationResult.Success); } public class CustomResource; }