github.com/grpc-ecosystem/grpc-gateway/v2@v2.19.1/runtime/errors_test.go (about)

     1  package runtime_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"strconv"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    13  	"google.golang.org/genproto/googleapis/rpc/errdetails"
    14  	statuspb "google.golang.org/genproto/googleapis/rpc/status"
    15  	"google.golang.org/grpc/codes"
    16  	"google.golang.org/grpc/status"
    17  )
    18  
    19  func TestDefaultHTTPError(t *testing.T) {
    20  	ctx := context.Background()
    21  
    22  	statusWithDetails, _ := status.New(codes.FailedPrecondition, "failed precondition").WithDetails(
    23  		&errdetails.PreconditionFailure{},
    24  	)
    25  
    26  	for i, spec := range []struct {
    27  		err         error
    28  		status      int
    29  		msg         string
    30  		marshaler   runtime.Marshaler
    31  		contentType string
    32  		details     string
    33  	}{
    34  		{
    35  			err:         errors.New("example error"),
    36  			status:      http.StatusInternalServerError,
    37  			marshaler:   &runtime.JSONPb{},
    38  			contentType: "application/json",
    39  			msg:         "example error",
    40  		},
    41  		{
    42  			err:         status.Error(codes.NotFound, "no such resource"),
    43  			status:      http.StatusNotFound,
    44  			marshaler:   &runtime.JSONPb{},
    45  			contentType: "application/json",
    46  			msg:         "no such resource",
    47  		},
    48  		{
    49  			err:         statusWithDetails.Err(),
    50  			status:      http.StatusBadRequest,
    51  			marshaler:   &runtime.JSONPb{},
    52  			contentType: "application/json",
    53  			msg:         "failed precondition",
    54  			details:     "type.googleapis.com/google.rpc.PreconditionFailure",
    55  		},
    56  		{
    57  			err:         errors.New("example error"),
    58  			status:      http.StatusInternalServerError,
    59  			marshaler:   &CustomMarshaler{&runtime.JSONPb{}},
    60  			contentType: "Custom-Content-Type",
    61  			msg:         "example error",
    62  		},
    63  		{
    64  			err: &runtime.HTTPStatusError{
    65  				HTTPStatus: http.StatusMethodNotAllowed,
    66  				Err:        status.Error(codes.Unimplemented, http.StatusText(http.StatusMethodNotAllowed)),
    67  			},
    68  			status:      http.StatusMethodNotAllowed,
    69  			marshaler:   &runtime.JSONPb{},
    70  			contentType: "application/json",
    71  			msg:         "Method Not Allowed",
    72  		},
    73  	} {
    74  		t.Run(strconv.Itoa(i), func(t *testing.T) {
    75  			w := httptest.NewRecorder()
    76  			req, _ := http.NewRequestWithContext(ctx, "", "", nil) // Pass in an empty request to match the signature
    77  			mux := runtime.NewServeMux()
    78  			marshaler := &runtime.JSONPb{}
    79  			runtime.HTTPError(ctx, mux, marshaler, w, req, spec.err)
    80  
    81  			if got, want := w.Header().Get("Content-Type"), "application/json"; got != want {
    82  				t.Errorf(`w.Header().Get("Content-Type") = %q; want %q; on spec.err=%v`, got, want, spec.err)
    83  			}
    84  			if got, want := w.Code, spec.status; got != want {
    85  				t.Errorf("w.Code = %d; want %d", got, want)
    86  			}
    87  
    88  			var st statuspb.Status
    89  			if err := marshaler.Unmarshal(w.Body.Bytes(), &st); err != nil {
    90  				t.Errorf("marshaler.Unmarshal(%q, &body) failed with %v; want success", w.Body.Bytes(), err)
    91  				return
    92  			}
    93  
    94  			if got, want := st.Message, spec.msg; !strings.Contains(got, want) {
    95  				t.Errorf(`st.Message = %q; want %q; on spec.err=%v`, got, want, spec.err)
    96  			}
    97  
    98  			if spec.details != "" {
    99  				if len(st.Details) != 1 {
   100  					t.Errorf(`len(st.Details) = %v; want 1`, len(st.Details))
   101  					return
   102  				}
   103  				if st.Details[0].TypeUrl != spec.details {
   104  					t.Errorf(`details.type_url = %s; want %s`, st.Details[0].TypeUrl, spec.details)
   105  				}
   106  			}
   107  		})
   108  	}
   109  }