// SPDX-License-Identifier: GPL-3.0-or-later package plan import ( "fmt" "testing" "time" "krino/internal/config" ) // TestBuildRefusesDotNames: a rename whose placeholders produce "", "." or // ".." names a directory, not a file; the step is skipped with a reason // instead of renaming a file onto its own folder or the one above. func TestBuildRefusesDotNames(t *testing.T) { for _, capture := range []string{"", ".", ".."} { in := []Input{{ File: file("/r", "x.pdf"), Rules: []RuleMatch{{Name: "a", Captures: []string{"x.pdf", capture}, Actions: []config.Action{act(config.Rename, "{1}")}}}, }} c := Build("/r", in, time.Now(), NoDisk{}, NewClaims())[0] if want := fmt.Sprintf("rename produced the name %q", capture); c.Steps[0].Skip != want { t.Errorf("capture %q: Skip = %q, want %q", capture, c.Steps[0].Skip, want) } } } // TestBuildRefusesDotDotFromPlaceholder: a file name can capture "..", // though never "/", so a destination whose placeholders add a ".." segment // would leave the directory the rule names: that step is skipped. A ".." // the rule wrote itself is the rule's own business, and "..x" is a name, // not a segment. func TestBuildRefusesDotDotFromPlaceholder(t *testing.T) { step := func(dest string) Step { in := []Input{{ File: file("/r", "x.pdf"), Rules: []RuleMatch{{Name: "a", Captures: []string{"x.pdf", ".."}, Actions: []config.Action{act(config.Move, dest)}}}, }} return Build("/r", in, time.Now(), NoDisk{}, NewClaims())[0].Steps[0] } if s := step("/w/{1}/in"); s.Skip != `destination "/w/../in" leaves its directory through a placeholder` { t.Errorf("placeholder ..: Skip = %q, Dst = %q", s.Skip, s.Dst) } if s := step("/w/../in"); s.Skip != "" || s.Dst != "/in/x.pdf" { t.Errorf("written ..: Skip = %q, Dst = %q", s.Skip, s.Dst) } if s := step("/w/{1}x"); s.Skip != "" || s.Dst != "/w/..x/x.pdf" { t.Errorf("..x: Skip = %q, Dst = %q", s.Skip, s.Dst) } }