package auth import ( "context" "errors" "testing" "time" "github.com/alicebob/miniredis/v2" "github.com/redis/go-redis/v9" "github.com/stretchr/testify/require" "github.com/gochat/gochat/internal/config" "github.com/gochat/gochat/internal/model" ) func TestJWTAccessTokenUsesConfiguredMinuteExpiry(t *testing.T) { cfg := &config.JWTConfig{Secret: "test-secret", AccessExpiryMinutes: 15, RefreshExpiryHours: 168} service := NewJWTService(cfg) pair, err := service.GenerateTokenPair(&model.User{Base: model.Base{ID: 7}, Provider: "email"}, 3, "agent") require.NoError(t, err) require.WithinDuration(t, time.Now().Add(15*time.Minute), pair.ExpiresAt, 2*time.Second) } func TestRefreshRotationRejectsReusedToken(t *testing.T) { cfg := &config.JWTConfig{Secret: "test-secret", AccessExpiryMinutes: 15, RefreshExpiryHours: 168} store := NewRefreshTokenStore(nil, cfg) ctx := context.Background() require.NoError(t, store.StoreForClient(ctx, 7, "browser", "old-token")) require.NoError(t, store.RotateForClient(ctx, 7, "browser", "new-token")) valid, err := store.ValidateForClient(ctx, 7, "browser", "old-token") require.NoError(t, err) require.False(t, valid) } func TestWSTicketIsOneTimeAndExpires(t *testing.T) { mr := miniredis.RunT(t) rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()}) t.Cleanup(func() { _ = rdb.Close() }) store := NewWSTicketStore(rdb, 5*time.Second) ctx := context.Background() want := WSTicketClaims{UserID: 7, AccountID: 3, Role: "agent", Provider: "email"} ticket, err := store.Issue(ctx, want) require.NoError(t, err) got, err := store.Consume(ctx, ticket) require.NoError(t, err) require.Equal(t, want, *got) _, err = store.Consume(ctx, ticket) require.ErrorIs(t, err, ErrInvalidWSTicket) expiring, err := store.Issue(ctx, want) require.NoError(t, err) mr.FastForward(6 * time.Second) _, err = store.Consume(ctx, expiring) require.True(t, errors.Is(err, ErrInvalidWSTicket)) }