1
18
19 package status
20
21 import (
22 "context"
23 "errors"
24 "fmt"
25 "testing"
26
27 "github.com/google/go-cmp/cmp"
28 cpb "google.golang.org/genproto/googleapis/rpc/code"
29 epb "google.golang.org/genproto/googleapis/rpc/errdetails"
30 spb "google.golang.org/genproto/googleapis/rpc/status"
31 "google.golang.org/protobuf/proto"
32 "google.golang.org/protobuf/protoadapt"
33 "google.golang.org/protobuf/reflect/protoreflect"
34 "google.golang.org/protobuf/types/known/anypb"
35 "google.golang.org/protobuf/types/known/durationpb"
36
37 "google.golang.org/grpc/codes"
38 "google.golang.org/grpc/internal/grpctest"
39 "google.golang.org/grpc/internal/status"
40 )
41
42 type s struct {
43 grpctest.Tester
44 }
45
46 func Test(t *testing.T) {
47 grpctest.RunSubTests(t, s{})
48 }
49
50
51
52 func errEqual(err1, err2 error) bool {
53 status1, ok := FromError(err1)
54 if !ok {
55 return false
56 }
57 status2, ok := FromError(err2)
58 if !ok {
59 return false
60 }
61 return proto.Equal(status1.Proto(), status2.Proto())
62 }
63
64 func (s) TestErrorsWithSameParameters(t *testing.T) {
65 const description = "some description"
66 e1 := Errorf(codes.AlreadyExists, description)
67 e2 := Errorf(codes.AlreadyExists, description)
68 if e1 == e2 || !errEqual(e1, e2) {
69 t.Fatalf("Errors should be equivalent but unique - e1: %v, %v e2: %p, %v", e1.(*status.Error), e1, e2.(*status.Error), e2)
70 }
71 }
72
73 func (s) TestFromToProto(t *testing.T) {
74 s := &spb.Status{
75 Code: int32(codes.Internal),
76 Message: "test test test",
77 Details: []*anypb.Any{{TypeUrl: "foo", Value: []byte{3, 2, 1}}},
78 }
79
80 err := FromProto(s)
81 if got := err.Proto(); !proto.Equal(s, got) {
82 t.Fatalf("Expected errors to be identical - s: %v got: %v", s, got)
83 }
84 }
85
86 func (s) TestFromNilProto(t *testing.T) {
87 tests := []*Status{nil, FromProto(nil)}
88 for _, s := range tests {
89 if c := s.Code(); c != codes.OK {
90 t.Errorf("s: %v - Expected s.Code() = OK; got %v", s, c)
91 }
92 if m := s.Message(); m != "" {
93 t.Errorf("s: %v - Expected s.Message() = \"\"; got %q", s, m)
94 }
95 if p := s.Proto(); p != nil {
96 t.Errorf("s: %v - Expected s.Proto() = nil; got %q", s, p)
97 }
98 if e := s.Err(); e != nil {
99 t.Errorf("s: %v - Expected s.Err() = nil; got %v", s, e)
100 }
101 }
102 }
103
104 func (s) TestError(t *testing.T) {
105 err := Error(codes.Internal, "test description")
106 if got, want := err.Error(), "rpc error: code = Internal desc = test description"; got != want {
107 t.Fatalf("err.Error() = %q; want %q", got, want)
108 }
109 s, _ := FromError(err)
110 if got, want := s.Code(), codes.Internal; got != want {
111 t.Fatalf("err.Code() = %s; want %s", got, want)
112 }
113 if got, want := s.Message(), "test description"; got != want {
114 t.Fatalf("err.Message() = %s; want %s", got, want)
115 }
116 }
117
118 func (s) TestErrorOK(t *testing.T) {
119 err := Error(codes.OK, "foo")
120 if err != nil {
121 t.Fatalf("Error(codes.OK, _) = %p; want nil", err.(*status.Error))
122 }
123 }
124
125 func (s) TestErrorProtoOK(t *testing.T) {
126 s := &spb.Status{Code: int32(codes.OK)}
127 if got := ErrorProto(s); got != nil {
128 t.Fatalf("ErrorProto(%v) = %v; want nil", s, got)
129 }
130 }
131
132 func (s) TestFromError(t *testing.T) {
133 code, message := codes.Internal, "test description"
134 err := Error(code, message)
135 s, ok := FromError(err)
136 if !ok || s.Code() != code || s.Message() != message || s.Err() == nil {
137 t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q, Err()!=nil>, true", err, s, ok, code, message)
138 }
139 }
140
141 func (s) TestFromErrorOK(t *testing.T) {
142 code, message := codes.OK, ""
143 s, ok := FromError(nil)
144 if !ok || s.Code() != code || s.Message() != message || s.Err() != nil {
145 t.Fatalf("FromError(nil) = %v, %v; want <Code()=%s, Message()=%q, Err=nil>, true", s, ok, code, message)
146 }
147 }
148
149 type customError struct {
150 Code codes.Code
151 Message string
152 Details []*anypb.Any
153 }
154
155 func (c customError) Error() string {
156 return fmt.Sprintf("rpc error: code = %s desc = %s", c.Code, c.Message)
157 }
158
159 func (c customError) GRPCStatus() *Status {
160 return status.FromProto(&spb.Status{
161 Code: int32(c.Code),
162 Message: c.Message,
163 Details: c.Details,
164 })
165 }
166
167 func (s) TestFromErrorImplementsInterface(t *testing.T) {
168 code, message := codes.Internal, "test description"
169 details := []*anypb.Any{{
170 TypeUrl: "testUrl",
171 Value: []byte("testValue"),
172 }}
173 err := customError{
174 Code: code,
175 Message: message,
176 Details: details,
177 }
178 s, ok := FromError(err)
179 if !ok || s.Code() != code || s.Message() != message || s.Err() == nil {
180 t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q, Err()!=nil>, true", err, s, ok, code, message)
181 }
182 pd := s.Proto().GetDetails()
183 if len(pd) != 1 || !proto.Equal(pd[0], details[0]) {
184 t.Fatalf("s.Proto.GetDetails() = %v; want <Details()=%s>", pd, details)
185 }
186 }
187
188 func (s) TestFromErrorUnknownError(t *testing.T) {
189 code, message := codes.Unknown, "unknown error"
190 err := errors.New("unknown error")
191 s, ok := FromError(err)
192 if ok || s.Code() != code || s.Message() != message {
193 t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q>, false", err, s, ok, code, message)
194 }
195 }
196
197 func (s) TestFromErrorWrapped(t *testing.T) {
198 const code, message = codes.Internal, "test description"
199 err := fmt.Errorf("wrapped error: %w", Error(code, message))
200 s, ok := FromError(err)
201 if !ok || s.Code() != code || s.Message() != err.Error() || s.Err() == nil {
202 t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q, Err()!=nil>, true", err, s, ok, code, message)
203 }
204 }
205
206 type customErrorNilStatus struct {
207 }
208
209 func (c customErrorNilStatus) Error() string {
210 return "test"
211 }
212
213 func (c customErrorNilStatus) GRPCStatus() *Status {
214 return nil
215 }
216
217 func (s) TestFromErrorImplementsInterfaceReturnsOKStatus(t *testing.T) {
218 err := customErrorNilStatus{}
219 s, ok := FromError(err)
220 if ok || s.Code() != codes.Unknown || s.Message() != err.Error() {
221 t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q, Err()!=nil>, true", err, s, ok, codes.Unknown, err.Error())
222 }
223 }
224
225 func (s) TestFromErrorImplementsInterfaceReturnsOKStatusWrapped(t *testing.T) {
226 err := fmt.Errorf("wrapping: %w", customErrorNilStatus{})
227 s, ok := FromError(err)
228 if ok || s.Code() != codes.Unknown || s.Message() != err.Error() {
229 t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q, Err()!=nil>, true", err, s, ok, codes.Unknown, err.Error())
230 }
231 }
232
233 func (s) TestFromErrorImplementsInterfaceWrapped(t *testing.T) {
234 const code, message = codes.Internal, "test description"
235 err := fmt.Errorf("wrapped error: %w", customError{Code: code, Message: message})
236 s, ok := FromError(err)
237 if !ok || s.Code() != code || s.Message() != err.Error() || s.Err() == nil {
238 t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q, Err()!=nil>, true", err, s, ok, code, message)
239 }
240 }
241
242 func (s) TestCode(t *testing.T) {
243 const code = codes.Internal
244 err := Error(code, "test description")
245 if s := Code(err); s != code {
246 t.Fatalf("Code(%v) = %v; want <Code()=%s>", err, s, code)
247 }
248 }
249
250 func (s) TestCodeOK(t *testing.T) {
251 if s, code := Code(nil), codes.OK; s != code {
252 t.Fatalf("Code(%v) = %v; want <Code()=%s>", nil, s, code)
253 }
254 }
255
256 func (s) TestCodeImplementsInterface(t *testing.T) {
257 const code = codes.Internal
258 err := customError{Code: code, Message: "test description"}
259 if s := Code(err); s != code {
260 t.Fatalf("Code(%v) = %v; want <Code()=%s>", err, s, code)
261 }
262 }
263
264 func (s) TestCodeUnknownError(t *testing.T) {
265 const code = codes.Unknown
266 err := errors.New("unknown error")
267 if s := Code(err); s != code {
268 t.Fatalf("Code(%v) = %v; want <Code()=%s>", err, s, code)
269 }
270 }
271
272 func (s) TestCodeWrapped(t *testing.T) {
273 const code = codes.Internal
274 err := fmt.Errorf("wrapped: %w", Error(code, "test description"))
275 if s := Code(err); s != code {
276 t.Fatalf("Code(%v) = %v; want <Code()=%s>", err, s, code)
277 }
278 }
279
280 func (s) TestCodeImplementsInterfaceWrapped(t *testing.T) {
281 const code = codes.Internal
282 err := fmt.Errorf("wrapped: %w", customError{Code: code, Message: "test description"})
283 if s := Code(err); s != code {
284 t.Fatalf("Code(%v) = %v; want <Code()=%s>", err, s, code)
285 }
286 }
287
288 func (s) TestConvertKnownError(t *testing.T) {
289 code, message := codes.Internal, "test description"
290 err := Error(code, message)
291 s := Convert(err)
292 if s.Code() != code || s.Message() != message {
293 t.Fatalf("Convert(%v) = %v; want <Code()=%s, Message()=%q>", err, s, code, message)
294 }
295 }
296
297 func (s) TestConvertUnknownError(t *testing.T) {
298 code, message := codes.Unknown, "unknown error"
299 err := errors.New("unknown error")
300 s := Convert(err)
301 if s.Code() != code || s.Message() != message {
302 t.Fatalf("Convert(%v) = %v; want <Code()=%s, Message()=%q>", err, s, code, message)
303 }
304 }
305
306 func (s) TestStatus_ErrorDetails(t *testing.T) {
307 tests := []struct {
308 code codes.Code
309 details []protoadapt.MessageV1
310 }{
311 {
312 code: codes.NotFound,
313 details: nil,
314 },
315 {
316 code: codes.NotFound,
317 details: []protoadapt.MessageV1{
318 &epb.ResourceInfo{
319 ResourceType: "book",
320 ResourceName: "projects/1234/books/5678",
321 Owner: "User",
322 },
323 },
324 },
325 {
326 code: codes.Internal,
327 details: []protoadapt.MessageV1{
328 &epb.DebugInfo{
329 StackEntries: []string{
330 "first stack",
331 "second stack",
332 },
333 },
334 },
335 },
336 {
337 code: codes.Unavailable,
338 details: []protoadapt.MessageV1{
339 &epb.RetryInfo{
340 RetryDelay: &durationpb.Duration{Seconds: 60},
341 },
342 &epb.ResourceInfo{
343 ResourceType: "book",
344 ResourceName: "projects/1234/books/5678",
345 Owner: "User",
346 },
347 },
348 },
349 }
350
351 for _, tc := range tests {
352 s, err := New(tc.code, "").WithDetails(tc.details...)
353 if err != nil {
354 t.Fatalf("(%v).WithDetails(%+v) failed: %v", str(s), tc.details, err)
355 }
356 details := s.Details()
357 for i := range details {
358 if !proto.Equal(details[i].(protoreflect.ProtoMessage), tc.details[i].(protoreflect.ProtoMessage)) {
359 t.Fatalf("(%v).Details()[%d] = %+v, want %+v", str(s), i, details[i], tc.details[i])
360 }
361 }
362 }
363 }
364
365 func (s) TestStatus_WithDetails_Fail(t *testing.T) {
366 tests := []*Status{
367 nil,
368 FromProto(nil),
369 New(codes.OK, ""),
370 }
371 for _, s := range tests {
372 if s, err := s.WithDetails(); err == nil || s != nil {
373 t.Fatalf("(%v).WithDetails(%+v) = %v, %v; want nil, non-nil", str(s), []proto.Message{}, s, err)
374 }
375 }
376 }
377
378 func (s) TestStatus_ErrorDetails_Fail(t *testing.T) {
379 tests := []struct {
380 s *Status
381 want []any
382 }{
383 {
384 s: nil,
385 want: nil,
386 },
387 {
388 s: FromProto(nil),
389 want: nil,
390 },
391 {
392 s: New(codes.OK, ""),
393 want: []any{},
394 },
395 {
396 s: FromProto(&spb.Status{
397 Code: int32(cpb.Code_CANCELLED),
398 Details: []*anypb.Any{
399 {
400 TypeUrl: "",
401 Value: []byte{},
402 },
403 mustMarshalAny(&epb.ResourceInfo{
404 ResourceType: "book",
405 ResourceName: "projects/1234/books/5678",
406 Owner: "User",
407 }),
408 },
409 }),
410 want: []any{
411 errors.New("invalid empty type URL"),
412 &epb.ResourceInfo{
413 ResourceType: "book",
414 ResourceName: "projects/1234/books/5678",
415 Owner: "User",
416 },
417 },
418 },
419 }
420 for _, tc := range tests {
421 details := tc.s.Details()
422 if len(details) != len(tc.want) {
423 t.Fatalf("len(s.Details()) = %v, want = %v.", len(details), len(tc.want))
424 }
425 for i, d := range details {
426
427
428
429 if _, ok := d.(error); ok {
430 if (d != nil) != (tc.want[i] != nil) {
431 t.Fatalf("s.Details()[%v] was %v; want %v", i, d, tc.want[i])
432 }
433 continue
434 }
435 if !cmp.Equal(d, tc.want[i], cmp.Comparer(proto.Equal)) {
436 t.Fatalf("s.Details()[%v] was %v; want %v", i, d, tc.want[i])
437 }
438 }
439 }
440 }
441
442 func str(s *Status) string {
443 if s == nil {
444 return "nil"
445 }
446 if s.Proto() == nil {
447 return "<Code=OK>"
448 }
449 return fmt.Sprintf("<Code=%v, Message=%q, Details=%+v>", s.Code(), s.Message(), s.Details())
450 }
451
452
453 func mustMarshalAny(msg proto.Message) *anypb.Any {
454 any, err := anypb.New(msg)
455 if err != nil {
456 panic(fmt.Sprintf("anypb.New(%+v) failed: %v", msg, err))
457 }
458 return any
459 }
460
461 func (s) TestFromContextError(t *testing.T) {
462 testCases := []struct {
463 in error
464 want *Status
465 }{
466 {in: nil, want: New(codes.OK, "")},
467 {in: context.DeadlineExceeded, want: New(codes.DeadlineExceeded, context.DeadlineExceeded.Error())},
468 {in: context.Canceled, want: New(codes.Canceled, context.Canceled.Error())},
469 {in: errors.New("other"), want: New(codes.Unknown, "other")},
470 {in: fmt.Errorf("wrapped: %w", context.DeadlineExceeded), want: New(codes.DeadlineExceeded, "wrapped: "+context.DeadlineExceeded.Error())},
471 {in: fmt.Errorf("wrapped: %w", context.Canceled), want: New(codes.Canceled, "wrapped: "+context.Canceled.Error())},
472 }
473 for _, tc := range testCases {
474 got := FromContextError(tc.in)
475 if got.Code() != tc.want.Code() || got.Message() != tc.want.Message() {
476 t.Errorf("FromContextError(%v) = %v; want %v", tc.in, got, tc.want)
477 }
478 }
479 }
480
View as plain text