Files
sm-user-plugin/mail_test.go
Jakub Zych af976e789f refactor(10.2-01): use nested framework modules
- Rewrite application and plugin framework imports
2026-09-28 02:21:38 +02:00

113 lines
4.0 KiB
Go

package user
import (
"context"
"net/url"
"strings"
"testing"
"time"
"git.golem15.com/golem15/fonoteka/plugins/golem15/user/controllers"
"git.golem15.com/golem15/fonoteka/plugins/golem15/user/models"
"git.golem15.com/golem15/summercms/modules/backpack"
"git.golem15.com/golem15/summercms/modules/postcard"
"gorm.io/gorm"
)
type recordingMail struct {
inner postcard.Mailer
msgs []postcard.Message
}
func (r *recordingMail) Send(ctx context.Context, msg postcard.Message) error {
r.msgs = append(r.msgs, msg)
if r.inner == nil {
return nil
}
return r.inner.Send(ctx, msg)
}
func TestMail(t *testing.T) {
app, gdb := sessionApp(t, "0s")
disableRegisterThrottle(t, app)
if err := app.Config.Set("golem15.user.activation.activate_mode", "user"); err != nil {
t.Fatal(err)
}
rec, mem := publishMemoryMail(t, app)
postRegister(t, app, "198.51.100.10", `{"email":"mail-pl@example.test","password":"correct-horse","name":"Ada","preferred_locale":"pl"}`)
postRegister(t, app, "198.51.100.11", `{"email":"mail-en@example.test","password":"correct-horse","name":"Grace","preferred_locale":"en"}`)
assertTemplate(t, rec, "golem15.user::mail.activate", "mail-pl@example.test")
assertTemplate(t, rec, "golem15.user::mail.activate-en", "mail-en@example.test")
for _, msg := range rec.msgs {
if !strings.HasPrefix(msg.Template, "golem15.user::mail.activate") {
continue
}
link, _ := msg.Vars["link"].(string)
code, _ := msg.Vars["code"].(string)
decoded, err := url.QueryUnescape(link)
if err != nil {
t.Fatal(err)
}
if link == "" || code == "" || !strings.Contains(decoded, code) {
t.Fatalf("activate vars template=%s link=%q code=%q", msg.Template, link, code)
}
}
if len(mem.Messages()) < 2 {
t.Fatalf("memory driver recorded %d messages", len(mem.Messages()))
}
before := len(rec.msgs)
postJSON(t, controllers.ForgotPassword(app), "/_user/api/v1/forgot-password", `{"email":"mail-pl@example.test"}`)
postJSON(t, controllers.ForgotPassword(app), "/_user/api/v1/forgot-password", `{"email":"mail-en@example.test"}`)
assertTemplate(t, rec, "golem15.user::mail.restore", "mail-pl@example.test")
assertTemplate(t, rec, "golem15.user::mail.restore-en", "mail-en@example.test")
if len(mem.Messages()) < before+2 {
t.Fatalf("memory driver recorded %d messages after restore", len(mem.Messages()))
}
softDelete(t, gdb, "react-pl@example.test", "pl")
softDelete(t, gdb, "react-en@example.test", "en")
postJSON(t, controllers.Login(app), "/_user/api/v1/login", `{"email":"react-pl@example.test","password":"correct-horse"}`)
postJSON(t, controllers.Login(app), "/_user/api/v1/login", `{"email":"react-en@example.test","password":"correct-horse"}`)
assertTemplate(t, rec, "golem15.user::mail.reactivate", "react-pl@example.test")
assertTemplate(t, rec, "golem15.user::mail.reactivate-en", "react-en@example.test")
}
func publishMemoryMail(t *testing.T, app *backpack.App) (*recordingMail, *postcard.MemoryDriver) {
t.Helper()
cat := postcard.NewCatalog()
p := &Plugin{}
if err := cat.Register(p.ID(), p.MailTemplatesFS(), p.MailTemplates(), p.MailLayouts()); err != nil {
t.Fatal(err)
}
mem := postcard.NewMemoryDriver()
inner := postcard.NewMailer(cat, mem, postcard.Options{From: "noreply@example.test"})
rec := &recordingMail{inner: inner}
if err := app.Publish[postcard.Mailer](rec); err != nil {
t.Fatal(err)
}
return rec, mem
}
func assertTemplate(t *testing.T, rec *recordingMail, template, to string) {
t.Helper()
for _, msg := range rec.msgs {
if msg.Template == template && len(msg.To) == 1 && msg.To[0] == to {
return
}
}
t.Fatalf("missing send %s to %s in %d messages", template, to, len(rec.msgs))
}
func softDelete(t *testing.T, gdb *gorm.DB, email, locale string) {
t.Helper()
user := insertUser(t, gdb, email, "correct-horse")
if err := gdb.Model(user).Update("preferred_locale", locale).Error; err != nil {
t.Fatal(err)
}
if err := gdb.Model(&models.User{}).Where("id = ?", user.ID).Update("deleted_at", time.Now()).Error; err != nil {
t.Fatal(err)
}
}