package mail import ( "context" "errors" "testing" "galaxy/authsession/internal/domain/common" "galaxy/authsession/internal/ports" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestStubSenderSendLoginCode(t *testing.T) { t.Parallel() t.Run("zero value defaults to sent", func(t *testing.T) { t.Parallel() sender := &StubSender{} result, err := sender.SendLoginCode(context.Background(), validInput()) require.NoError(t, err) assert.Equal(t, ports.SendLoginCodeOutcomeSent, result.Outcome) attempts := sender.RecordedAttempts() require.Len(t, attempts, 1) assert.Equal(t, StubModeSent, attempts[0].Mode) assert.Equal(t, validInput(), attempts[0].Input) }) t.Run("default suppressed", func(t *testing.T) { t.Parallel() sender := &StubSender{DefaultMode: StubModeSuppressed} result, err := sender.SendLoginCode(context.Background(), validInput()) require.NoError(t, err) assert.Equal(t, ports.SendLoginCodeOutcomeSuppressed, result.Outcome) attempts := sender.RecordedAttempts() require.Len(t, attempts, 1) assert.Equal(t, StubModeSuppressed, attempts[0].Mode) }) t.Run("default failed uses configured error", func(t *testing.T) { t.Parallel() wantErr := errors.New("delivery refused") sender := &StubSender{ DefaultMode: StubModeFailed, DefaultError: wantErr, } result, err := sender.SendLoginCode(context.Background(), validInput()) require.Error(t, err) assert.ErrorIs(t, err, wantErr) assert.Equal(t, ports.SendLoginCodeResult{}, result) attempts := sender.RecordedAttempts() require.Len(t, attempts, 1) assert.Equal(t, StubModeFailed, attempts[0].Mode) }) t.Run("default failed uses stable fallback error", func(t *testing.T) { t.Parallel() sender := &StubSender{DefaultMode: StubModeFailed} _, err := sender.SendLoginCode(context.Background(), validInput()) require.Error(t, err) assert.EqualError(t, err, "stub mail sender: forced failure") }) t.Run("script overrides default and is consumed fifo", func(t *testing.T) { t.Parallel() wantErr := errors.New("step failed") sender := &StubSender{ DefaultMode: StubModeSent, Script: []StubStep{ {Mode: StubModeSuppressed}, {Mode: StubModeFailed, Err: wantErr}, }, } first, err := sender.SendLoginCode(context.Background(), validInput()) require.NoError(t, err) assert.Equal(t, ports.SendLoginCodeOutcomeSuppressed, first.Outcome) second, err := sender.SendLoginCode(context.Background(), validInput()) require.Error(t, err) assert.ErrorIs(t, err, wantErr) assert.Equal(t, ports.SendLoginCodeResult{}, second) third, err := sender.SendLoginCode(context.Background(), validInput()) require.NoError(t, err) assert.Equal(t, ports.SendLoginCodeOutcomeSent, third.Outcome) attempts := sender.RecordedAttempts() require.Len(t, attempts, 3) assert.Equal(t, []StubMode{StubModeSuppressed, StubModeFailed, StubModeSent}, []StubMode{ attempts[0].Mode, attempts[1].Mode, attempts[2].Mode, }) assert.Empty(t, sender.Script) }) t.Run("invalid default mode returns adapter error", func(t *testing.T) { t.Parallel() sender := &StubSender{DefaultMode: StubMode("queued")} _, err := sender.SendLoginCode(context.Background(), validInput()) require.Error(t, err) assert.ErrorContains(t, err, `default mode "queued" is unsupported`) assert.Empty(t, sender.RecordedAttempts()) }) t.Run("invalid scripted mode returns adapter error", func(t *testing.T) { t.Parallel() sender := &StubSender{ Script: []StubStep{ {Mode: StubMode("queued")}, }, } _, err := sender.SendLoginCode(context.Background(), validInput()) require.Error(t, err) assert.ErrorContains(t, err, `mode "queued" is unsupported`) assert.Empty(t, sender.RecordedAttempts()) assert.Empty(t, sender.Script) }) } func TestStubSenderRecordedAttemptsAreDefensive(t *testing.T) { t.Parallel() sender := &StubSender{} _, err := sender.SendLoginCode(context.Background(), validInput()) require.NoError(t, err) attempts := sender.RecordedAttempts() require.Len(t, attempts, 1) attempts[0].Mode = StubModeFailed attempts[0].Input.Code = "000000" again := sender.RecordedAttempts() require.Len(t, again, 1) assert.Equal(t, StubModeSent, again[0].Mode) assert.Equal(t, "654321", again[0].Input.Code) } func TestStubSenderSendLoginCodeNilContext(t *testing.T) { t.Parallel() sender := &StubSender{} _, err := sender.SendLoginCode(nil, validInput()) require.Error(t, err) assert.ErrorContains(t, err, "nil context") assert.Empty(t, sender.RecordedAttempts()) } func TestStubSenderSendLoginCodeCancelledContext(t *testing.T) { t.Parallel() sender := &StubSender{} ctx, cancel := context.WithCancel(context.Background()) cancel() _, err := sender.SendLoginCode(ctx, validInput()) require.Error(t, err) assert.ErrorIs(t, err, context.Canceled) assert.Empty(t, sender.RecordedAttempts()) } func TestStubSenderSendLoginCodeInvalidInput(t *testing.T) { t.Parallel() sender := &StubSender{} _, err := sender.SendLoginCode(context.Background(), ports.SendLoginCodeInput{}) require.Error(t, err) assert.ErrorContains(t, err, "send login code input email") assert.Empty(t, sender.RecordedAttempts()) } func validInput() ports.SendLoginCodeInput { return ports.SendLoginCodeInput{ Email: common.Email("pilot@example.com"), Code: "654321", } }