aboutsummaryrefslogtreecommitdiff
path: root/internal/engine/nodelete_test.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-17 13:59:52 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-17 13:59:52 +0200
commitb44222fc2b061382dc601014cde287cc41b857ca (patch)
tree3181b6d3336bb6766803a9fc0ab76e09198aa253 /internal/engine/nodelete_test.go
parent9ab6686b98499c745024a474a71e3d99b6e14973 (diff)
downloadkrino-b44222fc2b061382dc601014cde287cc41b857ca.tar.gz
krino-b44222fc2b061382dc601014cde287cc41b857ca.zip
a duplicate test in an exclude protects like one in a rule
The scopes that drive "no rule deletes a file krino found to be a duplicate" were collected from rules only. A directory whose duplicate tests lived in (exclude ...) forms had no scopes at all, so the protection never engaged: krino explain said "yes duplicate" and the next rule permanently deleted every copy. The README's promise was false in that shape, and the spec's wording permitted it. What matters is what krino knows, not which form taught it. The spec and krino.conf(5) now say so too.
Diffstat (limited to 'internal/engine/nodelete_test.go')
-rw-r--r--internal/engine/nodelete_test.go34
1 files changed, 34 insertions, 0 deletions
diff --git a/internal/engine/nodelete_test.go b/internal/engine/nodelete_test.go
index af7597c..6b8ffa9 100644
--- a/internal/engine/nodelete_test.go
+++ b/internal/engine/nodelete_test.go
@@ -210,3 +210,37 @@ func TestNoDeleteWhenTheDuplicateCheckFails(t *testing.T) {
t.Errorf("a.pdf was not deleted: %v", err)
}
}
+
+// TestDuplicateInAnExcludeStillProtects: the promise is that no rule may
+// delete a file krino has found to be a duplicate under any scope its
+// directory uses (README, "Safety"). The scopes were collected from rules
+// only, so a directory whose duplicate tests live in (exclude ...) forms
+// had no scopes at all and the protection never engaged - a later rule
+// permanently deleted every copy.
+func TestDuplicateInAnExcludeStillProtects(t *testing.T) {
+ home, _ := dlTree(t, map[string]int{"a.pdf": 1, "b.pdf": 2, "c.pdf": 3}, sameBytes)
+ conf := "(path \"~/dl\")\n(recursive yes)\n(min-age 0s)\n" +
+ "(exclude (not (duplicate)))\n" +
+ "(rule \"purge\" (when (type pdf)) (delete permanent))\n"
+ main := writeConfig(t, home, `(include "dl")`, map[string]string{"dl": conf})
+ e, errs := Load(main)
+ if len(errs) > 0 {
+ t.Fatal(errs)
+ }
+ dp, err := e.Plan(context.Background(), e.Dirs[0], plan.NewClaims())
+ if err != nil {
+ t.Fatal(err)
+ }
+ planned := 0
+ for _, c := range dp.Chains {
+ for _, st := range c.Steps {
+ if st.Kind == plan.DeletePermanent && st.Skip == "" {
+ planned++
+ t.Errorf("%s: a duplicate is planned for permanent deletion", c.File.Rel)
+ }
+ }
+ }
+ if planned == 0 && len(dp.Chains) == 0 {
+ t.Skip("no chains were planned at all; the fixture does not exercise the rule")
+ }
+}