Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ [new PlannedRelease("Fixture", "src/Fixture/Fixture.csproj", "1.2.4-beta.1",
[new ConventionalCommitReason(SemverBump.Minor, ["abc"]),
new DependencyReleaseReason("Foundation")], ["Foundation"]),
new PlannedRelease("NewPackage", "src/NewPackage/NewPackage.csproj", "0.1.0",
[new InitialReleaseReason()], [])], "target-sha", "Release",
[new InitialReleaseReason()], [])], "Release",
[new ReferencedPackage("Foundation", "src/Foundation/Foundation.csproj", "3.2.1")], "solutions");

string json = JsonSerializer.Serialize(plan, PlanJsonOptions.Options);
Expand All @@ -30,7 +30,6 @@ [new InitialReleaseReason()], [])], "target-sha", "Release",
await Assert.That(package.PackageId).IsEqualTo("Fixture");
await Assert.That(copy.Packages[1].Reasons.Single() is InitialReleaseReason).IsTrue();
await Assert.That(copy.Packages[1].Version).IsEqualTo("0.1.0");
await Assert.That(copy.TargetCommitSha).IsEqualTo("target-sha");
await Assert.That(copy.Configuration).IsEqualTo("Release");
await Assert.That(copy.WorkingDirectory).IsEqualTo("solutions");
await Assert.That(package.DependencyIds).IsEquivalentTo(["Foundation"]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public async Task PlanWritesReadableVersionsAndReasonTypes()
ReleasePlan plan = new(
[new PlannedRelease("Fixture", "src/Fixture/Fixture.csproj", "1.2.4",
[new ConventionalCommitReason(SemverBump.Patch, ["abc"]),
new DependencyReleaseReason("Foundation")], ["Foundation"])], "target-sha", "Release",
new DependencyReleaseReason("Foundation")], ["Foundation"])], "Release",
[new ReferencedPackage("Foundation", "src/Foundation/Foundation.csproj", "3.2.1")], ".");
StubPlanner planner = new(plan);
TestBootstrap bootstrap = new(services => services.AddSingleton<IPackageCascadePlanner>(planner));
Expand Down Expand Up @@ -108,7 +108,7 @@ public ReleasePlan Generate(string repositoryPath, string solutionPath, string c
SolutionPath = solutionPath;
Configuration = configuration;

return _plan ?? new([], "target-sha", configuration, [], ".");
return _plan ?? new([], configuration, [], ".");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ await File.WriteAllTextAsync(Path.Combine(projectDirectory, "Fixture.csproj"), "

using (Assert.Multiple())
{
await Assert.That(plan.TargetCommitSha).IsEqualTo(_repository.Head.Tip!.Sha);
await Assert.That(plan.Configuration).IsEqualTo("Release");
await Assert.That(release.PackageId).IsEqualTo("Fixture");
await Assert.That(release.Version).IsEqualTo("1.2.4");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,27 @@ public PackageTagWriterTests()
}

[Test]
public async Task CreatesLightweightTagsAtPlanCommitAfterHeadMoves()
public async Task CreatesLightweightTagsAtCurrentHead()
{
Commit target = Commit("target");
Commit("new head");
Commit("target");
Commit head = Commit("new head");

IReadOnlyList<string> created = _writer.Create(_directory, Plan(target, ("Acme.Core", "1.2.3"), ("Acme.Tools", "2.0.0")));
IReadOnlyList<string> created = _writer.Create(_directory, Plan(("Acme.Core", "1.2.3"), ("Acme.Tools", "2.0.0")));

using (Assert.Multiple())
{
await Assert.That(created).IsEquivalentTo(new[] { "Acme.Core-v1.2.3", "Acme.Tools-v2.0.0" });
await Assert.That(_repository.Tags["Acme.Core-v1.2.3"].Target).IsTypeOf<Commit>();
await Assert.That(_repository.Tags["Acme.Core-v1.2.3"].PeeledTarget.Id).IsEqualTo(target.Id);
await Assert.That(_repository.Tags["Acme.Tools-v2.0.0"].PeeledTarget.Id).IsEqualTo(target.Id);
await Assert.That(_repository.Tags["Acme.Core-v1.2.3"].PeeledTarget.Id).IsEqualTo(head.Id);
await Assert.That(_repository.Tags["Acme.Tools-v2.0.0"].PeeledTarget.Id).IsEqualTo(head.Id);
}
}

[Test]
public async Task RepeatedCreateThrowsAndKeepsFirstTag()
{
Commit target = Commit("target");
ReleasePlan plan = Plan(target, ("Acme.Core", "1.0.0"));
ReleasePlan plan = Plan(("Acme.Core", "1.0.0"));
_writer.Create(_directory, plan);

Assert.Throws(typeof(LibGit2SharpException), () => _writer.Create(_directory, plan));
Expand All @@ -53,7 +53,7 @@ public async Task ConflictingSecondTagLeavesFirstTag()
_repository.Tags.Add("Acme.Tools-v2.0.0", other);

Assert.Throws(typeof(LibGit2SharpException), () => _writer.Create(_directory,
Plan(target, ("Acme.Core", "1.0.0"), ("Acme.Tools", "2.0.0"))));
Plan(("Acme.Core", "1.0.0"), ("Acme.Tools", "2.0.0"))));

using (Assert.Multiple())
{
Expand All @@ -68,7 +68,7 @@ public async Task InvalidSecondTagLeavesFirstTag()
Commit target = Commit("target");

Assert.Throws(typeof(LibGit2SharpException), () => _writer.Create(_directory,
Plan(target, ("Acme.Core", "1.0.0"), ("Bad..Name", "2.0.0"))));
Plan(("Acme.Core", "1.0.0"), ("Bad..Name", "2.0.0"))));

