fix: 修复 AI 服务商配置新增失败 (#222)
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"cmautobuy/admin/internal/testutil"
|
||||
"cmautobuy/admin/model"
|
||||
"cmautobuy/admin/repository"
|
||||
"cmautobuy/admin/service"
|
||||
)
|
||||
|
||||
func TestRedirectAIConfig_区分成功和校验失败(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
tests := []struct {
|
||||
name, message, errorMessage, wantKey, wantValue string
|
||||
}{
|
||||
{name: "成功", message: "配置已保存", wantKey: "msg", wantValue: "配置已保存"},
|
||||
{name: "校验失败", errorMessage: "Base URL 无效", wantKey: "error", wantValue: "Base URL 无效"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
context, _ := gin.CreateTestContext(recorder)
|
||||
context.Request = httptest.NewRequest(http.MethodPost, "/settings/ai/save", nil)
|
||||
redirectAIConfig(context, test.message, test.errorMessage)
|
||||
if context.Writer.Status() != http.StatusSeeOther {
|
||||
t.Fatalf("状态码=%d,期望 303", context.Writer.Status())
|
||||
}
|
||||
location, err := url.Parse(recorder.Header().Get("Location"))
|
||||
if err != nil || location.Path != "/settings/ai" {
|
||||
t.Fatalf("Location=%q err=%v", recorder.Header().Get("Location"), err)
|
||||
}
|
||||
if got := location.Query().Get(test.wantKey); got != test.wantValue {
|
||||
t.Fatalf("%s=%q,期望 %q", test.wantKey, got, test.wantValue)
|
||||
}
|
||||
otherKey := "msg"
|
||||
if test.wantKey == "msg" {
|
||||
otherKey = "error"
|
||||
}
|
||||
if location.Query().Has(otherKey) {
|
||||
t.Fatalf("成功和错误参数不能混用: %s", location.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAIConfigSave_校验失败使用Error跳转(t *testing.T) {
|
||||
context, recorder, _ := aiConfigSaveContext(t, url.Values{"confidence_threshold": {"not-a-number"}})
|
||||
context.Set(currentUserKey, &model.User{Role: model.RoleAdmin, Status: model.UserActive})
|
||||
(&Handler{}).AIConfigSave(context)
|
||||
location, err := url.Parse(recorder.Header().Get("Location"))
|
||||
if context.Writer.Status() != http.StatusSeeOther || err != nil || location.Query().Get("error") == "" || location.Query().Has("msg") {
|
||||
t.Fatalf("校验失败跳转错误: code=%d location=%q err=%v", context.Writer.Status(), recorder.Header().Get("Location"), err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAIConfigSave_数据库异常返回500而非成功跳转(t *testing.T) {
|
||||
db, err := sql.Open("mysql", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
context, recorder, engine := aiConfigSaveContext(t, validAIConfigForm())
|
||||
context.Set(currentUserKey, &model.User{UserID: "USR-1", Role: model.RoleAdmin, Status: model.UserActive})
|
||||
engine.SetHTMLTemplate(template.Must(template.New("root").Parse(
|
||||
`{{define "partials/error"}}{{.Message}}{{end}}`,
|
||||
)))
|
||||
(&Handler{db: db, aiPolicy: service.NewAIEndpointPolicy(nil)}).AIConfigSave(context)
|
||||
if recorder.Code != http.StatusInternalServerError || recorder.Header().Get("Location") != "" {
|
||||
t.Fatalf("数据库异常不能伪装成 303 成功: code=%d location=%q", recorder.Code, recorder.Header().Get("Location"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAIConfigSave_真实MySQL新增后列表可见并使用Msg跳转(t *testing.T) {
|
||||
db := testutil.OpenMySQL(t)
|
||||
actor := model.User{UserID: "USR-WEB-AI", Username: "web-ai-admin", PasswordHash: "test-hash",
|
||||
Role: model.RoleAdmin, Status: model.UserActive, PasswordChangedAt: model.NowISO(),
|
||||
CreatedAt: model.NowISO(), UpdatedAt: model.NowISO()}
|
||||
if err := repository.CreateUser(db, actor); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
context, recorder, _ := aiConfigSaveContext(t, validAIConfigForm())
|
||||
context.Set(currentUserKey, &actor)
|
||||
(&Handler{db: db, aiPolicy: service.NewAIEndpointPolicy(nil)}).AIConfigSave(context)
|
||||
location, err := url.Parse(recorder.Header().Get("Location"))
|
||||
if context.Writer.Status() != http.StatusSeeOther || err != nil || location.Query().Get("msg") == "" || location.Query().Has("error") {
|
||||
t.Fatalf("成功跳转错误: code=%d location=%q err=%v", context.Writer.Status(), recorder.Header().Get("Location"), err)
|
||||
}
|
||||
items, err := repository.ListAIProviders(db)
|
||||
if err != nil || len(items) != 1 || items[0].Name != "测试主线路" {
|
||||
t.Fatalf("跳转后应能查询新增配置: items=%+v err=%v", items, err)
|
||||
}
|
||||
}
|
||||
|
||||
func aiConfigSaveContext(t *testing.T, values url.Values) (*gin.Context, *httptest.ResponseRecorder, *gin.Engine) {
|
||||
t.Helper()
|
||||
gin.SetMode(gin.TestMode)
|
||||
recorder := httptest.NewRecorder()
|
||||
context, engine := gin.CreateTestContext(recorder)
|
||||
request := httptest.NewRequest(http.MethodPost, "/settings/ai/save", strings.NewReader(values.Encode()))
|
||||
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
context.Request = request
|
||||
return context, recorder, engine
|
||||
}
|
||||
|
||||
func validAIConfigForm() url.Values {
|
||||
return url.Values{
|
||||
"name": {"测试主线路"}, "base_url": {"https://api.example.com/v1"}, "model": {"model-1"},
|
||||
"timeout_seconds": {"30"}, "max_concurrency": {"2"}, "confidence_threshold": {"85"},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user