mirror of
https://github.com/dnote/dnote
synced 2026-03-14 22:45:50 +01:00
Simplify email backend
This commit is contained in:
parent
b6ae8dafa8
commit
521f94b81a
14 changed files with 162 additions and 133 deletions
|
|
@ -50,8 +50,6 @@ func TestServerStart(t *testing.T) {
|
|||
cmd := exec.Command(testServerBinary, "start", "--port", port)
|
||||
cmd.Env = append(os.Environ(),
|
||||
"DBPath="+tmpDB,
|
||||
"WebURL=http://localhost:"+port,
|
||||
"APP_ENV=PRODUCTION",
|
||||
)
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
|
|
@ -140,7 +138,6 @@ func TestServerStartHelp(t *testing.T) {
|
|||
|
||||
outputStr := string(output)
|
||||
assert.Equal(t, strings.Contains(outputStr, "dnote-server start [flags]"), true, "output should contain usage")
|
||||
assert.Equal(t, strings.Contains(outputStr, "--appEnv"), true, "output should contain appEnv flag")
|
||||
assert.Equal(t, strings.Contains(outputStr, "--port"), true, "output should contain port flag")
|
||||
assert.Equal(t, strings.Contains(outputStr, "--webUrl"), true, "output should contain webUrl flag")
|
||||
assert.Equal(t, strings.Contains(outputStr, "--dbPath"), true, "output should contain dbPath flag")
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import (
|
|||
"github.com/dnote/dnote/pkg/server/app"
|
||||
"github.com/dnote/dnote/pkg/server/controllers"
|
||||
"github.com/dnote/dnote/pkg/server/database"
|
||||
"github.com/dnote/dnote/pkg/server/mailer"
|
||||
apitest "github.com/dnote/dnote/pkg/server/testutils"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
|
|
@ -98,7 +97,6 @@ func setupTestServer(t *testing.T, serverTime time.Time) (*httptest.Server, *gor
|
|||
|
||||
a := app.NewTest()
|
||||
a.Clock = mockClock
|
||||
a.EmailTemplates = mailer.Templates{}
|
||||
a.EmailBackend = &apitest.MockEmailbackendImplementation{}
|
||||
a.DB = db
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
APP_ENV=DEVELOPMENT
|
||||
DBPath=../../dev-server.db
|
||||
|
|
@ -29,8 +29,6 @@ var (
|
|||
ErrEmptyClock = errors.New("No clock was provided")
|
||||
// ErrEmptyWebURL is an error for missing WebURL content in the app configuration
|
||||
ErrEmptyWebURL = errors.New("No WebURL was provided")
|
||||
// ErrEmptyEmailTemplates is an error for missing EmailTemplates content in the app configuration
|
||||
ErrEmptyEmailTemplates = errors.New("No EmailTemplate store was provided")
|
||||
// ErrEmptyEmailBackend is an error for missing EmailBackend content in the app configuration
|
||||
ErrEmptyEmailBackend = errors.New("No EmailBackend was provided")
|
||||
// ErrEmptyHTTP500Page is an error for missing HTTP 500 page content
|
||||
|
|
@ -41,7 +39,6 @@ var (
|
|||
type App struct {
|
||||
DB *gorm.DB
|
||||
Clock clock.Clock
|
||||
EmailTemplates mailer.Templates
|
||||
EmailBackend mailer.Backend
|
||||
Files map[string][]byte
|
||||
HTTP500Page []byte
|
||||
|
|
@ -60,9 +57,6 @@ func (a *App) Validate() error {
|
|||
if a.Clock == nil {
|
||||
return ErrEmptyClock
|
||||
}
|
||||
if a.EmailTemplates == nil {
|
||||
return ErrEmptyEmailTemplates
|
||||
}
|
||||
if a.EmailBackend == nil {
|
||||
return ErrEmptyEmailBackend
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,21 +64,18 @@ func getNoreplySender(webURL string) (string, error) {
|
|||
|
||||
// SendWelcomeEmail sends welcome email
|
||||
func (a *App) SendWelcomeEmail(email string) error {
|
||||
body, err := a.EmailTemplates.Execute(mailer.EmailTypeWelcome, mailer.EmailKindText, mailer.WelcomeTmplData{
|
||||
AccountEmail: email,
|
||||
WebURL: a.WebURL,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "executing reset verification template for %s", email)
|
||||
}
|
||||
|
||||
from, err := GetSenderEmail(a.WebURL, defaultSender)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting the sender email")
|
||||
}
|
||||
|
||||
if err := a.EmailBackend.Queue("Welcome to Dnote!", from, []string{email}, mailer.EmailKindText, body); err != nil {
|
||||
return errors.Wrapf(err, "queueing email for %s", email)
|
||||
data := mailer.WelcomeTmplData{
|
||||
AccountEmail: email,
|
||||
WebURL: a.WebURL,
|
||||
}
|
||||
|
||||
if err := a.EmailBackend.SendEmail(mailer.EmailTypeWelcome, from, []string{email}, data); err != nil {
|
||||
return errors.Wrapf(err, "sending welcome email for %s", email)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -90,26 +87,23 @@ func (a *App) SendPasswordResetEmail(email, tokenValue string) error {
|
|||
return ErrEmailRequired
|
||||
}
|
||||
|
||||
body, err := a.EmailTemplates.Execute(mailer.EmailTypeResetPassword, mailer.EmailKindText, mailer.EmailResetPasswordTmplData{
|
||||
AccountEmail: email,
|
||||
Token: tokenValue,
|
||||
WebURL: a.WebURL,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "executing reset password template for %s", email)
|
||||
}
|
||||
|
||||
from, err := GetSenderEmail(a.WebURL, defaultSender)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting the sender email")
|
||||
}
|
||||
|
||||
if err := a.EmailBackend.Queue("Reset your password", from, []string{email}, mailer.EmailKindText, body); err != nil {
|
||||
data := mailer.EmailResetPasswordTmplData{
|
||||
AccountEmail: email,
|
||||
Token: tokenValue,
|
||||
WebURL: a.WebURL,
|
||||
}
|
||||
|
||||
if err := a.EmailBackend.SendEmail(mailer.EmailTypeResetPassword, from, []string{email}, data); err != nil {
|
||||
if errors.Cause(err) == mailer.ErrSMTPNotConfigured {
|
||||
return ErrInvalidSMTPConfig
|
||||
}
|
||||
|
||||
return errors.Wrapf(err, "queueing email for %s", email)
|
||||
return errors.Wrapf(err, "sending password reset email for %s", email)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -117,21 +111,18 @@ func (a *App) SendPasswordResetEmail(email, tokenValue string) error {
|
|||
|
||||
// SendPasswordResetAlertEmail sends email that notifies users of a password change
|
||||
func (a *App) SendPasswordResetAlertEmail(email string) error {
|
||||
body, err := a.EmailTemplates.Execute(mailer.EmailTypeResetPasswordAlert, mailer.EmailKindText, mailer.EmailResetPasswordAlertTmplData{
|
||||
AccountEmail: email,
|
||||
WebURL: a.WebURL,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "executing reset password alert template for %s", email)
|
||||
}
|
||||
|
||||
from, err := GetSenderEmail(a.WebURL, defaultSender)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting the sender email")
|
||||
}
|
||||
|
||||
if err := a.EmailBackend.Queue("Dnote password changed", from, []string{email}, mailer.EmailKindText, body); err != nil {
|
||||
return errors.Wrapf(err, "queueing email for %s", email)
|
||||
data := mailer.EmailResetPasswordAlertTmplData{
|
||||
AccountEmail: email,
|
||||
WebURL: a.WebURL,
|
||||
}
|
||||
|
||||
if err := a.EmailBackend.SendEmail(mailer.EmailTypeResetPasswordAlert, from, []string{email}, data); err != nil {
|
||||
return errors.Wrapf(err, "sending password reset alert email for %s", email)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package app
|
|||
import (
|
||||
"github.com/dnote/dnote/pkg/clock"
|
||||
"github.com/dnote/dnote/pkg/server/assets"
|
||||
"github.com/dnote/dnote/pkg/server/mailer"
|
||||
"github.com/dnote/dnote/pkg/server/testutils"
|
||||
)
|
||||
|
||||
|
|
@ -26,7 +25,6 @@ import (
|
|||
func NewTest() App {
|
||||
return App{
|
||||
Clock: clock.NewMock(),
|
||||
EmailTemplates: mailer.NewTemplates(),
|
||||
EmailBackend: &testutils.MockEmailbackendImplementation{},
|
||||
HTTP500Page: assets.MustGetHTTP500ErrorPage(),
|
||||
WebURL: "http://127.0.0.0.1",
|
||||
|
|
|
|||
|
|
@ -37,20 +37,24 @@ func initDB(dbPath string) *gorm.DB {
|
|||
return db
|
||||
}
|
||||
|
||||
func getEmailBackend() mailer.Backend {
|
||||
defaultBackend, err := mailer.NewDefaultBackend()
|
||||
if err != nil {
|
||||
log.Debug("SMTP not configured, using StdoutBackend for emails")
|
||||
return mailer.NewStdoutBackend()
|
||||
}
|
||||
|
||||
log.Debug("Email backend configured")
|
||||
return defaultBackend
|
||||
}
|
||||
|
||||
func initApp(cfg config.Config) app.App {
|
||||
db := initDB(cfg.DBPath)
|
||||
|
||||
emailBackend, err := mailer.NewDefaultBackend()
|
||||
if err != nil {
|
||||
emailBackend = &mailer.DefaultBackend{Enabled: false}
|
||||
} else {
|
||||
log.Info("Email backend configured")
|
||||
}
|
||||
emailBackend := getEmailBackend()
|
||||
|
||||
return app.App{
|
||||
DB: db,
|
||||
Clock: clock.New(),
|
||||
EmailTemplates: mailer.NewTemplates(),
|
||||
EmailBackend: emailBackend,
|
||||
HTTP500Page: cfg.HTTP500Page,
|
||||
WebURL: cfg.WebURL,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ var ErrSMTPNotConfigured = errors.New("SMTP is not configured")
|
|||
|
||||
// Backend is an interface for sending emails.
|
||||
type Backend interface {
|
||||
Queue(subject, from string, to []string, contentType, body string) error
|
||||
SendEmail(templateType, from string, to []string, data interface{}) error
|
||||
}
|
||||
|
||||
// EmailDialer is an interface for sending email messages
|
||||
|
|
@ -44,9 +44,10 @@ type gomailDialer struct {
|
|||
|
||||
// DefaultBackend is an implementation of the Backend
|
||||
// that sends an email without queueing.
|
||||
// This backend is always enabled and will send emails via SMTP.
|
||||
type DefaultBackend struct {
|
||||
Dialer EmailDialer
|
||||
Enabled bool
|
||||
Dialer EmailDialer
|
||||
Templates Templates
|
||||
}
|
||||
|
||||
type dialerParams struct {
|
||||
|
|
@ -91,24 +92,24 @@ func NewDefaultBackend() (*DefaultBackend, error) {
|
|||
d := gomail.NewDialer(p.Host, p.Port, p.Username, p.Password)
|
||||
|
||||
return &DefaultBackend{
|
||||
Dialer: &gomailDialer{Dialer: d},
|
||||
Enabled: true,
|
||||
Dialer: &gomailDialer{Dialer: d},
|
||||
Templates: NewTemplates(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Queue is an implementation of Backend.Queue.
|
||||
func (b *DefaultBackend) Queue(subject, from string, to []string, contentType, body string) error {
|
||||
// If not enabled, just log the email
|
||||
if !b.Enabled {
|
||||
log.WithFields(log.Fields{
|
||||
"subject": subject,
|
||||
"to": to,
|
||||
"from": from,
|
||||
"body": body,
|
||||
}).Info("Not sending email because email backend is not configured.")
|
||||
return nil
|
||||
// SendEmail is an implementation of Backend.SendEmail.
|
||||
// It renders the template and sends the email immediately via SMTP.
|
||||
func (b *DefaultBackend) SendEmail(templateType, from string, to []string, data interface{}) error {
|
||||
subject, body, err := b.Templates.Execute(templateType, EmailKindText, data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "executing template")
|
||||
}
|
||||
|
||||
return b.queue(subject, from, to, EmailKindText, body)
|
||||
}
|
||||
|
||||
// queue sends the email immediately via SMTP.
|
||||
func (b *DefaultBackend) queue(subject, from string, to []string, contentType, body string) error {
|
||||
m := gomail.NewMessage()
|
||||
m.SetHeader("From", from)
|
||||
m.SetHeader("To", to...)
|
||||
|
|
@ -121,3 +122,34 @@ func (b *DefaultBackend) Queue(subject, from string, to []string, contentType, b
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StdoutBackend is an implementation of the Backend
|
||||
// that prints emails to stdout instead of sending them.
|
||||
// This is useful for development and testing.
|
||||
type StdoutBackend struct {
|
||||
Templates Templates
|
||||
}
|
||||
|
||||
// NewStdoutBackend creates a stdout backend
|
||||
func NewStdoutBackend() *StdoutBackend {
|
||||
return &StdoutBackend{
|
||||
Templates: NewTemplates(),
|
||||
}
|
||||
}
|
||||
|
||||
// SendEmail is an implementation of Backend.SendEmail.
|
||||
// It renders the template and logs the email to stdout instead of sending it.
|
||||
func (b *StdoutBackend) SendEmail(templateType, from string, to []string, data interface{}) error {
|
||||
subject, body, err := b.Templates.Execute(templateType, EmailKindText, data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "executing template")
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"subject": subject,
|
||||
"to": to,
|
||||
"from": from,
|
||||
"body": body,
|
||||
}).Info("Email (not sent, using StdoutBackend)")
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,40 +31,28 @@ func (m *mockDialer) DialAndSend(msgs ...*gomail.Message) error {
|
|||
return m.err
|
||||
}
|
||||
|
||||
func TestDefaultBackendQueue(t *testing.T) {
|
||||
t.Run("enabled sends email", func(t *testing.T) {
|
||||
func TestDefaultBackendSendEmail(t *testing.T) {
|
||||
t.Run("sends email", func(t *testing.T) {
|
||||
mock := &mockDialer{}
|
||||
backend := &DefaultBackend{
|
||||
Dialer: mock,
|
||||
Enabled: true,
|
||||
Dialer: mock,
|
||||
Templates: NewTemplates(),
|
||||
}
|
||||
|
||||
err := backend.Queue("Test Subject", "alice@example.com", []string{"bob@example.com"}, "text/plain", "Test body")
|
||||
data := WelcomeTmplData{
|
||||
AccountEmail: "bob@example.com",
|
||||
WebURL: "https://example.com",
|
||||
}
|
||||
|
||||
err := backend.SendEmail(EmailTypeWelcome, "alice@example.com", []string{"bob@example.com"}, data)
|
||||
if err != nil {
|
||||
t.Fatalf("Queue failed: %v", err)
|
||||
t.Fatalf("SendEmail failed: %v", err)
|
||||
}
|
||||
|
||||
if len(mock.sentMessages) != 1 {
|
||||
t.Errorf("expected 1 message sent, got %d", len(mock.sentMessages))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("disabled does not send email", func(t *testing.T) {
|
||||
mock := &mockDialer{}
|
||||
backend := &DefaultBackend{
|
||||
Dialer: mock,
|
||||
Enabled: false,
|
||||
}
|
||||
|
||||
err := backend.Queue("Test Subject", "alice@example.com", []string{"bob@example.com"}, "text/plain", "Test body")
|
||||
if err != nil {
|
||||
t.Fatalf("Queue failed: %v", err)
|
||||
}
|
||||
|
||||
if len(mock.sentMessages) != 0 {
|
||||
t.Errorf("expected 0 messages sent when disabled, got %d", len(mock.sentMessages))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewDefaultBackend(t *testing.T) {
|
||||
|
|
@ -79,9 +67,6 @@ func TestNewDefaultBackend(t *testing.T) {
|
|||
t.Fatalf("NewDefaultBackend failed: %v", err)
|
||||
}
|
||||
|
||||
if backend.Enabled != true {
|
||||
t.Errorf("expected Enabled to be true, got %v", backend.Enabled)
|
||||
}
|
||||
if backend.Dialer == nil {
|
||||
t.Error("expected Dialer to be set")
|
||||
}
|
||||
|
|
@ -102,3 +87,21 @@ func TestNewDefaultBackend(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestStdoutBackendSendEmail(t *testing.T) {
|
||||
t.Run("logs email without sending", func(t *testing.T) {
|
||||
backend := NewStdoutBackend()
|
||||
|
||||
data := WelcomeTmplData{
|
||||
AccountEmail: "bob@example.com",
|
||||
WebURL: "https://example.com",
|
||||
}
|
||||
|
||||
err := backend.SendEmail(EmailTypeWelcome, "alice@example.com", []string{"bob@example.com"}, data)
|
||||
if err != nil {
|
||||
t.Fatalf("SendEmail failed: %v", err)
|
||||
}
|
||||
|
||||
// StdoutBackend should never return an error, just log
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,13 +40,19 @@ var (
|
|||
EmailKindText = "text/plain"
|
||||
)
|
||||
|
||||
// template is the common interface shared between Template from
|
||||
// tmpl is the common interface shared between Template from
|
||||
// html/template and text/template
|
||||
type template interface {
|
||||
type tmpl interface {
|
||||
Execute(wr io.Writer, data interface{}) error
|
||||
}
|
||||
|
||||
// Templates holds the parsed email templates
|
||||
// template wraps a template with its subject line
|
||||
type template struct {
|
||||
tmpl tmpl
|
||||
subject string
|
||||
}
|
||||
|
||||
// Templates holds the parsed email templates with their subjects
|
||||
type Templates map[string]template
|
||||
|
||||
func getTemplateKey(name, kind string) string {
|
||||
|
|
@ -56,16 +62,19 @@ func getTemplateKey(name, kind string) string {
|
|||
func (tmpl Templates) get(name, kind string) (template, error) {
|
||||
key := getTemplateKey(name, kind)
|
||||
t := tmpl[key]
|
||||
if t == nil {
|
||||
return nil, errors.Errorf("unsupported template '%s' with type '%s'", name, kind)
|
||||
if t.tmpl == nil {
|
||||
return template{}, errors.Errorf("unsupported template '%s' with type '%s'", name, kind)
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (tmpl Templates) set(name, kind string, t template) {
|
||||
func (tmpl Templates) set(name, kind string, t tmpl, subject string) {
|
||||
key := getTemplateKey(name, kind)
|
||||
tmpl[key] = t
|
||||
tmpl[key] = template{
|
||||
tmpl: t,
|
||||
subject: subject,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTemplates initializes templates
|
||||
|
|
@ -84,15 +93,15 @@ func NewTemplates() Templates {
|
|||
}
|
||||
|
||||
T := Templates{}
|
||||
T.set(EmailTypeResetPassword, EmailKindText, passwordResetText)
|
||||
T.set(EmailTypeResetPasswordAlert, EmailKindText, passwordResetAlertText)
|
||||
T.set(EmailTypeWelcome, EmailKindText, welcomeText)
|
||||
T.set(EmailTypeResetPassword, EmailKindText, passwordResetText, "Reset your Dnote password")
|
||||
T.set(EmailTypeResetPasswordAlert, EmailKindText, passwordResetAlertText, "Your Dnote password was changed")
|
||||
T.set(EmailTypeWelcome, EmailKindText, welcomeText, "Welcome to Dnote!")
|
||||
|
||||
return T
|
||||
}
|
||||
|
||||
// initTextTmpl returns a template instance by parsing the template with the given name
|
||||
func initTextTmpl(templateName string) (template, error) {
|
||||
func initTextTmpl(templateName string) (tmpl, error) {
|
||||
filename := fmt.Sprintf("%s.txt", templateName)
|
||||
|
||||
content, err := templates.Files.ReadFile(filename)
|
||||
|
|
@ -108,17 +117,17 @@ func initTextTmpl(templateName string) (template, error) {
|
|||
return t, nil
|
||||
}
|
||||
|
||||
// Execute executes the template with the given name with the givn data
|
||||
func (tmpl Templates) Execute(name, kind string, data any) (string, error) {
|
||||
// Execute executes the template and returns the subject, body, and any error
|
||||
func (tmpl Templates) Execute(name, kind string, data any) (subject, body string, err error) {
|
||||
t, err := tmpl.get(name, kind)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "getting template")
|
||||
return "", "", errors.Wrap(err, "getting template")
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := t.Execute(buf, data); err != nil {
|
||||
return "", errors.Wrap(err, "executing the template")
|
||||
if err := t.tmpl.Execute(buf, data); err != nil {
|
||||
return "", "", errors.Wrap(err, "executing the template")
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
return t.subject, buf.String(), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,11 +65,14 @@ func TestResetPasswordEmail(t *testing.T) {
|
|||
Token: tc.token,
|
||||
WebURL: tc.webURL,
|
||||
}
|
||||
body, err := tmpl.Execute(EmailTypeResetPassword, EmailKindText, dat)
|
||||
subject, body, err := tmpl.Execute(EmailTypeResetPassword, EmailKindText, dat)
|
||||
if err != nil {
|
||||
t.Fatal(errors.Wrap(err, "executing"))
|
||||
}
|
||||
|
||||
if subject != "Reset your Dnote password" {
|
||||
t.Errorf("expected subject 'Reset your Dnote password', got '%s'", subject)
|
||||
}
|
||||
if ok := strings.Contains(body, tc.webURL); !ok {
|
||||
t.Errorf("email body did not contain %s", tc.webURL)
|
||||
}
|
||||
|
|
@ -103,11 +106,14 @@ func TestWelcomeEmail(t *testing.T) {
|
|||
AccountEmail: tc.accountEmail,
|
||||
WebURL: tc.webURL,
|
||||
}
|
||||
body, err := tmpl.Execute(EmailTypeWelcome, EmailKindText, dat)
|
||||
subject, body, err := tmpl.Execute(EmailTypeWelcome, EmailKindText, dat)
|
||||
if err != nil {
|
||||
t.Fatal(errors.Wrap(err, "executing"))
|
||||
}
|
||||
|
||||
if subject != "Welcome to Dnote!" {
|
||||
t.Errorf("expected subject 'Welcome to Dnote!', got '%s'", subject)
|
||||
}
|
||||
if ok := strings.Contains(body, tc.webURL); !ok {
|
||||
t.Errorf("email body did not contain %s", tc.webURL)
|
||||
}
|
||||
|
|
@ -141,11 +147,14 @@ func TestResetPasswordAlertEmail(t *testing.T) {
|
|||
AccountEmail: tc.accountEmail,
|
||||
WebURL: tc.webURL,
|
||||
}
|
||||
body, err := tmpl.Execute(EmailTypeResetPasswordAlert, EmailKindText, dat)
|
||||
subject, body, err := tmpl.Execute(EmailTypeResetPasswordAlert, EmailKindText, dat)
|
||||
if err != nil {
|
||||
t.Fatal(errors.Wrap(err, "executing"))
|
||||
}
|
||||
|
||||
if subject != "Your Dnote password was changed" {
|
||||
t.Errorf("expected subject 'Your Dnote password was changed', got '%s'", subject)
|
||||
}
|
||||
if ok := strings.Contains(body, tc.webURL); !ok {
|
||||
t.Errorf("email body did not contain %s", tc.webURL)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ package middleware
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -143,7 +142,7 @@ func (rl *RateLimiter) Limit(next http.Handler) http.HandlerFunc {
|
|||
func ApplyLimit(h http.HandlerFunc, rateLimit bool) http.Handler {
|
||||
ret := h
|
||||
|
||||
if rateLimit && os.Getenv("APP_ENV") != "TEST" {
|
||||
if rateLimit {
|
||||
ret = defaultLimiter.Limit(ret)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -210,10 +210,10 @@ func MustRespondJSON(t *testing.T, w http.ResponseWriter, i interface{}, message
|
|||
|
||||
// MockEmail is a mock email data
|
||||
type MockEmail struct {
|
||||
Subject string
|
||||
From string
|
||||
To []string
|
||||
Body string
|
||||
TemplateType string
|
||||
From string
|
||||
To []string
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
// MockEmailbackendImplementation is an email backend that simply discards the emails
|
||||
|
|
@ -230,16 +230,16 @@ func (b *MockEmailbackendImplementation) Clear() {
|
|||
b.Emails = []MockEmail{}
|
||||
}
|
||||
|
||||
// Queue is an implementation of Backend.Queue.
|
||||
func (b *MockEmailbackendImplementation) Queue(subject, from string, to []string, contentType, body string) error {
|
||||
// SendEmail is an implementation of Backend.SendEmail.
|
||||
func (b *MockEmailbackendImplementation) SendEmail(templateType, from string, to []string, data interface{}) error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
b.Emails = append(b.Emails, MockEmail{
|
||||
Subject: subject,
|
||||
From: from,
|
||||
To: to,
|
||||
Body: body,
|
||||
TemplateType: templateType,
|
||||
From: from,
|
||||
To: to,
|
||||
Data: data,
|
||||
})
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -7,11 +7,8 @@ dir=$(dirname "${BASH_SOURCE[0]}")
|
|||
basePath="$dir/../.."
|
||||
serverPath="$basePath/pkg/server"
|
||||
|
||||
# load env
|
||||
set -a
|
||||
dotenvPath="$serverPath/.env.dev"
|
||||
source "$dotenvPath"
|
||||
set +a
|
||||
# Set env
|
||||
DBPath=../../dev-server.db
|
||||
|
||||
# copy assets
|
||||
mkdir -p "$basePath/pkg/server/static"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue