package testkit import ( "context" "sync" "galaxy/authsession/internal/ports" ) // RecordingMailSender is a deterministic MailSender double that records every // delivery request and returns preconfigured outcomes or errors. type RecordingMailSender struct { mu sync.Mutex // Results stores queued results consumed by SendLoginCode before // DefaultResult is used. Results []ports.SendLoginCodeResult // DefaultResult stores the result used when Results is empty. DefaultResult ports.SendLoginCodeResult // Err is returned directly from SendLoginCode when set. Err error recordedInputs []ports.SendLoginCodeInput } // SendLoginCode records input and returns the next configured result. func (s *RecordingMailSender) SendLoginCode(ctx context.Context, input ports.SendLoginCodeInput) (ports.SendLoginCodeResult, error) { 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() s.recordedInputs = append(s.recordedInputs, input) if s.Err != nil { return ports.SendLoginCodeResult{}, s.Err } if len(s.Results) > 0 { result := s.Results[0] s.Results = s.Results[1:] if err := result.Validate(); err != nil { return ports.SendLoginCodeResult{}, err } return result, nil } result := s.DefaultResult if result.Outcome == "" { result.Outcome = ports.SendLoginCodeOutcomeSent } if err := result.Validate(); err != nil { return ports.SendLoginCodeResult{}, err } return result, nil } // RecordedInputs returns a stable snapshot of every recorded mail request. func (s *RecordingMailSender) RecordedInputs() []ports.SendLoginCodeInput { s.mu.Lock() defer s.mu.Unlock() return append([]ports.SendLoginCodeInput(nil), s.recordedInputs...) } var _ ports.MailSender = (*RecordingMailSender)(nil)