From ecfaeabf2a92e26c6a521d5fac404a0fff6263b6 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 16 Sep 2026 01:34:45 +0200 Subject: milestone 1 review: claims span the run, explain's chain is opt-in and its own, overrides keyed by clean path, splice and enum guards --- internal/engine/session_test.go | 90 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) (limited to 'internal/engine/session_test.go') diff --git a/internal/engine/session_test.go b/internal/engine/session_test.go index 8fd4593..98ea1ee 100644 --- a/internal/engine/session_test.go +++ b/internal/engine/session_test.go @@ -180,3 +180,93 @@ func TestSessionLockDirsReleasesOnFailure(t *testing.T) { l.Release() } } + +// TestSessionKeepsEveryAppliedDestinationClaimed: what a directory's files +// ended up at stays protected for the whole run, even across a directory +// that applies nothing (plan 13 review F3): a later (on-conflict overwrite) +// takes a free name instead of trashing an earlier directory's result. +func TestSessionKeepsEveryAppliedDestinationClaimed(t *testing.T) { + h := sandbox(t) + old := time.Now().Add(-2 * time.Hour) + dirs := map[string]string{ + "alpha": "(path \"~/alpha\")\n(rule \"out\" (move \"~/shared\"))\n", + "beta": "(path \"~/beta\")\n(rule \"none\" (when (type zzz)) (move \"~/shared\"))\n", + "gamma": "(path \"~/gamma\")\n(on-conflict overwrite)\n(rule \"out\" (move \"~/shared\"))\n", + } + for _, n := range []string{"alpha", "beta", "gamma"} { + p := filepath.Join(h, n, "x.pdf") + os.MkdirAll(filepath.Dir(p), 0o755) + os.WriteFile(p, []byte("from "+n), 0o644) + os.Chtimes(p, old, old) + } + main := writeConfig(t, h, `(include "alpha" "beta" "gamma")`, dirs) + e, errs := Load(main) + if len(errs) > 0 { + t.Fatal(errs) + } + s, err := e.NewSession(false) + if err != nil { + t.Fatal(err) + } + defer s.Close() + for _, d := range e.Dirs { + dp, err := s.Plan(context.Background(), d) + if err != nil { + t.Fatal(err) + } + if _, err := s.Apply(context.Background(), dp, map[string]bool{"x.pdf": true}); err != nil { + t.Fatal(err) + } + s.FinishDirectory() + } + for rel, want := range map[string]string{"shared/x.pdf": "from alpha", "shared/x_1.pdf": "from gamma"} { + if b, err := os.ReadFile(filepath.Join(h, rel)); err != nil || string(b) != want { + t.Errorf("%s: %q, %v; want %q", rel, b, err, want) + } + } + if entries, _ := os.ReadDir(filepath.Join(h, ".local", "share", "Trash", "files")); len(entries) != 0 { + t.Errorf("the Trash holds %d entries; an earlier directory's result was displaced", len(entries)) + } +} + +// TestSessionDropsUnappliedClaims: a destination a directory planned but did +// not apply (declined) is free for the next directory. +func TestSessionDropsUnappliedClaims(t *testing.T) { + h := sandbox(t) + old := time.Now().Add(-2 * time.Hour) + dirs := map[string]string{ + "alpha": "(path \"~/alpha\")\n(rule \"out\" (move \"~/shared\"))\n", + "beta": "(path \"~/beta\")\n(rule \"out\" (move \"~/shared\"))\n", + } + for _, n := range []string{"alpha", "beta"} { + p := filepath.Join(h, n, "x.pdf") + os.MkdirAll(filepath.Dir(p), 0o755) + os.WriteFile(p, []byte("from "+n), 0o644) + os.Chtimes(p, old, old) + } + main := writeConfig(t, h, `(include "alpha" "beta")`, dirs) + e, errs := Load(main) + if len(errs) > 0 { + t.Fatal(errs) + } + s, err := e.NewSession(false) + if err != nil { + t.Fatal(err) + } + defer s.Close() + dp, err := s.Plan(context.Background(), e.Dirs[0]) + if err != nil { + t.Fatal(err) + } + if _, err := s.Apply(context.Background(), dp, map[string]bool{}); err != nil { // declined + t.Fatal(err) + } + s.FinishDirectory() + dp, err = s.Plan(context.Background(), e.Dirs[1]) + if err != nil { + t.Fatal(err) + } + if got := dp.Chains[0].Steps[0].Dst; filepath.Base(got) != "x.pdf" { + t.Errorf("beta planned %q; the declined destination should be free", got) + } +} -- cgit v1.3