package confirmemailcode import ( "context" "errors" "testing" "time" "galaxy/authsession/internal/domain/challenge" "galaxy/authsession/internal/domain/common" "galaxy/authsession/internal/domain/devicesession" "galaxy/authsession/internal/service/shared" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestExecuteConfirmsChallengeAfterTransientProjectionPublishFailures(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) deps.publisher.Errors = []error{errors.New("publish failed"), nil} require.NoError(t, deps.userDirectory.SeedExisting(common.Email("pilot@example.com"), common.UserID("user-1"))) require.NoError(t, deps.challengeStore.Create( context.Background(), sentChallengeFixture(t, deps.hasher, "challenge-1", "pilot@example.com", "654321", deps.now.Add(-time.Minute), deps.now.Add(time.Minute)), )) service := mustNewConfirmService(t, deps) result, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), TimeZone: confirmEmailCodeTimeZone, }) require.NoError(t, err) assert.Equal(t, "device-session-1", result.DeviceSessionID) require.Len(t, deps.publisher.PublishedSnapshots(), 2) } func TestExecuteConfirmedRetryRepublishesAfterTransientProjectionPublishFailures(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) deps.publisher.Errors = []error{errors.New("publish failed"), nil} key := mustClientPublicKey(t, publicKeyString()) require.NoError(t, deps.challengeStore.Create( context.Background(), confirmedChallengeFixture(t, deps.hasher, "challenge-1", "pilot@example.com", "654321", "device-session-1", key, deps.now.Add(-time.Minute), deps.now.Add(time.Minute)), )) require.NoError(t, deps.sessionStore.Create( context.Background(), activeSessionFixture("device-session-1", "user-1", key, deps.now.Add(-time.Minute)), )) service := mustNewConfirmService(t, deps) result, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), TimeZone: confirmEmailCodeTimeZone, }) require.NoError(t, err) assert.Equal(t, "device-session-1", result.DeviceSessionID) require.Len(t, deps.publisher.PublishedSnapshots(), 2) } func TestExecuteRepairsProjectionOnIdenticalRetryAfterExhaustedPublishRetries(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) deps.publisher.Err = errors.New("publish failed") require.NoError(t, deps.userDirectory.SeedExisting(common.Email("pilot@example.com"), common.UserID("user-1"))) require.NoError(t, deps.challengeStore.Create( context.Background(), sentChallengeFixture(t, deps.hasher, "challenge-1", "pilot@example.com", "654321", deps.now.Add(-time.Minute), deps.now.Add(time.Minute)), )) service := mustNewConfirmService(t, deps) _, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), TimeZone: confirmEmailCodeTimeZone, }) require.Error(t, err) assert.Equal(t, shared.ErrorCodeServiceUnavailable, shared.CodeOf(err)) require.Len(t, deps.publisher.PublishedSnapshots(), shared.MaxProjectionPublishAttempts) sessionRecord, getErr := deps.sessionStore.Get(context.Background(), common.DeviceSessionID("device-session-1")) require.NoError(t, getErr) assert.Equal(t, devicesession.StatusActive, sessionRecord.Status) challengeRecord, getErr := deps.challengeStore.Get(context.Background(), common.ChallengeID("challenge-1")) require.NoError(t, getErr) assert.Equal(t, challenge.StatusConfirmedPendingExpire, challengeRecord.Status) require.NotNil(t, challengeRecord.Confirmation) deps.publisher.Err = nil result, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), TimeZone: confirmEmailCodeTimeZone, }) require.NoError(t, err) assert.Equal(t, "device-session-1", result.DeviceSessionID) require.Len(t, deps.publisher.PublishedSnapshots(), shared.MaxProjectionPublishAttempts+1) }