Files
httpx/error_test.go
Aleksey Shakhmatov f9a05f5c57 Add Client with response wrapper, request helpers, and full middleware assembly
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
2026-03-20 14:22:22 +03:00

91 lines
2.1 KiB
Go

package httpx_test
import (
"errors"
"testing"
"git.codelab.vc/pkg/httpx"
)
func TestError(t *testing.T) {
t.Run("formats without endpoint", func(t *testing.T) {
inner := errors.New("connection refused")
e := &httpx.Error{
Op: "Get",
URL: "http://example.com/api",
Err: inner,
}
want := "httpx: Get http://example.com/api: connection refused"
if got := e.Error(); got != want {
t.Errorf("got %q, want %q", got, want)
}
})
t.Run("formats with endpoint different from url", func(t *testing.T) {
inner := errors.New("timeout")
e := &httpx.Error{
Op: "Do",
URL: "http://example.com/api",
Endpoint: "http://node1.example.com/api",
Err: inner,
}
want := "httpx: Do http://example.com/api (endpoint http://node1.example.com/api): timeout"
if got := e.Error(); got != want {
t.Errorf("got %q, want %q", got, want)
}
})
t.Run("formats with endpoint same as url", func(t *testing.T) {
inner := errors.New("not found")
e := &httpx.Error{
Op: "Get",
URL: "http://example.com/api",
Endpoint: "http://example.com/api",
Err: inner,
}
want := "httpx: Get http://example.com/api: not found"
if got := e.Error(); got != want {
t.Errorf("got %q, want %q", got, want)
}
})
t.Run("unwrap returns inner error", func(t *testing.T) {
inner := errors.New("underlying")
e := &httpx.Error{Op: "Get", URL: "http://example.com", Err: inner}
if got := e.Unwrap(); got != inner {
t.Errorf("Unwrap() = %v, want %v", got, inner)
}
if !errors.Is(e, inner) {
t.Error("errors.Is should find the inner error")
}
})
}
func TestSentinelErrors(t *testing.T) {
t.Run("ErrRetryExhausted", func(t *testing.T) {
if httpx.ErrRetryExhausted == nil {
t.Fatal("ErrRetryExhausted is nil")
}
if httpx.ErrRetryExhausted.Error() == "" {
t.Fatal("ErrRetryExhausted has empty message")
}
})
t.Run("ErrCircuitOpen", func(t *testing.T) {
if httpx.ErrCircuitOpen == nil {
t.Fatal("ErrCircuitOpen is nil")
}
})
t.Run("ErrNoHealthy", func(t *testing.T) {
if httpx.ErrNoHealthy == nil {
t.Fatal("ErrNoHealthy is nil")
}
})
}