package confirmemailcode import ( "context" "errors" "github.com/stretchr/testify/require" "testing" "time" "galaxy/authsession/internal/domain/challenge" "galaxy/authsession/internal/domain/common" "galaxy/authsession/internal/domain/devicesession" "galaxy/authsession/internal/domain/userresolution" "galaxy/authsession/internal/service/shared" "galaxy/authsession/internal/testkit" ) func TestExecuteConfirmsChallengeForExistingUser(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) if err := deps.userDirectory.SeedExisting(common.Email("pilot@example.com"), common.UserID("user-1")); err != nil { require.Failf(t, "test failed", "SeedExisting() returned error: %v", err) } if err := 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))); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service := mustNewConfirmService(t, deps) result, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), }) if err != nil { require.Failf(t, "test failed", "Execute() returned error: %v", err) } if result.DeviceSessionID != "device-session-1" { require.Failf(t, "test failed", "Execute().DeviceSessionID = %q, want %q", result.DeviceSessionID, "device-session-1") } record, err := deps.sessionStore.Get(context.Background(), common.DeviceSessionID("device-session-1")) if err != nil { require.Failf(t, "test failed", "Get() returned error: %v", err) } if record.Status != devicesession.StatusActive { require.Failf(t, "test failed", "session status = %q, want %q", record.Status, devicesession.StatusActive) } challengeRecord, err := deps.challengeStore.Get(context.Background(), common.ChallengeID("challenge-1")) if err != nil { require.Failf(t, "test failed", "Get() returned error: %v", err) } if challengeRecord.Status != challenge.StatusConfirmedPendingExpire || challengeRecord.Confirmation == nil { require.Failf(t, "test failed", "challenge status = %q, confirmation = %+v", challengeRecord.Status, challengeRecord.Confirmation) } if len(deps.publisher.PublishedSnapshots()) != 1 { require.Failf(t, "test failed", "PublishedSnapshots() length = %d, want 1", len(deps.publisher.PublishedSnapshots())) } } func TestExecuteConfirmsChallengeByCreatingUser(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) if err := deps.userDirectory.QueueCreatedUserIDs(common.UserID("user-created")); err != nil { require.Failf(t, "test failed", "QueueCreatedUserIDs() returned error: %v", err) } if err := deps.challengeStore.Create(context.Background(), sentChallengeFixture(t, deps.hasher, "challenge-1", "new@example.com", "654321", deps.now.Add(-time.Minute), deps.now.Add(time.Minute))); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service := mustNewConfirmService(t, deps) result, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), }) if err != nil { require.Failf(t, "test failed", "Execute() returned error: %v", err) } if result.DeviceSessionID != "device-session-1" { require.Failf(t, "test failed", "Execute().DeviceSessionID = %q, want %q", result.DeviceSessionID, "device-session-1") } record, err := deps.sessionStore.Get(context.Background(), common.DeviceSessionID("device-session-1")) if err != nil { require.Failf(t, "test failed", "Get() returned error: %v", err) } if record.UserID != common.UserID("user-created") { require.Failf(t, "test failed", "session user id = %q, want %q", record.UserID, common.UserID("user-created")) } } func TestExecuteConfirmsSuppressedChallenge(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) if err := deps.userDirectory.SeedExisting(common.Email("pilot@example.com"), common.UserID("user-1")); err != nil { require.Failf(t, "test failed", "SeedExisting() returned error: %v", err) } record := sentChallengeFixture(t, deps.hasher, "challenge-1", "pilot@example.com", "654321", deps.now.Add(-time.Minute), deps.now.Add(time.Minute)) record.Status = challenge.StatusDeliverySuppressed record.DeliveryState = challenge.DeliverySuppressed if err := record.Validate(); err != nil { require.Failf(t, "test failed", "Validate() returned error: %v", err) } if err := deps.challengeStore.Create(context.Background(), record); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service := mustNewConfirmService(t, deps) result, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), }) if err != nil { require.Failf(t, "test failed", "Execute() returned error: %v", err) } if result.DeviceSessionID != "device-session-1" { require.Failf(t, "test failed", "Execute().DeviceSessionID = %q, want %q", result.DeviceSessionID, "device-session-1") } } func TestExecuteReturnsChallengeNotFound(t *testing.T) { t.Parallel() service := mustNewConfirmService(t, newConfirmDeps(t)) _, err := service.Execute(context.Background(), Input{ ChallengeID: "missing", Code: "654321", ClientPublicKey: publicKeyString(), }) if shared.CodeOf(err) != shared.ErrorCodeChallengeNotFound { require.Failf(t, "test failed", "Execute() error code = %q, want %q", shared.CodeOf(err), shared.ErrorCodeChallengeNotFound) } } func TestExecuteReturnsChallengeExpiredAndMarksExpired(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) if err := deps.challengeStore.Create(context.Background(), sentChallengeFixture(t, deps.hasher, "challenge-1", "pilot@example.com", "654321", deps.now.Add(-2*time.Minute), deps.now.Add(-time.Second))); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service := mustNewConfirmService(t, deps) _, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), }) if shared.CodeOf(err) != shared.ErrorCodeChallengeExpired { require.Failf(t, "test failed", "Execute() error code = %q, want %q", shared.CodeOf(err), shared.ErrorCodeChallengeExpired) } record, err := deps.challengeStore.Get(context.Background(), common.ChallengeID("challenge-1")) if err != nil { require.Failf(t, "test failed", "Get() returned error: %v", err) } if record.Status != challenge.StatusExpired { require.Failf(t, "test failed", "challenge status = %q, want %q", record.Status, challenge.StatusExpired) } } func TestExecuteReturnsChallengeExpiredForConfirmedChallengeAfterRetentionWindow(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) key, err := shared.ParseClientPublicKey(publicKeyString()) if err != nil { require.Failf(t, "test failed", "ParseClientPublicKey() returned error: %v", err) } record := confirmedChallengeFixture( t, deps.hasher, "challenge-1", "pilot@example.com", "654321", "device-session-1", key, deps.now.Add(-2*challenge.ConfirmedRetention), deps.now.Add(-time.Second), ) if err := deps.challengeStore.Create(context.Background(), record); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service := mustNewConfirmService(t, deps) _, err = service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), }) if shared.CodeOf(err) != shared.ErrorCodeChallengeExpired { require.Failf(t, "test failed", "Execute() error code = %q, want %q", shared.CodeOf(err), shared.ErrorCodeChallengeExpired) } updated, err := deps.challengeStore.Get(context.Background(), common.ChallengeID("challenge-1")) if err != nil { require.Failf(t, "test failed", "Get() returned error: %v", err) } if updated.Status != challenge.StatusExpired { require.Failf(t, "test failed", "challenge status = %q, want %q", updated.Status, challenge.StatusExpired) } if updated.Confirmation != nil { require.Failf(t, "test failed", "Confirmation = %+v, want nil after expiration", updated.Confirmation) } } func TestExecuteReturnsInvalidClientPublicKey(t *testing.T) { t.Parallel() service := mustNewConfirmService(t, newConfirmDeps(t)) _, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: "invalid", }) if shared.CodeOf(err) != shared.ErrorCodeInvalidClientPublicKey { require.Failf(t, "test failed", "Execute() error code = %q, want %q", shared.CodeOf(err), shared.ErrorCodeInvalidClientPublicKey) } } func TestExecuteInvalidCodeIncrementsAttempts(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) if err := 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))); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service := mustNewConfirmService(t, deps) _, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "000000", ClientPublicKey: publicKeyString(), }) if shared.CodeOf(err) != shared.ErrorCodeInvalidCode { require.Failf(t, "test failed", "Execute() error code = %q, want %q", shared.CodeOf(err), shared.ErrorCodeInvalidCode) } record, err := deps.challengeStore.Get(context.Background(), common.ChallengeID("challenge-1")) if err != nil { require.Failf(t, "test failed", "Get() returned error: %v", err) } if record.Attempts.Confirm != 1 { require.Failf(t, "test failed", "Attempts.Confirm = %d, want 1", record.Attempts.Confirm) } } func TestExecuteFifthInvalidAttemptMarksChallengeFailed(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) record := sentChallengeFixture(t, deps.hasher, "challenge-1", "pilot@example.com", "654321", deps.now.Add(-time.Minute), deps.now.Add(time.Minute)) record.Attempts.Confirm = 4 if err := deps.challengeStore.Create(context.Background(), record); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service := mustNewConfirmService(t, deps) _, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "000000", ClientPublicKey: publicKeyString(), }) if shared.CodeOf(err) != shared.ErrorCodeInvalidCode { require.Failf(t, "test failed", "Execute() error code = %q, want %q", shared.CodeOf(err), shared.ErrorCodeInvalidCode) } updated, err := deps.challengeStore.Get(context.Background(), common.ChallengeID("challenge-1")) if err != nil { require.Failf(t, "test failed", "Get() returned error: %v", err) } if updated.Status != challenge.StatusFailed { require.Failf(t, "test failed", "challenge status = %q, want %q", updated.Status, challenge.StatusFailed) } } func TestExecuteDoesNotCreateSessionAfterTooManyAttempts(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) if err := deps.userDirectory.SeedExisting(common.Email("pilot@example.com"), common.UserID("user-1")); err != nil { require.Failf(t, "test failed", "SeedExisting() returned error: %v", err) } record := sentChallengeFixture(t, deps.hasher, "challenge-1", "pilot@example.com", "654321", deps.now.Add(-time.Minute), deps.now.Add(time.Minute)) record.Attempts.Confirm = challenge.MaxInvalidConfirmAttempts record.Status = challenge.StatusFailed if err := record.Validate(); err != nil { require.Failf(t, "test failed", "Validate() returned error: %v", err) } if err := deps.challengeStore.Create(context.Background(), record); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service := mustNewConfirmService(t, deps) _, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), }) if shared.CodeOf(err) != shared.ErrorCodeInvalidCode { require.Failf(t, "test failed", "Execute() error code = %q, want %q", shared.CodeOf(err), shared.ErrorCodeInvalidCode) } if got, err := deps.sessionStore.CountActiveByUserID(context.Background(), common.UserID("user-1")); err != nil { require.Failf(t, "test failed", "CountActiveByUserID() returned error: %v", err) } else if got != 0 { require.Failf(t, "test failed", "CountActiveByUserID() = %d, want 0", got) } } func TestExecuteReturnsSameSessionIDForIdempotentRetryAndRepublishes(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) key, err := shared.ParseClientPublicKey(publicKeyString()) if err != nil { require.Failf(t, "test failed", "ParseClientPublicKey() returned error: %v", err) } record := confirmedChallengeFixture(t, deps.hasher, "challenge-1", "pilot@example.com", "654321", "device-session-1", key, deps.now.Add(-time.Minute), deps.now.Add(time.Minute)) if err := deps.challengeStore.Create(context.Background(), record); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } if err := deps.sessionStore.Create(context.Background(), activeSessionFixture("device-session-1", "user-1", key, deps.now.Add(-time.Minute))); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service := mustNewConfirmService(t, deps) result, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), }) if err != nil { require.Failf(t, "test failed", "Execute() returned error: %v", err) } if result.DeviceSessionID != "device-session-1" { require.Failf(t, "test failed", "Execute().DeviceSessionID = %q, want %q", result.DeviceSessionID, "device-session-1") } if len(deps.publisher.PublishedSnapshots()) != 1 { require.Failf(t, "test failed", "PublishedSnapshots() length = %d, want 1", len(deps.publisher.PublishedSnapshots())) } } func TestExecuteReturnsInvalidCodeForDifferentKeyDuringIdempotentRetry(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) key, err := shared.ParseClientPublicKey(publicKeyString()) if err != nil { require.Failf(t, "test failed", "ParseClientPublicKey() returned error: %v", err) } record := confirmedChallengeFixture(t, deps.hasher, "challenge-1", "pilot@example.com", "654321", "device-session-1", key, deps.now.Add(-time.Minute), deps.now.Add(time.Minute)) if err := deps.challengeStore.Create(context.Background(), record); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } if err := deps.sessionStore.Create(context.Background(), activeSessionFixture("device-session-1", "user-1", key, deps.now.Add(-time.Minute))); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service := mustNewConfirmService(t, deps) _, err = service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: alternatePublicKeyString(), }) if shared.CodeOf(err) != shared.ErrorCodeInvalidCode { require.Failf(t, "test failed", "Execute() error code = %q, want %q", shared.CodeOf(err), shared.ErrorCodeInvalidCode) } updated, err := deps.challengeStore.Get(context.Background(), common.ChallengeID("challenge-1")) if err != nil { require.Failf(t, "test failed", "Get() returned error: %v", err) } if updated.Attempts.Confirm != 0 { require.Failf(t, "test failed", "Attempts.Confirm = %d, want 0", updated.Attempts.Confirm) } if updated.Confirmation == nil { require.FailNow(t, "Confirmation = nil, want metadata to stay intact") } if updated.Confirmation.SessionID != common.DeviceSessionID("device-session-1") { require.Failf(t, "test failed", "Confirmation.SessionID = %q, want %q", updated.Confirmation.SessionID, common.DeviceSessionID("device-session-1")) } } func TestExecuteReturnsInvalidCodeForNonConfirmableStates(t *testing.T) { t.Parallel() tests := []struct { name string status challenge.Status deliveryState challenge.DeliveryState }{ {name: "pending send", status: challenge.StatusPendingSend, deliveryState: challenge.DeliveryPending}, {name: "failed", status: challenge.StatusFailed, deliveryState: challenge.DeliveryFailed}, {name: "cancelled", status: challenge.StatusCancelled, deliveryState: challenge.DeliverySent}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) record := sentChallengeFixture(t, deps.hasher, "challenge-1", "pilot@example.com", "654321", deps.now.Add(-time.Minute), deps.now.Add(time.Minute)) record.Status = tt.status record.DeliveryState = tt.deliveryState if err := record.Validate(); err != nil { require.Failf(t, "test failed", "Validate() returned error: %v", err) } if err := deps.challengeStore.Create(context.Background(), record); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service := mustNewConfirmService(t, deps) _, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), }) if shared.CodeOf(err) != shared.ErrorCodeInvalidCode { require.Failf(t, "test failed", "Execute() error code = %q, want %q", shared.CodeOf(err), shared.ErrorCodeInvalidCode) } updated, err := deps.challengeStore.Get(context.Background(), common.ChallengeID("challenge-1")) if err != nil { require.Failf(t, "test failed", "Get() returned error: %v", err) } if updated.Attempts.Confirm != 0 { require.Failf(t, "test failed", "Attempts.Confirm = %d, want 0", updated.Attempts.Confirm) } }) } } func TestExecuteMarksChallengeFailedAndReturnsBlockedByPolicy(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) if err := deps.userDirectory.SeedBlockedEmail(common.Email("pilot@example.com"), userresolution.BlockReasonCode("policy_block")); err != nil { require.Failf(t, "test failed", "SeedBlockedEmail() returned error: %v", err) } if err := 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))); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service := mustNewConfirmService(t, deps) _, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), }) if shared.CodeOf(err) != shared.ErrorCodeBlockedByPolicy { require.Failf(t, "test failed", "Execute() error code = %q, want %q", shared.CodeOf(err), shared.ErrorCodeBlockedByPolicy) } record, err := deps.challengeStore.Get(context.Background(), common.ChallengeID("challenge-1")) if err != nil { require.Failf(t, "test failed", "Get() returned error: %v", err) } if record.Status != challenge.StatusFailed { require.Failf(t, "test failed", "challenge status = %q, want %q", record.Status, challenge.StatusFailed) } } func TestExecuteReturnsSessionLimitExceededWithoutConsumingChallenge(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) if err := deps.userDirectory.SeedExisting(common.Email("pilot@example.com"), common.UserID("user-1")); err != nil { require.Failf(t, "test failed", "SeedExisting() returned error: %v", err) } if err := 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))); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } if err := deps.sessionStore.Create(context.Background(), activeSessionFixture("device-session-existing", "user-1", mustClientPublicKey(t, publicKeyString()), deps.now.Add(-2*time.Minute))); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } limit := 1 deps.configProvider.Config.ActiveSessionLimit = &limit service := mustNewConfirmService(t, deps) _, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), }) if shared.CodeOf(err) != shared.ErrorCodeSessionLimitExceeded { require.Failf(t, "test failed", "Execute() error code = %q, want %q", shared.CodeOf(err), shared.ErrorCodeSessionLimitExceeded) } record, err := deps.challengeStore.Get(context.Background(), common.ChallengeID("challenge-1")) if err != nil { require.Failf(t, "test failed", "Get() returned error: %v", err) } if record.Status != challenge.StatusSent { require.Failf(t, "test failed", "challenge status = %q, want %q", record.Status, challenge.StatusSent) } if record.Attempts.Confirm != 0 { require.Failf(t, "test failed", "Attempts.Confirm = %d, want 0", record.Attempts.Confirm) } } func TestExecuteReturnsServiceUnavailableThenSucceedsIdempotentlyAfterPublishFailure(t *testing.T) { t.Parallel() deps := newConfirmDeps(t) deps.publisher.Err = errors.New("publish failed") if err := deps.userDirectory.SeedExisting(common.Email("pilot@example.com"), common.UserID("user-1")); err != nil { require.Failf(t, "test failed", "SeedExisting() returned error: %v", err) } if err := 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))); err != nil { require.Failf(t, "test failed", "Create() returned error: %v", err) } service := mustNewConfirmService(t, deps) _, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), }) if shared.CodeOf(err) != shared.ErrorCodeServiceUnavailable { require.Failf(t, "test failed", "first Execute() error code = %q, want %q", shared.CodeOf(err), shared.ErrorCodeServiceUnavailable) } deps.publisher.Err = nil result, err := service.Execute(context.Background(), Input{ ChallengeID: "challenge-1", Code: "654321", ClientPublicKey: publicKeyString(), }) if err != nil { require.Failf(t, "test failed", "second Execute() returned error: %v", err) } if result.DeviceSessionID != "device-session-1" { require.Failf(t, "test failed", "second Execute().DeviceSessionID = %q, want %q", result.DeviceSessionID, "device-session-1") } } type confirmDeps struct { challengeStore *testkit.InMemoryChallengeStore sessionStore *testkit.InMemorySessionStore userDirectory *testkit.InMemoryUserDirectory configProvider testkit.StaticConfigProvider publisher *testkit.RecordingProjectionPublisher idGenerator *testkit.SequenceIDGenerator hasher testkit.DeterministicCodeHasher now time.Time } func newConfirmDeps(t *testing.T) confirmDeps { t.Helper() return confirmDeps{ challengeStore: &testkit.InMemoryChallengeStore{}, sessionStore: &testkit.InMemorySessionStore{}, userDirectory: &testkit.InMemoryUserDirectory{}, configProvider: testkit.StaticConfigProvider{}, publisher: &testkit.RecordingProjectionPublisher{}, idGenerator: &testkit.SequenceIDGenerator{ DeviceSessionIDs: []common.DeviceSessionID{"device-session-1"}, }, hasher: testkit.DeterministicCodeHasher{}, now: time.Unix(20, 0).UTC(), } } func mustNewConfirmService(t *testing.T, deps confirmDeps) *Service { t.Helper() service, err := New( deps.challengeStore, deps.sessionStore, deps.userDirectory, deps.configProvider, deps.publisher, deps.idGenerator, deps.hasher, testkit.FixedClock{Time: deps.now}, ) if err != nil { require.Failf(t, "test failed", "New() returned error: %v", err) } return service } func sentChallengeFixture( t *testing.T, hasher testkit.DeterministicCodeHasher, challengeID string, email string, code string, createdAt time.Time, expiresAt time.Time, ) challenge.Challenge { t.Helper() codeHash, err := hasher.Hash(code) if err != nil { require.Failf(t, "test failed", "Hash() returned error: %v", err) } record := challenge.Challenge{ ID: common.ChallengeID(challengeID), Email: common.Email(email), CodeHash: codeHash, Status: challenge.StatusSent, DeliveryState: challenge.DeliverySent, CreatedAt: createdAt, ExpiresAt: expiresAt, } if err := record.Validate(); err != nil { require.Failf(t, "test failed", "Validate() returned error: %v", err) } return record } func confirmedChallengeFixture( t *testing.T, hasher testkit.DeterministicCodeHasher, challengeID string, email string, code string, deviceSessionID string, clientPublicKey common.ClientPublicKey, createdAt time.Time, expiresAt time.Time, ) challenge.Challenge { t.Helper() record := sentChallengeFixture(t, hasher, challengeID, email, code, createdAt, expiresAt) record.Status = challenge.StatusConfirmedPendingExpire record.Confirmation = &challenge.Confirmation{ SessionID: common.DeviceSessionID(deviceSessionID), ClientPublicKey: clientPublicKey, ConfirmedAt: createdAt.Add(time.Minute), } if err := record.Validate(); err != nil { require.Failf(t, "test failed", "Validate() returned error: %v", err) } return record } func activeSessionFixture(deviceSessionID string, userID string, clientPublicKey common.ClientPublicKey, createdAt time.Time) devicesession.Session { return devicesession.Session{ ID: common.DeviceSessionID(deviceSessionID), UserID: common.UserID(userID), ClientPublicKey: clientPublicKey, Status: devicesession.StatusActive, CreatedAt: createdAt, } } func mustClientPublicKey(t *testing.T, value string) common.ClientPublicKey { t.Helper() key, err := shared.ParseClientPublicKey(value) if err != nil { require.Failf(t, "test failed", "ParseClientPublicKey() returned error: %v", err) } return key } func publicKeyString() string { return "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=" } func alternatePublicKeyString() string { return "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=" }