summaryrefslogtreecommitdiff
path: root/internal/engine/explain_test.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-16 01:34:45 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-16 01:34:45 +0200
commitecfaeabf2a92e26c6a521d5fac404a0fff6263b6 (patch)
tree4147dd6809a45fe5b773447352354dbee2295900 /internal/engine/explain_test.go
parentbe4b1275c76b9cad984dfafaa8023db94eb3eecf (diff)
downloadkrino-ecfaeabf2a92e26c6a521d5fac404a0fff6263b6.tar.gz
krino-ecfaeabf2a92e26c6a521d5fac404a0fff6263b6.zip
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
Diffstat (limited to 'internal/engine/explain_test.go')
-rw-r--r--internal/engine/explain_test.go83
1 files changed, 79 insertions, 4 deletions
diff --git a/internal/engine/explain_test.go b/internal/engine/explain_test.go
index 4d304f9..83011c5 100644
--- a/internal/engine/explain_test.go
+++ b/internal/engine/explain_test.go
@@ -4,10 +4,12 @@ package engine
import (
"context"
+ "os"
"path/filepath"
"reflect"
"strings"
"testing"
+ "time"
"krino/internal/plan"
)
@@ -26,7 +28,7 @@ func TestExplainReportsCapturesAndChain(t *testing.T) {
if len(errs) > 0 {
t.Fatal(errs)
}
- x, err := e.Explain(context.Background(), filepath.Join(dl, "Screenshot_2026-09-01.png"))
+ x, err := e.ExplainWithChain(context.Background(), filepath.Join(dl, "Screenshot_2026-09-01.png"))
if err != nil {
t.Fatal(err)
}
@@ -38,8 +40,9 @@ func TestExplainReportsCapturesAndChain(t *testing.T) {
}
}
-// TestExplainChainMatchesThePlan: the chain explain reports is the one the
-// directory's plan gives the same file, conflicts and skips included.
+// TestExplainChainMatchesThePlan: for a file planned on its own, the chain
+// is the one the directory's plan gives it, conflicts with files already on
+// disk included.
func TestExplainChainMatchesThePlan(t *testing.T) {
h, dl := excludeTree(t, map[string]string{"a.pdf": "one", "Out/a.pdf": "taken"})
main := writeConfig(t, h, `(include "dl")`, map[string]string{"dl": `
@@ -55,7 +58,7 @@ func TestExplainChainMatchesThePlan(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- x, err := e.Explain(context.Background(), filepath.Join(dl, "a.pdf"))
+ x, err := e.ExplainWithChain(context.Background(), filepath.Join(dl, "a.pdf"))
if err != nil {
t.Fatal(err)
}
@@ -63,3 +66,75 @@ func TestExplainChainMatchesThePlan(t *testing.T) {
t.Errorf("explain chain %+v\nplan chain %+v", x.Chain, dp.Chains[0].Steps)
}
}
+
+// TestExplainChainOnlyWhenAsked: the command line's explain does not build
+// the chain (it can hash files to resolve a conflict), and a file no rule
+// acts on has none at all (plan 13 review F1, F2).
+func TestExplainChainOnlyWhenAsked(t *testing.T) {
+ h, dl := excludeTree(t, map[string]string{"a.pdf": "one", "keep-b.pdf": "two", "new.pdf": "three"})
+ now := time.Now()
+ os.Chtimes(filepath.Join(dl, "new.pdf"), now, now)
+ main := writeConfig(t, h, `(include "dl")`, map[string]string{"dl": `
+(path "~/dl")
+(exclude (name "^keep-"))
+(rule "all" (move "Out"))
+`})
+ e, errs := Load(main)
+ if len(errs) > 0 {
+ t.Fatal(errs)
+ }
+ x, err := e.Explain(context.Background(), filepath.Join(dl, "a.pdf"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if x.Chain != nil {
+ t.Errorf("Explain built a chain: %+v", x.Chain)
+ }
+ if x, err = e.ExplainWithChain(context.Background(), filepath.Join(dl, "a.pdf")); err != nil || len(x.Chain) != 1 {
+ t.Fatalf("ExplainWithChain: %+v, %v", x.Chain, err)
+ }
+ for _, rel := range []string{"keep-b.pdf", "new.pdf"} {
+ x, err := e.ExplainWithChain(context.Background(), filepath.Join(dl, rel))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if x.Chain != nil {
+ t.Errorf("%s (%s%s) has a chain: %+v", rel, x.Skip, x.Excluded, x.Chain)
+ }
+ }
+}
+
+// TestExplainChainIsThisFileAlone: with two files competing for one name,
+// the chain shows what this file alone would do; the plan is where the two
+// are resolved against each other (plan 13 review F2).
+func TestExplainChainIsThisFileAlone(t *testing.T) {
+ h, dl := excludeTree(t, map[string]string{"a.pdf": "one", "b.pdf": "two"})
+ main := writeConfig(t, h, `(include "dl")`, map[string]string{"dl": `
+(path "~/dl")
+(rule "r" (rename "same.pdf"))
+`})
+ 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)
+ }
+ var planned string
+ for _, c := range dp.Chains {
+ if c.File.Rel == "b.pdf" {
+ planned = filepath.Base(c.Steps[0].Dst)
+ }
+ }
+ if planned != "same_1.pdf" {
+ t.Fatalf("the plan gave b.pdf %q; expected the suffix", planned)
+ }
+ x, err := e.ExplainWithChain(context.Background(), filepath.Join(dl, "b.pdf"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got := filepath.Base(x.Chain[0].Dst); got != "same.pdf" {
+ t.Errorf("chain for b.pdf alone = %q, want same.pdf (the plan resolves the clash with a.pdf)", got)
+ }
+}