package config import ( "os" "path/filepath" "testing" ) func TestLoadSeeds(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) cfg, err := Load() if err != nil { t.Fatal(err) } if cfg.DefaultVersion != "vul" || cfg.Offline { t.Errorf("defaults wrong: %+v", cfg) } // Versions defaults to the corpora actually available in this build, Vulgate // first then the rest sorted. Tests build with -tags fullbible, so all four // are present (see Makefile); a default vul-only binary would list just vul. wantVersions := []string{"vul", "drb", "grb", "wuj"} if len(cfg.Versions) != len(wantVersions) { t.Errorf("versions default = %v, want %v", cfg.Versions, wantVersions) } else { for i, v := range wantVersions { if cfg.Versions[i] != v { t.Errorf("versions default = %v, want %v", cfg.Versions, wantVersions) break } } } if cfg.Lectionary != "new" { t.Errorf("lectionary default wrong: %+v", cfg) } if _, err := os.Stat(filepath.Join(dir, "lectio", "config.ini")); err != nil { t.Error("config not seeded") } } // TestLoadMigratesBT checks a legacy config still carrying the retired "bt" // version (both as default_version and in the versions list) loads and is // migrated to wuj: bt is dropped from the list when wuj is already present // (no duplicate column) and default_version bt becomes wuj. func TestLoadMigratesBT(t *testing.T) { dir := t.TempDir() t.Setenv("LECTIO_CONFIG", filepath.Join(dir, "config.ini")) os.WriteFile(filepath.Join(dir, "config.ini"), []byte("default_version = bt\nversions = bt, wuj, vul\n"), 0o644) cfg, err := Load() if err != nil { t.Fatal(err) } if cfg.DefaultVersion != "wuj" { t.Errorf("default_version bt not migrated: %q", cfg.DefaultVersion) } for _, v := range cfg.Versions { if v == "bt" { t.Errorf("versions still carries bt: %v", cfg.Versions) } } if len(cfg.Versions) != 2 || cfg.Versions[0] != "wuj" || cfg.Versions[1] != "vul" { t.Errorf("versions after migration = %v, want [wuj vul]", cfg.Versions) } } func TestLoadOverride(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) os.MkdirAll(filepath.Join(dir, "lectio"), 0o755) os.WriteFile(filepath.Join(dir, "lectio", "config.ini"), []byte("offline = true\ndefault_version = wuj\n"), 0o644) cfg, _ := Load() if !cfg.Offline || cfg.DefaultVersion != "wuj" { t.Errorf("override not applied: %+v", cfg) } } func TestWebDefaults(t *testing.T) { def := Default() if def.WebMono { t.Errorf("WebMono default should be false") } if len(def.WebVersions) != 0 { t.Errorf("WebVersions default should be empty, got %v", def.WebVersions) } if def.WebTheme != "transfiguration" { t.Errorf("WebTheme default wrong: got %q, want %q", def.WebTheme, "transfiguration") } if def.WebPort != 0 { t.Errorf("WebPort default wrong: got %d, want 0", def.WebPort) } } func TestWebLoadsFromSeed(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) cfg, err := Load() if err != nil { t.Fatal(err) } if cfg.WebTheme != "transfiguration" { t.Errorf("WebTheme from seed wrong: got %q, want %q", cfg.WebTheme, "transfiguration") } if cfg.WebPort != 0 { t.Errorf("WebPort from seed wrong: got %d, want 0", cfg.WebPort) } if cfg.WebDisplay != "horizontal" { t.Errorf("WebDisplay from seed wrong: got %q, want %q", cfg.WebDisplay, "horizontal") } } func TestWebDisplayDefault(t *testing.T) { def := Default() if def.WebDisplay != "horizontal" { t.Errorf("WebDisplay default wrong: got %q, want %q", def.WebDisplay, "horizontal") } } func TestWebDisplayLoadsSetting(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) os.MkdirAll(filepath.Join(dir, "lectio"), 0o755) os.WriteFile(filepath.Join(dir, "lectio", "config.ini"), []byte("web_display = vertical\n"), 0o644) cfg, err := Load() if err != nil { t.Fatal(err) } if cfg.WebDisplay != "vertical" { t.Errorf("WebDisplay = %q, want %q", cfg.WebDisplay, "vertical") } } func TestWebDisplayNormalizesUnknown(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) os.MkdirAll(filepath.Join(dir, "lectio"), 0o755) os.WriteFile(filepath.Join(dir, "lectio", "config.ini"), []byte("web_display = bogus\n"), 0o644) cfg, err := Load() if err != nil { t.Fatal(err) } if cfg.WebDisplay != "horizontal" { t.Errorf("WebDisplay = %q, want %q (normalized from bogus)", cfg.WebDisplay, "horizontal") } } func TestUILanguageDefault(t *testing.T) { def := Default() if def.UILanguage != "en" { t.Errorf("UILanguage default wrong: got %q, want %q", def.UILanguage, "en") } } func TestUILanguageLoadsPL(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) os.MkdirAll(filepath.Join(dir, "lectio"), 0o755) os.WriteFile(filepath.Join(dir, "lectio", "config.ini"), []byte("ui_language = pl\n"), 0o644) cfg, err := Load() if err != nil { t.Fatal(err) } if cfg.UILanguage != "pl" { t.Errorf("UILanguage = %q, want %q", cfg.UILanguage, "pl") } } func TestUILanguagePreservesAnyCode(t *testing.T) { // Any ui_language code is kept (lower-cased): it drives day/saint-name // localisation via names/.ini. The UI chrome, resolved through // i18n.Get, still falls back to English for a code it has no table for. dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) os.MkdirAll(filepath.Join(dir, "lectio"), 0o755) os.WriteFile(filepath.Join(dir, "lectio", "config.ini"), []byte("ui_language = FR\n"), 0o644) cfg, err := Load() if err != nil { t.Fatal(err) } if cfg.UILanguage != "fr" { t.Errorf("UILanguage = %q, want %q (lower-cased, not clamped)", cfg.UILanguage, "fr") } } func TestPagerDefault(t *testing.T) { def := Default() if def.Pager != "" { t.Errorf("Pager default should be empty, got %q", def.Pager) } } func TestPagerLoadsFromSeed(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) cfg, err := Load() if err != nil { t.Fatal(err) } if cfg.Pager != "" { t.Errorf("Pager from seed wrong: got %q, want empty", cfg.Pager) } } func TestPagerLoadsSetting(t *testing.T) { dir := t.TempDir() t.Setenv("XDG_CONFIG_HOME", dir) os.MkdirAll(filepath.Join(dir, "lectio"), 0o755) os.WriteFile(filepath.Join(dir, "lectio", "config.ini"), []byte("pager = less -R\n"), 0o644) cfg, err := Load() if err != nil { t.Fatal(err) } if cfg.Pager != "less -R" { t.Errorf("Pager = %q, want %q", cfg.Pager, "less -R") } } func TestSiglaStyleDefaultsAndDialect(t *testing.T) { if got := Default().SiglaStyle; got != "auto" { t.Errorf("default SiglaStyle=%q want auto", got) } cases := []struct{ style, ui, want string }{ {"auto", "en", "en"}, {"auto", "pl", "pl"}, {"polish", "en", "pl"}, {"english", "pl", "en"}, } for _, c := range cases { cfg := Config{SiglaStyle: NormalizeSiglaStyle(c.style), UILanguage: c.ui} if got := cfg.SiglaLang(); got != c.want { t.Errorf("SiglaLang(style=%q,ui=%q)=%q want %q", c.style, c.ui, got, c.want) } } if NormalizeSiglaStyle("nonsense") != "auto" { t.Error("unknown sigla_style should normalize to auto") } } func TestSaveRoundTrip(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "config.ini") t.Setenv("LECTIO_CONFIG", path) cfg := Default() cfg.UILanguage = "pl" cfg.SiglaStyle = "english" cfg.WebMono = true cfg.WebVersions = []string{"wuj", "vul"} cfg.Pager = "less -R" cfg.Parts = map[string]map[string]bool{"new": {"psalm": false}} if err := Save(cfg); err != nil { t.Fatal(err) } got, err := Load() if err != nil { t.Fatalf("reload: %v", err) } if got.UILanguage != "pl" || got.SiglaStyle != "english" || !got.WebMono || len(got.WebVersions) != 2 || got.Pager != "less -R" || got.PartShown("new", "psalm") { t.Errorf("round-trip mismatch: %+v", got) } } func TestSelectionFromINI(t *testing.T) { dir := t.TempDir() t.Setenv("LECTIO_CONFIG", filepath.Join(dir, "config.ini")) os.WriteFile(filepath.Join(dir, "config.ini"), []byte("lectionary = new\n[calendar]\nepiphany = sunday\nascension = sunday\n"), 0o644) cfg, err := Load() if err != nil { t.Fatal(err) } sel := cfg.Selection() if sel.Form != "new" || sel.Epiphany != "sunday" || sel.Ascension != "sunday" || sel.CorpusChristi != "thursday" { t.Fatalf("selection = %+v", sel) } } func TestUseLayers(t *testing.T) { dir := t.TempDir() t.Setenv("LECTIO_CONFIG", filepath.Join(dir, "config.ini")) os.WriteFile(filepath.Join(dir, "config.ini"), []byte("use = poland, krakow\n"), 0o644) cfg, err := Load() if err != nil { t.Fatal(err) } if len(cfg.Use) != 2 || cfg.Use[0] != "poland" || cfg.Use[1] != "krakow" { t.Fatalf("Use = %v", cfg.Use) } // round-trips through Save if err := Save(cfg); err != nil { t.Fatal(err) } got, _ := Load() if len(got.Use) != 2 || got.Use[1] != "krakow" { t.Errorf("Use after round-trip = %v", got.Use) } } func TestPartShown(t *testing.T) { var empty Config if !empty.PartShown("new", "psalm") { t.Error("nil Parts: psalm should be shown") } if !empty.PartShown("new", "ewangelia") { t.Error("nil Parts: ewangelia should be shown") } hidden := Config{Parts: map[string]map[string]bool{ "new": {"psalm": false}, }} if hidden.PartShown("new", "psalm") { t.Error("psalm explicitly false should be hidden") } if !hidden.PartShown("new", "ewangelia") { t.Error("ewangelia not mentioned should still be shown") } } func TestReadingCorpusResolution(t *testing.T) { // drb is an embedded corpus with lang=en. c := Config{ReadingVersion: "drb"} if got := c.ReadingCorpus(); got != "drb" { t.Fatalf("explicit reading_version: got %q", got) } c = Config{ReadingLang: "en"} if got := c.ReadingCorpus(); got != "drb" { t.Fatalf("reading_lang match: got %q", got) } c = Config{UILanguage: "en"} if got := c.ReadingCorpus(); got != "drb" { t.Fatalf("ui_language fallback: got %q", got) } c = Config{UILanguage: "pl"} // no pl corpus embedded if got := c.ReadingCorpus(); got != "" { t.Fatalf("no match should be empty: got %q", got) } }