package ucca import ( "testing" "time" ) func makeTestRegulations() map[string]*V2RegulationFile { future30 := time.Now().AddDate(0, 0, 30).Format("2006-01-02") future90 := time.Now().AddDate(0, 0, 90).Format("2006-01-02") past := time.Now().AddDate(0, 0, -10).Format("2006-01-02") return map[string]*V2RegulationFile{ "TestReg": { Regulation: "TestReg", Obligations: []V2Obligation{ { ID: "TR-001", Title: "Upcoming Critical", Deadline: &V2Deadline{Date: future30}, News: &V2ObligationNews{ Headline: "Critical Deadline", Summary: "Test summary", ActionRequired: "Do something", Affected: "All", ActionLink: "/sdk/test", }, LegalBasis: []V2LegalBasis{{Norm: "TestLaw", Article: "Art. 1"}}, }, { ID: "TR-002", Title: "Upcoming Medium", Description: "Medium priority regulation change.", Deadline: &V2Deadline{Date: future90}, LegalBasis: []V2LegalBasis{{Norm: "TestLaw", Article: "Art. 2"}}, }, { ID: "TR-003", Title: "Past Deadline", Deadline: &V2Deadline{Date: past}, }, { ID: "TR-004", Title: "No Deadline", }, }, }, } } func TestGetRegulatoryNews_SortedByUrgency(t *testing.T) { regs := makeTestRegulations() items := GetRegulatoryNews(regs, RegulatoryNewsFilter{Limit: 10}) if len(items) != 2 { t.Fatalf("expected 2 items (future only), got %d", len(items)) } // First item should be the more urgent one (30 days) if items[0].ID != "TR-001" { t.Errorf("expected TR-001 first (most urgent), got %s", items[0].ID) } if items[1].ID != "TR-002" { t.Errorf("expected TR-002 second, got %s", items[1].ID) } } func TestGetRegulatoryNews_UsesNewsField(t *testing.T) { regs := makeTestRegulations() items := GetRegulatoryNews(regs, RegulatoryNewsFilter{Limit: 10}) // TR-001 has hand-crafted news if items[0].Headline != "Critical Deadline" { t.Errorf("expected hand-crafted headline, got %q", items[0].Headline) } if items[0].ActionLink != "/sdk/test" { t.Errorf("expected /sdk/test, got %q", items[0].ActionLink) } } func TestGetRegulatoryNews_AutoGeneratesWithoutNews(t *testing.T) { regs := makeTestRegulations() items := GetRegulatoryNews(regs, RegulatoryNewsFilter{Limit: 10}) // TR-002 has no news field — should auto-generate if items[1].Headline == "" { t.Error("expected auto-generated headline") } if items[1].Summary == "" { t.Error("expected auto-generated summary from description") } } func TestGetRegulatoryNews_ExcludesPastDeadlines(t *testing.T) { regs := makeTestRegulations() items := GetRegulatoryNews(regs, RegulatoryNewsFilter{Limit: 10}) for _, item := range items { if item.ID == "TR-003" { t.Error("past deadline should be excluded") } if item.ID == "TR-004" { t.Error("no-deadline obligation should be excluded") } } } func TestGetRegulatoryNews_LimitWorks(t *testing.T) { regs := makeTestRegulations() items := GetRegulatoryNews(regs, RegulatoryNewsFilter{Limit: 1}) if len(items) != 1 { t.Errorf("expected 1 item with limit=1, got %d", len(items)) } } func TestComputeUrgency(t *testing.T) { tests := []struct { days int expected string }{ {5, "critical"}, {30, "critical"}, {31, "high"}, {90, "high"}, {91, "medium"}, {180, "medium"}, {181, "low"}, {365, "low"}, } for _, tc := range tests { got := computeUrgency(tc.days) if got != tc.expected { t.Errorf("computeUrgency(%d) = %q, want %q", tc.days, got, tc.expected) } } } func TestResolveDeadline_DeadlineDate(t *testing.T) { obl := V2Obligation{Deadline: &V2Deadline{Date: "2026-06-19"}} d, ok := resolveDeadline(obl) if !ok { t.Fatal("expected deadline resolved") } if d.Format("2006-01-02") != "2026-06-19" { t.Errorf("got %s", d.Format("2006-01-02")) } } func TestResolveDeadline_ValidFrom(t *testing.T) { obl := V2Obligation{ValidFrom: "2026-08-02"} d, ok := resolveDeadline(obl) if !ok { t.Fatal("expected deadline resolved from valid_from") } if d.Format("2006-01-02") != "2026-08-02" { t.Errorf("got %s", d.Format("2006-01-02")) } } func TestResolveDeadline_NoDate(t *testing.T) { obl := V2Obligation{} _, ok := resolveDeadline(obl) if ok { t.Error("expected no deadline for empty obligation") } } func TestFormatLegalReference(t *testing.T) { bases := []V2LegalBasis{ {Norm: "DSGVO", Article: "Art. 22"}, {Norm: "BGB", Article: "§ 356a"}, } ref := formatLegalReference(bases) if ref != "Art. 22 DSGVO, § 356a BGB" { t.Errorf("got %q", ref) } } func TestGetRegulatoryNews_FromRealFiles(t *testing.T) { regs, err := LoadAllV2Regulations() if err != nil { t.Skipf("could not load v2 regulations: %v", err) } items := GetRegulatoryNews(regs, RegulatoryNewsFilter{Limit: 20, HorizonDays: 730}) // Should find at least the Widerrufsbutton obligation found := false for _, item := range items { if item.ID == "VBR-OBL-001" { found = true if item.Headline != "Widerrufsbutton-Pflicht ab 19. Juni 2026" { t.Errorf("unexpected headline: %q", item.Headline) } } } if !found { t.Error("expected VBR-OBL-001 in regulatory news") } }