diff --git a/OpenNest.Data/SetReleaseText.cs b/OpenNest.Data/SetReleaseText.cs
index d95e4ccd..77232025 100644
--- a/OpenNest.Data/SetReleaseText.cs
+++ b/OpenNest.Data/SetReleaseText.cs
@@ -75,6 +75,25 @@ public static class SetReleaseText
+ "\n\nUndo the rest and leave these as they are, or restore them from their history.";
}
+ ///
+ /// What the release did to one of its nests, and what undoing it did: "Created",
+ /// "Merged (revision 3 → 4)", "Created; undo deleted it".
+ ///
+ public static string Change(SetReleaseNest nest)
+ {
+ ArgumentNullException.ThrowIfNull(nest);
+ var text = nest.Kind == SetReleaseNestKind.Merged
+ ? $"Merged (revision {nest.RevisionBefore} → {nest.RevisionAfter})"
+ : "Created";
+ return nest.Undo switch
+ {
+ SetReleaseNestUndo.Deleted => text + "; undo deleted it",
+ SetReleaseNestUndo.Restored => text + $"; undo restored revision {nest.RevisionBefore}",
+ SetReleaseNestUndo.Skipped => text + "; undo left it as it was",
+ _ => text,
+ };
+ }
+
/// The state of one nest of a release: its status name, or "Deleted".
public static string NestState(SetReleaseNest nest)
{
diff --git a/OpenNest.Tests/Data/InMemoryDrawingSets.cs b/OpenNest.Tests/Data/InMemoryDrawingSets.cs
index fa3e8097..36cfb19e 100644
--- a/OpenNest.Tests/Data/InMemoryDrawingSets.cs
+++ b/OpenNest.Tests/Data/InMemoryDrawingSets.cs
@@ -165,8 +165,19 @@ internal sealed class InMemoryDrawingSets : IDrawingSetStore
});
}
- public Task UndoReleaseAsync(Guid id, SetReleaseUndoRequest request, CancellationToken cancellationToken = default) =>
- throw new NotSupportedException("The in-memory store does not hold nests, so it cannot undo a release.");
+ /// Every undo request, in order.
+ public List<(Guid Id, SetReleaseUndoRequest Request)> UndoRequests { get; } = new();
+
+ /// Answers an undo; this store holds no nests, so a test decides what the server says.
+ public Func? UndoHandler { get; set; }
+
+ public Task UndoReleaseAsync(Guid id, SetReleaseUndoRequest request, CancellationToken cancellationToken = default)
+ {
+ UndoRequests.Add((id, new SetReleaseUndoRequest { UndoneBy = request.UndoneBy, SkipNests = request.SkipNests.ToList() }));
+ return UndoHandler is { } handler
+ ? Task.FromResult(handler(id, request))
+ : throw new NotSupportedException("The in-memory store does not hold nests, so it cannot undo a release.");
+ }
private Task SetArchived(Guid id, int expectedRevision, bool archived)
{
diff --git a/OpenNest.Tests/Data/SetReleaseTextTests.cs b/OpenNest.Tests/Data/SetReleaseTextTests.cs
index cc0bf4d4..25c4f5ba 100644
--- a/OpenNest.Tests/Data/SetReleaseTextTests.cs
+++ b/OpenNest.Tests/Data/SetReleaseTextTests.cs
@@ -53,6 +53,25 @@ public class SetReleaseTextTests
Assert.Equal("Released from set 'Synthetic kit' revision 3 ×2 by Synthetic operator", text);
}
+ [Theory]
+ [InlineData(SetReleaseNestKind.Created, null, "Created")]
+ [InlineData(SetReleaseNestKind.Created, SetReleaseNestUndo.Deleted, "Created; undo deleted it")]
+ [InlineData(SetReleaseNestKind.Merged, null, "Merged (revision 3 → 4)")]
+ [InlineData(SetReleaseNestKind.Merged, SetReleaseNestUndo.Restored, "Merged (revision 3 → 4); undo restored revision 3")]
+ [InlineData(SetReleaseNestKind.Merged, SetReleaseNestUndo.Skipped, "Merged (revision 3 → 4); undo left it as it was")]
+ public void Change_SaysWhatTheReleaseAndItsUndoDid(SetReleaseNestKind kind, SetReleaseNestUndo? undo, string expected)
+ {
+ var nest = new SetReleaseNest
+ {
+ Kind = kind,
+ RevisionBefore = kind == SetReleaseNestKind.Merged ? 3 : null,
+ RevisionAfter = kind == SetReleaseNestKind.Merged ? 4 : 1,
+ Undo = undo,
+ };
+
+ Assert.Equal(expected, SetReleaseText.Change(nest));
+ }
+
[Fact]
public void UndoRefusal_NamesEachChangedNest_OrSaysItWasUndoneBefore()
{
diff --git a/OpenNest.WinForms.Tests/Forms/SetReleaseFormsTests.cs b/OpenNest.WinForms.Tests/Forms/SetReleaseFormsTests.cs
index 1c4e4650..bab6d707 100644
--- a/OpenNest.WinForms.Tests/Forms/SetReleaseFormsTests.cs
+++ b/OpenNest.WinForms.Tests/Forms/SetReleaseFormsTests.cs
@@ -43,13 +43,13 @@ public class SetReleaseFormsTests
Assert.Equal(2, grid.Rows.Count);
Assert.True(skipped.Visible);
Assert.Contains("'SYN-OLD'", skipped.Text);
- Field(form, "multiplierBox").Value = 3;
+ Field(form, "setGrid").Rows[0].Cells["kitsColumn"].Value = "3";
Assert.Equal(new object[] { 12, 9 }, grid.Rows.Cast().Select(r => r.Cells["partsColumn"].Value));
Field(form, "notesBox").Text = "Synthetic run";
Run(form.ReleaseAsync());
- Assert.Contains("Create 2 Draft nests from set 'Synthetic kit' ×3", Assert.Single(questions));
+ Assert.Contains("Release set 'Synthetic kit' ×3 into 2 Draft nests", Assert.Single(questions));
Assert.Equal(2, nests.Uploaded.Count);
Assert.Equal(2, opened.Count);
Assert.All(opened, o => Assert.Equal(NestStatus.Draft, o.Nest.Status));
@@ -64,7 +64,8 @@ public class SetReleaseFormsTests
Assert.StartsWith("Released: 2 nests", Field