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) } }