fix(setup):修复系统初始化故障问题

This commit is contained in:
chengqian committed 2026-08-07 17:56:13 +08:00
1 parent 3256d84c30
commit 4f749930d6
4 files changed
+163 -3

No files matched your search

+4 -3
View File
@@ -2,6 +2,7 @@ package setup
import (
"encoding/json"
"net/http"
"sync/atomic"
"unicode/utf8"
@@ -69,19 +70,19 @@ func HandleSetupInitialize(r *ghttp.Request) {
// Validate username format
if utf8.RuneCountInString(req.Username) < 3 || utf8.RuneCountInString(req.Username) > 20 {
response.ErrorMsg(r, consts.CodeSetupInvalidUsername, consts.MsgSetupInvalidUsername)
response.ErrorWithCode(r, http.StatusUnprocessableEntity, consts.CodeSetupInvalidUsername, consts.MsgSetupInvalidUsername)
return
}
// Validate password strength
if err := common.ValidatePassword(req.Password); err != nil {
response.ErrorMsg(r, consts.CodePasswordTooWeak, err.Error())
response.ErrorWithCode(r, http.StatusUnprocessableEntity, consts.CodePasswordTooWeak, err.Error())
return
}
// Validate password confirmation
if req.Password != req.ConfirmPassword {
response.ErrorMsg(r, consts.CodeSetupPasswordMismatch, consts.MsgSetupPasswordMismatch)
response.ErrorWithCode(r, http.StatusUnprocessableEntity, consts.CodeSetupPasswordMismatch, consts.MsgSetupPasswordMismatch)
return
}
+74
View File
@@ -0,0 +1,74 @@
package setup_test
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/guid"
"github.com/qianfree/team-api/internal/consts"
setuphandler "github.com/qianfree/team-api/internal/handler/setup"
)
func TestHandleSetupInitializeValidationErrors(t *testing.T) {
s := g.Server(guid.S())
s.SetDumpRouterMap(false)
s.BindHandler("/initialize", setuphandler.HandleSetupInitialize)
server := httptest.NewServer(s)
defer server.Close()
tests := []struct {
name string
body string
wantCode int
}{
{
name: "invalid username",
body: `{"username":"ab","password":"Password1","confirmPassword":"Password1"}`,
wantCode: consts.CodeSetupInvalidUsername,
},
{
name: "weak password",
body: `{"username":"admin","password":"short","confirmPassword":"short"}`,
wantCode: consts.CodePasswordTooWeak,
},
{
name: "password mismatch",
body: `{"username":"admin","password":"Password1","confirmPassword":"Password2"}`,
wantCode: consts.CodeSetupPasswordMismatch,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res, err := server.Client().Post(
server.URL+"/initialize",
"application/json",
strings.NewReader(tt.body),
)
if err != nil {
t.Fatalf("POST /initialize: %v", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusUnprocessableEntity {
t.Errorf("status = %d, want %d", res.StatusCode, http.StatusUnprocessableEntity)
}
var body struct {
Code int `json:"code"`
}
if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
t.Fatalf("decode response: %v", err)
}
if body.Code != tt.wantCode {
t.Errorf("code = %d, want %d", body.Code, tt.wantCode)
}
})
}
}
+4
View File
@@ -2,6 +2,7 @@ package response
import (
"errors"
"net/http"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
@@ -128,6 +129,9 @@ func ErrorMsg(r *ghttp.Request, code int, message string) {
// ErrorWithCode writes an error with separate HTTP status and business code.
// Use this when HTTP status differs from the business code (e.g., HTTP 401 with code 10020).
func ErrorWithCode(r *ghttp.Request, httpStatus int, code int, message string) {
if httpStatus < 100 || httpStatus > 599 {
httpStatus = http.StatusInternalServerError
}
requestID := getRequestID(r)
r.Response.WriteHeader(httpStatus)
r.Response.WriteJson(jsonResp{
+81
View File
@@ -0,0 +1,81 @@
package response_test
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/util/guid"
apiresponse "github.com/qianfree/team-api/internal/response"
)
func TestErrorResponseStatusAndCode(t *testing.T) {
s := g.Server(guid.S())
s.SetDumpRouterMap(false)
s.BindHandler("/standard", func(r *ghttp.Request) {
apiresponse.ErrorMsg(r, http.StatusBadRequest, "bad request")
})
s.BindHandler("/business", func(r *ghttp.Request) {
apiresponse.ErrorWithCode(r, http.StatusUnprocessableEntity, 10079, "invalid username")
})
s.BindHandler("/invalid-status", func(r *ghttp.Request) {
apiresponse.ErrorWithCode(r, 10079, 10079, "invalid username")
})
server := httptest.NewServer(s)
defer server.Close()
tests := []struct {
name string
path string
wantStatus int
wantCode int
}{
{
name: "standard HTTP error",
path: "/standard",
wantStatus: http.StatusBadRequest,
wantCode: http.StatusBadRequest,
},
{
name: "business error uses separate HTTP status",
path: "/business",
wantStatus: http.StatusUnprocessableEntity,
wantCode: 10079,
},
{
name: "invalid HTTP status falls back to internal error",
path: "/invalid-status",
wantStatus: http.StatusInternalServerError,
wantCode: 10079,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res, err := server.Client().Get(server.URL + tt.path)
if err != nil {
t.Fatalf("GET %s: %v", tt.path, err)
}
defer res.Body.Close()
if res.StatusCode != tt.wantStatus {
t.Errorf("status = %d, want %d", res.StatusCode, tt.wantStatus)
}
var body struct {
Code int `json:"code"`
}
if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
t.Fatalf("decode response: %v", err)
}
if body.Code != tt.wantCode {
t.Errorf("code = %d, want %d", body.Code, tt.wantCode)
}
})
}
}