await Assert.That(_repository.Tags["Acme.Core-v1.0.0"].PeeledTarget.Id).IsEqualTo(target.Id);
}
Expand All @@ -81,7 +81,7 @@ public async Task TagWriteFailureLeavesEarlierTag()
File.WriteAllText(lockPath, "held");

Assert.Throws(typeof(LibGit2SharpException), () => _writer.Create(_directory,
Plan(target, ("Acme.Core", "1.0.0"), ("Acme.Tools", "2.0.0"))));
Plan(("Acme.Core", "1.0.0"), ("Acme.Tools", "2.0.0"))));

using (Assert.Multiple())
{
Expand All @@ -104,6 +104,6 @@ private Commit Commit(string contents)
return _repository.Commit(contents, author, author);
}

private static ReleasePlan Plan(Commit target, params (string PackageId, string Version)[] packages)
=> new(packages.Select(package => new PlannedRelease(package.PackageId, $"{package.PackageId}.csproj", package.Version, [], [])).ToArray(), target.Sha, "Release", [], ".");
private static ReleasePlan Plan(params (string PackageId, string Version)[] packages)
=> new(packages.Select(package => new PlannedRelease(package.PackageId, $"{package.PackageId}.csproj", package.Version, [], [])).ToArray(), "Release", [], ".");
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public sealed class PackageArchiveVerifierTests : IDisposable
private readonly PackageArchiveVerifier _verifier = new();

