Files
tamakyi e3e59e7c69 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.
2026-09-10 10:56:27 +08:00

95 lines
3.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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)
}
}