1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
package cli
import (
"bytes"
"strings"
"testing"
)
func TestHelp(t *testing.T) {
var out, errb bytes.Buffer
code := Run([]string{"help"}, nil, &out, &errb)
if code != 0 || !strings.Contains(out.String(), "lectio") {
t.Errorf("help code=%d out=%q", code, out.String())
}
}
func TestHelpFlag(t *testing.T) {
var out, errb bytes.Buffer
code := Run([]string{"-h"}, nil, &out, &errb)
if code != 0 || !strings.Contains(out.String(), "lectio") {
t.Errorf("-h code=%d out=%q", code, out.String())
}
}
func TestUnexpectedPositional(t *testing.T) {
var out, errb bytes.Buffer
if code := Run([]string{"bogus"}, nil, &out, &errb); code != 2 {
t.Errorf("unexpected positional code=%d want 2 (stderr=%q)", code, errb.String())
}
}
func TestVersion(t *testing.T) {
var out, errb bytes.Buffer
if code := Run([]string{"--version"}, nil, &out, &errb); code != 0 {
t.Errorf("version code=%d", code)
}
}
// TestBadDate exercises the usage-error path for a malformed date: a token
// that doesn't match dateRe's YYYY-MM-DD shape is left as an unrecognised
// positional argument once flag parsing is done, which is a usage error.
func TestBadDate(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
var out, errb bytes.Buffer
if code := Run([]string{"13-13-13"}, nil, &out, &errb); code != 2 {
t.Errorf("bad date code=%d want 2 (stderr=%q)", code, errb.String())
}
}
// TestTwoDatesIsAmbiguous exercises extractDate's ambiguity check: two
// dateRe-shaped tokens can't both be the positional date.
func TestTwoDatesIsAmbiguous(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
var out, errb bytes.Buffer
if code := Run([]string{"2026-07-22", "2026-07-23"}, nil, &out, &errb); code != 2 {
t.Errorf("two dates code=%d want 2 (stderr=%q)", code, errb.String())
}
}
// TestUnknownVersion exercises -b/--bible's version-validation usage-error
// path without touching the network: the version code is checked against
// the five known codes before any fetch is attempted.
func TestUnknownVersion(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
var out, errb bytes.Buffer
if code := Run([]string{"-b", "zzz"}, nil, &out, &errb); code != 2 {
t.Errorf("unknown version code=%d want 2 (stderr=%q)", code, errb.String())
}
}
// TestLectionaryTradMapsToTraditional exercises -l/--lectionary's trad ->
// traditional normalisation. It goes through extractDate/flag parsing and
// validation only (no network): a bogus date lets it exit before any fetch
// is attempted, but only after the -l value has already been validated and
// mapped, which is what this test checks indirectly via the exit code (a
// bad --lectionary would exit 2 for a different reason, so this alone
// wouldn't distinguish the two -- see TestLectionaryBogus for that side).
// Exercised directly against parseFlags below.
func TestLectionaryTradMapsToTraditional(t *testing.T) {
lectionary, err := normalizeLectionary("trad")
if err != nil {
t.Fatalf("normalizeLectionary(trad): %v", err)
}
if lectionary != "traditional" {
t.Errorf("normalizeLectionary(trad) = %q, want traditional", lectionary)
}
}
func TestLectionaryBogus(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
var out, errb bytes.Buffer
if code := Run([]string{"-l", "bogus"}, nil, &out, &errb); code != 2 {
t.Errorf("bogus lectionary code=%d want 2 (stderr=%q)", code, errb.String())
}
if _, err := normalizeLectionary("bogus"); err == nil {
t.Error("normalizeLectionary(bogus): want error, got nil")
}
}
func TestLangBogus(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
var out, errb bytes.Buffer
if code := Run([]string{"-g", "de"}, nil, &out, &errb); code != 2 {
t.Errorf("bogus lang code=%d want 2 (stderr=%q)", code, errb.String())
}
}
// TestBannerForLang checks bannerFor's wording follows lang: pl reproduces
// the pre-i18n Polish banner exactly ("Ewangelia na D" / "Czytania na D"),
// en gives "Gospel for D" / "Readings for D".
func TestBannerForLang(t *testing.T) {
cases := []struct {
lang string
all bool
want string
}{
{"pl", false, "Ewangelia na 2026-07-22"},
{"pl", true, "Czytania na 2026-07-22"},
{"en", false, "Gospel for 2026-07-22"},
{"en", true, "Readings for 2026-07-22"},
}
for _, c := range cases {
if got := bannerFor(c.lang, c.all, "2026-07-22"); got != c.want {
t.Errorf("bannerFor(%q, %v, ...) = %q, want %q", c.lang, c.all, got, c.want)
}
}
}
// TestDateTokenAnyPosition exercises extractDate directly: the date token
// is found regardless of where it appears among other flags.
func TestDateTokenAnyPosition(t *testing.T) {
cases := [][]string{
{"-a", "2026-07-22"},
{"2026-07-22", "-a"},
{"-a", "2026-07-22", "-r"},
}
for _, args := range cases {
date, rest, err := extractDate(args)
if err != nil {
t.Fatalf("extractDate(%v): %v", args, err)
}
if date != "2026-07-22" {
t.Errorf("extractDate(%v) date = %q, want 2026-07-22", args, date)
}
for _, r := range rest {
if r == "2026-07-22" {
t.Errorf("extractDate(%v) rest = %v still contains the date", args, rest)
}
}
}
}
// TestExtractDateDefaultsToday checks that omitting a date token leaves
// today's date and passes every token through untouched.
func TestExtractDateDefaultsToday(t *testing.T) {
date, rest, err := extractDate([]string{"-a", "-r"})
if err != nil {
t.Fatalf("extractDate: %v", err)
}
if date != today() {
t.Errorf("extractDate date = %q, want today() = %q", date, today())
}
if len(rest) != 2 || rest[0] != "-a" || rest[1] != "-r" {
t.Errorf("extractDate rest = %v, want [-a -r]", rest)
}
}
|