diff --git a/src/NuGet.Clients/NuGet.PackageManagement.UI/ViewModels/PackageDetailsTabViewModel.cs b/src/NuGet.Clients/NuGet.PackageManagement.UI/ViewModels/PackageDetailsTabViewModel.cs
index a51f64f9e29..be490248dd6 100644
--- a/src/NuGet.Clients/NuGet.PackageManagement.UI/ViewModels/PackageDetailsTabViewModel.cs
+++ b/src/NuGet.Clients/NuGet.PackageManagement.UI/ViewModels/PackageDetailsTabViewModel.cs
@@ -110,7 +110,7 @@ private void DetailControlModel_PropertyChanged(object sender, PropertyChangedEv
{
NuGetUIThreadHelper.JoinableTaskFactory.RunAsync(async () =>
{
- if (_readmeTabEnabled)
+ if (_readmeTabEnabled && e.PropertyName == nameof(DetailControlModel.PackageMetadata))
{
await ReadmePreviewViewModel.SetPackageMetadataAsync(DetailControlModel.PackageMetadata, CancellationToken.None);
}
diff --git a/src/NuGet.Clients/NuGet.PackageManagement.UI/ViewModels/ReadmePreviewViewModel.cs b/src/NuGet.Clients/NuGet.PackageManagement.UI/ViewModels/ReadmePreviewViewModel.cs
index ff897fe3b1b..c28e59d21a2 100644
--- a/src/NuGet.Clients/NuGet.PackageManagement.UI/ViewModels/ReadmePreviewViewModel.cs
+++ b/src/NuGet.Clients/NuGet.PackageManagement.UI/ViewModels/ReadmePreviewViewModel.cs
@@ -13,28 +13,46 @@ namespace NuGet.PackageManagement.UI.ViewModels
{
public sealed class ReadmePreviewViewModel : TitledPageViewModelBase
{
- private bool _errorLoadingReadme;
+ private bool _errorWithReadme;
private INuGetPackageFileService _nugetPackageFileService;
private string _rawReadme;
private DetailedPackageMetadata _packageMetadata;
private bool _canRenderLocalReadme;
+ private bool _isBusy;
public ReadmePreviewViewModel(INuGetPackageFileService packageFileService, ItemFilter itemFilter, bool isReadmeFeatureEnabled)
{
_nugetPackageFileService = packageFileService ?? throw new ArgumentNullException(nameof(packageFileService));
_canRenderLocalReadme = CanRenderLocalReadme(itemFilter);
_nugetPackageFileService = packageFileService;
- _errorLoadingReadme = false;
+ _errorWithReadme = false;
+ _isBusy = false;
_rawReadme = string.Empty;
_packageMetadata = null;
Title = Resources.Label_Readme_Tab;
IsVisible = isReadmeFeatureEnabled;
}
- public bool ErrorLoadingReadme
+ public bool IsReadmeReady { get => !IsBusy && !ErrorWithReadme; }
+
+ public bool ErrorWithReadme
{
- get => _errorLoadingReadme;
- set => SetAndRaisePropertyChanged(ref _errorLoadingReadme, value);
+ get => _errorWithReadme;
+ set
+ {
+ SetAndRaisePropertyChanged(ref _errorWithReadme, value);
+ RaisePropertyChanged(nameof(IsReadmeReady));
+ }
+ }
+
+ public bool IsBusy
+ {
+ get => _isBusy;
+ set
+ {
+ SetAndRaisePropertyChanged(ref _isBusy, value);
+ RaisePropertyChanged(nameof(IsReadmeReady));
+ }
}
public string ReadmeMarkdown
@@ -76,7 +94,7 @@ private async Task LoadReadmeAsync(CancellationToken cancellationToken)
{
ReadmeMarkdown = _canRenderLocalReadme && !string.IsNullOrWhiteSpace(_packageMetadata.PackagePath) ? Resources.Text_NoReadme : string.Empty;
IsVisible = !string.IsNullOrWhiteSpace(ReadmeMarkdown);
- ErrorLoadingReadme = false;
+ ErrorWithReadme = false;
return;
}
@@ -85,29 +103,34 @@ private async Task LoadReadmeAsync(CancellationToken cancellationToken)
{
ReadmeMarkdown = string.Empty;
IsVisible = false;
- ErrorLoadingReadme = false;
+ ErrorWithReadme = false;
return;
}
var readme = Resources.Text_NoReadme;
- await ThreadHelper.JoinableTaskFactory.RunAsync(async () =>
+ try
{
- await TaskScheduler.Default;
- using var readmeStream = await _nugetPackageFileService.GetReadmeAsync(readmeUrl, cancellationToken);
- if (readmeStream is null)
+ IsBusy = true;
+ ErrorWithReadme = false;
+ await ThreadHelper.JoinableTaskFactory.RunAsync(async () =>
{
- return;
- }
+ await TaskScheduler.Default;
+ using var readmeStream = await _nugetPackageFileService.GetReadmeAsync(readmeUrl, cancellationToken);
+ if (readmeStream is null)
+ {
+ return;
+ }
- using StreamReader streamReader = new StreamReader(readmeStream);
- readme = await streamReader.ReadToEndAsync();
- });
-
- if (!cancellationToken.IsCancellationRequested)
+ using StreamReader streamReader = new StreamReader(readmeStream);
+ readme = await streamReader.ReadToEndAsync();
+ });
+ }
+ finally
{
ReadmeMarkdown = readme;
IsVisible = !string.IsNullOrWhiteSpace(readme);
- ErrorLoadingReadme = false;
+ ErrorWithReadme = false;
+ IsBusy = false;
}
}
}
diff --git a/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/PackageReadmeControl.xaml b/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/PackageReadmeControl.xaml
index f582abcf9e1..7ac5326c491 100644
--- a/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/PackageReadmeControl.xaml
+++ b/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/PackageReadmeControl.xaml
@@ -9,6 +9,8 @@
xmlns:imagingTheme="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Imaging"
xmlns:catalog="clr-namespace:Microsoft.VisualStudio.Imaging;assembly=Microsoft.VisualStudio.ImageCatalog"
xmlns:ui="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Utilities"
+ Background="{DynamicResource {x:Static nuget:Brushes.DetailPaneBackground}}"
+ Foreground="{DynamicResource {x:Static nuget:Brushes.UIText}}"
DataContextChanged="UserControl_DataContextChanged"
Unloaded="PackageReadmeControl_Unloaded"
Loaded="PackageReadmeControl_Loaded"
@@ -28,12 +30,14 @@
x:Uid="descriptionMarkdownPreview"
ClipToBounds="True"
Focusable="False"
- Visibility="{Binding Path=ErrorLoadingReadme, Mode=OneWay, Converter={StaticResource NegatedBooleanToVisibilityConverter}}"
- />
+ Visibility="{Binding Path=IsReadmeReady, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}" />
+
+ Visibility="{Binding Path=ErrorWithReadme, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}" />
diff --git a/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/PackageReadmeControl.xaml.cs b/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/PackageReadmeControl.xaml.cs
index 05a3a80f580..caf94947c14 100644
--- a/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/PackageReadmeControl.xaml.cs
+++ b/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/PackageReadmeControl.xaml.cs
@@ -7,7 +7,6 @@
using System.Windows;
using System.Windows.Controls;
using Microsoft.VisualStudio.Markdown.Platform;
-using Microsoft.VisualStudio.Shell;
using NuGet.PackageManagement.UI.ViewModels;
using NuGet.VisualStudio;
using NuGet.VisualStudio.Telemetry;
@@ -39,7 +38,7 @@ private void ReadmeViewModel_PropertyChanged(object sender, PropertyChangedEvent
{
if (e.PropertyName == nameof(ReadmePreviewViewModel.ReadmeMarkdown))
{
- NuGetUIThreadHelper.JoinableTaskFactory.Run(UpdateMarkdownAsync);
+ NuGetUIThreadHelper.JoinableTaskFactory.RunAsync(UpdateMarkdownAsync).PostOnFailure(nameof(PackageReadmeControl), nameof(ReadmeViewModel_PropertyChanged));
}
}
@@ -79,7 +78,7 @@ private async Task UpdateMarkdownAsync()
}
catch (Exception ex) when (ex is ArgumentException || ex is InvalidOperationException)
{
- ReadmeViewModel.ErrorLoadingReadme = true;
+ ReadmeViewModel.ErrorWithReadme = true;
ReadmeViewModel.ReadmeMarkdown = string.Empty;
await TelemetryUtility.PostFaultAsync(ex, nameof(ReadmePreviewViewModel));
}
@@ -104,10 +103,8 @@ private void PackageReadmeControl_Unloaded(object sender, RoutedEventArgs e)
private void PackageReadmeControl_Loaded(object sender, RoutedEventArgs e)
{
- ThreadHelper.JoinableTaskFactory.Run(async () =>
- {
- await UpdateMarkdownAsync();
- });
+ NuGetUIThreadHelper.JoinableTaskFactory.RunAsync(UpdateMarkdownAsync)
+ .PostOnFailure(nameof(PackageReadmeControl), nameof(PackageReadmeControl_Loaded));
}
}
}
diff --git a/src/NuGet.Core/NuGet.Protocol/Model/PackageSearchMetadata.cs b/src/NuGet.Core/NuGet.Protocol/Model/PackageSearchMetadata.cs
index e8cf3e97452..27230ab7542 100644
--- a/src/NuGet.Core/NuGet.Protocol/Model/PackageSearchMetadata.cs
+++ b/src/NuGet.Core/NuGet.Protocol/Model/PackageSearchMetadata.cs
@@ -106,8 +106,8 @@ public string Owners
[JsonConverter(typeof(SafeUriConverter))]
public Uri ReadmeUrl { get; private set; }
- [JsonProperty(PropertyName = JsonProperties.ReadmeFileUrl)]
- public string ReadmeFileUrl { get; private set; }
+ [JsonIgnore]
+ public string ReadmeFileUrl { get; internal set; }
[JsonIgnore]
public Uri ReportAbuseUrl { get; set; }
diff --git a/src/NuGet.Core/NuGet.Protocol/Providers/PackageMetadataResourceV3Provider.cs b/src/NuGet.Core/NuGet.Protocol/Providers/PackageMetadataResourceV3Provider.cs
index 0916f2db6d5..259156fc3aa 100644
--- a/src/NuGet.Core/NuGet.Protocol/Providers/PackageMetadataResourceV3Provider.cs
+++ b/src/NuGet.Core/NuGet.Protocol/Providers/PackageMetadataResourceV3Provider.cs
@@ -23,6 +23,7 @@ public override async Task> TryCreate(SourceReposito
{
var regResource = await source.GetResourceAsync(token);
var reportAbuseResource = await source.GetResourceAsync(token);
+ var readmeResource = await source.GetResourceAsync(token);
var packageDetailsUriResource = await source.GetResourceAsync(token);
var httpSourceResource = await source.GetResourceAsync(token);
@@ -32,7 +33,8 @@ public override async Task> TryCreate(SourceReposito
httpSourceResource.HttpSource,
regResource,
reportAbuseResource,
- packageDetailsUriResource);
+ packageDetailsUriResource,
+ readmeResource);
}
return new Tuple(curResource != null, curResource);
diff --git a/src/NuGet.Core/NuGet.Protocol/Providers/ReadmeUriTemplateResourceProvider.cs b/src/NuGet.Core/NuGet.Protocol/Providers/ReadmeUriTemplateResourceProvider.cs
new file mode 100644
index 00000000000..6acc478494a
--- /dev/null
+++ b/src/NuGet.Core/NuGet.Protocol/Providers/ReadmeUriTemplateResourceProvider.cs
@@ -0,0 +1,39 @@
+// 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.
+#nullable enable
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using NuGet.Protocol.Core.Types;
+
+namespace NuGet.Protocol
+{
+ /// NuGet.Protocol resource provider for .
+ /// When successful, returns an instance of .
+ internal class ReadmeUriTemplateResourceProvider : ResourceProvider
+ {
+ public ReadmeUriTemplateResourceProvider()
+ : base(typeof(ReadmeUriTemplateResource),
+ nameof(ReadmeUriTemplateResource),
+ NuGetResourceProviderPositions.Last)
+ {
+ }
+
+ ///
+ public override async Task> TryCreate(SourceRepository source, CancellationToken token)
+ {
+ ReadmeUriTemplateResource? resource = null;
+ var serviceIndex = await source.GetResourceAsync(token);
+ if (serviceIndex != null)
+ {
+ var uriTemplate = serviceIndex.GetServiceEntryUri(ServiceTypes.ReadmeFileUrl)?.OriginalString;
+
+ // construct a new resource
+ resource = string.IsNullOrWhiteSpace(uriTemplate) ? null : new ReadmeUriTemplateResource(uriTemplate);
+ }
+
+ return new Tuple(resource != null, resource);
+ }
+ }
+}
diff --git a/src/NuGet.Core/NuGet.Protocol/Repository.cs b/src/NuGet.Core/NuGet.Protocol/Repository.cs
index 606a2a06860..9e4a15d2fca 100644
--- a/src/NuGet.Core/NuGet.Protocol/Repository.cs
+++ b/src/NuGet.Core/NuGet.Protocol/Repository.cs
@@ -53,6 +53,7 @@ public virtual IEnumerable> GetCoreV3()
yield return new Lazy(() => new RegistrationResourceV3Provider());
yield return new Lazy(() => new SymbolPackageUpdateResourceV3Provider());
yield return new Lazy(() => new ReportAbuseResourceV3Provider());
+ yield return new Lazy(() => new ReadmeUriTemplateResourceProvider());
yield return new Lazy(() => new PackageDetailsUriResourceV3Provider());
yield return new Lazy(() => new ServiceIndexResourceV3Provider());
yield return new Lazy(() => new ODataServiceDocumentResourceV2Provider());
diff --git a/src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs b/src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
index 7ec23f3c980..f32598f4aae 100644
--- a/src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
+++ b/src/NuGet.Core/NuGet.Protocol/Resources/PackageMetadataResourceV3.cs
@@ -21,6 +21,7 @@ public class PackageMetadataResourceV3 : PackageMetadataResource
{
private readonly RegistrationResourceV3 _regResource;
private readonly ReportAbuseResourceV3 _reportAbuseResource;
+ private readonly ReadmeUriTemplateResource _readmeUriTemplateResource;
private readonly PackageDetailsUriResourceV3 _packageDetailsUriResource;
private readonly HttpSource _client;
@@ -36,6 +37,16 @@ public PackageMetadataResourceV3(
_packageDetailsUriResource = packageDetailsUriResource;
}
+ internal PackageMetadataResourceV3(
+ HttpSource client,
+ RegistrationResourceV3 regResource,
+ ReportAbuseResourceV3 reportAbuseResource,
+ PackageDetailsUriResourceV3 packageDetailsUriResource,
+ ReadmeUriTemplateResource readmeResource) : this(client, regResource, reportAbuseResource, packageDetailsUriResource)
+ {
+ _readmeUriTemplateResource = readmeResource;
+ }
+
/// PackageId for package we're looking.
/// Whether to include PreRelease versions into result.
/// Whether to include Unlisted versions into result.
@@ -269,6 +280,10 @@ private void ProcessRegistrationPage(
{
catalogEntry.ReportAbuseUrl = _reportAbuseResource?.GetReportAbuseUrl(catalogEntry.PackageId, catalogEntry.Version);
catalogEntry.PackageDetailsUrl = _packageDetailsUriResource?.GetUri(catalogEntry.PackageId, catalogEntry.Version);
+ if (string.IsNullOrWhiteSpace(catalogEntry.ReadmeFileUrl))
+ {
+ catalogEntry.ReadmeFileUrl = _readmeUriTemplateResource?.GetReadmeUrl(catalogEntry.PackageId, catalogEntry.Version);
+ }
catalogEntry = metadataCache.GetObject(catalogEntry);
results.Add(catalogEntry);
}
diff --git a/src/NuGet.Core/NuGet.Protocol/Resources/ReadmeUriTemplateResource.cs b/src/NuGet.Core/NuGet.Protocol/Resources/ReadmeUriTemplateResource.cs
new file mode 100644
index 00000000000..de928aecd97
--- /dev/null
+++ b/src/NuGet.Core/NuGet.Protocol/Resources/ReadmeUriTemplateResource.cs
@@ -0,0 +1,52 @@
+// 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 NuGet.Protocol.Core.Types;
+using NuGet.Versioning;
+
+#if NETCOREAPP
+using System;
+#endif
+
+namespace NuGet.Protocol
+{
+ ///
+ /// A resource that provides the URI for downloading a README file based on a template.
+ ///
+ internal class ReadmeUriTemplateResource : INuGetResource
+ {
+ private readonly string _uriTemplate;
+ private const string LowerId = "{lower_id}";
+ private const string LowerVersion = "{lower_version}";
+
+ public ReadmeUriTemplateResource(string uriTemplate)
+ {
+ _uriTemplate = uriTemplate;
+ }
+
+ ///
+ /// Get the URL for downloading the readme file.
+ ///
+ /// The package id
+ /// The package version
+ /// URL to download README, built using the URI template.
+ public string GetReadmeUrl(string id, NuGetVersion version)
+ {
+ if (_uriTemplate == null)
+ {
+ return string.Empty;
+ }
+
+ var uriString = _uriTemplate
+#if NETCOREAPP
+ .Replace(LowerId, id.ToLowerInvariant(), StringComparison.OrdinalIgnoreCase)
+ .Replace(LowerVersion, version.ToNormalizedString().ToLowerInvariant(), StringComparison.OrdinalIgnoreCase);
+#else
+ .Replace(LowerId, id.ToLowerInvariant())
+ .Replace(LowerVersion, version.ToNormalizedString().ToLowerInvariant());
+#endif
+
+ return uriString;
+ }
+ }
+}
diff --git a/src/NuGet.Core/NuGet.Protocol/ServiceTypes.cs b/src/NuGet.Core/NuGet.Protocol/ServiceTypes.cs
index 729c6ec0bb1..a8adc9e0cf3 100644
--- a/src/NuGet.Core/NuGet.Protocol/ServiceTypes.cs
+++ b/src/NuGet.Core/NuGet.Protocol/ServiceTypes.cs
@@ -18,11 +18,13 @@ public static class ServiceTypes
public static readonly string Version510 = "/5.1.0";
internal const string Version670 = "/6.7.0";
internal const string Version6110 = "/6.11.0";
+ internal const string Version6130 = "/6.13.0";
public static readonly string[] SearchQueryService = { "SearchQueryService" + Versioned, "SearchQueryService" + Version340, "SearchQueryService" + Version300beta };
public static readonly string[] RegistrationsBaseUrl = { $"RegistrationsBaseUrl{Versioned}", $"RegistrationsBaseUrl{Version360}", $"RegistrationsBaseUrl{Version340}", $"RegistrationsBaseUrl{Version300rc}", $"RegistrationsBaseUrl{Version300beta}", "RegistrationsBaseUrl" };
public static readonly string[] SearchAutocompleteService = { "SearchAutocompleteService" + Versioned, "SearchAutocompleteService" + Version300beta };
public static readonly string[] ReportAbuse = { "ReportAbuseUriTemplate" + Versioned, "ReportAbuseUriTemplate" + Version300 };
+ internal static readonly string[] ReadmeFileUrl = { "ReadmeUriTemplate" + Versioned, "ReadmeUriTemplate" + Version6130 };
public static readonly string[] PackageDetailsUriTemplate = { "PackageDetailsUriTemplate" + Version510 };
public static readonly string[] LegacyGallery = { "LegacyGallery" + Versioned, "LegacyGallery" + Version200 };
public static readonly string[] PackagePublish = { "PackagePublish" + Versioned, "PackagePublish" + Version200 };
diff --git a/test/NuGet.Clients.Tests/NuGet.PackageManagement.UI.Test/ViewModels/ReadmePreviewViewModelTests.cs b/test/NuGet.Clients.Tests/NuGet.PackageManagement.UI.Test/ViewModels/ReadmePreviewViewModelTests.cs
index ecd4a848c0e..8037fec25cf 100644
--- a/test/NuGet.Clients.Tests/NuGet.PackageManagement.UI.Test/ViewModels/ReadmePreviewViewModelTests.cs
+++ b/test/NuGet.Clients.Tests/NuGet.PackageManagement.UI.Test/ViewModels/ReadmePreviewViewModelTests.cs
@@ -36,7 +36,7 @@ public void Constructor_Defaults()
var target = new ReadmePreviewViewModel(mockFileService.Object, ItemFilter.All, true);
//Assert
- Assert.False(target.ErrorLoadingReadme);
+ Assert.False(target.ErrorWithReadme);
Assert.Equal(string.Empty, target.ReadmeMarkdown);
}
@@ -56,7 +56,7 @@ public async Task SetPackageMetadataAsync_WithoutReadmeUrl_NoReadmeReturned(stri
await target.SetPackageMetadataAsync(package, CancellationToken.None);
//Assert
- Assert.False(target.ErrorLoadingReadme);
+ Assert.False(target.ErrorWithReadme);
Assert.Equal(string.Empty, target.ReadmeMarkdown);
}
@@ -77,7 +77,7 @@ public async Task SetCurrentFilter_ItemFilterAll_NoLocalReadmeReturned()
await target.ItemFilterChangedAsync(ItemFilter.All);
//Assert
- Assert.False(target.ErrorLoadingReadme);
+ Assert.False(target.ErrorWithReadme);
Assert.Equal(string.Empty, target.ReadmeMarkdown);
}
@@ -101,7 +101,7 @@ public async Task SetCurrentFilter_ItemFilterRenderingLocalReadme_LocalReadmeRet
await target.ItemFilterChangedAsync(filter);
//Assert
- Assert.False(target.ErrorLoadingReadme);
+ Assert.False(target.ErrorWithReadme);
Assert.Equal(readmeContents, target.ReadmeMarkdown);
}
@@ -121,7 +121,7 @@ public async Task SetPackageMetadataAsync_WithLocalReadmeUrl_RenderLocalReadmeTr
await target.SetPackageMetadataAsync(package, CancellationToken.None);
//Assert
- Assert.False(target.ErrorLoadingReadme);
+ Assert.False(target.ErrorWithReadme);
Assert.Equal(readmeContents, target.ReadmeMarkdown);
}
@@ -141,7 +141,7 @@ public async Task SetPackageMetadataAsync_WithLocalReadmeUrl_RenderLocalReadmeFa
await target.SetPackageMetadataAsync(package, CancellationToken.None);
//Assert
- Assert.False(target.ErrorLoadingReadme);
+ Assert.False(target.ErrorWithReadme);
Assert.Equal(string.Empty, target.ReadmeMarkdown);
}
@@ -159,7 +159,7 @@ public async Task SetPackageMetadataAsync_WithLocalReadmeUrl_FileNotFound_NoRead
await target.SetPackageMetadataAsync(package, CancellationToken.None);
//Assert
- Assert.False(target.ErrorLoadingReadme);
+ Assert.False(target.ErrorWithReadme);
Assert.Equal(Resources.Text_NoReadme, target.ReadmeMarkdown);
}
}
diff --git a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Providers/ReadmeUriTemplateResourceProviderTests.cs b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Providers/ReadmeUriTemplateResourceProviderTests.cs
new file mode 100644
index 00000000000..24c0e6c21cf
--- /dev/null
+++ b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Providers/ReadmeUriTemplateResourceProviderTests.cs
@@ -0,0 +1,110 @@
+// 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 System.Threading;
+using System.Threading.Tasks;
+using Moq;
+using Newtonsoft.Json.Linq;
+using NuGet.Configuration;
+using NuGet.Protocol.Core.Types;
+using Xunit;
+
+namespace NuGet.Protocol.Tests.Providers
+{
+ public class ReadmeUriTemplateResourceProviderTests
+ {
+ private const string ResourceType = "ReadmeUriTemplate/6.13.0";
+
+ private readonly PackageSource _packageSource;
+ private readonly ReadmeUriTemplateResourceProvider _target;
+
+ public ReadmeUriTemplateResourceProviderTests()
+ {
+ _packageSource = new PackageSource("https://unit.test");
+ _target = new ReadmeUriTemplateResourceProvider();
+ }
+
+ [Fact]
+ public async Task TryCreate_WhenResourceDoesNotExist_ReturnsNull()
+ {
+ var resourceProviders = new ResourceProvider[]
+ {
+ CreateServiceIndexResourceV3Provider(),
+ _target
+ };
+ var sourceRepository = new SourceRepository(_packageSource, resourceProviders);
+
+ Tuple result = await _target.TryCreate(sourceRepository, CancellationToken.None);
+
+ Assert.False(result.Item1);
+ Assert.Null(result.Item2);
+ }
+
+ [Fact]
+ public async Task TryCreate_WhenResourceExists_ReturnsValidResourceAsync()
+ {
+ var serviceEntry = new RawServiceIndexEntry("https://unit.test/packages/{lower_id}/{lower_version}/readme", ResourceType);
+ var resourceProviders = new ResourceProvider[]
+ {
+ CreateServiceIndexResourceV3Provider(serviceEntry),
+ _target
+ };
+ var sourceRepository = new SourceRepository(_packageSource, resourceProviders);
+
+ Tuple result = await _target.TryCreate(sourceRepository, CancellationToken.None);
+
+ Assert.True(result.Item1);
+ Assert.IsType(result.Item2);
+ }
+
+ private static ServiceIndexResourceV3Provider CreateServiceIndexResourceV3Provider(params RawServiceIndexEntry[] entries)
+ {
+ var provider = new Mock();
+
+ provider.Setup(x => x.Name)
+ .Returns(nameof(ServiceIndexResourceV3Provider));
+ provider.Setup(x => x.ResourceType)
+ .Returns(typeof(ServiceIndexResourceV3));
+
+ var resources = new JArray();
+
+ foreach (var entry in entries)
+ {
+ resources.Add(
+ new JObject(
+ new JProperty("@id", entry.Uri),
+ new JProperty("@type", entry.Type)));
+ }
+
+ var index = new JObject();
+
+ index.Add("version", "3.0.0");
+ index.Add("resources", resources);
+ index.Add("@context",
+ new JObject(
+ new JProperty("@vocab", "http://schema.nuget.org/schema#"),
+ new JProperty("comment", "http://www.w3.org/2000/01/rdf-schema#comment")));
+
+ var serviceIndexResource = new ServiceIndexResourceV3(index, DateTime.UtcNow);
+ var tryCreateResult = new Tuple(true, serviceIndexResource);
+
+ provider.Setup(x => x.TryCreate(It.IsAny(), It.IsAny()))
+ .Returns(Task.FromResult(tryCreateResult));
+
+ return provider.Object;
+ }
+
+ private class RawServiceIndexEntry
+ {
+ public RawServiceIndexEntry(string uri, string type)
+ {
+ Uri = uri;
+ Type = type ?? throw new ArgumentNullException(nameof(type));
+ }
+
+ public string Uri { get; }
+ public string Type { get; }
+ }
+ }
+}
diff --git a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/ReadmeUriTemplateResourceTests.cs b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/ReadmeUriTemplateResourceTests.cs
new file mode 100644
index 00000000000..4fc29638c39
--- /dev/null
+++ b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/ReadmeUriTemplateResourceTests.cs
@@ -0,0 +1,36 @@
+// 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 NuGet.Versioning;
+using Xunit;
+
+namespace NuGet.Protocol.Tests
+{
+ public class ReadmeUriTemplateResourceTests
+ {
+ [Theory]
+ [InlineData("")]
+ [InlineData(null)]
+ public void ReadmeUriTemplateResource_GetReadmeUrl_BlankTemplate_ReturnsEmptyString(string uriTemplate)
+ {
+ string expectedResult = string.Empty;
+ var resource = new ReadmeUriTemplateResource(uriTemplate);
+
+ var actual = resource.GetReadmeUrl("TestPackage", NuGetVersion.Parse("1.0.0"));
+
+ Assert.Equal(expectedResult, actual);
+ }
+
+ [Fact]
+ public void ReadmeUriTemplateResource_GetReadmeUrl_ReturnsFormedUrl()
+ {
+ const string uriTemplate = "https://test.nuget.org/{lower_id}/{lower_version}/readme";
+ const string expectedResult = "https://test.nuget.org/testpackage/1.0.0/readme";
+ var resource = new ReadmeUriTemplateResource(uriTemplate);
+
+ var actual = resource.GetReadmeUrl("TestPackage", NuGetVersion.Parse("1.0.0"));
+
+ Assert.Equal(expectedResult, actual.ToString());
+ }
+ }
+}
diff --git a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/RepositoryTests.cs b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/RepositoryTests.cs
index 0546f38aae5..a08cd7b0249 100644
--- a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/RepositoryTests.cs
+++ b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/RepositoryTests.cs
@@ -63,7 +63,7 @@ public void Provider_WithDefaultProvider_ReturnsDefaultResourceProviders()
int actualCount = resourceProviders.Count();
- Assert.Equal(48, actualCount);
+ Assert.Equal(49, actualCount);
}
}
}