package readings import ( "strings" "testing" "github.com/lukaszkasprzak/lectio/internal/config" "github.com/lukaszkasprzak/lectio/internal/liturgy" ) func sectionsForFilterTests() []liturgy.Section { return []liturgy.Section{ {PartID: "pierwsze_czytanie", Heading: "1. czytanie"}, {PartID: "psalm", Heading: "Psalm"}, {PartID: "ewangelia", Heading: "Ewangelia"}, } } func TestFilterPartsGospelOnly(t *testing.T) { secs := sectionsForFilterTests() got := filterParts(secs, config.Config{}, false) if len(got) != 1 { t.Fatalf("got %d sections, want 1: %+v", len(got), got) } if got[0].PartID != "ewangelia" { t.Errorf("got PartID %q, want ewangelia", got[0].PartID) } } func TestFilterPartsAll(t *testing.T) { secs := sectionsForFilterTests() cfg := config.Config{ Lectionary: "new", Parts: map[string]map[string]bool{ "new": {"psalm": false}, }, } got := filterParts(secs, cfg, true) if len(got) != 2 { t.Fatalf("got %d sections, want 2: %+v", len(got), got) } for _, s := range got { if s.PartID == "psalm" { t.Errorf("psalm should have been filtered out: %+v", got) } } ids := map[string]bool{got[0].PartID: true, got[1].PartID: true} if !ids["pierwsze_czytanie"] || !ids["ewangelia"] { t.Errorf("got %+v, want pierwsze_czytanie and ewangelia", got) } } func TestFilterPartsTraditionalAllReadingsOnly(t *testing.T) { secs := []liturgy.Section{ {PartID: "introitus", Heading: "Introit"}, {PartID: "oratio", Heading: "Kolekta"}, {PartID: "lectio", Heading: "Lekcja"}, {PartID: "graduale", Heading: "GraduaƂ"}, {PartID: "evangelium", Heading: "Ewangelia"}, {PartID: "offertorium", Heading: "Ofiarowanie"}, {PartID: "secreta", Heading: "Sekreta"}, {PartID: "communio", Heading: "Komunia"}, {PartID: "postcommunio", Heading: "Pokomunia"}, } cfg := config.Config{Lectionary: "traditional"} got := filterParts(secs, cfg, true) if len(got) != 2 { t.Fatalf("got %d sections, want 2: %+v", len(got), got) } if got[0].PartID != "lectio" || got[1].PartID != "evangelium" { t.Errorf("got %+v, want [lectio evangelium] in order", got) } } // TestLoadModern computes the Ordinary Form day offline: the gospel section is // present and the header carries the celebration name (English by default, // since the universal sanctoral is authored in English). func TestLoadModern(t *testing.T) { cfg := config.Config{Lectionary: "new"} secs, info, err := Load(cfg, Options{Date: "2026-07-22", All: true}) if err != nil { t.Fatalf("Load: %v", err) } found := false for _, s := range secs { if s.PartID == "ewangelia" { found = true } } if !found { t.Errorf("no gospel section (PartID=ewangelia) found in %+v", secs) } if !strings.Contains(info.Name, "Mary Magdalene") { t.Errorf("DayInfo.Name = %q, want it to contain %q", info.Name, "Mary Magdalene") } } // TestSundayRankIsDisplayOnly guards the Ordinary Form's Sunday display rank. // Sunday is its own category in the 1969 Universal Norms' Table of Liturgical // Days -- never a solemnity -- so an ordinary "Nth Sunday of " temporal // day (Ordinary Time, Advent, Lent, the Easter season, Christmas time; this // also covers Palm Sunday) must report "sunday". A NAMED solemnity of the Lord // that happens to fall on a Sunday (Easter Sunday itself, Pentecost) is a // genuine solemnity and must keep reporting "solemnity"; a feast of the Lord // (Holy Family) must keep reporting "feast". This is display-only: it must // never touch the engine's internal precedence, so the 1962 form (which has no // such Sunday category -- Sundays report their class exactly as before) is // asserted unchanged too. Dates are the ones from the original bug report, // plus the Easter/Pentecost/Holy Family dates they imply for the same // liturgical year. func TestSundayRankIsDisplayOnly(t *testing.T) { cases := []struct { date, lect, wantRank, why string }{ {"2026-08-09", "new", "sunday", "19th Sunday in Ordinary Time"}, {"2026-11-29", "new", "sunday", "1st Sunday of Advent"}, {"2027-03-28", "new", "solemnity", "Easter Sunday itself"}, {"2027-05-16", "new", "solemnity", "Pentecost"}, {"2026-12-27", "new", "feast", "Holy Family"}, {"2026-08-09", "traditional", "class-2", "1962 Sunday after Pentecost"}, {"2026-11-29", "traditional", "class-1", "1962 1st Sunday of Advent"}, } for _, c := range cases { cfg := config.Config{Lectionary: c.lect} _, info, err := Load(cfg, Options{Date: c.date, All: true}) if err != nil { t.Fatalf("%s (%s, %s): Load: %v", c.date, c.lect, c.why, err) } if info.Rank != c.wantRank { t.Errorf("%s (%s, %s): Rank = %q, want %q (name=%q)", c.date, c.lect, c.why, info.Rank, c.wantRank, info.Name) } } } // TestLoadTraditional computes the Extraordinary Form day offline: it never // needs the network, and yields the EF epistle+gospel with a header name. func TestLoadTraditional(t *testing.T) { cfg := config.Config{Lectionary: "traditional"} secs, info, err := Load(cfg, Options{Date: "2026-07-22", All: true}) if err != nil { t.Fatalf("Load: %v", err) } found := false for _, s := range secs { if s.PartID == "evangelium" { found = true } } if !found { t.Errorf("no EF gospel section (PartID=evangelium) found in %+v", secs) } if info.Name == "" { t.Error("DayInfo.Name empty for a traditional feast day") } }