feat(adminalert): operator email on new feedback / word complaints
New adminalert worker polls for feedback + word complaints arriving since the last check and coalesces a burst into one digest email per interval (5 min), inert unless a distinct admin sender (BACKEND_SMTP_ADMIN_FROM) and recipient (BACKEND_ADMIN_EMAIL, comma-separated allowed) are configured. The mailer gains a per-message From override and splits a comma-separated To into separate recipients (go-mail needs them as a list). Feedback/game stores gain CountSince/CountComplaintsSince. Unit tests cover the digest, the skip-when- empty, and the recipient split.
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
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", "https://erudit-game.ru/_gm", 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)
|
||||
}
|
||||
if !strings.Contains(msg.Text, "/_gm") {
|
||||
t.Errorf("digest body = %q, want the console link", msg.Text)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user