From 67ada0b5bc25cb6cff3ab780d82cb0bfe64e4968 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Mon, 14 Sep 2026 23:58:43 +0200 Subject: tests: apply error exits 1 and w stops krino through the real command; undo projection occupied half --- internal/engine/undo_identity_test.go | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'internal') diff --git a/internal/engine/undo_identity_test.go b/internal/engine/undo_identity_test.go index f42c5da..01f57d8 100644 --- a/internal/engine/undo_identity_test.go +++ b/internal/engine/undo_identity_test.go @@ -572,3 +572,56 @@ func TestUndoRunWithADamagedLineIsStillAnUndo(t *testing.T) { t.Errorf("PlanUndo(undo run with a damaged line) = %v; want refused as an undo", err) } } + +// TestUndoOverwriteThenMove: a move that replaced an existing file and was +// then moved on is undone whole - the moved file goes back, and the file it +// replaced is restored to the path the later reversal vacates (triage 34m). +func TestUndoOverwriteThenMove(t *testing.T) { + e, run, h, logPath := appliedRun(t, map[string]map[string]string{"dl": {"a.pdf": "one", "Out/a.pdf": "old"}}, + map[string]string{"dl": "(path \"~/dl\")\n(ignore \"Out/\")\n(recursive yes)\n(rule \"r\" (on-conflict overwrite) (move \"Out\") (move \"Out2\"))\n"}) + if b, _ := os.ReadFile(filepath.Join(h, "dl", "Out2", "a.pdf")); string(b) != "one" { + t.Fatalf("setup: the chain did not run as planned") + } + up, err := e.PlanUndo(run) + if err != nil { + t.Fatal(err) + } + for _, f := range up.Files { + if f.Refused != "" { + t.Fatalf("%s refused: %s (%+v)", f.File, f.Refused, f.Steps) + } + } + j, err := journal.Open(logPath) + if err != nil { + t.Fatal(err) + } + defer j.Close() + if _, err := e.ApplyUndo(context.Background(), up, j, journal.NewRunID(time.Now())); err != nil { + t.Fatal(err) + } + for rel, want := range map[string]string{"a.pdf": "one", "Out/a.pdf": "old"} { + if b, err := os.ReadFile(filepath.Join(h, "dl", rel)); err != nil || string(b) != want { + t.Errorf("%s after undo: %q, %v; want %q", rel, b, err, want) + } + } +} + +// TestProjectionSeesAPathAnEarlierStepWillFill: a path not on disk yet that +// a queued reversal will put a file at is occupied for the steps after it, +// and free again once a later one moves that file on (triage 34m: the +// "occupied" half of the projection had no test that could fail). +func TestProjectionSeesAPathAnEarlierStepWillFill(t *testing.T) { + x := filepath.Join(t.TempDir(), "x.pdf") + p := newUndoProjection() + if p.occupiedNow(x) { + t.Fatal("an empty path reads as occupied") + } + p.record(UndoStep{Action: "undo-move", Src: x + ".elsewhere", Dst: x}) + if !p.occupiedNow(x) { + t.Error("a path a queued undo-move fills reads as free") + } + p.record(UndoStep{Action: "undo-rename", Src: x, Dst: x + ".back"}) + if p.occupiedNow(x) { + t.Error("a path a later reversal vacates still reads as occupied") + } +} -- cgit v1.3