package adminalert import ( "context" "strings" "testing" "time" "scrabble/backend/internal/account" ) // The fakes ignore the watermark and return a fixed count, which is all the digest logic // needs. type fbCounter struct{ n int } func (f fbCounter) CountSince(context.Context, time.Time) (int, error) { return f.n, nil } type cpCounter struct{ n int } func (c cpCounter) CountComplaintsSince(context.Context, time.Time) (int, error) { return c.n, nil } type recordingMailer struct{ sent []account.Message } func (m *recordingMailer) Send(_ context.Context, msg account.Message) error { m.sent = append(m.sent, msg) return nil } func TestNotifierSkipsWhenNothingNew(t *testing.T) { mailer := &recordingMailer{} n := New(mailer, fbCounter{0}, cpCounter{0}, "alerts@erudit-game.ru", "op@x.ru", nil) n.tick(context.Background()) if len(mailer.sent) != 0 { t.Fatalf("sent %d emails, want 0 when nothing is new", len(mailer.sent)) } } func TestNotifierDigestsNewItems(t *testing.T) { mailer := &recordingMailer{} n := New(mailer, fbCounter{2}, cpCounter{1}, "alerts@erudit-game.ru", "op@x.ru, two@x.ru", nil) n.tick(context.Background()) if len(mailer.sent) != 1 { t.Fatalf("sent %d emails, want 1 digest", len(mailer.sent)) } msg := mailer.sent[0] if msg.From != "alerts@erudit-game.ru" || msg.To != "op@x.ru, two@x.ru" { t.Errorf("digest addressing = From %q To %q", msg.From, msg.To) } if !strings.Contains(msg.Subject, "2 new feedback") || !strings.Contains(msg.Subject, "1 new word complaint") { t.Errorf("digest subject = %q, want the feedback + complaint counts", msg.Subject) } // The digest must never carry an admin-console link — an admin URL in an email is a leak // (mail providers cache/index it). if strings.Contains(msg.Text, "/_gm") || strings.Contains(strings.ToLower(msg.Text), "admin console") { t.Errorf("digest body = %q, must not carry an admin-console link", msg.Text) } }