Fix FileText status code and unify request body size limits

- FileText: now respects the provided status code instead of defaulting to 200 OK
- Request body limits: prepareRequestBody() is now only called when MaxRequestBodySize > 0
  - ShouldBindJSON, ShouldBindWANF, ShouldBindGOB, ShouldBindForm, GetReqBody, PostForm
    all now use the original c.Request.Body path when no limit is configured
- maxBytesReader: fixed exact-limit boundary case where body size == limit was
  incorrectly rejected
- Added regression tests for FileText status codes and body limit behavior

All existing tests pass, and new tests verify the corrected behavior.
This commit is contained in:
wjqserver 2026-03-31 16:38:04 +08:00
parent ef965f4a6a
commit 64e2ad9e7b
3 changed files with 252 additions and 56 deletions

125
context_bodylimit_test.go Normal file
View file

@ -0,0 +1,125 @@
package touka
import (
"errors"
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
)
func TestFileTextUsesProvidedStatusCode(t *testing.T) {
t.Helper()
dir := t.TempDir()
filePath := filepath.Join(dir, "hello.txt")
if err := os.WriteFile(filePath, []byte("hello touka"), 0o644); err != nil {
t.Fatalf("write temp file: %v", err)
}
rr := httptest.NewRecorder()
c, _ := CreateTestContext(rr)
c.FileText(http.StatusCreated, filePath)
if rr.Code != http.StatusCreated {
t.Fatalf("expected status %d, got %d", http.StatusCreated, rr.Code)
}
if got := rr.Header().Get("Content-Type"); got != "text/plain; charset=utf-8" {
t.Fatalf("unexpected content type: %q", got)
}
if body := rr.Body.String(); body != "hello touka" {
t.Fatalf("unexpected body: %q", body)
}
}
func TestMaxBytesReaderAllowsExactLimit(t *testing.T) {
t.Helper()
reader := NewMaxBytesReader(io.NopCloser(strings.NewReader("abcd")), 4)
defer reader.Close()
data, err := io.ReadAll(reader)
if err != nil {
t.Fatalf("expected exact limit read to succeed, got %v", err)
}
if string(data) != "abcd" {
t.Fatalf("unexpected data: %q", string(data))
}
}
func TestMaxBytesReaderRejectsOverLimit(t *testing.T) {
t.Helper()
reader := NewMaxBytesReader(io.NopCloser(strings.NewReader("abcde")), 4)
defer reader.Close()
_, err := io.ReadAll(reader)
if !errors.Is(err, ErrBodyTooLarge) {
t.Fatalf("expected ErrBodyTooLarge, got %v", err)
}
}
func TestShouldBindJSONHonorsMaxRequestBodySize(t *testing.T) {
t.Helper()
body := strings.NewReader(`{"name":"abcdef"}`)
req := httptest.NewRequest(http.MethodPost, "/json", body)
req.Header.Set("Content-Type", "application/json")
c, _ := CreateTestContextWithRequest(httptest.NewRecorder(), req)
c.SetMaxRequestBodySize(8)
var payload struct {
Name string `json:"name"`
}
err := c.ShouldBindJSON(&payload)
if !errors.Is(err, ErrBodyTooLarge) {
t.Fatalf("expected ErrBodyTooLarge, got %v", err)
}
}
func TestShouldBindFormHonorsMaxRequestBodySize(t *testing.T) {
t.Helper()
body := strings.NewReader("name=abcdef")
req := httptest.NewRequest(http.MethodPost, "/form", body)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
c, _ := CreateTestContextWithRequest(httptest.NewRecorder(), req)
c.SetMaxRequestBodySize(4)
var payload struct {
Name string `form:"name"`
}
err := c.ShouldBindForm(&payload)
if !errors.Is(err, ErrBodyTooLarge) {
t.Fatalf("expected ErrBodyTooLarge, got %v", err)
}
}
func TestPostFormHonorsMaxRequestBodySize(t *testing.T) {
t.Helper()
body := strings.NewReader("name=abcdef")
req := httptest.NewRequest(http.MethodPost, "/form", body)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
c, _ := CreateTestContextWithRequest(httptest.NewRecorder(), req)
c.SetMaxRequestBodySize(4)
if got := c.PostForm("name"); got != "" {
t.Fatalf("expected empty value on over-limit form body, got %q", got)
}
if len(c.Errors) == 0 {
t.Fatal("expected parse error to be recorded")
}
if !errors.Is(c.Errors[0], ErrBodyTooLarge) {
t.Fatalf("expected recorded error to wrap ErrBodyTooLarge, got %v", c.Errors[0])
}
}