aboutsummaryrefslogtreecommitdiff
path: root/internal/config/settings_test.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-11 14:47:10 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-11 15:01:57 +0200
commit42b02c47be9b285099203e44a2570636d4ca6f03 (patch)
tree82bcb9e19bd886f36e1ca7b2d94a204c988f1fd1 /internal/config/settings_test.go
downloadkrino-42b02c47be9b285099203e44a2570636d4ca6f03.tar.gz
krino-42b02c47be9b285099203e44a2570636d4ca6f03.zip
krino: foundation — sexp reader, config language, init/new/check
Diffstat (limited to 'internal/config/settings_test.go')
-rw-r--r--internal/config/settings_test.go85
1 files changed, 85 insertions, 0 deletions
diff --git a/internal/config/settings_test.go b/internal/config/settings_test.go
new file mode 100644
index 0000000..e46c880
--- /dev/null
+++ b/internal/config/settings_test.go
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package config
+
+import (
+ "reflect"
+ "testing"
+ "time"
+
+ "krino/internal/sexp"
+)
+
+func parseSettings(t *testing.T, src string) (Settings, []*Diag) {
+ t.Helper()
+ nodes, err := sexp.Parse("s.conf", []byte(src))
+ if err != nil {
+ t.Fatal(err)
+ }
+ var s Settings
+ d := &diags{file: "s.conf"}
+ seen := map[string]*sexp.Node{}
+ for _, n := range nodes {
+ s.parse(n, d, seen)
+ }
+ return s, d.list
+}
+
+func TestSettingsResolve(t *testing.T) {
+ s, errs := parseSettings(t, `(case strict) (fold no) (recursive yes) (max-depth 3)
+ (min-age 5m) (max-read 1G) (busy ".tmp") (on-conflict skip)`)
+ if len(errs) > 0 {
+ t.Fatal(errs)
+ }
+ want := Resolved{Case: CaseStrict, Fold: false, Recursive: true, MaxDepth: 3,
+ MinAge: 5 * time.Minute, MaxRead: 1 << 30, Busy: []string{".tmp"}, OnConflict: ConflictSkip}
+ if got := s.Over(Builtin()); !reflect.DeepEqual(got, want) {
+ t.Fatalf("got %+v\nwant %+v", got, want)
+ }
+}
+
+func TestBuiltin(t *testing.T) {
+ want := Resolved{Case: CaseIgnore, Fold: true, MinAge: 2 * time.Minute, MaxRead: 50 << 20,
+ Busy: []string{".part", ".aria2", ".crdownload"}, OnConflict: ConflictSuffix}
+ if got := Builtin(); !reflect.DeepEqual(got, want) {
+ t.Fatalf("got %+v\nwant %+v", got, want)
+ }
+}
+
+func TestSettingsLayering(t *testing.T) {
+ defaults, _ := parseSettings(t, `(min-age 5m) (fold no)`)
+ dir, _ := parseSettings(t, `(fold yes)`)
+ got := dir.Over(defaults.Over(Builtin()))
+ if got.MinAge != 5*time.Minute || !got.Fold || got.MaxRead != 50<<20 {
+ t.Fatalf("got %+v", got)
+ }
+}
+
+func TestBusyEmptyDisables(t *testing.T) {
+ s, errs := parseSettings(t, `(busy)`)
+ if len(errs) > 0 || s.Busy == nil {
+ t.Fatalf("errs %v, busy %v", errs, s.Busy)
+ }
+ if got := s.Over(Builtin()).Busy; len(got) != 0 {
+ t.Fatalf("busy = %v, want none", got)
+ }
+}
+
+func TestSettingErrors(t *testing.T) {
+ tests := []struct{ src, want string }{
+ {`(case loud)`, `s.conf:1:7: case is ignore or strict, not loud`},
+ {`(case "ignore")`, `s.conf:1:7: case values are bare words: write (case ignore)`},
+ {`(fold)`, `s.conf:1:1: fold takes one value: (fold yes) or (fold no)`},
+ {`(max-depth 0)`, `s.conf:1:12: max-depth is a whole number from 1, not 0`},
+ {`(min-age soon)`, `s.conf:1:10: min-age: bad duration "soon": want a whole number followed by s, m, h, d or w, like 30d`},
+ {`(max-read 5m)`, `s.conf:1:11: max-read: bad size "5m": want a whole number with an optional K, M, G or T, like 50M`},
+ {`(busy part)`, `s.conf:1:7: busy takes strings, like ".part"; got part`},
+ {`(fold yes) (fold no)`, `s.conf:1:12: fold set twice (first at line 1)`},
+ }
+ for _, tt := range tests {
+ _, errs := parseSettings(t, tt.src)
+ if len(errs) != 1 || errs[0].Error() != tt.want {
+ t.Errorf("%s:\n got %v\n want %s", tt.src, errs, tt.want)
+ }
+ }
+}