feat: authsession service
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
// Package mail provides runtime mail-delivery adapters for the auth/session
|
||||
// service.
|
||||
package mail
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"galaxy/authsession/internal/ports"
|
||||
)
|
||||
|
||||
var errForcedFailure = errors.New("stub mail sender: forced failure")
|
||||
|
||||
// StubMode identifies the deterministic outcome used by StubSender for one
|
||||
// delivery attempt.
|
||||
type StubMode string
|
||||
|
||||
const (
|
||||
// StubModeSent reports that the adapter accepts delivery and returns the
|
||||
// stable sent outcome expected by the auth flow.
|
||||
StubModeSent StubMode = "sent"
|
||||
|
||||
// StubModeSuppressed reports that the adapter intentionally suppresses
|
||||
// outward delivery while still returning a successful suppressed outcome.
|
||||
StubModeSuppressed StubMode = "suppressed"
|
||||
|
||||
// StubModeFailed reports that the adapter returns an explicit delivery
|
||||
// failure instead of a successful outcome.
|
||||
StubModeFailed StubMode = "failed"
|
||||
)
|
||||
|
||||
// IsKnown reports whether mode is one of the supported stub delivery modes.
|
||||
func (mode StubMode) IsKnown() bool {
|
||||
switch mode {
|
||||
case StubModeSent, StubModeSuppressed, StubModeFailed:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// StubStep overrides the default stub behavior for one queued delivery
|
||||
// attempt.
|
||||
type StubStep struct {
|
||||
// Mode selects the delivery behavior for this queued step.
|
||||
Mode StubMode
|
||||
|
||||
// Err optionally overrides the failure returned when Mode is StubModeFailed.
|
||||
Err error
|
||||
}
|
||||
|
||||
// Validate reports whether step contains one supported queued behavior.
|
||||
func (step StubStep) Validate() error {
|
||||
if !step.Mode.IsKnown() {
|
||||
return fmt.Errorf("stub mail step mode %q is unsupported", step.Mode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Attempt records one validated delivery request handled by StubSender.
|
||||
type Attempt struct {
|
||||
// Input stores the validated cleartext mail-delivery request exactly as it
|
||||
// was passed into SendLoginCode.
|
||||
Input ports.SendLoginCodeInput
|
||||
|
||||
// Mode stores the resolved stub mode after queued overrides were applied.
|
||||
Mode StubMode
|
||||
}
|
||||
|
||||
// StubSender is a deterministic runtime MailSender implementation intended
|
||||
// for development, local integration, and explicit stub-based tests.
|
||||
//
|
||||
// The zero value is ready to use and defaults to StubModeSent.
|
||||
type StubSender struct {
|
||||
// DefaultMode controls the fallback behavior when Script is empty. The zero
|
||||
// value is treated as StubModeSent so the zero-value sender is usable
|
||||
// without extra configuration.
|
||||
DefaultMode StubMode
|
||||
|
||||
// DefaultError optionally overrides the failure returned when DefaultMode
|
||||
// resolves to StubModeFailed.
|
||||
DefaultError error
|
||||
|
||||
// Script stores queued one-shot overrides consumed in FIFO order before the
|
||||
// default behavior is used.
|
||||
Script []StubStep
|
||||
|
||||
mu sync.Mutex
|
||||
attempts []Attempt
|
||||
}
|
||||
|
||||
// SendLoginCode records one validated delivery request and returns the
|
||||
// deterministic stub outcome selected by the queued script or the default
|
||||
// mode.
|
||||
func (s *StubSender) SendLoginCode(ctx context.Context, input ports.SendLoginCodeInput) (ports.SendLoginCodeResult, error) {
|
||||
if ctx == nil {
|
||||
return ports.SendLoginCodeResult{}, errors.New("stub mail sender: nil context")
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return ports.SendLoginCodeResult{}, err
|
||||
}
|
||||
if err := input.Validate(); err != nil {
|
||||
return ports.SendLoginCodeResult{}, err
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
mode, errOverride, err := s.resolveNextStepLocked()
|
||||
if err != nil {
|
||||
return ports.SendLoginCodeResult{}, err
|
||||
}
|
||||
|
||||
s.attempts = append(s.attempts, Attempt{
|
||||
Input: input,
|
||||
Mode: mode,
|
||||
})
|
||||
|
||||
switch mode {
|
||||
case StubModeSent:
|
||||
return ports.SendLoginCodeResult{Outcome: ports.SendLoginCodeOutcomeSent}, nil
|
||||
case StubModeSuppressed:
|
||||
return ports.SendLoginCodeResult{Outcome: ports.SendLoginCodeOutcomeSuppressed}, nil
|
||||
case StubModeFailed:
|
||||
if errOverride != nil {
|
||||
return ports.SendLoginCodeResult{}, errOverride
|
||||
}
|
||||
return ports.SendLoginCodeResult{}, errForcedFailure
|
||||
default:
|
||||
return ports.SendLoginCodeResult{}, fmt.Errorf("stub mail sender: unsupported resolved mode %q", mode)
|
||||
}
|
||||
}
|
||||
|
||||
// RecordedAttempts returns a stable defensive copy of every validated delivery
|
||||
// attempt handled by the stub.
|
||||
func (s *StubSender) RecordedAttempts() []Attempt {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return append([]Attempt(nil), s.attempts...)
|
||||
}
|
||||
|
||||
func (s *StubSender) resolveNextStepLocked() (StubMode, error, error) {
|
||||
if len(s.Script) > 0 {
|
||||
step := s.Script[0]
|
||||
s.Script = append([]StubStep(nil), s.Script[1:]...)
|
||||
if err := step.Validate(); err != nil {
|
||||
return "", nil, fmt.Errorf("stub mail sender: %w", err)
|
||||
}
|
||||
if step.Mode == StubModeFailed {
|
||||
if step.Err != nil {
|
||||
return step.Mode, step.Err, nil
|
||||
}
|
||||
return step.Mode, errForcedFailure, nil
|
||||
}
|
||||
return step.Mode, nil, nil
|
||||
}
|
||||
|
||||
mode := s.DefaultMode
|
||||
if mode == "" {
|
||||
mode = StubModeSent
|
||||
}
|
||||
if !mode.IsKnown() {
|
||||
return "", nil, fmt.Errorf("stub mail sender: default mode %q is unsupported", mode)
|
||||
}
|
||||
if mode == StubModeFailed {
|
||||
if s.DefaultError != nil {
|
||||
return mode, s.DefaultError, nil
|
||||
}
|
||||
return mode, errForcedFailure, nil
|
||||
}
|
||||
|
||||
return mode, nil, nil
|
||||
}
|
||||
|
||||
var _ ports.MailSender = (*StubSender)(nil)
|
||||
Reference in New Issue
Block a user