chore: baseline snapshot before moderation-strictness feature
Repo .git was damaged (refs/ missing, index truncated, object store near-empty). Repaired via git init (rebuilt refs), stale index preserved as .git/index.corrupt-20260910.bak, then full worktree committed as a rollback baseline.
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
// Copyright 2026 tamakyi. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package form
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/flamego/flamego"
|
||||
"github.com/flamego/template"
|
||||
|
||||
"github.com/tamakyi/TamaBox/internal/context"
|
||||
)
|
||||
|
||||
// TestBindURLEncodedSkipsFileHeaderField 回归测试:application/x-www-form-urlencoded 提交
|
||||
// 到带 []*multipart.FileHeader 字段的结构体时不应 panic。
|
||||
//
|
||||
// 背景:Bind 的非 multipart 分支曾对所有字段无条件 Set(string),遇到 NewQuestion.Images
|
||||
// ([]*multipart.FileHeader)触发 reflect.Set 类型不匹配 panic(提问表单因带
|
||||
// enctype="multipart/form-data" 走 multipart 分支而从未触发;独立私聊表单等纯文本提交
|
||||
// 会命中此路径,导致提问关闭时提交私聊 500)。
|
||||
func TestBindURLEncodedSkipsFileHeaderField(t *testing.T) {
|
||||
f := flamego.New()
|
||||
// 最小依赖注入:提供项目 context.Context 与 template.Data(Bind 仅用到这两个参数)。
|
||||
f.Use(func(ctx flamego.Context) {
|
||||
data := template.Data{}
|
||||
ctx.Map(context.Context{Context: ctx, Data: data})
|
||||
ctx.Map(data)
|
||||
})
|
||||
|
||||
var bound NewQuestion
|
||||
f.Post("/", Bind(NewQuestion{}), func(q NewQuestion) {
|
||||
bound = q
|
||||
})
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/",
|
||||
strings.NewReader("content=你好&start_chat_val=true"))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
rec := httptest.NewRecorder()
|
||||
f.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code >= http.StatusInternalServerError {
|
||||
t.Fatalf("unexpected status %d, want < 500 (reflect.Set panic 不应发生)", rec.Code)
|
||||
}
|
||||
if bound.Content != "你好" {
|
||||
t.Fatalf("Content = %q, want %q", bound.Content, "你好")
|
||||
}
|
||||
if bound.StartChat != "true" {
|
||||
t.Fatalf("StartChat = %q, want %q", bound.StartChat, "true")
|
||||
}
|
||||
if bound.Images != nil {
|
||||
t.Fatalf("Images = %v, want nil(非 multipart 请求应跳过图片字段)", bound.Images)
|
||||
}
|
||||
}
|
||||
|
||||
// TestBindMultipartStillBindsImages 确认 multipart 分支(提问表单路径)不受影响:
|
||||
// 文件字段仍能正常绑定。
|
||||
func TestBindMultipartStillBindsImages(t *testing.T) {
|
||||
f := flamego.New()
|
||||
f.Use(func(ctx flamego.Context) {
|
||||
data := template.Data{}
|
||||
ctx.Map(context.Context{Context: ctx, Data: data})
|
||||
ctx.Map(data)
|
||||
})
|
||||
|
||||
var bound NewQuestion
|
||||
f.Post("/", Bind(NewQuestion{}), func(q NewQuestion) {
|
||||
bound = q
|
||||
})
|
||||
|
||||
// multipart 请求,携带一个普通字段,无文件
|
||||
body := "--BOUND\r\n" +
|
||||
"Content-Disposition: form-data; name=\"content\"\r\n\r\n" +
|
||||
"hello\r\n" +
|
||||
"--BOUND--\r\n"
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "multipart/form-data; boundary=BOUND")
|
||||
rec := httptest.NewRecorder()
|
||||
f.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code >= http.StatusInternalServerError {
|
||||
t.Fatalf("unexpected status %d, want < 500", rec.Code)
|
||||
}
|
||||
if bound.Content != "hello" {
|
||||
t.Fatalf("Content = %q, want %q", bound.Content, "hello")
|
||||
}
|
||||
if bound.Images != nil {
|
||||
t.Fatalf("Images = %v, want nil", bound.Images)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user