Skip to content

Commit

Permalink
Return numSent from Agent.Send, add log/err info
Browse files Browse the repository at this point in the history
While drafting the upcoming handler.sendHandler and email.SendResponse,
it became apparent that this extra information would be useful.
  • Loading branch information
mbland committed May 17, 2023
1 parent 6e86d27 commit b8ee5eb
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 26 deletions.
20 changes: 12 additions & 8 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type SubscriptionAgent interface {
) (ops.OperationResult, error)
Remove(ctx context.Context, email string) error
Restore(ctx context.Context, email string) error
Send(ctx context.Context, msg *email.Message) error
Send(ctx context.Context, msg *email.Message) (numSent int, err error)
}

type ProdAgent struct {
Expand Down Expand Up @@ -217,31 +217,35 @@ func (a *ProdAgent) Restore(ctx context.Context, address string) (err error) {
return
}

func (a *ProdAgent) Send(ctx context.Context, msg *email.Message) (err error) {
func (a *ProdAgent) Send(
ctx context.Context, msg *email.Message,
) (numSent int, err error) {
if err = msg.Validate(email.CheckDomain(a.EmailDomainName)); err != nil {
return err
return
}

mt := email.NewMessageTemplate(msg)

var sendErr error

sender := db.SubscriberFunc(func(sub *db.Subscriber) bool {
recipient := &email.Recipient{Email: sub.Email, Uid: sub.Uid}
recipient.SetUnsubscribeInfo(a.UnsubscribeEmail, a.ApiBaseUrl)

msg := mt.GenerateMessage(recipient)
m := mt.GenerateMessage(recipient)
var msgId string

if msgId, sendErr = a.Mailer.Send(ctx, sub.Email, msg); sendErr != nil {
if msgId, sendErr = a.Mailer.Send(ctx, sub.Email, m); sendErr != nil {
return false
}
a.Log.Printf("sent %s to %s", msgId, sub.Email)
a.Log.Printf("sent \"%s\" id: %s to: %s", msg.Subject, msgId, sub.Email)
numSent++
return true
})

err = a.Db.ProcessSubscribersInState(ctx, db.SubscriberVerified, sender)
if err = errors.Join(err, sendErr); err != nil {
err = fmt.Errorf("error sending message to list: %w", err)
const errFmt = "error sending \"%s\" to list: %w"
err = fmt.Errorf(errFmt, msg.Subject, err)
}
return
}
37 changes: 23 additions & 14 deletions agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -602,13 +602,16 @@ func TestSend(t *testing.T) {
) {
t.Helper()

msgId, msg := mailer.GetMessageTo(t, sub.Email)
msgId, m := mailer.GetMessageTo(t, sub.Email)
unsubUrl := ops.UnsubscribeUrl(testUnsubBaseUrl, sub.Email, sub.Uid)
unsubMailto := ops.UnsubscribeMailto(testUnsubEmail, sub.Email, sub.Uid)
expectedLogMsg := fmt.Sprintf(
"sent \"%s\" id: %s to: %s", msg.Subject, msgId, sub.Email,
)
assert.Equal(t, mailer.MessageIds[sub.Email], msgId)
assert.Assert(t, is.Contains(msg, unsubUrl))
assert.Assert(t, is.Contains(msg, unsubMailto))
logs.AssertContains(t, "sent "+msgId+" to "+sub.Email)
assert.Assert(t, is.Contains(m, unsubUrl))
assert.Assert(t, is.Contains(m, unsubMailto))
logs.AssertContains(t, expectedLogMsg)
}

assertSentToVerifiedSubscribers := func(
Expand All @@ -634,35 +637,41 @@ func TestSend(t *testing.T) {
t.Run("Succeeds", func(t *testing.T) {
agent, _, mailer, logs, ctx := setup()

err := agent.Send(ctx, msg)
numSent, err := agent.Send(ctx, msg)

assert.NilError(t, err)
assertSentToVerifiedSubscribers(t, mailer, logs)
assertDidNotSendToPendingSubscribers(t, mailer)
assert.Equal(t, len(db.TestVerifiedSubscribers), numSent)
})

t.Run("FailsIfMessageFailsValidation", func(t *testing.T) {
agent, _, mailer, _, ctx := setup()
agent, _, _, _, ctx := setup()
badMsg := *msg
badMsg.From = ""

err := agent.Send(ctx, &badMsg)
numSent, err := agent.Send(ctx, &badMsg)

assert.ErrorContains(t, err, "missing From")
assert.Equal(t, 0, len(mailer.RecipientMessages))
const expectedErrMsg = "message failed validation: missing From"
assert.ErrorContains(t, err, expectedErrMsg)
assert.Equal(t, 0, numSent)
})

t.Run("FailsIfProcessSubscribersInStateFails", func(t *testing.T) {
agent, dbase, mailer, _, ctx := setup()
agent, dbase, _, _, ctx := setup()
procSubsErr := errors.New("ProcSubsInState error")
dbase.SimulateProcSubsErr = func(_ string) error {
return procSubsErr
}

err := agent.Send(ctx, msg)
numSent, err := agent.Send(ctx, msg)

expectedErrMsg := fmt.Sprintf(
"error sending \"%s\" to list: ProcSubsInState error", msg.Subject,
)
assert.Error(t, err, expectedErrMsg)
assert.Assert(t, tu.ErrorIs(err, procSubsErr))
assert.Equal(t, 0, len(mailer.RecipientMessages))
assert.Equal(t, 0, numSent)
})

t.Run("StopsProcessingAndFailsIfSendFails", func(t *testing.T) {
Expand All @@ -671,13 +680,13 @@ func TestSend(t *testing.T) {
sendErr := errors.New("Mailer.Send failed")
mailer.RecipientErrors[secondSub.Email] = sendErr

err := agent.Send(ctx, msg)
numSent, err := agent.Send(ctx, msg)

assert.Assert(t, tu.ErrorIs(err, sendErr))
assertSentToVerifiedSubscriber(
t, 0, db.TestVerifiedSubscribers[0], mailer, logs,
)
mailer.AssertNoMessageSent(t, secondSub.Email)
assert.Equal(t, 1, len(mailer.RecipientMessages))
assert.Equal(t, 1, numSent)
})
}
4 changes: 2 additions & 2 deletions agent/decoy_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ func (a *DecoyAgent) Restore(ctx context.Context, email string) error {
return nil
}

func (a *DecoyAgent) Send(ctx context.Context, msg *email.Message) error {
return nil
func (a *DecoyAgent) Send(_ context.Context, _ *email.Message) (int, error) {
return 0, nil
}
5 changes: 3 additions & 2 deletions handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type testAgent struct {
Email string
Uid uuid.UUID
OpResult ops.OperationResult
NumSent int
Error error
Calls []testAgentCalls
}
Expand Down Expand Up @@ -77,9 +78,9 @@ func (a *testAgent) Restore(ctx context.Context, email string) error {
return a.Error
}

func (a *testAgent) Send(ctx context.Context, msg *email.Message) error {
func (a *testAgent) Send(_ context.Context, msg *email.Message) (int, error) {
a.Calls = append(a.Calls, testAgentCalls{Method: "Send", Msg: msg})
return a.Error
return a.NumSent, a.Error
}

const testEmailDomain = "mike-bland.com"
Expand Down

0 comments on commit b8ee5eb

Please sign in to comment.