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()) } }