aboutsummaryrefslogtreecommitdiff
path: root/internal/engine/engine_test.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-14 10:56:21 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-14 10:56:21 +0200
commit1506c7dcd6032c04c1df6f5785b6dd3dfe4511cc (patch)
treefb6d573ca8726c1b1d91ba273a284ba666cde3f6 /internal/engine/engine_test.go
parentebdd7bb254a0f19f815a644d26ce232de7be0adb (diff)
downloadkrino-1506c7dcd6032c04c1df6f5785b6dd3dfe4511cc.tar.gz
krino-1506c7dcd6032c04c1df6f5785b6dd3dfe4511cc.zip
krino: duplicates are found, never deleted
A rule combining (duplicate) with a delete action is refused at load, and a file that is a duplicate under any duplicate scope its directory uses gets no delete step from any rule: the plan shows it skipped and the chain continues. A failed duplicate check blocks the delete too. Tests cover (matched), another rule's own condition, a test evaluation skipped, two scopes and a failed check, each checking every copy is still on disk.
Diffstat (limited to 'internal/engine/engine_test.go')
-rw-r--r--internal/engine/engine_test.go44
1 files changed, 44 insertions, 0 deletions
diff --git a/internal/engine/engine_test.go b/internal/engine/engine_test.go
index 8dcfcb3..18b35e7 100644
--- a/internal/engine/engine_test.go
+++ b/internal/engine/engine_test.go
@@ -213,3 +213,47 @@ func TestContentVariantsComputedAtLoad(t *testing.T) {
t.Fatalf("ContentVariants = %+v, want %+v", got, want)
}
}
+
+// TestLoadRefusesDuplicateWithDelete is spec §4.5: a (duplicate) test
+// anywhere in a rule's condition, and a delete action in the same rule, is
+// a load error, however the test is nested.
+func TestLoadRefusesDuplicateWithDelete(t *testing.T) {
+ tests := []struct{ rule, want string }{
+ {`(rule "a" (when (duplicate)) (delete))`,
+ `rule "a": (duplicate) cannot be combined with (delete): duplicates are never deleted, move them aside instead`},
+ {`(rule "a" (when (duplicate "Archive")) (delete permanent))`,
+ `rule "a": (duplicate) cannot be combined with (delete permanent)`},
+ {`(rule "a" (when (or (type pdf) (duplicate))) (move "Keep") (delete))`,
+ `rule "a": (duplicate) cannot be combined with (delete)`},
+ {`(rule "a" (when (not (duplicate))) (delete))`,
+ `rule "a": (duplicate) cannot be combined with (delete)`},
+ }
+ for _, tt := range tests {
+ h := sandbox(t)
+ main := writeConfig(t, h, `(include "dl")`, map[string]string{"dl": `(path "/tmp")
+` + tt.rule})
+ _, errs := Load(main, "dl")
+ joined := ""
+ for _, d := range errs {
+ joined += d.Error() + "\n"
+ }
+ if len(errs) != 1 || !strings.Contains(joined, tt.want) {
+ t.Errorf("rule %s: errs %v, want %q", tt.rule, errs, tt.want)
+ }
+ }
+}
+
+// TestLoadAcceptsDuplicateWithMove: the way §5.5 recommends dealing with
+// duplicates loads clean, and so does a delete rule with no duplicate test.
+func TestLoadAcceptsDuplicateWithMove(t *testing.T) {
+ for _, rule := range []string{
+ `(rule "dupes" (when (duplicate "Archive")) (move "~/.dupes/") (stop))`,
+ `(rule "old" (when (type iso) (age > 90d)) (delete))`,
+ } {
+ h := sandbox(t)
+ main := writeConfig(t, h, `(include "dl")`, map[string]string{"dl": "(path \"/tmp\")\n" + rule})
+ if _, errs := Load(main, "dl"); len(errs) != 0 {
+ t.Errorf("rule %s: errs %v, want none", rule, errs)
+ }
+ }
+}