summaryrefslogtreecommitdiff
path: root/internal/engine/undo_identity_test.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-14 21:33:39 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-14 21:33:39 +0200
commit93cf0729ec7cf3129de0808cbcc3403b86d86fff (patch)
treed04bf6825019b68a6e48f4b9e6a5dbdfa0e56c64 /internal/engine/undo_identity_test.go
parent643eac4a2cba7b34b8a33af1b3dbdc54d9816a53 (diff)
downloadkrino-93cf0729ec7cf3129de0808cbcc3403b86d86fff.tar.gz
krino-93cf0729ec7cf3129de0808cbcc3403b86d86fff.zip
plan 9: an interrupted or failed undo can be finished
Diffstat (limited to 'internal/engine/undo_identity_test.go')
-rw-r--r--internal/engine/undo_identity_test.go51
1 files changed, 51 insertions, 0 deletions
diff --git a/internal/engine/undo_identity_test.go b/internal/engine/undo_identity_test.go
index 9c93b3b..d4169be 100644
--- a/internal/engine/undo_identity_test.go
+++ b/internal/engine/undo_identity_test.go
@@ -223,3 +223,54 @@ func TestUndoLeavesNoDirectoriesBehind(t *testing.T) {
t.Errorf("dl/Out is left behind: %v", err)
}
}
+
+// TestUndoCanBeFinishedAfterAFailure: an undo whose last reversal failed
+// (something took the original name) leaves the file part way back; once
+// the obstacle is gone, undoing the same run again offers only the step
+// that is left, and finishes it (review M10).
+func TestUndoCanBeFinishedAfterAFailure(t *testing.T) {
+ e, run, h, logPath := appliedRun(t, map[string]map[string]string{"dl": {"a.pdf": "one"}},
+ map[string]string{"dl": "(path \"~/dl\")\n(rule \"r\" (rename \"r-{name}\") (move \"Out\"))\n"})
+ up, err := e.PlanUndo(run)
+ if err != nil {
+ t.Fatal(err)
+ }
+ blocker := filepath.Join(h, "dl", "a.pdf")
+ if err := os.WriteFile(blocker, []byte("in the way"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ undo := func(up *UndoPlan) *ApplyResult {
+ t.Helper()
+ j, err := journal.Open(logPath)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer j.Close()
+ res, err := e.ApplyUndo(context.Background(), up, j, journal.NewRunID(time.Now()))
+ if err != nil {
+ t.Fatal(err)
+ }
+ return res
+ }
+ if res := undo(up); res.Failed != 1 {
+ t.Fatalf("first undo: Failed = %d, want 1 (the rename back is blocked)", res.Failed)
+ }
+ if err := os.Remove(blocker); err != nil {
+ t.Fatal(err)
+ }
+ time.Sleep(10 * time.Millisecond) // a new run id
+ again, err := e.PlanUndo(run)
+ if err != nil {
+ t.Fatal(err)
+ }
+ f := undoFileNamed(t, again, "dl", "a.pdf")
+ if f.Refused != "" || len(f.Steps) != 1 || f.Steps[0].Action != "undo-rename" {
+ t.Fatalf("second plan: %+v; want one undo-rename step", f)
+ }
+ if res := undo(again); res.Failed != 0 || res.Applied != 1 {
+ t.Fatalf("second undo: %+v", res)
+ }
+ if b, err := os.ReadFile(filepath.Join(h, "dl", "a.pdf")); err != nil || string(b) != "one" {
+ t.Errorf("dl/a.pdf = %q, %v; want the original back", b, err)
+ }
+}