[Test]
public void AcceptsNamespacedGroupedDependenciesAndSymbols()
public async Task AcceptsNamespacedGroupedDependenciesAndSymbols()
{
string grouped = Archive("A.nupkg", Manifest("A", "1.2.3", """
<dependencies><group targetFramework="net11.0"><dependency id="B" version="[2.0.0,)" /></group><group targetFramework="net10.0"><dependency id="Other" version="1.0.0" /></group></dependencies>
Expand All @@ -21,129 +21,138 @@ public void AcceptsNamespacedGroupedDependenciesAndSymbols()
string symbolsB = Archive("B.snupkg", Manifest("B", "2.0.0"));
ExpectedPackage a = Expected("A", "1.2.3", new Dictionary<string, string> { ["B"] = "2.0.0" });

_verifier.Verify([grouped, ungrouped, symbolsA, symbolsB], [a, Expected("B", "2.0.0")], includeSymbols: true);
await _verifier.Verify([grouped, ungrouped, symbolsA, symbolsB], [a, Expected("B", "2.0.0")], includeSymbols: true);
}

[Test]
[Arguments("2.0.0")]
[Arguments("[2.0.0]")]
[Arguments("[2.0.0,3.0.0)")]
public void AcceptsUngroupedDependencyWithPlannedMinimum(string range)
public async Task AcceptsUngroupedDependencyWithPlannedMinimum(string range)
{
string archive = Archive("A.nupkg", Manifest("A", "1.2.3",
$"<dependencies><dependency id=\"B\" version=\"{range}\" /></dependencies>"));
_verifier.Verify([archive], [Expected("A", "1.2.3", new Dictionary<string, string> { ["B"] = "2.0.0" })], false);
await _verifier.Verify([archive], [Expected("A", "1.2.3", new Dictionary<string, string> { ["B"] = "2.0.0" })], false);
}

[Test]
public void RejectsMissingPackageArchive()
public async Task RejectsMissingPackageArchive()
{
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([], [Expected("A", "1.0.0")], false));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([], [Expected("A", "1.0.0")], false));
}

[Test]
public void RejectsDuplicatePackageArchive()
public async Task RejectsCancelledVerification()
{
using CancellationTokenSource cancellation = new();
await cancellation.CancelAsync();
await Assert.ThrowsAsync<OperationCanceledException>(() =>
_verifier.Verify([], [], false, cancellation.Token));
}

[Test]
public async Task RejectsDuplicatePackageArchive()
{
string a = Archive("A.nupkg", Manifest("A", "1.0.0"));
string duplicate = Archive("copy.nupkg", Manifest("A", "1.0.0"));
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([a, duplicate], [Expected("A", "1.0.0")], false));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([a, duplicate], [Expected("A", "1.0.0")], false));
}

[Test]
public void RejectsUnexpectedPackageArchive()
public async Task RejectsUnexpectedPackageArchive()
{
string a = Archive("A.nupkg", Manifest("A", "1.0.0"));
string unexpected = Archive("other.nupkg", Manifest("Other", "1.0.0"));
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([a, unexpected], [Expected("A", "1.0.0")], false));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([a, unexpected], [Expected("A", "1.0.0")], false));
}

[Test]
public void RejectsMissingSymbolArchive()
public async Task RejectsMissingSymbolArchive()
{
string a = Archive("A.nupkg", Manifest("A", "1.0.0"));
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([a], [Expected("A", "1.0.0")], true));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([a], [Expected("A", "1.0.0")], true));
}

[Test]
public void RejectsDuplicateSymbolArchive()
public async Task RejectsDuplicateSymbolArchive()
{
string a = Archive("A.nupkg", Manifest("A", "1.0.0"));
string symbols = Archive("A.snupkg", Manifest("A", "1.0.0"));
string duplicateSymbols = Archive("copy.snupkg", Manifest("A", "1.0.0"));
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([a, symbols, duplicateSymbols], [Expected("A", "1.0.0")], true));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([a, symbols, duplicateSymbols], [Expected("A", "1.0.0")], true));
}

[Test]
[Arguments(false, "B", "1.0.0")]
[Arguments(false, "A", "2.0.0")]
[Arguments(true, "B", "1.0.0")]
[Arguments(true, "A", "2.0.0")]
public void RejectsWrongIdentityOrVersion(bool isSymbol, string archiveId, string archiveVersion)
public async Task RejectsWrongIdentityOrVersion(bool isSymbol, string archiveId, string archiveVersion)
{
string archive = Archive(isSymbol ? "A.snupkg" : "A.nupkg", Manifest(archiveId, archiveVersion));
string[] archives = isSymbol ? [Archive("A.nupkg", Manifest("A", "1.0.0")), archive] : [archive];
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify(archives, [Expected("A", "1.0.0")], false));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify(archives, [Expected("A", "1.0.0")], false));
}

[Test]
public void RejectsCorruptArchive()
public async Task RejectsCorruptArchive()
{
string corrupt = Path.Combine(_directory, "corrupt.nupkg");
File.WriteAllText(corrupt, "not a zip");
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([corrupt], [Expected("A", "1.0.0")], false));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([corrupt], [Expected("A", "1.0.0")], false));
}

[Test]
public void RejectsArchiveWithoutManifest()
public async Task RejectsArchiveWithoutManifest()
{
string noManifest = Archive("empty.nupkg");
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([noManifest], [Expected("A", "1.0.0")], false));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([noManifest], [Expected("A", "1.0.0")], false));
}

[Test]
public void RejectsNestedManifest()
public async Task RejectsNestedManifest()
{
string nested = Archive("nested.nupkg", Manifest("A", "1.0.0"), "nested/A.nuspec");
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([nested], [Expected("A", "1.0.0")], false));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([nested], [Expected("A", "1.0.0")], false));
}

[Test]
public void RejectsMultipleRootManifests()
public async Task RejectsMultipleRootManifests()
{
string twoManifests = Archive("two.nupkg", Manifest("A", "1.0.0"), "A.nuspec", "B.nuspec");
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([twoManifests], [Expected("A", "1.0.0")], false));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([twoManifests], [Expected("A", "1.0.0")], false));
}

[Test]
public void RejectsMalformedManifest()
public async Task RejectsMalformedManifest()
{
string malformed = Archive("malformed.nupkg", "<package><metadata>");
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([malformed], [Expected("A", "1.0.0")], false));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([malformed], [Expected("A", "1.0.0")], false));
}

[Test]
public void RejectsManifestWithDtd()
public async Task RejectsManifestWithDtd()
{
string dtd = Archive("dtd.nupkg", "<!DOCTYPE package [<!ENTITY x 'A'>]><package><metadata><id>&x;</id><version>1.0.0</version></metadata></package>");
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([dtd], [Expected("A", "1.0.0")], false));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([dtd], [Expected("A", "1.0.0")], false));
}

[Test]
public void RejectsMissingDependency()
public async Task RejectsMissingDependency()
{
ExpectedPackage package = Expected("A", "1.0.0", new Dictionary<string, string> { ["B"] = "2.0.0" });
string missing = Archive("missing-dependency.nupkg", Manifest("A", "1.0.0"));
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([missing], [package], false));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([missing], [package], false));
}

[Test]
public void RejectsStaleDependencyOccurrenceInAnotherGroup()
public async Task RejectsStaleDependencyOccurrenceInAnotherGroup()
{
ExpectedPackage package = Expected("A", "1.0.0", new Dictionary<string, string> { ["B"] = "2.0.0" });
string stale = Archive("stale-dependency.nupkg", Manifest("A", "1.0.0", """
<dependencies><group targetFramework="net10.0"><dependency id="B" version="[2.0.0,)" /></group><group targetFramework="net11.0"><dependency id="B" version="[1.0.0,)" /></group></dependencies>
"""));
Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([stale], [package], false));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([stale], [package], false));
}

[Test]
Expand All @@ -152,12 +161,12 @@ public void RejectsStaleDependencyOccurrenceInAnotherGroup()
[Arguments("[3.0.0,)")]
[Arguments("not-a-range")]
[Arguments("2.*")]
public void RejectsInvalidDependencyRange(string range)
public async Task RejectsInvalidDependencyRange(string range)
{
string archive = Archive("A.nupkg", Manifest("A", "1.0.0", $"<dependencies><dependency id=\"B\" version=\"{range}\" /></dependencies>"));
ExpectedPackage package = Expected("A", "1.0.0", new Dictionary<string, string> { ["B"] = "2.0.0" });

Assert.Throws(typeof(InvalidOperationException), () => _verifier.Verify([archive], [package], false));
await Assert.ThrowsAsync<InvalidOperationException>(() => _verifier.Verify([archive], [package], false));
}

public void Dispose()
Expand Down
Loading