Add ParseRange, StreamFile, StreamRange for full and partial content delivery. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
179 lines
4.2 KiB
Go
179 lines
4.2 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func setupTestContext() (*httptest.ResponseRecorder, *gin.Context) {
|
|
gin.SetMode(gin.TestMode)
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
return w, c
|
|
}
|
|
|
|
func parseResponse(t *testing.T, w *httptest.ResponseRecorder) APIResponse {
|
|
t.Helper()
|
|
var resp APIResponse
|
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
|
t.Fatalf("failed to parse response body: %v", err)
|
|
}
|
|
return resp
|
|
}
|
|
|
|
func TestOK(t *testing.T) {
|
|
w, c := setupTestContext()
|
|
OK(c, map[string]string{"msg": "hello"})
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d", w.Code)
|
|
}
|
|
resp := parseResponse(t, w)
|
|
if !resp.Success {
|
|
t.Fatal("expected success=true")
|
|
}
|
|
}
|
|
|
|
func TestCreated(t *testing.T) {
|
|
w, c := setupTestContext()
|
|
Created(c, map[string]int{"id": 1})
|
|
|
|
if w.Code != http.StatusCreated {
|
|
t.Fatalf("expected 201, got %d", w.Code)
|
|
}
|
|
resp := parseResponse(t, w)
|
|
if !resp.Success {
|
|
t.Fatal("expected success=true")
|
|
}
|
|
}
|
|
|
|
func TestBadRequest(t *testing.T) {
|
|
w, c := setupTestContext()
|
|
BadRequest(c, "invalid input")
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected 400, got %d", w.Code)
|
|
}
|
|
resp := parseResponse(t, w)
|
|
if resp.Success {
|
|
t.Fatal("expected success=false")
|
|
}
|
|
if resp.Error != "invalid input" {
|
|
t.Fatalf("expected error 'invalid input', got '%s'", resp.Error)
|
|
}
|
|
}
|
|
|
|
func TestNotFound(t *testing.T) {
|
|
w, c := setupTestContext()
|
|
NotFound(c, "resource missing")
|
|
|
|
if w.Code != http.StatusNotFound {
|
|
t.Fatalf("expected 404, got %d", w.Code)
|
|
}
|
|
resp := parseResponse(t, w)
|
|
if resp.Success {
|
|
t.Fatal("expected success=false")
|
|
}
|
|
if resp.Error != "resource missing" {
|
|
t.Fatalf("expected error 'resource missing', got '%s'", resp.Error)
|
|
}
|
|
}
|
|
|
|
func TestInternalError(t *testing.T) {
|
|
w, c := setupTestContext()
|
|
InternalError(c, "something broke")
|
|
|
|
if w.Code != http.StatusInternalServerError {
|
|
t.Fatalf("expected 500, got %d", w.Code)
|
|
}
|
|
resp := parseResponse(t, w)
|
|
if resp.Success {
|
|
t.Fatal("expected success=false")
|
|
}
|
|
if resp.Error != "something broke" {
|
|
t.Fatalf("expected error 'something broke', got '%s'", resp.Error)
|
|
}
|
|
}
|
|
|
|
func TestErrorWithStatus(t *testing.T) {
|
|
w, c := setupTestContext()
|
|
ErrorWithStatus(c, http.StatusConflict, "already exists")
|
|
|
|
if w.Code != http.StatusConflict {
|
|
t.Fatalf("expected 409, got %d", w.Code)
|
|
}
|
|
resp := parseResponse(t, w)
|
|
if resp.Success {
|
|
t.Fatal("expected success=false")
|
|
}
|
|
if resp.Error != "already exists" {
|
|
t.Fatalf("expected error 'already exists', got '%s'", resp.Error)
|
|
}
|
|
}
|
|
|
|
func TestParseRangeStandard(t *testing.T) {
|
|
tests := []struct {
|
|
rangeHeader string
|
|
fileSize int64
|
|
wantStart int64
|
|
wantEnd int64
|
|
wantErr bool
|
|
}{
|
|
{"bytes=0-1023", 10000, 0, 1023, false},
|
|
{"bytes=1024-", 10000, 1024, 9999, false},
|
|
{"bytes=-1024", 10000, 8976, 9999, false},
|
|
{"bytes=0-0", 10000, 0, 0, false},
|
|
{"bytes=9999-", 10000, 9999, 9999, false},
|
|
}
|
|
for _, tt := range tests {
|
|
start, end, err := ParseRange(tt.rangeHeader, tt.fileSize)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("ParseRange(%q, %d) error = %v, wantErr %v", tt.rangeHeader, tt.fileSize, err, tt.wantErr)
|
|
continue
|
|
}
|
|
if !tt.wantErr {
|
|
if start != tt.wantStart || end != tt.wantEnd {
|
|
t.Errorf("ParseRange(%q, %d) = (%d, %d), want (%d, %d)", tt.rangeHeader, tt.fileSize, start, end, tt.wantStart, tt.wantEnd)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseRangeInvalidAndMultiPart(t *testing.T) {
|
|
tests := []struct {
|
|
rangeHeader string
|
|
fileSize int64
|
|
}{
|
|
{"", 10000},
|
|
{"bytes=9999-0", 10000},
|
|
{"bytes=20000-", 10000},
|
|
{"bytes=0-100,200-300", 10000},
|
|
{"bytes=0-100, 400-500", 10000},
|
|
{"bytes=", 10000},
|
|
{"chars=0-100", 10000},
|
|
}
|
|
for _, tt := range tests {
|
|
_, _, err := ParseRange(tt.rangeHeader, tt.fileSize)
|
|
if err == nil {
|
|
t.Errorf("ParseRange(%q, %d) expected error, got nil", tt.rangeHeader, tt.fileSize)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseRangeEdgeCases(t *testing.T) {
|
|
start, end, err := ParseRange("bytes=0-99999", 10000)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if end != 9999 {
|
|
t.Errorf("end = %d, want 9999 (clamped to fileSize-1)", end)
|
|
}
|
|
if start != 0 {
|
|
t.Errorf("start = %d, want 0", start)
|
|
}
|
|
}
|