package notify import ( "io" "log" "strings" "testing" ) // v0.120.0 — the household hears about an update ONCE per app per event (R-629: a storm is a // defect), and two apps on one night are two mails. // // COMPANION RED-PROOF (REPORT.md): drop the perAppCustomerCooldownEvents suffix in processCustomer — // the second app's mail is swallowed by the first app's cooldown and this fails on the count. func TestAppUpdateEvents_OneMailPerAppPerEvent(t *testing.T) { st := opOnlyStore(t) if err := st.SaveNotificationPrefs("c1", "customer@example.com", []string{"app_update_undone", "app_update_held"}, 6); err != nil { t.Fatal(err) } var subjects []string d := NewDispatcher(st, "test-key", "from@felhom.eu", "operator@felhom.eu", true, log.New(io.Discard, "", 0)) d.sendEmailFn = func(to, subject, _ string, _ map[string]string) error { if to == "customer@example.com" { subjects = append(subjects, subject) } return nil } fire := func(et, app string) { d.ProcessEvent("c1", et, "warning", "A(z) "+app+" frissitese nem sikerult.", `{"app":"`+app+`","stack_name":"`+app+`"}`, "controller") } fire("app_update_undone", "docmost") fire("app_update_undone", "romm") // another app, same night → its own mail fire("app_update_undone", "docmost") // the same app again → no second mail fire("app_update_held", "docmost") // another EVENT for the same app → its own mail if len(subjects) != 3 { t.Fatalf("want 3 household mails (docmost undone, romm undone, docmost held), got %d: %q", len(subjects), subjects) } for i, app := range []string{"docmost", "romm", "docmost"} { if !strings.Contains(subjects[i], app+":") { t.Errorf("mail %d must name %s in the subject, got %q", i, app, subjects[i]) } } } // The other types keep their customer-side grain: the new register is not a blanket change. func TestAppUpdateEvents_OtherTypesKeepTheirCustomerGrain(t *testing.T) { st := opOnlyStore(t) if err := st.SaveNotificationPrefs("c1", "customer@example.com", []string{"app_start_failed"}, 6); err != nil { t.Fatal(err) } n := 0 d := NewDispatcher(st, "test-key", "from@felhom.eu", "operator@felhom.eu", true, log.New(io.Discard, "", 0)) d.sendEmailFn = func(to, _, _ string, _ map[string]string) error { if to == "customer@example.com" { n++ } return nil } d.ProcessEvent("c1", "app_start_failed", "error", "x", `{"stack_name":"a"}`, "controller") d.ProcessEvent("c1", "app_start_failed", "error", "x", `{"stack_name":"b"}`, "controller") if n != 1 { t.Fatalf("app_start_failed's household cooldown stays per TYPE (unchanged by v0.120.0), got %d mails", n) } }