diff --git a/src/Catalog/Helpers/NuGetVersionUtility.cs b/src/Catalog/Helpers/NuGetVersionUtility.cs index e99457418..e4224b52b 100644 --- a/src/Catalog/Helpers/NuGetVersionUtility.cs +++ b/src/Catalog/Helpers/NuGetVersionUtility.cs @@ -21,13 +21,13 @@ public static string NormalizeVersion(string version) return parsedVersion.ToNormalizedString(); } - - public static string NormalizeVersionRange(string versionRange) + + public static string NormalizeVersionRange(string versionRange, string defaultValue) { VersionRange parsedVersionRange; if (!VersionRange.TryParse(versionRange, out parsedVersionRange)) { - return versionRange; + return defaultValue; } return parsedVersionRange.ToNormalizedString(); diff --git a/src/Catalog/Helpers/XsltHelper.cs b/src/Catalog/Helpers/XsltHelper.cs index 54f8c92a2..3d15b71e4 100644 --- a/src/Catalog/Helpers/XsltHelper.cs +++ b/src/Catalog/Helpers/XsltHelper.cs @@ -12,6 +12,13 @@ namespace NuGet.Services.Metadata.Catalog { public class XsltHelper { + /// + /// Default to an empty string if the dependency version range is invalid or missing. This is meant to be a + /// predictable signal to the client that they need to handle this invalid version case. The official NuGet + /// client treats this as a dependency of any version. + /// + private static readonly string DefaultVersionRange = string.Empty; + public XPathNavigator Split(string original) { char[] trimChar = { ',', ' ', '\t', '|', ';' }; @@ -52,7 +59,7 @@ public string GetFullVersionString(string original) public string NormalizeVersionRange(string original) { - return NuGetVersionUtility.NormalizeVersionRange(original); + return NuGetVersionUtility.NormalizeVersionRange(original, DefaultVersionRange); } public string IsPrerelease(string original) diff --git a/src/NuGet.Services.Metadata.Catalog.Monitoring/NuGet.Services.Metadata.Catalog.Monitoring.csproj b/src/NuGet.Services.Metadata.Catalog.Monitoring/NuGet.Services.Metadata.Catalog.Monitoring.csproj index 2486cac07..c533ad68f 100644 --- a/src/NuGet.Services.Metadata.Catalog.Monitoring/NuGet.Services.Metadata.Catalog.Monitoring.csproj +++ b/src/NuGet.Services.Metadata.Catalog.Monitoring/NuGet.Services.Metadata.Catalog.Monitoring.csproj @@ -168,6 +168,7 @@ + @@ -208,4 +209,4 @@ ..\..\build - \ No newline at end of file + diff --git a/src/NuGet.Services.Metadata.Catalog.Monitoring/Validation/Test/Exceptions/TimestampComparisonException.cs b/src/NuGet.Services.Metadata.Catalog.Monitoring/Validation/Test/Exceptions/TimestampComparisonException.cs new file mode 100644 index 000000000..87a51103f --- /dev/null +++ b/src/NuGet.Services.Metadata.Catalog.Monitoring/Validation/Test/Exceptions/TimestampComparisonException.cs @@ -0,0 +1,24 @@ +// 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 System; +using Newtonsoft.Json; + +namespace NuGet.Services.Metadata.Catalog.Monitoring +{ + public class TimestampComparisonException : ValidationException + { + public PackageTimestampMetadata TimestampV2 { get; } + public PackageTimestampMetadata TimestampCatalog { get; } + + public TimestampComparisonException(PackageTimestampMetadata timestampV2, PackageTimestampMetadata timestampCatalog, string message) + : base(message) + { + TimestampV2 = timestampV2; + TimestampCatalog = timestampCatalog; + + Data.Add(nameof(TimestampV2), JsonConvert.SerializeObject(TimestampV2)); + Data.Add(nameof(TimestampCatalog), JsonConvert.SerializeObject(TimestampCatalog)); + } + } +} diff --git a/src/NuGet.Services.Metadata.Catalog.Monitoring/Validation/Test/Exceptions/ValidationException.cs b/src/NuGet.Services.Metadata.Catalog.Monitoring/Validation/Test/Exceptions/ValidationException.cs index 42eff0c20..35341f262 100644 --- a/src/NuGet.Services.Metadata.Catalog.Monitoring/Validation/Test/Exceptions/ValidationException.cs +++ b/src/NuGet.Services.Metadata.Catalog.Monitoring/Validation/Test/Exceptions/ValidationException.cs @@ -7,7 +7,7 @@ namespace NuGet.Services.Metadata.Catalog.Monitoring { /// - /// Base class for exceptions throw by . + /// Base class for exceptions throw by . /// public class ValidationException : Exception { diff --git a/src/NuGet.Services.Metadata.Catalog.Monitoring/Validation/Test/Validator.cs b/src/NuGet.Services.Metadata.Catalog.Monitoring/Validation/Test/Validator.cs index 77a941414..2545ee7d0 100644 --- a/src/NuGet.Services.Metadata.Catalog.Monitoring/Validation/Test/Validator.cs +++ b/src/NuGet.Services.Metadata.Catalog.Monitoring/Validation/Test/Validator.cs @@ -85,17 +85,20 @@ protected virtual async Task ShouldRun(ValidationContext data) if (!timestampV2.Last.HasValue) { - throw new ValidationException("Cannot get timestamp data for package from the V2 feed!"); + throw new TimestampComparisonException(timestampV2, timestampCatalog, + "Cannot get timestamp data for package from the V2 feed!"); } if (!timestampCatalog.Last.HasValue) { - throw new ValidationException("Cannot get timestamp data for package from the catalog!"); + throw new TimestampComparisonException(timestampV2, timestampCatalog, + "Cannot get timestamp data for package from the catalog!"); } if (timestampCatalog.Last > timestampV2.Last) { - throw new ValidationException("The timestamp in the catalog is newer than the timestamp in the feed! This should never happen because all data flows from the catalog into the feed!"); + throw new TimestampComparisonException(timestampV2, timestampCatalog, + "The timestamp in the catalog is newer than the timestamp in the feed! This should never happen because all data flows from the feed into the catalog!"); } // If the timestamp metadata in the catalog is LESS than that of the feed, we must not be looking at the latest entry that corresponds with this package, so skip the test for now. diff --git a/tests/CatalogTests/CatalogTests.csproj b/tests/CatalogTests/CatalogTests.csproj index b9990092a..15ff394b8 100644 --- a/tests/CatalogTests/CatalogTests.csproj +++ b/tests/CatalogTests/CatalogTests.csproj @@ -194,6 +194,24 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -224,6 +242,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + diff --git a/tests/CatalogTests/Helpers/NuGetVersionUtilityTests.cs b/tests/CatalogTests/Helpers/NuGetVersionUtilityTests.cs index 41277e82a..b7da54bff 100644 --- a/tests/CatalogTests/Helpers/NuGetVersionUtilityTests.cs +++ b/tests/CatalogTests/Helpers/NuGetVersionUtilityTests.cs @@ -34,13 +34,30 @@ public void NormalizeVersion(string input, string expected) [InlineData("invalid", "invalid")] public void NormalizeVersionRange(string input, string expected) { - // Arrange & Act - var actual = NuGetVersionUtility.NormalizeVersionRange(input); + // Arrange + var defaultValue = input; + + // Arrange + var actual = NuGetVersionUtility.NormalizeVersionRange(input, defaultValue); // Assert Assert.Equal(expected, actual); } + [Fact] + public void NormalizeVersionRange_UsesDifferentDefault() + { + // Arrange + var input = "invalid"; + var defaultValue = "(, )"; + + // Act + var actual = NuGetVersionUtility.NormalizeVersionRange(input, defaultValue); + + // Assert + Assert.Equal(defaultValue, actual); + } + [Theory] [InlineData("1.0.0-alpha.1", "1.0.0-alpha.1")] [InlineData("1.0.0-alpha+githash", "1.0.0-alpha+githash")] diff --git a/tests/CatalogTests/PackageCatalogItemTests.cs b/tests/CatalogTests/PackageCatalogItemTests.cs index 39cdef1aa..d43e43f0c 100644 --- a/tests/CatalogTests/PackageCatalogItemTests.cs +++ b/tests/CatalogTests/PackageCatalogItemTests.cs @@ -22,6 +22,10 @@ public class PackageCatalogItemTests [InlineData("OneValidDependencyOneEmptyId.0.1.0")] // One valid dependency and one with empty string ID [InlineData("OneValidDependencyOneEmptyIdWithGroups.0.1.0")] // Using dependency groups, one valid dependency and one with empty string ID [InlineData("WhitespaceDependencyId.0.1.0")] // One dependency with an ID only containing whitespace + [InlineData("EmptyDependencyVersionRange.0.1.0")] // A dependency with a version range that is an empty string + [InlineData("InvalidDependencyVersionRange.0.1.0")] //A dependency with a version range that is invalid + [InlineData("MissingDependencyVersionRange.0.1.0")] // A dependency with no version range attribute + [InlineData("WhitespaceDependencyVersionRange.0.1.0")] // A dependency with a version range that is whitespace public void CreateContent_ProducesExpectedJson(string packageName) { // Arrange diff --git a/tests/CatalogTests/TestData/EmptyDependencyVersionRange.0.1.0.json b/tests/CatalogTests/TestData/EmptyDependencyVersionRange.0.1.0.json new file mode 100644 index 000000000..b0dfaca17 --- /dev/null +++ b/tests/CatalogTests/TestData/EmptyDependencyVersionRange.0.1.0.json @@ -0,0 +1,84 @@ +{ + "@id": "http://example/data/2017.01.04.08.15.00/emptydependencyversionrange.0.1.0.json", + "@type": [ + "PackageDetails", + "catalog:Permalink" + ], + "authors": "EmptyDependencyVersionRange", + "catalog:commitId": "4aee0ef4-a039-4460-bd5f-98f944e33289", + "catalog:commitTimeStamp": "2017-01-04T08:15:00Z", + "created": "2017-01-01T08:15:00Z", + "description": "EmptyDependencyVersionRange", + "id": "EmptyDependencyVersionRange", + "isPrerelease": false, + "lastEdited": "2017-01-02T08:15:00Z", + "listed": true, + "packageHash": "J1NrM0aeDXk3kdjY3Aby8duP+mAt1JN/srVW5AMDIFXJXhWjZgmwAnAKBBrO6VDWXg2hi1x+uaWLHI0dUXraUg==", + "packageHashAlgorithm": "SHA512", + "packageSize": 450, + "published": "2017-01-03T08:15:00Z", + "title": "EmptyDependencyVersionRange", + "verbatimVersion": "0.1.0", + "version": "0.1.0", + "dependencyGroups": [ + { + "@id": "http://example/data/2017.01.04.08.15.00/emptydependencyversionrange.0.1.0.json#dependencygroup", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "http://example/data/2017.01.04.08.15.00/emptydependencyversionrange.0.1.0.json#dependencygroup/nuget.versioning", + "@type": "PackageDependency", + "id": "NuGet.Versioning", + "range": "" + } + ] + } + ], + "packageEntries": [ + { + "@id": "http://example/data/2017.01.04.08.15.00/emptydependencyversionrange.0.1.0.json#EmptyDependencyVersionRange.nuspec", + "@type": "PackageEntry", + "compressedLength": 248, + "fullName": "EmptyDependencyVersionRange.nuspec", + "length": 504, + "name": "EmptyDependencyVersionRange.nuspec" + } + ], + "@context": { + "@vocab": "http://schema.nuget.org/schema#", + "catalog": "http://schema.nuget.org/catalog#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "dependencies": { + "@id": "dependency", + "@container": "@set" + }, + "dependencyGroups": { + "@id": "dependencyGroup", + "@container": "@set" + }, + "packageEntries": { + "@id": "packageEntry", + "@container": "@set" + }, + "supportedFrameworks": { + "@id": "supportedFramework", + "@container": "@set" + }, + "tags": { + "@id": "tag", + "@container": "@set" + }, + "published": { + "@type": "xsd:dateTime" + }, + "created": { + "@type": "xsd:dateTime" + }, + "lastEdited": { + "@type": "xsd:dateTime" + }, + "catalog:commitTimeStamp": { + "@type": "xsd:dateTime" + } + } +} \ No newline at end of file diff --git a/tests/CatalogTests/TestData/EmptyDependencyVersionRange.0.1.0.nupkg b/tests/CatalogTests/TestData/EmptyDependencyVersionRange.0.1.0.nupkg new file mode 100644 index 000000000..72de5fefd Binary files /dev/null and b/tests/CatalogTests/TestData/EmptyDependencyVersionRange.0.1.0.nupkg differ diff --git a/tests/CatalogTests/TestData/InvalidDependencyVersionRange.0.1.0.json b/tests/CatalogTests/TestData/InvalidDependencyVersionRange.0.1.0.json new file mode 100644 index 000000000..8becf3372 --- /dev/null +++ b/tests/CatalogTests/TestData/InvalidDependencyVersionRange.0.1.0.json @@ -0,0 +1,84 @@ +{ + "@id": "http://example/data/2017.01.04.08.15.00/invaliddependencyversionrange.0.1.0.json", + "@type": [ + "PackageDetails", + "catalog:Permalink" + ], + "authors": "InvalidDependencyVersionRange", + "catalog:commitId": "4aee0ef4-a039-4460-bd5f-98f944e33289", + "catalog:commitTimeStamp": "2017-01-04T08:15:00Z", + "created": "2017-01-01T08:15:00Z", + "description": "InvalidDependencyVersionRange", + "id": "InvalidDependencyVersionRange", + "isPrerelease": false, + "lastEdited": "2017-01-02T08:15:00Z", + "listed": true, + "packageHash": "RFCB7aV+m/vj0CYW3SmnLUd8jMYYJpOw+fz7hDNERlwUjz2rU+6lCVA2bdIko3YYc68jJN3n07S2OgKrUDwPBg==", + "packageHashAlgorithm": "SHA512", + "packageSize": 473, + "published": "2017-01-03T08:15:00Z", + "title": "InvalidDependencyVersionRange", + "verbatimVersion": "0.1.0", + "version": "0.1.0", + "dependencyGroups": [ + { + "@id": "http://example/data/2017.01.04.08.15.00/invaliddependencyversionrange.0.1.0.json#dependencygroup", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "http://example/data/2017.01.04.08.15.00/invaliddependencyversionrange.0.1.0.json#dependencygroup/nuget.versioning", + "@type": "PackageDependency", + "id": "NuGet.Versioning", + "range": "" + } + ] + } + ], + "packageEntries": [ + { + "@id": "http://example/data/2017.01.04.08.15.00/invaliddependencyversionrange.0.1.0.json#InvalidDependencyVersionRange.nuspec", + "@type": "PackageEntry", + "compressedLength": 267, + "fullName": "InvalidDependencyVersionRange.nuspec", + "length": 530, + "name": "InvalidDependencyVersionRange.nuspec" + } + ], + "@context": { + "@vocab": "http://schema.nuget.org/schema#", + "catalog": "http://schema.nuget.org/catalog#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "dependencies": { + "@id": "dependency", + "@container": "@set" + }, + "dependencyGroups": { + "@id": "dependencyGroup", + "@container": "@set" + }, + "packageEntries": { + "@id": "packageEntry", + "@container": "@set" + }, + "supportedFrameworks": { + "@id": "supportedFramework", + "@container": "@set" + }, + "tags": { + "@id": "tag", + "@container": "@set" + }, + "published": { + "@type": "xsd:dateTime" + }, + "created": { + "@type": "xsd:dateTime" + }, + "lastEdited": { + "@type": "xsd:dateTime" + }, + "catalog:commitTimeStamp": { + "@type": "xsd:dateTime" + } + } +} \ No newline at end of file diff --git a/tests/CatalogTests/TestData/InvalidDependencyVersionRange.0.1.0.nupkg b/tests/CatalogTests/TestData/InvalidDependencyVersionRange.0.1.0.nupkg new file mode 100644 index 000000000..abbbb218f Binary files /dev/null and b/tests/CatalogTests/TestData/InvalidDependencyVersionRange.0.1.0.nupkg differ diff --git a/tests/CatalogTests/TestData/MissingDependencyVersionRange.0.1.0.json b/tests/CatalogTests/TestData/MissingDependencyVersionRange.0.1.0.json new file mode 100644 index 000000000..0482a483b --- /dev/null +++ b/tests/CatalogTests/TestData/MissingDependencyVersionRange.0.1.0.json @@ -0,0 +1,84 @@ +{ + "@id": "http://example/data/2017.01.04.08.15.00/missingdependencyversionrange.0.1.0.json", + "@type": [ + "PackageDetails", + "catalog:Permalink" + ], + "authors": "MissingDependencyVersionRange", + "catalog:commitId": "4aee0ef4-a039-4460-bd5f-98f944e33289", + "catalog:commitTimeStamp": "2017-01-04T08:15:00Z", + "created": "2017-01-01T08:15:00Z", + "description": "MissingDependencyVersionRange", + "id": "MissingDependencyVersionRange", + "isPrerelease": false, + "lastEdited": "2017-01-02T08:15:00Z", + "listed": true, + "packageHash": "7K0E8VKZX8of+E4LU2gpnQbjA74nubVDhVDPLOdiRuYyMecuOu3eZ33QmVJ+ZsqLpZB/jnVEUUmUvL7sA4Y+JA==", + "packageHashAlgorithm": "SHA512", + "packageSize": 450, + "published": "2017-01-03T08:15:00Z", + "title": "MissingDependencyVersionRange", + "verbatimVersion": "0.1.0", + "version": "0.1.0", + "dependencyGroups": [ + { + "@id": "http://example/data/2017.01.04.08.15.00/missingdependencyversionrange.0.1.0.json#dependencygroup", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "http://example/data/2017.01.04.08.15.00/missingdependencyversionrange.0.1.0.json#dependencygroup/nuget.versioning", + "@type": "PackageDependency", + "id": "NuGet.Versioning", + "range": "" + } + ] + } + ], + "packageEntries": [ + { + "@id": "http://example/data/2017.01.04.08.15.00/missingdependencyversionrange.0.1.0.json#MissingDependencyVersionRange.nuspec", + "@type": "PackageEntry", + "compressedLength": 244, + "fullName": "MissingDependencyVersionRange.nuspec", + "length": 501, + "name": "MissingDependencyVersionRange.nuspec" + } + ], + "@context": { + "@vocab": "http://schema.nuget.org/schema#", + "catalog": "http://schema.nuget.org/catalog#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "dependencies": { + "@id": "dependency", + "@container": "@set" + }, + "dependencyGroups": { + "@id": "dependencyGroup", + "@container": "@set" + }, + "packageEntries": { + "@id": "packageEntry", + "@container": "@set" + }, + "supportedFrameworks": { + "@id": "supportedFramework", + "@container": "@set" + }, + "tags": { + "@id": "tag", + "@container": "@set" + }, + "published": { + "@type": "xsd:dateTime" + }, + "created": { + "@type": "xsd:dateTime" + }, + "lastEdited": { + "@type": "xsd:dateTime" + }, + "catalog:commitTimeStamp": { + "@type": "xsd:dateTime" + } + } +} \ No newline at end of file diff --git a/tests/CatalogTests/TestData/MissingDependencyVersionRange.0.1.0.nupkg b/tests/CatalogTests/TestData/MissingDependencyVersionRange.0.1.0.nupkg new file mode 100644 index 000000000..b86910a32 Binary files /dev/null and b/tests/CatalogTests/TestData/MissingDependencyVersionRange.0.1.0.nupkg differ diff --git a/tests/CatalogTests/TestData/WhitespaceDependencyVersionRange.0.1.0.json b/tests/CatalogTests/TestData/WhitespaceDependencyVersionRange.0.1.0.json new file mode 100644 index 000000000..f839fc8d2 --- /dev/null +++ b/tests/CatalogTests/TestData/WhitespaceDependencyVersionRange.0.1.0.json @@ -0,0 +1,84 @@ +{ + "@id": "http://example/data/2017.01.04.08.15.00/whitespacedependencyversionrange.0.1.0.json", + "@type": [ + "PackageDetails", + "catalog:Permalink" + ], + "authors": "WhitespaceDependencyVersionRange", + "catalog:commitId": "4aee0ef4-a039-4460-bd5f-98f944e33289", + "catalog:commitTimeStamp": "2017-01-04T08:15:00Z", + "created": "2017-01-01T08:15:00Z", + "description": "WhitespaceDependencyVersionRange", + "id": "WhitespaceDependencyVersionRange", + "isPrerelease": false, + "lastEdited": "2017-01-02T08:15:00Z", + "listed": true, + "packageHash": "+a19ygbSmG7q/Ehq8KjBSMf7ToLxNDUcYkkeDQ338dqm9FOsaICc0VemK9smCMcejAMaeWlV0va1u75LHJWBLw==", + "packageHashAlgorithm": "SHA512", + "packageSize": 464, + "published": "2017-01-03T08:15:00Z", + "title": "WhitespaceDependencyVersionRange", + "verbatimVersion": "0.1.0", + "version": "0.1.0", + "dependencyGroups": [ + { + "@id": "http://example/data/2017.01.04.08.15.00/whitespacedependencyversionrange.0.1.0.json#dependencygroup", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "http://example/data/2017.01.04.08.15.00/whitespacedependencyversionrange.0.1.0.json#dependencygroup/nuget.versioning", + "@type": "PackageDependency", + "id": "NuGet.Versioning", + "range": "" + } + ] + } + ], + "packageEntries": [ + { + "@id": "http://example/data/2017.01.04.08.15.00/whitespacedependencyversionrange.0.1.0.json#WhitespaceDependencyVersionRange.nuspec", + "@type": "PackageEntry", + "compressedLength": 252, + "fullName": "WhitespaceDependencyVersionRange.nuspec", + "length": 530, + "name": "WhitespaceDependencyVersionRange.nuspec" + } + ], + "@context": { + "@vocab": "http://schema.nuget.org/schema#", + "catalog": "http://schema.nuget.org/catalog#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "dependencies": { + "@id": "dependency", + "@container": "@set" + }, + "dependencyGroups": { + "@id": "dependencyGroup", + "@container": "@set" + }, + "packageEntries": { + "@id": "packageEntry", + "@container": "@set" + }, + "supportedFrameworks": { + "@id": "supportedFramework", + "@container": "@set" + }, + "tags": { + "@id": "tag", + "@container": "@set" + }, + "published": { + "@type": "xsd:dateTime" + }, + "created": { + "@type": "xsd:dateTime" + }, + "lastEdited": { + "@type": "xsd:dateTime" + }, + "catalog:commitTimeStamp": { + "@type": "xsd:dateTime" + } + } +} \ No newline at end of file diff --git a/tests/CatalogTests/TestData/WhitespaceDependencyVersionRange.0.1.0.nupkg b/tests/CatalogTests/TestData/WhitespaceDependencyVersionRange.0.1.0.nupkg new file mode 100644 index 000000000..6922bafda Binary files /dev/null and b/tests/CatalogTests/TestData/WhitespaceDependencyVersionRange.0.1.0.nupkg differ