Implements the top-level httpx.Client that composes the full chain: Logging → User Middlewares → Retry → Circuit Breaker → Balancer → Transport - Response wrapper with JSON/XML/Bytes decoding and body caching - NewJSONRequest helper with Content-Type and GetBody support - Functional options: WithBaseURL, WithTimeout, WithRetry, WithEndpoints, etc. - Integration tests covering retry, balancing, error mapping, and JSON round-trips
97 lines
2.3 KiB
Go
97 lines
2.3 KiB
Go
package httpx
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func makeTestResponse(statusCode int, body string) *Response {
|
|
return newResponse(&http.Response{
|
|
StatusCode: statusCode,
|
|
Body: io.NopCloser(strings.NewReader(body)),
|
|
Header: make(http.Header),
|
|
})
|
|
}
|
|
|
|
func TestResponse(t *testing.T) {
|
|
t.Run("Bytes returns body", func(t *testing.T) {
|
|
r := makeTestResponse(200, "hello world")
|
|
b, err := r.Bytes()
|
|
if err != nil {
|
|
t.Fatalf("Bytes() error: %v", err)
|
|
}
|
|
if string(b) != "hello world" {
|
|
t.Errorf("Bytes() = %q, want %q", string(b), "hello world")
|
|
}
|
|
})
|
|
|
|
t.Run("body caching returns same data", func(t *testing.T) {
|
|
r := makeTestResponse(200, "cached body")
|
|
b1, err := r.Bytes()
|
|
if err != nil {
|
|
t.Fatalf("first Bytes() error: %v", err)
|
|
}
|
|
b2, err := r.Bytes()
|
|
if err != nil {
|
|
t.Fatalf("second Bytes() error: %v", err)
|
|
}
|
|
if string(b1) != string(b2) {
|
|
t.Errorf("Bytes() returned different data: %q vs %q", b1, b2)
|
|
}
|
|
})
|
|
|
|
t.Run("String returns body as string", func(t *testing.T) {
|
|
r := makeTestResponse(200, "string body")
|
|
s, err := r.String()
|
|
if err != nil {
|
|
t.Fatalf("String() error: %v", err)
|
|
}
|
|
if s != "string body" {
|
|
t.Errorf("String() = %q, want %q", s, "string body")
|
|
}
|
|
})
|
|
|
|
t.Run("JSON decodes body", func(t *testing.T) {
|
|
r := makeTestResponse(200, `{"name":"test","value":42}`)
|
|
var result struct {
|
|
Name string `json:"name"`
|
|
Value int `json:"value"`
|
|
}
|
|
if err := r.JSON(&result); err != nil {
|
|
t.Fatalf("JSON() error: %v", err)
|
|
}
|
|
if result.Name != "test" {
|
|
t.Errorf("Name = %q, want %q", result.Name, "test")
|
|
}
|
|
if result.Value != 42 {
|
|
t.Errorf("Value = %d, want %d", result.Value, 42)
|
|
}
|
|
})
|
|
|
|
t.Run("IsSuccess for 2xx", func(t *testing.T) {
|
|
for _, code := range []int{200, 201, 204, 299} {
|
|
r := makeTestResponse(code, "")
|
|
if !r.IsSuccess() {
|
|
t.Errorf("IsSuccess() = false for status %d", code)
|
|
}
|
|
if r.IsError() {
|
|
t.Errorf("IsError() = true for status %d", code)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("IsError for 4xx and 5xx", func(t *testing.T) {
|
|
for _, code := range []int{400, 404, 500, 503} {
|
|
r := makeTestResponse(code, "")
|
|
if !r.IsError() {
|
|
t.Errorf("IsError() = false for status %d", code)
|
|
}
|
|
if r.IsSuccess() {
|
|
t.Errorf("IsSuccess() = true for status %d", code)
|
|
}
|
|
}
|
|
})
|
|
}
|