Implement notifications service with shared messaging and email drivers (#36)

This commit is contained in:
Stephan D committed 2026-09-12 14:41:55 +02:00
1 parent 36277b6c45
commit 5c2d36281b
54 files changed
+3746 -6

No files matched your search

@@ -0,0 +1,64 @@
package service
import (
"context"
"testing"
"time"
"git.sendico.io/tech/sxchnge/notifications/email"
"git.sendico.io/tech/sxchnge/notifications/model"
"go.uber.org/zap"
)
type workerRepo struct {
Repository
job model.Notification
saved *model.Notification
}
func (r *workerRepo) Recover(context.Context, time.Time) error { return nil }
func (r *workerRepo) Claim(context.Context, string, time.Time) (*model.Notification, error) {
n := r.job
return &n, nil
}
func (r *workerRepo) Finish(_ context.Context, n *model.Notification) error { r.saved = n; return nil }
type workerSender struct {
calls int
outcome email.Outcome
}
func (s *workerSender) Send(context.Context, string, model.Email) email.Outcome {
s.calls++
return s.outcome
}
func TestWorkerTerminalAndRetryOutcomes(t *testing.T) {
for _, tc := range []struct {
name string
expiry time.Time
driver string
attempts uint32
outcome model.State
want model.State
calls int
}{
{"expired", time.Now().Add(-time.Minute), "resend", 1, model.Accepted, model.Expired, 0},
{"driver changed", time.Now().Add(time.Hour), "sendgrid", 1, model.Accepted, model.Failed, 0},
{"retry exhausted", time.Now().Add(time.Hour), "resend", 5, model.RetryWait, model.Failed, 1},
{"retry expiry", time.Now().Add(time.Second), "resend", 1, model.RetryWait, model.Expired, 1},
{"ambiguous", time.Now().Add(time.Hour), "resend", 1, model.Unknown, model.Unknown, 1},
{"invalid driver outcome", time.Now().Add(time.Hour), "resend", 1, model.Queued, model.Unknown, 1},
} {
t.Run(tc.name, func(t *testing.T) {
repo := &workerRepo{job: model.Notification{Driver: tc.driver, ExpiresAt: tc.expiry, Attempts: tc.attempts}}
sender := &workerSender{outcome: email.Outcome{State: tc.outcome}}
svc := New(repo, nil, sender, "resend", zap.NewNop())
if _, err := svc.Dispatch(context.Background(), "id"); err != nil {
t.Fatal(err)
}
if repo.saved == nil || repo.saved.State != tc.want || sender.calls != tc.calls {
t.Fatal(repo.saved, sender.calls)
}
})
}
}