package shared import ( "context" "errors" "testing" "galaxy/authsession/internal/domain/devicesession" "galaxy/authsession/internal/domain/gatewayprojection" "galaxy/authsession/internal/testkit" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestPublishSessionProjectionRetriesUntilSuccess(t *testing.T) { t.Parallel() tests := []struct { name string errors []error wantAttempts int }{ { name: "success on second attempt", errors: []error{errors.New("transient publish failure"), nil}, wantAttempts: 2, }, { name: "success on third attempt", errors: []error{errors.New("transient publish failure"), errors.New("transient publish failure"), nil}, wantAttempts: 3, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() publisher := &testkit.RecordingProjectionPublisher{Errors: tt.errors} err := PublishSessionProjection(context.Background(), publisher, revokedSessionFixture()) require.NoError(t, err) require.Len(t, publisher.PublishedSnapshots(), tt.wantAttempts) }) } } func TestPublishSessionProjectionReturnsServiceUnavailableAfterExhaustedRetries(t *testing.T) { t.Parallel() publisher := &testkit.RecordingProjectionPublisher{Err: errors.New("publish failed")} err := PublishSessionProjection(context.Background(), publisher, revokedSessionFixture()) require.Error(t, err) assert.Equal(t, ErrorCodeServiceUnavailable, CodeOf(err)) require.Len(t, publisher.PublishedSnapshots(), MaxProjectionPublishAttempts) } func TestPublishProjectionSnapshotStopsRetriesWhenContextIsCanceled(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) publisher := &cancelingProjectionPublisher{ cancel: cancel, err: errors.New("publish failed"), } err := PublishProjectionSnapshot(ctx, publisher, mustProjectionSnapshot(t)) require.Error(t, err) assert.Equal(t, ErrorCodeServiceUnavailable, CodeOf(err)) assert.Equal(t, 1, publisher.attempts) } func TestPublishSessionProjectionReturnsInternalErrorForInvalidLocalRecord(t *testing.T) { t.Parallel() publisher := &testkit.RecordingProjectionPublisher{} err := PublishSessionProjection(context.Background(), publisher, invalidSessionFixture()) require.Error(t, err) assert.Equal(t, ErrorCodeInternalError, CodeOf(err)) assert.Empty(t, publisher.PublishedSnapshots()) } type cancelingProjectionPublisher struct { attempts int cancel context.CancelFunc err error } func (p *cancelingProjectionPublisher) PublishSession(_ context.Context, snapshot gatewayprojection.Snapshot) error { if err := snapshot.Validate(); err != nil { return err } p.attempts++ if p.cancel != nil { p.cancel() p.cancel = nil } return p.err } func mustProjectionSnapshot(t *testing.T) gatewayprojection.Snapshot { t.Helper() snapshot, err := ToGatewayProjectionSnapshot(revokedSessionFixture()) require.NoError(t, err) return snapshot } func invalidSessionFixture() devicesession.Session { return devicesession.Session{} }