diff --git a/src/NuGetGallery/Controllers/ODataV2CuratedFeedController.cs b/src/NuGetGallery/Controllers/ODataV2CuratedFeedController.cs index 27bb2ded96..4a6659ec7e 100644 --- a/src/NuGetGallery/Controllers/ODataV2CuratedFeedController.cs +++ b/src/NuGetGallery/Controllers/ODataV2CuratedFeedController.cs @@ -52,6 +52,7 @@ public IHttpActionResult Get(ODataQueryOptions options, string cu } var queryable = _curatedFeedService.GetPackages(curatedFeedName) + .Where(p => p.SemVerLevelKey == SemVerLevelKey.Unknown) .ToV2FeedPackageQuery(_configurationService.GetSiteRoot(UseHttps()), _configurationService.Features.FriendlyLicenses) .InterceptWith(new NormalizeVersionInterceptor()); @@ -101,7 +102,8 @@ private async Task GetCore(ODataQueryOptions o } var packages = _curatedFeedService.GetPackages(curatedFeedName) - .Where(p => p.PackageRegistration.Id.Equals(id, StringComparison.OrdinalIgnoreCase)); + .Where(p => p.SemVerLevelKey == SemVerLevelKey.Unknown + && p.PackageRegistration.Id.Equals(id, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(version)) { @@ -201,6 +203,7 @@ public async Task Search( // Perform actual search var curatedFeed = _curatedFeedService.GetFeedByName(curatedFeedName, includePackages: false); var packages = _curatedFeedService.GetPackages(curatedFeedName) + .Where(p => p.SemVerLevelKey == SemVerLevelKey.Unknown) .OrderBy(p => p.PackageRegistration.Id).ThenBy(p => p.Version); // todo: search hijack should take queryOptions instead of manually parsing query options diff --git a/tests/NuGetGallery.Facts/Controllers/ODataFeedControllerFactsBase.cs b/tests/NuGetGallery.Facts/Controllers/ODataFeedControllerFactsBase.cs index 4c6339fc57..38124b0f83 100644 --- a/tests/NuGetGallery.Facts/Controllers/ODataFeedControllerFactsBase.cs +++ b/tests/NuGetGallery.Facts/Controllers/ODataFeedControllerFactsBase.cs @@ -30,25 +30,25 @@ public abstract class ODataFeedControllerFactsBase protected readonly IReadOnlyCollection NonSemVer2Packages; protected readonly IReadOnlyCollection SemVer2Packages; protected readonly IEntityRepository PackagesRepository; + protected readonly IQueryable AllPackages; protected ODataFeedControllerFactsBase() { // Arrange - var packagesQueryable = CreatePackagesQueryable(); - NonSemVer2Packages = packagesQueryable.Where(p => p.SemVerLevelKey == SemVerLevelKey.Unknown).ToList(); - SemVer2Packages = packagesQueryable.Where(p => p.SemVerLevelKey == SemVerLevelKey.SemVer2).ToList(); - + AllPackages = CreatePackagesQueryable(); + NonSemVer2Packages = AllPackages.Where(p => p.SemVerLevelKey == SemVerLevelKey.Unknown).ToList(); + SemVer2Packages = AllPackages.Where(p => p.SemVerLevelKey == SemVerLevelKey.SemVer2).ToList(); var packagesRepositoryMock = new Mock>(MockBehavior.Strict); - packagesRepositoryMock.Setup(m => m.GetAll()).Returns(packagesQueryable).Verifiable(); + packagesRepositoryMock.Setup(m => m.GetAll()).Returns(AllPackages).Verifiable(); PackagesRepository = packagesRepositoryMock.Object; } protected abstract TController CreateController( IEntityRepository packagesRepository, - IGalleryConfigurationService configurationService, + IGalleryConfigurationService configurationService, ISearchService searchService); - + protected TController CreateTestableODataFeedController(HttpRequestMessage request) { var searchService = new Mock().Object; @@ -56,14 +56,7 @@ protected TController CreateTestableODataFeedController(HttpRequestMessage reque configurationService.Current.SiteRoot = _siteRoot; var controller = CreateController(PackagesRepository, configurationService, searchService); - - InitializeRequestContext(request, controller); - - return controller; - } - - private static void InitializeRequestContext(HttpRequestMessage request, TController controller) - { + var httpRequest = new HttpRequest(string.Empty, request.RequestUri.AbsoluteUri, request.RequestUri.Query); var httpResponse = new HttpResponse(new StringWriter()); var httpContext = new HttpContext(httpRequest, httpResponse); @@ -75,6 +68,48 @@ private static void InitializeRequestContext(HttpRequestMessage request, TContro controller.ControllerContext.Controller = controller; controller.ControllerContext.Configuration = new HttpConfiguration(); + + return controller; + } + + protected async Task> GetCollection( + Func, IHttpActionResult> controllerAction, + string requestPath) + where TFeedPackage : class + { + var queryResult = InvokeODataFeedControllerAction(controllerAction, requestPath); + + return await GetValueFromQueryResult(queryResult); + } + + protected async Task> GetCollection( + Func, Task> asyncControllerAction, + string requestPath) + where TFeedPackage : class + { + var queryResult = await InvokeODataFeedControllerActionAsync(asyncControllerAction, requestPath); + + return await GetValueFromQueryResult(queryResult); + } + + protected async Task GetInt( + Func, IHttpActionResult> controllerAction, + string requestPath) + where TFeedPackage : class + { + var queryResult = InvokeODataFeedControllerAction(controllerAction, requestPath); + + return int.Parse(await GetValueFromQueryResult(queryResult)); + } + + protected async Task GetInt( + Func, Task> asyncControllerAction, + string requestPath) + where TFeedPackage : class + { + var queryResult = await InvokeODataFeedControllerActionAsync(asyncControllerAction, requestPath); + + return int.Parse(await GetValueFromQueryResult(queryResult)); } private static IQueryable CreatePackagesQueryable() @@ -130,7 +165,7 @@ private static IQueryable CreatePackagesQueryable() return list.AsQueryable(); } - protected static ODataQueryContext CreateODataQueryContext() + private static ODataQueryContext CreateODataQueryContext() where TFeedPackage : class { var oDataModelBuilder = new ODataConventionModelBuilder(); @@ -138,8 +173,8 @@ protected static ODataQueryContext CreateODataQueryContext() return new ODataQueryContext(oDataModelBuilder.GetEdmModel(), typeof(TFeedPackage)); } - - protected static async Task GetValueFromQueryResult(QueryResult queryResult) + + private static async Task GetValueFromQueryResult(QueryResult queryResult) { var httpResponseMessage = await queryResult.ExecuteAsync(CancellationToken.None); @@ -159,8 +194,8 @@ protected static async Task GetValueFromQueryResult(QueryResul return ((IQueryable)objectContent.Value).ToList(); } } - - protected async Task> InvokeODataFeedControllerActionAsync( + + private async Task> InvokeODataFeedControllerActionAsync( Func, Task> asyncControllerAction, string requestPath) where TFeedPackage : class @@ -172,9 +207,9 @@ protected async Task> InvokeODataFeedControllerActionA new ODataQueryOptions(CreateODataQueryContext(), request)); } - protected QueryResult InvokeODataFeedControllerAction( + private QueryResult InvokeODataFeedControllerAction( Func, IHttpActionResult> controllerAction, - string requestPath) + string requestPath) where TFeedPackage : class { var request = new HttpRequestMessage(HttpMethod.Get, $"{_siteRoot}{requestPath}"); diff --git a/tests/NuGetGallery.Facts/Controllers/ODataV1ControllerFactsBase.cs b/tests/NuGetGallery.Facts/Controllers/ODataV1ControllerFactsBase.cs deleted file mode 100644 index 9df9019c2b..0000000000 --- a/tests/NuGetGallery.Facts/Controllers/ODataV1ControllerFactsBase.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using NuGetGallery.Configuration; -using NuGetGallery.OData; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Web.Http; -using System.Web.Http.OData.Query; - -namespace NuGetGallery.Controllers -{ - public abstract class ODataV1ControllerFactsBase - : ODataFeedControllerFactsBase - { - protected override ODataV1FeedController CreateController(IEntityRepository packagesRepository, - IGalleryConfigurationService configurationService, ISearchService searchService) - { - return new ODataV1FeedController(packagesRepository, configurationService, searchService); - } - - protected async Task> GetCollection( - Func, IHttpActionResult> controllerAction, - string requestPath) - { - var queryResult = InvokeODataFeedControllerAction(controllerAction, requestPath); - - return await GetValueFromQueryResult(queryResult); - } - - protected async Task GetInt( - Func, IHttpActionResult> controllerAction, - string requestPath) - { - var queryResult = InvokeODataFeedControllerAction(controllerAction, requestPath); - - return int.Parse(await GetValueFromQueryResult(queryResult)); - } - - protected async Task> GetCollectionAsync( - Func, Task> asyncControllerAction, - string requestPath) - { - var queryResult = await InvokeODataFeedControllerActionAsync(asyncControllerAction, requestPath); - - return await GetValueFromQueryResult(queryResult); - } - - protected async Task GetIntAsync( - Func, Task> asyncControllerAction, - string requestPath) - { - var queryResult = await InvokeODataFeedControllerActionAsync(asyncControllerAction, requestPath); - - return int.Parse(await GetValueFromQueryResult(queryResult)); - } - } -} \ No newline at end of file diff --git a/tests/NuGetGallery.Facts/Controllers/ODataV1FeedControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/ODataV1FeedControllerFacts.cs index 058df0a09d..8621229f9a 100644 --- a/tests/NuGetGallery.Facts/Controllers/ODataV1FeedControllerFacts.cs +++ b/tests/NuGetGallery.Facts/Controllers/ODataV1FeedControllerFacts.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using NuGetGallery.Configuration; +using NuGetGallery.OData; using System.Linq; using System.Threading.Tasks; using Xunit; @@ -8,13 +10,13 @@ namespace NuGetGallery.Controllers { public class ODataV1FeedControllerFacts - : ODataV1ControllerFactsBase + : ODataFeedControllerFactsBase { [Fact] public async Task Get_FiltersSemVerV2PackageVersions() { // Act - var resultSet = await GetCollection( + var resultSet = await GetCollection( (controller, options) => controller.Get(options), "/api/v1/Packages"); @@ -37,7 +39,7 @@ public async Task Get_FiltersSemVerV2PackageVersions() public async Task GetCount_FiltersSemVerV2PackageVersions() { // Act - var count = await GetInt( + var count = await GetInt( (controller, options) => controller.GetCount(options), "/api/v1/Packages/$count"); @@ -49,7 +51,7 @@ public async Task GetCount_FiltersSemVerV2PackageVersions() public async Task FindPackagesById_FiltersSemVerV2PackageVersions() { // Act - var resultSet = await GetCollectionAsync( + var resultSet = await GetCollection( async (controller, options) => await controller.FindPackagesById(options, TestPackageId), $"/api/v1/FindPackagesById?id='{TestPackageId}'"); @@ -72,7 +74,7 @@ public async Task FindPackagesById_FiltersSemVerV2PackageVersions() public async Task Search_FiltersSemVerV2PackageVersions() { // Act - var resultSet = await GetCollectionAsync( + var resultSet = await GetCollection( async (controller, options) => await controller.Search(options, TestPackageId), $"/api/v1/Search?searchTerm='{TestPackageId}'"); @@ -95,12 +97,18 @@ public async Task Search_FiltersSemVerV2PackageVersions() public async Task SearchCount_FiltersSemVerV2PackageVersions() { // Act - var searchCount = await GetIntAsync( + var searchCount = await GetInt( async (controller, options) => await controller.SearchCount(options, TestPackageId), $"/api/v1/Search/$count?searchTerm='{TestPackageId}'"); // Assert Assert.Equal(NonSemVer2Packages.Count, searchCount); } + + protected override ODataV1FeedController CreateController(IEntityRepository packagesRepository, + IGalleryConfigurationService configurationService, ISearchService searchService) + { + return new ODataV1FeedController(packagesRepository, configurationService, searchService); + } } } \ No newline at end of file diff --git a/tests/NuGetGallery.Facts/Controllers/ODataV2ControllerFactsBase.cs b/tests/NuGetGallery.Facts/Controllers/ODataV2ControllerFactsBase.cs deleted file mode 100644 index 72f7f82e85..0000000000 --- a/tests/NuGetGallery.Facts/Controllers/ODataV2ControllerFactsBase.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using NuGetGallery.Configuration; -using NuGetGallery.OData; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Web.Http; -using System.Web.Http.OData.Query; - -namespace NuGetGallery.Controllers -{ - public abstract class ODataV2ControllerFactsBase - : ODataFeedControllerFactsBase - { - protected override ODataV2FeedController CreateController( - IEntityRepository packagesRepository, - IGalleryConfigurationService configurationService, - ISearchService searchService) - { - return new ODataV2FeedController(packagesRepository, configurationService, searchService); - } - - protected async Task> GetCollection( - Func, IHttpActionResult> controllerAction, - string requestPath) - { - var queryResult = InvokeODataFeedControllerAction(controllerAction, requestPath); - - return await GetValueFromQueryResult(queryResult); - } - - protected async Task GetInt( - Func, IHttpActionResult> controllerAction, - string requestPath) - { - var queryResult = InvokeODataFeedControllerAction(controllerAction, requestPath); - - return int.Parse(await GetValueFromQueryResult(queryResult)); - } - - protected async Task> GetCollection( - Func, Task> asyncControllerAction, - string requestPath) - { - var queryResult = await InvokeODataFeedControllerActionAsync(asyncControllerAction, requestPath); - - return await GetValueFromQueryResult(queryResult); - } - - protected async Task GetInt( - Func, Task> asyncControllerAction, - string requestPath) - { - var queryResult = await InvokeODataFeedControllerActionAsync(asyncControllerAction, requestPath); - - return int.Parse(await GetValueFromQueryResult(queryResult)); - } - } -} \ No newline at end of file diff --git a/tests/NuGetGallery.Facts/Controllers/ODataV2CuratedFeedControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/ODataV2CuratedFeedControllerFacts.cs new file mode 100644 index 0000000000..7734ebae65 --- /dev/null +++ b/tests/NuGetGallery.Facts/Controllers/ODataV2CuratedFeedControllerFacts.cs @@ -0,0 +1,147 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Moq; +using NuGetGallery.Configuration; +using NuGetGallery.OData; +using System.Data.Entity; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace NuGetGallery.Controllers +{ + public class ODataV2CuratedFeedControllerFacts + : ODataFeedControllerFactsBase + { + private const string _curatedFeedName = "dummy"; + + [Fact] + public async Task Get_FiltersSemVerV2PackageVersions() + { + // Act + var resultSet = await GetCollection( + (controller, options) => controller.Get(options, _curatedFeedName), + $"/api/v2/curated-feed/{_curatedFeedName}/Packages"); + + // Assert + foreach (var feedPackage in resultSet) + { + // Assert none of the items in the result set are SemVer v.2.0.0 packages (checking on original version is enough in this case) + Assert.Empty(SemVer2Packages.Where(p => string.Equals(p.Version, feedPackage.Version))); + + // Assert each of the items in the result set is a non-SemVer v2.0.0 package + Assert.Single(NonSemVer2Packages.Where(p => + string.Equals(p.Version, feedPackage.Version) && + string.Equals(p.PackageRegistration.Id, feedPackage.Id))); + } + + Assert.Equal(NonSemVer2Packages.Count, resultSet.Count); + } + + [Fact] + public async Task GetCount_FiltersSemVerV2PackageVersions() + { + // Act + var count = await GetInt( + (controller, options) => controller.GetCount(options, _curatedFeedName), + $"/api/v2/curated-feed/{_curatedFeedName}/Packages/$count"); + + // Assert + Assert.Equal(NonSemVer2Packages.Count, count); + } + + [Fact] + public async Task FindPackagesById_FiltersSemVerV2PackageVersions() + { + // Act + var resultSet = await GetCollection( + async (controller, options) => await controller.FindPackagesById(options, _curatedFeedName, TestPackageId), + $"/api/v2/curated-feed/{_curatedFeedName}/FindPackagesById?id='{TestPackageId}'"); + + // Assert + foreach (var feedPackage in resultSet) + { + // Assert none of the items in the result set are SemVer v.2.0.0 packages (checking on original version is enough in this case) + Assert.Empty(SemVer2Packages.Where(p => string.Equals(p.Version, feedPackage.Version))); + + // Assert each of the items in the result set is a non-SemVer v2.0.0 package + Assert.Single(NonSemVer2Packages.Where(p => + string.Equals(p.Version, feedPackage.Version) && + string.Equals(p.PackageRegistration.Id, feedPackage.Id))); + } + + Assert.Equal(NonSemVer2Packages.Count, resultSet.Count); + } + + [Fact] + public async Task Search_FiltersSemVerV2PackageVersions() + { + // Act + var resultSet = await GetCollection( + async (controller, options) => await controller.Search(options, _curatedFeedName, TestPackageId), + $"/api/v2/curated-feed/{_curatedFeedName}/Search?searchTerm='{TestPackageId}'"); + + // Assert + foreach (var feedPackage in resultSet) + { + // Assert none of the items in the result set are SemVer v.2.0.0 packages (checking on original version is enough in this case) + Assert.Empty(SemVer2Packages.Where(p => string.Equals(p.Version, feedPackage.Version))); + + // Assert each of the items in the result set is a non-SemVer v2.0.0 package + Assert.Single(NonSemVer2Packages.Where(p => + string.Equals(p.Version, feedPackage.Version) && + string.Equals(p.PackageRegistration.Id, feedPackage.Id))); + } + + Assert.Equal(NonSemVer2Packages.Count, resultSet.Count); + } + + [Fact] + public async Task SearchCount_FiltersSemVerV2PackageVersions() + { + // Act + var searchCount = await GetInt( + async (controller, options) => await controller.SearchCount(options, _curatedFeedName, TestPackageId), + $"/api/v2/curated-feed/{_curatedFeedName}/Search/$count?searchTerm='{TestPackageId}'"); + + // Assert + Assert.Equal(NonSemVer2Packages.Count, searchCount); + } + + protected override ODataV2CuratedFeedController CreateController( + IEntityRepository packagesRepository, + IGalleryConfigurationService configurationService, + ISearchService searchService) + { + var curatedFeed = new CuratedFeed { Name = _curatedFeedName }; + + var curatedFeedServiceMock = new Mock(MockBehavior.Strict); + curatedFeedServiceMock.Setup(m => m.GetPackages(_curatedFeedName)).Returns(AllPackages); + curatedFeedServiceMock.Setup(m => m.GetFeedByName(_curatedFeedName, false)).Returns(curatedFeed); + + var entitiesContextMock = new Mock(MockBehavior.Strict); + var curatedFeedDbSet = GetQueryableMockDbSet(curatedFeed); + entitiesContextMock.SetupGet(m => m.CuratedFeeds).Returns(curatedFeedDbSet); + + return new ODataV2CuratedFeedController( + entitiesContextMock.Object, + configurationService, + searchService, + curatedFeedServiceMock.Object); + } + + private static IDbSet GetQueryableMockDbSet(params T[] sourceList) where T : class + { + var queryable = sourceList.AsQueryable(); + + var dbSet = new Mock>(); + dbSet.As>().Setup(m => m.Provider).Returns(queryable.Provider); + dbSet.As>().Setup(m => m.Expression).Returns(queryable.Expression); + dbSet.As>().Setup(m => m.ElementType).Returns(queryable.ElementType); + dbSet.As>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator()); + + return dbSet.Object; + } + } +} \ No newline at end of file diff --git a/tests/NuGetGallery.Facts/Controllers/ODataV2FeedControllerFacts.cs b/tests/NuGetGallery.Facts/Controllers/ODataV2FeedControllerFacts.cs index 356d818e3f..96a7ccd6bb 100644 --- a/tests/NuGetGallery.Facts/Controllers/ODataV2FeedControllerFacts.cs +++ b/tests/NuGetGallery.Facts/Controllers/ODataV2FeedControllerFacts.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using NuGetGallery.Configuration; +using NuGetGallery.OData; using System.Linq; using System.Threading.Tasks; using Xunit; @@ -8,13 +10,13 @@ namespace NuGetGallery.Controllers { public class ODataV2FeedControllerFacts - : ODataV2ControllerFactsBase + : ODataFeedControllerFactsBase { [Fact] public async Task Get_FiltersSemVerV2PackageVersions() { // Act - var resultSet = await GetCollection( + var resultSet = await GetCollection( (controller, options) => controller.Get(options), "/api/v2/Packages"); @@ -37,7 +39,7 @@ public async Task Get_FiltersSemVerV2PackageVersions() public async Task GetCount_FiltersSemVerV2PackageVersions() { // Act - var count = await GetInt( + var count = await GetInt( (controller, options) => controller.GetCount(options), "/api/v2/Packages/$count"); @@ -49,7 +51,7 @@ public async Task GetCount_FiltersSemVerV2PackageVersions() public async Task FindPackagesById_FiltersSemVerV2PackageVersions() { // Act - var resultSet = await GetCollection( + var resultSet = await GetCollection( async (controller, options) => await controller.FindPackagesById(options, TestPackageId), $"/api/v2/FindPackagesById?id='{TestPackageId}'"); @@ -72,7 +74,7 @@ public async Task FindPackagesById_FiltersSemVerV2PackageVersions() public async Task Search_FiltersSemVerV2PackageVersions() { // Act - var resultSet = await GetCollection( + var resultSet = await GetCollection( async (controller, options) => await controller.Search(options, TestPackageId), $"/api/v2/Search?searchTerm='{TestPackageId}'"); @@ -95,12 +97,20 @@ public async Task Search_FiltersSemVerV2PackageVersions() public async Task SearchCount_FiltersSemVerV2PackageVersions() { // Act - var searchCount = await GetInt( + var searchCount = await GetInt( async (controller, options) => await controller.SearchCount(options, TestPackageId), $"/api/v2/Search/$count?searchTerm='{TestPackageId}'"); // Assert Assert.Equal(NonSemVer2Packages.Count, searchCount); } + + protected override ODataV2FeedController CreateController( + IEntityRepository packagesRepository, + IGalleryConfigurationService configurationService, + ISearchService searchService) + { + return new ODataV2FeedController(packagesRepository, configurationService, searchService); + } } } \ No newline at end of file diff --git a/tests/NuGetGallery.Facts/NuGetGallery.Facts.csproj b/tests/NuGetGallery.Facts/NuGetGallery.Facts.csproj index e50b8f6520..3404087a10 100644 --- a/tests/NuGetGallery.Facts/NuGetGallery.Facts.csproj +++ b/tests/NuGetGallery.Facts/NuGetGallery.Facts.csproj @@ -393,8 +393,7 @@ - - +