// SPDX-License-Identifier: GPL-3.0-or-later package model import ( "strings" "testing" ) // settingsFile has a header with some settings set and others left out, and // comments around them that must survive every edit. const settingsFile = `;; the directory krino sorts (path "~/dl") (min-age 2m) ; leave fresh files alone (ignore "*.part" ".*") (rule "all" (move "Out")) ` // TestSettingReads: a directory setting is read as it is written, and one // that is not in the file reads as absent. func TestSettingReads(t *testing.T) { r := openForms(t, settingsFile) for _, c := range []struct { head, want string set bool }{ {"path", `"~/dl"`, true}, {"min-age", "2m", true}, {"ignore", `"*.part" ".*"`, true}, {"recursive", "", false}, {"on-conflict", "", false}, } { got, ok, err := r.Setting(c.head) if err != nil { t.Fatal(err) } if ok != c.set || got != c.want { t.Errorf("Setting(%q) = %q, %v; want %q, %v", c.head, got, ok, c.want, c.set) } } } // TestSettingWrites: changing a setting rewrites that form and nothing // else; the comment on its line stays. func TestSettingWrites(t *testing.T) { r := openForms(t, settingsFile) if err := r.SetSetting("min-age", "1d"); err != nil { t.Fatal(err) } if !strings.Contains(r.Text, "(min-age 1d)") { t.Errorf("the new value is not in the file:\n%s", r.Text) } if strings.Contains(r.Text, "(min-age 2m)") { t.Errorf("the old value is still there:\n%s", r.Text) } for _, keep := range []string{";; the directory krino sorts", "; leave fresh files alone", `(ignore "*.part" ".*")`, `(rule "all"`} { if !strings.Contains(r.Text, keep) { t.Errorf("writing one setting lost %q:\n%s", keep, r.Text) } } if diags := r.Check(); len(diags) > 0 { t.Errorf("the file no longer loads: %v", diags) } } // TestSettingAdds: a setting the file does not have is written into the // header, above the rules, and the file still loads. func TestSettingAdds(t *testing.T) { r := openForms(t, settingsFile) if err := r.SetSetting("on-conflict", "skip"); err != nil { t.Fatal(err) } if !strings.Contains(r.Text, "(on-conflict skip)") { t.Errorf("the setting was not written:\n%s", r.Text) } if strings.Index(r.Text, "(on-conflict skip)") > strings.Index(r.Text, `(rule "all"`) { t.Errorf("the setting landed below the rules:\n%s", r.Text) } if diags := r.Check(); len(diags) > 0 { t.Errorf("the file no longer loads: %v", diags) } got, ok, err := r.Setting("on-conflict") if err != nil || !ok || got != "skip" { t.Errorf("reading it back = %q, %v, %v", got, ok, err) } } // TestSettingAddsAboveTheRules: a setting written below the rules - legal, // since order does not matter to krino - does not drag a new setting down // with it. The header is where settings go. func TestSettingAddsAboveTheRules(t *testing.T) { r := openForms(t, "(path \"~/dl\")\n\n(rule \"all\"\n (move \"Out\"))\n\n(min-age 2m)\n") if err := r.SetSetting("on-conflict", "skip"); err != nil { t.Fatal(err) } if strings.Index(r.Text, "(on-conflict skip)") > strings.Index(r.Text, `(rule "all"`) { t.Errorf("the new setting landed below the rules:\n%s", r.Text) } if diags := r.Check(); len(diags) > 0 { t.Errorf("the file no longer loads: %v", diags) } } // TestSettingRemoves: clearing a setting takes its whole line, leaving the // rest of the header as it was. func TestSettingRemoves(t *testing.T) { r := openForms(t, settingsFile) if err := r.SetSetting("min-age", ""); err != nil { t.Fatal(err) } if strings.Contains(r.Text, "min-age") { t.Errorf("the setting is still there:\n%s", r.Text) } if strings.Contains(r.Text, "leave fresh files alone") { t.Errorf("the comment on its line was left stranded:\n%s", r.Text) } if !strings.Contains(r.Text, `(path "~/dl")`) || !strings.Contains(r.Text, ";; the directory krino sorts") { t.Errorf("removing one setting took more than its line:\n%s", r.Text) } if diags := r.Check(); len(diags) > 0 { t.Errorf("the file no longer loads: %v", diags) } if _, ok, _ := r.Setting("min-age"); ok { t.Error("the removed setting still reads as set") } } // TestSettingRefusesThePathAway: a directory file without (path ...) does // not load, so clearing it is refused rather than written. func TestSettingRefusesThePathAway(t *testing.T) { r := openForms(t, settingsFile) before := r.Text if err := r.SetSetting("path", ""); err == nil { t.Error("clearing the path was accepted") } if r.Text != before { t.Error("the refused edit changed the file") } } // TestSettingRefusesAnUnknownHead: only the forms krino.conf(5) documents // as directory settings can be written this way. func TestSettingRefusesAnUnknownHead(t *testing.T) { r := openForms(t, settingsFile) if err := r.SetSetting("nonsense", "1"); err == nil { t.Error("an unknown setting was accepted") } if _, _, err := r.Setting("nonsense"); err == nil { t.Error("an unknown setting was read") } }