diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-12 12:58:14 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-12 12:58:14 +0200 |
| commit | 24a84671ace373ae331fa83a1ff484990f4dff0e (patch) | |
| tree | a6b6e3949d7dd241f1d13e079dfb982d758c89a2 /internal/plan/placeholder_test.go | |
| parent | 3b36a48b7ce5a53a9366f3b31f94311f178e2553 (diff) | |
| download | krino-24a84671ace373ae331fa83a1ff484990f4dff0e.tar.gz krino-24a84671ace373ae331fa83a1ff484990f4dff0e.zip | |
krino: planning — chains, placeholders, conflicts, JSON
Diffstat (limited to 'internal/plan/placeholder_test.go')
| -rw-r--r-- | internal/plan/placeholder_test.go | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/internal/plan/placeholder_test.go b/internal/plan/placeholder_test.go new file mode 100644 index 0000000..e155430 --- /dev/null +++ b/internal/plan/placeholder_test.go @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package plan + +import ( + "strings" + "testing" + "time" +) + +func facts() Facts { + return Facts{ + Name: "Invoice_2026-08.pdf", + Captures: []string{"2026-08", "2026", "a}}b"}, + ModTime: time.Date(2026, 8, 15, 14, 30, 45, 0, time.UTC), + Now: time.Date(2026, 9, 12, 9, 0, 0, 0, time.UTC), + } +} + +func TestExpand(t *testing.T) { + tests := []struct{ in, want string }{ + {"Work/Acme", "Work/Acme"}, + {"{name}", "Invoice_2026-08.pdf"}, + {"{stem}", "Invoice_2026-08"}, + {"{ext}", ".pdf"}, + {"{stem}{ext}", "Invoice_2026-08.pdf"}, + {"Work/{mtime:%Y}", "Work/2026"}, + {"{mtime:%Y-%m-%d}", "2026-08-15"}, + {"{mtime:%H%M%S}", "143045"}, + {"{mtime:%j}", "227"}, + {"{now:%Y-%m-%d}", "2026-09-12"}, + {"{1}", "2026"}, + // D2: an expanded value that itself contains "}}" must reach the + // output unchanged. Expand's single-pass scanner jumps past a + // placeholder's closing brace, so written bytes are never re-scanned; + // a refactor to scan-then-replace would silently re-collapse them. + {"{2}", "a}}b"}, + {"{{literal}}", "{literal}"}, + {"100{{%}}", "100{%}"}, + {"{mtime:%Y%%}", "2026%"}, + {"Photos/{mtime:%Y}/{stem}-{1}{ext}", "Photos/2026/Invoice_2026-08-2026.pdf"}, + } + for _, tt := range tests { + got, err := Expand(tt.in, facts()) + if err != nil || got != tt.want { + t.Errorf("Expand(%q) = %q, %v; want %q", tt.in, got, err, tt.want) + } + } +} + +func TestExpandNoExtension(t *testing.T) { + f := facts() + f.Name = "README" + for in, want := range map[string]string{"{stem}": "README", "{ext}": ""} { + if got, err := Expand(in, f); err != nil || got != want { + t.Errorf("Expand(%q) on README = %q, %v; want %q", in, got, err, want) + } + } + f.Name = ".bashrc" + if got, _ := Expand("{stem}", f); got != ".bashrc" { + t.Errorf("dotfile stem = %q, want .bashrc", got) + } + if got, _ := Expand("{ext}", f); got != "" { + t.Errorf("dotfile ext = %q, want empty", got) + } + f.Name = "archive.tar.gz" + if got, _ := Expand("{stem}|{ext}", f); got != "archive.tar|.gz" { + t.Errorf("double extension = %q, want archive.tar|.gz", got) + } +} + +func TestExpandErrors(t *testing.T) { + tests := []struct{ in, want string }{ + {"{7}", "no capture group 7"}, + {"{0}", "capture groups are numbered from 1"}, + {"{whatever}", "unknown placeholder {whatever}"}, + {"{name", "unclosed placeholder"}, + {"{mtime:%Q}", "unknown time format %Q in {mtime:...}"}, + {"{mtime}", "unknown placeholder {mtime}"}, + // B3: the error must name the placeholder actually written ("now"), + // not hardcode "mtime" - the {mtime:%Q} case above passes either + // way, which is why that defect survived. + {"{now:%Q}", "unknown time format %Q in {now:...}"}, + // D3: {1}...{9} is the syntax (spec §7.3); {10} and up must be + // rejected the same way an unknown placeholder is. + {"{10}", "unknown placeholder {10}"}, + } + for _, tt := range tests { + _, err := Expand(tt.in, facts()) + if err == nil || !strings.Contains(err.Error(), tt.want) { + t.Errorf("Expand(%q) error = %v; want %q", tt.in, err, tt.want) + } + } +} |
