Compare commits
No commits in common. "88de476b1bc0e280e4b728c17d0466ffd5be01ac" and "dd85c7f7412796e3eaa4c26c20523ed5477c2857" have entirely different histories.
88de476b1b
...
dd85c7f741
3 changed files with 64 additions and 227 deletions
Binary file not shown.
|
|
@ -1,9 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
|
||||||
<metadata>
|
|
||||||
<id>dummy.nuget</id>
|
|
||||||
<version>1.0.0</version>
|
|
||||||
<authors>isnd.tests</authors>
|
|
||||||
<description>Dummy test package used by integration tests.</description>
|
|
||||||
</metadata>
|
|
||||||
</package>
|
|
||||||
|
|
@ -1,39 +1,34 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System;
|
||||||
using System.Xml.Linq;
|
using System.Net.Http;
|
||||||
using Isn.Abstract;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore;
|
||||||
|
using Xunit;
|
||||||
using isnd.Data;
|
using isnd.Data;
|
||||||
using isnd.Data.ApiKeys;
|
|
||||||
using isnd.Helpers;
|
|
||||||
using isnd.tests;
|
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using NuGet.Common;
|
using System.Diagnostics;
|
||||||
using NuGet.Configuration;
|
using Microsoft.AspNetCore.Hosting.Server;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using isnd.tests;
|
||||||
using NuGet.Protocol;
|
using NuGet.Protocol;
|
||||||
|
using NuGet.Configuration;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using NuGet.Protocol.Core.Types;
|
using NuGet.Protocol.Core.Types;
|
||||||
using Xunit;
|
using NuGet.Common;
|
||||||
|
using Isn.Abstract;
|
||||||
|
using isnd.Helpers;
|
||||||
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace isnd.host.tests
|
namespace isnd.host.tests
|
||||||
{
|
{
|
||||||
[Collection("Web server collection")]
|
[Collection("Web server collection")]
|
||||||
public class UnitTestWebHost : IClassFixture<WebServerFixture>
|
public class UnitTestWebHost : IClassFixture<WebServerFixture>
|
||||||
{
|
{
|
||||||
private const string DummyPackageId = "dummy.nuget";
|
WebServerFixture server;
|
||||||
private const string DummyPackageVersion = "1.0.0";
|
|
||||||
private const string DummyArtifactRelativePath = "test/data/nuget-artifacts/dummy.nuget.1.0.0.nupkg";
|
|
||||||
|
|
||||||
private readonly WebServerFixture server;
|
|
||||||
|
|
||||||
public UnitTestWebHost(WebServerFixture server)
|
public UnitTestWebHost(WebServerFixture server)
|
||||||
{
|
{
|
||||||
this.server = server;
|
this.server = server;
|
||||||
|
|
@ -42,7 +37,8 @@ namespace isnd.host.tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TestHaveTestDbContextAndMigrate()
|
public void TestHaveTestDbContextAndMigrate()
|
||||||
{
|
{
|
||||||
using var serviceScope = server.Host.Services.CreateScope();
|
using (var serviceScope = server.Host.Services.CreateScope())
|
||||||
|
{
|
||||||
var services = serviceScope.ServiceProvider;
|
var services = serviceScope.ServiceProvider;
|
||||||
var myDependency = services.GetRequiredService<ApplicationDbContext>();
|
var myDependency = services.GetRequiredService<ApplicationDbContext>();
|
||||||
if (myDependency.Database.ProviderName?.Contains("InMemory", StringComparison.OrdinalIgnoreCase) == true)
|
if (myDependency.Database.ProviderName?.Contains("InMemory", StringComparison.OrdinalIgnoreCase) == true)
|
||||||
|
|
@ -54,6 +50,7 @@ namespace isnd.host.tests
|
||||||
myDependency.Database.Migrate();
|
myDependency.Database.Migrate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void LegacyApiKeyProtectorCanUnprotectValuesFromOlderClients()
|
public void LegacyApiKeyProtectorCanUnprotectValuesFromOlderClients()
|
||||||
|
|
@ -77,9 +74,11 @@ namespace isnd.host.tests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TestDropUser()
|
void TestDropUser()
|
||||||
|
{
|
||||||
|
|
||||||
|
using (var serviceScope = server.Host.Services.CreateScope())
|
||||||
{
|
{
|
||||||
using var serviceScope = server.Host.Services.CreateScope();
|
|
||||||
var services = serviceScope.ServiceProvider;
|
var services = serviceScope.ServiceProvider;
|
||||||
var dbContext = services.GetRequiredService<ApplicationDbContext>();
|
var dbContext = services.GetRequiredService<ApplicationDbContext>();
|
||||||
var paul = dbContext.Users.FirstOrDefaultAsync(u => u.Email == "paul@pschneider.fr").Result;
|
var paul = dbContext.Users.FirstOrDefaultAsync(u => u.Email == "paul@pschneider.fr").Result;
|
||||||
|
|
@ -89,109 +88,53 @@ namespace isnd.host.tests
|
||||||
dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void NugetInstallsTest()
|
public void NugetInstallsTest()
|
||||||
{
|
{
|
||||||
var pkgSourceUrl = GetServerBaseUrl() + "/" + Constants.APIPrefix + "index.json";
|
using (var serviceScope = server.Host.Services.CreateScope())
|
||||||
|
{
|
||||||
|
var isnSettings = serviceScope.ServiceProvider.GetService<IOptions<isnd.Entities.IsndSettings>>().Value;
|
||||||
|
string pkgSourceUrl = isnSettings.ExternalUrl + "/" + Constants.APIPrefix + "index.json";
|
||||||
using var client = new HttpClient();
|
using var client = new HttpClient();
|
||||||
var response = client.GetAsync(pkgSourceUrl).GetAwaiter().GetResult();
|
var response = client.GetAsync(pkgSourceUrl).GetAwaiter().GetResult();
|
||||||
var body = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
var body = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
||||||
|
|
||||||
Assert.True(response.IsSuccessStatusCode, $"Expected {pkgSourceUrl} to be reachable but got {(int)response.StatusCode} {response.ReasonPhrase}");
|
Assert.True(response.IsSuccessStatusCode, $"Expected {pkgSourceUrl} to be reachable but got {(int)response.StatusCode} {response.ReasonPhrase}");
|
||||||
Assert.False(string.IsNullOrWhiteSpace(body));
|
Assert.False(string.IsNullOrWhiteSpace(body));
|
||||||
Assert.Contains("PackagePublish/2.0.0", body, StringComparison.OrdinalIgnoreCase);
|
|
||||||
Assert.Contains("RegistrationsBaseUrl/Versioned", body, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void DummyNugetArtifactShouldBeAReadablePackage()
|
|
||||||
{
|
|
||||||
var packagePath = GetDummyArtifactPath();
|
|
||||||
Assert.True(File.Exists(packagePath), $"Missing dummy artifact at {packagePath}");
|
|
||||||
|
|
||||||
var fileInfo = new FileInfo(packagePath);
|
|
||||||
Assert.True(fileInfo.Length > 0, "Dummy artifact is empty.");
|
|
||||||
|
|
||||||
using var stream = File.OpenRead(packagePath);
|
|
||||||
using var archive = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: false);
|
|
||||||
var nuspecEntry = archive.GetEntry("dummy.nuget.nuspec");
|
|
||||||
Assert.NotNull(nuspecEntry);
|
|
||||||
|
|
||||||
using var nuspecStream = nuspecEntry.Open();
|
|
||||||
var nuspec = XDocument.Load(nuspecStream);
|
|
||||||
XNamespace ns = "http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd";
|
|
||||||
var id = nuspec.Root?.Element(ns + "metadata")?.Element(ns + "id")?.Value;
|
|
||||||
var version = nuspec.Root?.Element(ns + "metadata")?.Element(ns + "version")?.Value;
|
|
||||||
|
|
||||||
Assert.Equal(DummyPackageId, id);
|
|
||||||
Assert.Equal(DummyPackageVersion, version);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task PushDummyPackageAndDiscoverItThroughNuGetEndpoints()
|
|
||||||
{
|
|
||||||
var apiKeyValue = $"dummy-key-{Guid.NewGuid():N}";
|
|
||||||
var packagePath = GetDummyArtifactPath();
|
|
||||||
Assert.True(File.Exists(packagePath), $"Missing dummy artifact at {packagePath}");
|
|
||||||
|
|
||||||
using (var serviceScope = server.Host.Services.CreateScope())
|
|
||||||
{
|
|
||||||
var dbContext = serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
|
||||||
var userId = $"dummy-user-{Guid.NewGuid():N}";
|
|
||||||
|
|
||||||
await ResetDummyPackageStateAsync(serviceScope.ServiceProvider, dbContext);
|
|
||||||
|
|
||||||
dbContext.Users.Add(new ApplicationUser
|
|
||||||
{
|
|
||||||
Id = userId,
|
|
||||||
UserName = userId,
|
|
||||||
NormalizedUserName = userId.ToUpperInvariant(),
|
|
||||||
Email = $"{userId}@tests.local",
|
|
||||||
NormalizedEmail = $"{userId}@tests.local".ToUpperInvariant(),
|
|
||||||
EmailConfirmed = true
|
|
||||||
});
|
|
||||||
|
|
||||||
dbContext.ApiKeys.Add(new ApiKey
|
|
||||||
{
|
|
||||||
Id = apiKeyValue,
|
|
||||||
UserId = userId,
|
|
||||||
Name = "dummy-key",
|
|
||||||
CreationDate = DateTime.UtcNow,
|
|
||||||
ValidityPeriodInDays = 30
|
|
||||||
});
|
|
||||||
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
await PushPackageAsync(packagePath, apiKeyValue);
|
|
||||||
await AssertRegistrationContainsPushedVersionAsync(DummyPackageId, DummyPackageVersion);
|
|
||||||
await AssertAutocompleteContainsPushedVersionAsync(DummyPackageId, DummyPackageVersion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TestRegistrationV3Resource()
|
public void TestRegistrationV3Resource()
|
||||||
{
|
{
|
||||||
using var serviceScope = server.Host.Services.CreateScope();
|
using (var serviceScope = server.Host.Services.CreateScope())
|
||||||
var isnSettings = serviceScope.ServiceProvider.GetService<IOptions<isnd.Entities.IsndSettings>>().Value;
|
{ var isnSettings = serviceScope.ServiceProvider.GetService<IOptions<isnd.Entities.IsndSettings>>().Value;
|
||||||
string pkgSourceUrl = isnSettings.ExternalUrl + "/pkgs/index.json";
|
string pkgSourceUrl = isnSettings.ExternalUrl + "/pkgs/index.json";
|
||||||
var throttle = new NullThrottle();
|
NullThrottle throttle = new NullThrottle();
|
||||||
|
|
||||||
var packageSource = new PackageSource(pkgSourceUrl);
|
PackageSource packageSource = new PackageSource(pkgSourceUrl);
|
||||||
var client = new HttpSource(packageSource, PkgSourceMessageHandler, throttle);
|
HttpSource client = new HttpSource(packageSource, PkgSourceMessageHandler, throttle);
|
||||||
_ = new RegistrationResourceV3(client, new Uri(isnSettings.ExternalUrl + "/v3.4.0//registration"));
|
NuGet.Protocol.RegistrationResourceV3 res = new NuGet.Protocol.RegistrationResourceV3(client ,
|
||||||
|
new Uri(isnSettings.ExternalUrl + "/v3.4.0//registration"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TrueTestRegistrationV3Resource()
|
public void TrueTestRegistrationV3Resource()
|
||||||
{
|
{
|
||||||
using var serviceScope = server.Host.Services.CreateScope();
|
using (var serviceScope = server.Host.Services.CreateScope())
|
||||||
|
{
|
||||||
var isnSettings = serviceScope.ServiceProvider.GetService<IOptions<isnd.Entities.IsndSettings>>().Value;
|
var isnSettings = serviceScope.ServiceProvider.GetService<IOptions<isnd.Entities.IsndSettings>>().Value;
|
||||||
string pkgSourceUrl = isnSettings.ExternalUrl + "/pkgs/index.json";
|
string pkgSourceUrl = isnSettings.ExternalUrl + "/pkgs/index.json";
|
||||||
var prov = new RegistrationResourceV3Provider();
|
var prov = new RegistrationResourceV3Provider();
|
||||||
var source = new PackageSource(pkgSourceUrl);
|
var source = new PackageSource(pkgSourceUrl);
|
||||||
var repo = new SourceRepository(source, new INuGetResourceProvider[]{ prov });
|
var repo = new SourceRepository(source, new INuGetResourceProvider[]{ prov });
|
||||||
prov.TryCreate(repo, CancellationToken.None);
|
prov.TryCreate(repo, CancellationToken.None);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<HttpHandlerResource> PkgSourceMessageHandler()
|
private Task<HttpHandlerResource> PkgSourceMessageHandler()
|
||||||
|
|
@ -199,103 +142,6 @@ namespace isnd.host.tests
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetServerBaseUrl()
|
|
||||||
{
|
|
||||||
var address = server.Addresses.FirstOrDefault();
|
|
||||||
Assert.False(string.IsNullOrWhiteSpace(address), "No listening address was captured from WebServerFixture.");
|
|
||||||
return address.TrimEnd('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetDummyArtifactPath()
|
|
||||||
{
|
|
||||||
var root = FindRepoRoot();
|
|
||||||
return Path.Combine(root, DummyArtifactRelativePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string FindRepoRoot()
|
|
||||||
{
|
|
||||||
var dir = new DirectoryInfo(AppContext.BaseDirectory);
|
|
||||||
while (dir != null)
|
|
||||||
{
|
|
||||||
if (File.Exists(Path.Combine(dir.FullName, "isn.sln")))
|
|
||||||
{
|
|
||||||
return dir.FullName;
|
|
||||||
}
|
|
||||||
|
|
||||||
dir = dir.Parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new DirectoryNotFoundException("Could not locate repository root from AppContext.BaseDirectory.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task ResetDummyPackageStateAsync(IServiceProvider serviceProvider, ApplicationDbContext dbContext)
|
|
||||||
{
|
|
||||||
var settings = serviceProvider.GetRequiredService<IOptions<isnd.Entities.IsndSettings>>().Value;
|
|
||||||
var versions = dbContext.PackageVersions.Where(v => v.PackageId == DummyPackageId);
|
|
||||||
var packages = dbContext.Packages.Where(p => p.Id == DummyPackageId);
|
|
||||||
|
|
||||||
dbContext.PackageVersions.RemoveRange(versions);
|
|
||||||
dbContext.Packages.RemoveRange(packages);
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
|
|
||||||
var packageRootDir = settings.PackagesRootDir;
|
|
||||||
if (!Path.IsPathRooted(packageRootDir))
|
|
||||||
{
|
|
||||||
packageRootDir = Path.Combine(FindRepoRoot(), packageRootDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
var packageOnDiskPath = Path.Combine(packageRootDir, DummyPackageId);
|
|
||||||
if (Directory.Exists(packageOnDiskPath))
|
|
||||||
{
|
|
||||||
Directory.Delete(packageOnDiskPath, recursive: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task PushPackageAsync(string packagePath, string apiKey)
|
|
||||||
{
|
|
||||||
using var client = new HttpClient { BaseAddress = new Uri(GetServerBaseUrl()) };
|
|
||||||
using var request = new HttpRequestMessage(HttpMethod.Put, $"/{Constants.APIPrefix}{isnd.Entities.ApiConfig.Publish}");
|
|
||||||
using var multipart = new MultipartFormDataContent();
|
|
||||||
await using var packageStream = File.OpenRead(packagePath);
|
|
||||||
using var packageContent = new StreamContent(packageStream);
|
|
||||||
|
|
||||||
packageContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
|
|
||||||
multipart.Add(packageContent, "package", Path.GetFileName(packagePath));
|
|
||||||
request.Content = multipart;
|
|
||||||
request.Headers.Add("X-NuGet-ApiKey", apiKey);
|
|
||||||
request.Headers.Add("X-NuGet-Client-Version", "6.11.1");
|
|
||||||
|
|
||||||
var response = await client.SendAsync(request);
|
|
||||||
var responseBody = await response.Content.ReadAsStringAsync();
|
|
||||||
Assert.True(response.IsSuccessStatusCode,
|
|
||||||
$"Expected push to succeed but got {(int)response.StatusCode} {response.ReasonPhrase}. Body: {responseBody}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AssertRegistrationContainsPushedVersionAsync(string packageId, string packageVersion)
|
|
||||||
{
|
|
||||||
using var client = new HttpClient { BaseAddress = new Uri(GetServerBaseUrl()) };
|
|
||||||
var lowerId = packageId.ToLowerInvariant();
|
|
||||||
var route = $"/{Constants.APIPrefix}v3.4.0/{isnd.Entities.ApiConfig.Registration}/{lowerId}/index.json";
|
|
||||||
var response = await client.GetAsync(route);
|
|
||||||
var body = await response.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
Assert.True(response.IsSuccessStatusCode,
|
|
||||||
$"Expected registration endpoint to succeed but got {(int)response.StatusCode} {response.ReasonPhrase}. Body: {body}");
|
|
||||||
Assert.Contains(packageVersion, body, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AssertAutocompleteContainsPushedVersionAsync(string packageId, string packageVersion)
|
|
||||||
{
|
|
||||||
using var client = new HttpClient { BaseAddress = new Uri(GetServerBaseUrl()) };
|
|
||||||
var route = $"/{Constants.APIPrefix}{isnd.Entities.ApiConfig.AutoComplete}?id={packageId}&semVerLevel=3.0.0&prerelease=true";
|
|
||||||
var response = await client.GetAsync(route);
|
|
||||||
var body = await response.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
Assert.True(response.IsSuccessStatusCode,
|
|
||||||
$"Expected autocomplete endpoint to succeed but got {(int)response.StatusCode} {response.ReasonPhrase}. Body: {body}");
|
|
||||||
Assert.Contains(packageVersion, body, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class ThrowingProtector : IDataProtector
|
private sealed class ThrowingProtector : IDataProtector
|
||||||
{
|
{
|
||||||
public byte[] Protect(byte[] plaintext) => throw new CryptographicException("unexpected protect call");
|
public byte[] Protect(byte[] plaintext) => throw new CryptographicException("unexpected protect call");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue