summaryrefslogtreecommitdiff
path: root/internal/cli/feed_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/cli/feed_test.go')
-rw-r--r--internal/cli/feed_test.go146
1 files changed, 146 insertions, 0 deletions
diff --git a/internal/cli/feed_test.go b/internal/cli/feed_test.go
new file mode 100644
index 0000000..efbcfc9
--- /dev/null
+++ b/internal/cli/feed_test.go
@@ -0,0 +1,146 @@
+package cli
+
+import (
+ "bytes"
+ "encoding/json"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+// feedTestConfig points LECTIO_CONFIG at a fresh, non-existent file in a
+// temp dir so each test gets lectio's built-in defaults (Universal Roman
+// Calendar, no user layers) regardless of the machine's real config.
+func feedTestConfig(t *testing.T) {
+ t.Helper()
+ dir := t.TempDir()
+ t.Setenv("LECTIO_CONFIG", filepath.Join(dir, "config.ini"))
+}
+
+func TestFeedJSONRange(t *testing.T) {
+ feedTestConfig(t)
+ var out, errb bytes.Buffer
+ code := Run([]string{"--format", "json", "--from", "2026-01-01", "--to", "2026-01-03"}, nil, &out, &errb)
+ if code != 0 {
+ t.Fatalf("exit code %d (stderr=%q)", code, errb.String())
+ }
+
+ var payload struct {
+ Schema string `json:"schema"`
+ Days []struct {
+ Date string `json:"date"`
+ } `json:"days"`
+ }
+ if err := json.Unmarshal(out.Bytes(), &payload); err != nil {
+ t.Fatalf("stdout did not parse as JSON: %v\n%s", err, out.String())
+ }
+ if payload.Schema != "lectio.calendar/1" {
+ t.Errorf("schema = %q, want lectio.calendar/1", payload.Schema)
+ }
+ if len(payload.Days) != 3 {
+ t.Errorf("len(days) = %d, want 3", len(payload.Days))
+ }
+}
+
+func TestFeedICalYear(t *testing.T) {
+ feedTestConfig(t)
+ var out, errb bytes.Buffer
+ code := Run([]string{"--format", "ical", "--year", "2026"}, nil, &out, &errb)
+ if code != 0 {
+ t.Fatalf("exit code %d (stderr=%q)", code, errb.String())
+ }
+
+ got := out.String()
+ if !strings.HasPrefix(got, "BEGIN:VCALENDAR") {
+ end := len(got)
+ if end > 80 {
+ end = 80
+ }
+ t.Fatalf("output does not start with BEGIN:VCALENDAR: %q", got[:end])
+ }
+ if n := strings.Count(got, "BEGIN:VEVENT"); n != 365 {
+ t.Errorf("BEGIN:VEVENT count = %d, want 365 (2026 is not a leap year)", n)
+ }
+}
+
+func TestFeedInvertedRangeIsUsageError(t *testing.T) {
+ feedTestConfig(t)
+ var out, errb bytes.Buffer
+ code := Run([]string{"--format", "json", "--from", "2026-01-05", "--to", "2026-01-01"}, nil, &out, &errb)
+ if code != 2 {
+ t.Fatalf("exit code %d, want 2 (stdout=%q stderr=%q)", code, out.String(), errb.String())
+ }
+}
+
+func TestFeedYearOutOfDomainIsUsageError(t *testing.T) {
+ feedTestConfig(t)
+ var out, errb bytes.Buffer
+ code := Run([]string{"--format", "json", "--year", "1500"}, nil, &out, &errb)
+ if code != 2 {
+ t.Fatalf("exit code %d, want 2 (stdout=%q stderr=%q)", code, out.String(), errb.String())
+ }
+}
+
+// TestFeedSingleDayDefaultsToday exercises --format with no range flags at
+// all: the positional DATE (given explicitly here) is used as a single-day
+// range.
+func TestFeedSingleDayFromPositionalDate(t *testing.T) {
+ feedTestConfig(t)
+ var out, errb bytes.Buffer
+ code := Run([]string{"2026-01-06", "--format", "json"}, nil, &out, &errb)
+ if code != 0 {
+ t.Fatalf("exit code %d (stderr=%q)", code, errb.String())
+ }
+ var payload struct {
+ Days []struct {
+ Date string `json:"date"`
+ } `json:"days"`
+ }
+ if err := json.Unmarshal(out.Bytes(), &payload); err != nil {
+ t.Fatalf("stdout did not parse as JSON: %v\n%s", err, out.String())
+ }
+ if len(payload.Days) != 1 || payload.Days[0].Date != "2026-01-06" {
+ t.Errorf("days = %+v, want a single 2026-01-06", payload.Days)
+ }
+}
+
+// TestFeedFromWithoutFormatIsUsageError: --from/--to/--year/--form only mean
+// anything alongside --format; given alone they are a usage error rather
+// than silently ignored.
+func TestFeedFromWithoutFormatIsUsageError(t *testing.T) {
+ feedTestConfig(t)
+ var out, errb bytes.Buffer
+ code := Run([]string{"--from", "2026-01-01", "--to", "2026-01-03"}, nil, &out, &errb)
+ if code != 2 {
+ t.Fatalf("exit code %d, want 2 (stdout=%q stderr=%q)", code, out.String(), errb.String())
+ }
+}
+
+// TestFeedFormOverridesSelection: --form old switches the built calendar to
+// the Extraordinary Form even when config defaults to the OF (new).
+func TestFeedFormOverridesSelection(t *testing.T) {
+ feedTestConfig(t)
+ var out, errb bytes.Buffer
+ code := Run([]string{"--format", "json", "--form", "old", "2026-01-06"}, nil, &out, &errb)
+ if code != 0 {
+ t.Fatalf("exit code %d (stderr=%q)", code, errb.String())
+ }
+ var payload struct {
+ Form string `json:"form"`
+ }
+ if err := json.Unmarshal(out.Bytes(), &payload); err != nil {
+ t.Fatalf("stdout did not parse as JSON: %v\n%s", err, out.String())
+ }
+ if payload.Form != "old" {
+ t.Errorf("form = %q, want old", payload.Form)
+ }
+}
+
+func TestFeedBadFormatIsUsageError(t *testing.T) {
+ feedTestConfig(t)
+ var out, errb bytes.Buffer
+ code := Run([]string{"--format", "xml"}, nil, &out, &errb)
+ if code != 2 {
+ t.Fatalf("exit code %d, want 2 (stdout=%q stderr=%q)", code, out.String(), errb.String())
+ }
+}