diff --git a/internal/proxy/healthchecker_test.go b/internal/proxy/healthchecker_test.go index 72216bd..fb3f81c 100644 --- a/internal/proxy/healthchecker_test.go +++ b/internal/proxy/healthchecker_test.go @@ -3,7 +3,6 @@ package proxy import ( "context" "log/slog" - "net/http" "os" "testing" "time" @@ -42,19 +41,3 @@ func TestBasicHealthchecker(t *testing.T) { healthchecker.isHealthy = true assert.True(t, healthchecker.IsHealthy()) } - -func TestGasLeftCall(t *testing.T) { - client := &http.Client{} - url := env.GetDefault("RPC_GATEWAY_NODE_URL_1", "https://cloudflare-eth.com") - - result, err := performGasLeftCall(context.TODO(), client, url) - assert.NoError(t, err) - assert.NotZero(t, result) - - // testing the timeout - ctx, cancelFunc := context.WithTimeout(context.Background(), 10*time.Millisecond) - defer cancelFunc() - - _, err = performGasLeftCall(ctx, client, url) - assert.Error(t, err) -} diff --git a/internal/proxy/healthchecker_utils.go b/internal/proxy/healthchecker_utils.go index eff6748..4dd1afb 100644 --- a/internal/proxy/healthchecker_utils.go +++ b/internal/proxy/healthchecker_utils.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "fmt" "net/http" "strconv" "strings" @@ -25,7 +26,7 @@ func hexToUint(hexString string) (uint64, error) { } func performGasLeftCall(c context.Context, client *http.Client, url string) (uint64, error) { - var gasLeftCallRaw = []byte(` + var gasLeftCallRaw = bytes.NewBufferString(` { "method": "eth_call", "params": [ @@ -48,9 +49,9 @@ func performGasLeftCall(c context.Context, client *http.Client, url string) (uin } `) - r, err := http.NewRequestWithContext(c, http.MethodPost, url, bytes.NewBuffer(gasLeftCallRaw)) + r, err := http.NewRequestWithContext(c, http.MethodPost, url, gasLeftCallRaw) if err != nil { - return 0, errors.Wrap(err, "new request failed") + return 0, fmt.Errorf("performGasLeftCall: NewRequestWithContext error: %w", err) } r.Header.Add(headers.ContentType, "application/json") @@ -58,18 +59,18 @@ func performGasLeftCall(c context.Context, client *http.Client, url string) (uin resp, err := client.Do(r) if err != nil { - return 0, errors.Wrap(err, "request failed") + return 0, fmt.Errorf("performGasLeftCall: client.Do error: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return 0, errors.New("gas left check failed") + return 0, errors.New("performGasLeftCall: non-200 HTTP response") } result := &JSONRPCResponse{} err = json.NewDecoder(resp.Body).Decode(result) if err != nil { - return 0, errors.Wrap(err, "json response decode failed") + return 0, fmt.Errorf("performGasLeftCall: json.Decode error: %w", err) } return hexToUint(result.Result) diff --git a/internal/proxy/healthchecker_utils_test.go b/internal/proxy/healthchecker_utils_test.go new file mode 100644 index 0000000..4de2190 --- /dev/null +++ b/internal/proxy/healthchecker_utils_test.go @@ -0,0 +1,84 @@ +package proxy + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/go-http-utils/headers" + "github.com/stretchr/testify/assert" +) + +func TestPerformGasLeftCallErrors(t *testing.T) { + t.Parallel() + + t.Run("expect error when HTTP status is not 200", func(t *testing.T) { + t.Parallel() + + server := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if assert.Contains(t, r.Header, headers.ContentType) { + assert.Equal(t, "application/json", r.Header.Get(headers.ContentType)) + } + + w.WriteHeader(http.StatusServiceUnavailable) + }), + ) + defer server.Close() + + gas, err := performGasLeftCall(context.TODO(), &http.Client{}, server.URL) + + assert.Zero(t, gas) + assert.Error(t, err) + assert.ErrorContains(t, err, "non-200 HTTP response") + }) + + t.Run("expect error when JSON payload is invalid", func(t *testing.T) { + t.Parallel() + + server := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if assert.Contains(t, r.Header, headers.ContentType) { + assert.Equal(t, "application/json", r.Header.Get(headers.ContentType)) + } + + w.Write([]byte(`{{}`)) + w.WriteHeader(http.StatusOK) + }), + ) + defer server.Close() + + gas, err := performGasLeftCall(context.TODO(), &http.Client{}, server.URL) + + assert.Zero(t, gas) + assert.Error(t, err) + assert.ErrorContains(t, err, "json.Decode error") + }) + + t.Run("expect error when server timeouts", func(t *testing.T) { + t.Parallel() + + server := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if assert.Contains(t, r.Header, headers.ContentType) { + assert.Equal(t, "application/json", r.Header.Get(headers.ContentType)) + } + <-time.After(time.Second * 3) + + w.WriteHeader(http.StatusServiceUnavailable) + }), + ) + defer server.Close() + + timeout, cancel := context.WithTimeout(context.TODO(), time.Second*1) + defer cancel() + + gas, err := performGasLeftCall(timeout, &http.Client{}, server.URL) + + assert.Zero(t, gas) + assert.Error(t, err) + assert.ErrorIs(t, err, context.DeadlineExceeded) + }) +}