Some checks failed
Build docker and publish / build (20.15.1) (push) Failing after 7m57s
193 lines
5.3 KiB
Go
193 lines
5.3 KiB
Go
package user
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/alicebob/miniredis/v2"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/perfect-panel/server/internal/config"
|
|
"github.com/perfect-panel/server/internal/svc"
|
|
"github.com/perfect-panel/server/pkg/constant"
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
type handlerResponse struct {
|
|
Code uint32 `json:"code"`
|
|
Msg string `json:"msg"`
|
|
Data json.RawMessage `json:"data"`
|
|
}
|
|
|
|
func newDeleteAccountTestRouter(serverCtx *svc.ServiceContext) *gin.Engine {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.POST("/v1/public/user/delete_account", DeleteAccountHandler(serverCtx))
|
|
return router
|
|
}
|
|
|
|
func TestDeleteAccountHandlerInvalidParamsUsesUnifiedResponse(t *testing.T) {
|
|
router := newDeleteAccountTestRouter(&svc.ServiceContext{})
|
|
|
|
reqBody := bytes.NewBufferString(`{"email":"invalid-email"}`)
|
|
req := httptest.NewRequest(http.MethodPost, "/v1/public/user/delete_account", reqBody)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
recorder := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(recorder, req)
|
|
|
|
if recorder.Code != http.StatusOK {
|
|
t.Fatalf("expected HTTP 200, got %d", recorder.Code)
|
|
}
|
|
|
|
var resp handlerResponse
|
|
if err := json.Unmarshal(recorder.Body.Bytes(), &resp); err != nil {
|
|
t.Fatalf("failed to decode response: %v", err)
|
|
}
|
|
|
|
if resp.Code != 400 {
|
|
t.Fatalf("expected business code 400, got %d, body=%s", resp.Code, recorder.Body.String())
|
|
}
|
|
|
|
var raw map[string]interface{}
|
|
if err := json.Unmarshal(recorder.Body.Bytes(), &raw); err != nil {
|
|
t.Fatalf("failed to decode raw response: %v", err)
|
|
}
|
|
if _, exists := raw["error"]; exists {
|
|
t.Fatalf("unexpected raw error field in response: %s", recorder.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestDeleteAccountHandlerVerifyCodeErrorUsesUnifiedResponse(t *testing.T) {
|
|
redisClient := redis.NewClient(&redis.Options{
|
|
Addr: "invalid:6379",
|
|
Dialer: func(_ context.Context, _, _ string) (net.Conn, error) {
|
|
return nil, errors.New("dial disabled in test")
|
|
},
|
|
})
|
|
defer redisClient.Close()
|
|
|
|
serverCtx := &svc.ServiceContext{
|
|
Redis: redisClient,
|
|
Config: config.Config{
|
|
VerifyCode: config.VerifyCode{
|
|
VerifyCodeExpireTime: 900,
|
|
},
|
|
},
|
|
}
|
|
router := newDeleteAccountTestRouter(serverCtx)
|
|
|
|
reqBody := bytes.NewBufferString(`{"email":"user@example.com","code":"123456"}`)
|
|
req := httptest.NewRequest(http.MethodPost, "/v1/public/user/delete_account", reqBody)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
recorder := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(recorder, req)
|
|
|
|
if recorder.Code != http.StatusOK {
|
|
t.Fatalf("expected HTTP 200, got %d", recorder.Code)
|
|
}
|
|
|
|
var resp handlerResponse
|
|
if err := json.Unmarshal(recorder.Body.Bytes(), &resp); err != nil {
|
|
t.Fatalf("failed to decode response: %v", err)
|
|
}
|
|
|
|
if resp.Code != 70001 {
|
|
t.Fatalf("expected business code 70001, got %d, body=%s", resp.Code, recorder.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestVerifyEmailCode_DeleteAccountSceneConsume(t *testing.T) {
|
|
miniRedis := miniredis.RunT(t)
|
|
redisClient := redis.NewClient(&redis.Options{Addr: miniRedis.Addr()})
|
|
t.Cleanup(func() {
|
|
redisClient.Close()
|
|
miniRedis.Close()
|
|
})
|
|
|
|
serverCtx := &svc.ServiceContext{
|
|
Redis: redisClient,
|
|
Config: config.Config{
|
|
VerifyCode: config.VerifyCode{VerifyCodeExpireTime: 900},
|
|
},
|
|
}
|
|
|
|
email := "delete-account@example.com"
|
|
code := "112233"
|
|
cacheKey := seedDeleteSceneCode(t, redisClient, constant.DeleteAccount.String(), email, code)
|
|
|
|
err := verifyEmailCode(context.Background(), serverCtx, email, code)
|
|
if err != nil {
|
|
t.Fatalf("verifyEmailCode returned unexpected error: %v", err)
|
|
}
|
|
|
|
exists, err := redisClient.Exists(context.Background(), cacheKey).Result()
|
|
if err != nil {
|
|
t.Fatalf("failed to check redis key: %v", err)
|
|
}
|
|
if exists != 0 {
|
|
t.Fatalf("expected verification code to be consumed, key still exists")
|
|
}
|
|
}
|
|
|
|
func TestVerifyEmailCode_SecurityFallbackConsume(t *testing.T) {
|
|
miniRedis := miniredis.RunT(t)
|
|
redisClient := redis.NewClient(&redis.Options{Addr: miniRedis.Addr()})
|
|
t.Cleanup(func() {
|
|
redisClient.Close()
|
|
miniRedis.Close()
|
|
})
|
|
|
|
serverCtx := &svc.ServiceContext{
|
|
Redis: redisClient,
|
|
Config: config.Config{
|
|
VerifyCode: config.VerifyCode{VerifyCodeExpireTime: 900},
|
|
},
|
|
}
|
|
|
|
email := "security-fallback@example.com"
|
|
code := "445566"
|
|
cacheKey := seedDeleteSceneCode(t, redisClient, constant.Security.String(), email, code)
|
|
|
|
err := verifyEmailCode(context.Background(), serverCtx, email, code)
|
|
if err != nil {
|
|
t.Fatalf("verifyEmailCode fallback returned unexpected error: %v", err)
|
|
}
|
|
|
|
exists, err := redisClient.Exists(context.Background(), cacheKey).Result()
|
|
if err != nil {
|
|
t.Fatalf("failed to check redis key: %v", err)
|
|
}
|
|
if exists != 0 {
|
|
t.Fatalf("expected fallback verification code to be consumed, key still exists")
|
|
}
|
|
}
|
|
|
|
func seedDeleteSceneCode(t *testing.T, redisClient *redis.Client, scene string, email string, code string) string {
|
|
t.Helper()
|
|
|
|
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeCacheKey, scene, email)
|
|
payload := map[string]interface{}{
|
|
"code": code,
|
|
"lastAt": time.Now().Unix(),
|
|
}
|
|
payloadRaw, err := json.Marshal(payload)
|
|
if err != nil {
|
|
t.Fatalf("failed to marshal payload: %v", err)
|
|
}
|
|
err = redisClient.Set(context.Background(), cacheKey, payloadRaw, time.Minute*15).Err()
|
|
if err != nil {
|
|
t.Fatalf("failed to seed redis payload: %v", err)
|
|
}
|
|
|
|
return cacheKey
|
|
}
|