package main import ( "bytes" "mime/multipart" "net/http" "net/http/httptest" "strconv" "testing" "github.com/gin-gonic/gin" ) // 分片数学: 片数 ceil 与每片期望大小 func TestChunkMath(t *testing.T) { if got := expectedChunks(5<<30 + 1); got != 6 { t.Fatalf("expectedChunks(5GB+1) = %d, want 6", got) } if got := expectedChunks(5 << 30); got != 5 { t.Fatalf("expectedChunks(5GB) = %d, want 5", got) } if got := expectedChunkSize(5<<30+1, 0); got != chunkSize { t.Fatalf("chunk0 size = %d, want 1GB", got) } if got := expectedChunkSize(5<<30+1, 5); got != 1 { t.Fatalf("last chunk size = %d, want 1", got) } } func chunkRequest(t *testing.T, uploadID string, idx, totalChunks int, filename string, totalSize int64, content []byte) *gin.Context { t.Helper() var buf bytes.Buffer w := multipart.NewWriter(&buf) fw, err := w.CreateFormFile("chunk", "chunk") if err != nil { t.Fatal(err) } fw.Write(content) w.Close() req := httptest.NewRequest("POST", "/upload-chunk", &buf) req.Header.Set("Content-Type", w.FormDataContentType()) req.Header.Set("X-Upload-Id", uploadID) req.Header.Set("X-Chunk-Index", strconv.Itoa(idx)) req.Header.Set("X-Total-Chunks", strconv.Itoa(totalChunks)) req.Header.Set("X-Filename", filename) req.Header.Set("X-Total-Size", strconv.FormatInt(totalSize, 10)) rec := httptest.NewRecorder() c, _ := gin.CreateTestContext(rec) c.Request = req return c } // 校验拒绝路径: 非法 id / 小于阈值 / 越界 / 片数不匹配 / 超配额, 均不落盘 func TestChunkHandlerRejections(t *testing.T) { *auditDir = "" // 测试不写审计日志 oldQuota := *quota *quota = 1 << 30 defer func() { *quota = oldQuota }() cases := []struct { name string id string idx int total int fn string size int64 status int }{ {"invalid upload id", "bad id!", 0, 6, "x.bin", chunkThreshold + 1, http.StatusBadRequest}, {"below threshold", "uploadid123456", 0, 1, "x.bin", chunkThreshold, http.StatusBadRequest}, {"index out of range", "uploadid123456", 6, 6, "x.bin", chunkThreshold + 1, http.StatusBadRequest}, {"chunk count mismatch", "uploadid123456", 0, 2, "x.bin", chunkThreshold + 1, http.StatusBadRequest}, {"quota exceeded", "uploadid123456", 0, 6, "x.bin", chunkThreshold + 1, http.StatusInsufficientStorage}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { c := chunkRequest(t, tc.id, tc.idx, tc.total, tc.fn, tc.size, []byte("data")) uploadChunkHandler(c) if c.Writer.Status() != tc.status { t.Fatalf("status = %d, want %d", c.Writer.Status(), tc.status) } }) } }