github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/status/status_test.go (about) 1 /* 2 * 3 * Copyright 2017 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package status 20 21 import ( 22 "context" 23 "errors" 24 "fmt" 25 "testing" 26 27 "github.com/golang/protobuf/proto" 28 "github.com/golang/protobuf/ptypes" 29 apb "github.com/golang/protobuf/ptypes/any" 30 dpb "github.com/golang/protobuf/ptypes/duration" 31 "github.com/google/go-cmp/cmp" 32 "github.com/hxx258456/ccgo/grpc/codes" 33 "github.com/hxx258456/ccgo/grpc/internal/grpctest" 34 "github.com/hxx258456/ccgo/grpc/internal/status" 35 cpb "google.golang.org/genproto/googleapis/rpc/code" 36 epb "google.golang.org/genproto/googleapis/rpc/errdetails" 37 spb "google.golang.org/genproto/googleapis/rpc/status" 38 ) 39 40 type s struct { 41 grpctest.Tester 42 } 43 44 func Test(t *testing.T) { 45 grpctest.RunSubTests(t, s{}) 46 } 47 48 // errEqual is essentially a copy of testutils.StatusErrEqual(), to avoid a 49 // cyclic dependency. 50 func errEqual(err1, err2 error) bool { 51 status1, ok := FromError(err1) 52 if !ok { 53 return false 54 } 55 status2, ok := FromError(err2) 56 if !ok { 57 return false 58 } 59 return proto.Equal(status1.Proto(), status2.Proto()) 60 } 61 62 func (s) TestErrorsWithSameParameters(t *testing.T) { 63 const description = "some description" 64 e1 := Errorf(codes.AlreadyExists, description) 65 e2 := Errorf(codes.AlreadyExists, description) 66 if e1 == e2 || !errEqual(e1, e2) { 67 t.Fatalf("Errors should be equivalent but unique - e1: %v, %v e2: %p, %v", e1.(*status.Error), e1, e2.(*status.Error), e2) 68 } 69 } 70 71 func (s) TestFromToProto(t *testing.T) { 72 s := &spb.Status{ 73 Code: int32(codes.Internal), 74 Message: "test test test", 75 Details: []*apb.Any{{TypeUrl: "foo", Value: []byte{3, 2, 1}}}, 76 } 77 78 err := FromProto(s) 79 if got := err.Proto(); !proto.Equal(s, got) { 80 t.Fatalf("Expected errors to be identical - s: %v got: %v", s, got) 81 } 82 } 83 84 func (s) TestFromNilProto(t *testing.T) { 85 tests := []*Status{nil, FromProto(nil)} 86 for _, s := range tests { 87 if c := s.Code(); c != codes.OK { 88 t.Errorf("s: %v - Expected s.Code() = OK; got %v", s, c) 89 } 90 if m := s.Message(); m != "" { 91 t.Errorf("s: %v - Expected s.Message() = \"\"; got %q", s, m) 92 } 93 if p := s.Proto(); p != nil { 94 t.Errorf("s: %v - Expected s.Proto() = nil; got %q", s, p) 95 } 96 if e := s.Err(); e != nil { 97 t.Errorf("s: %v - Expected s.Err() = nil; got %v", s, e) 98 } 99 } 100 } 101 102 func (s) TestError(t *testing.T) { 103 err := Error(codes.Internal, "test description") 104 if got, want := err.Error(), "rpc error: code = Internal desc = test description"; got != want { 105 t.Fatalf("err.Error() = %q; want %q", got, want) 106 } 107 s, _ := FromError(err) 108 if got, want := s.Code(), codes.Internal; got != want { 109 t.Fatalf("err.Code() = %s; want %s", got, want) 110 } 111 if got, want := s.Message(), "test description"; got != want { 112 t.Fatalf("err.Message() = %s; want %s", got, want) 113 } 114 } 115 116 func (s) TestErrorOK(t *testing.T) { 117 err := Error(codes.OK, "foo") 118 if err != nil { 119 t.Fatalf("Error(codes.OK, _) = %p; want nil", err.(*status.Error)) 120 } 121 } 122 123 func (s) TestErrorProtoOK(t *testing.T) { 124 s := &spb.Status{Code: int32(codes.OK)} 125 if got := ErrorProto(s); got != nil { 126 t.Fatalf("ErrorProto(%v) = %v; want nil", s, got) 127 } 128 } 129 130 func (s) TestFromError(t *testing.T) { 131 code, message := codes.Internal, "test description" 132 err := Error(code, message) 133 s, ok := FromError(err) 134 if !ok || s.Code() != code || s.Message() != message || s.Err() == nil { 135 t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q, Err()!=nil>, true", err, s, ok, code, message) 136 } 137 } 138 139 func (s) TestFromErrorOK(t *testing.T) { 140 code, message := codes.OK, "" 141 s, ok := FromError(nil) 142 if !ok || s.Code() != code || s.Message() != message || s.Err() != nil { 143 t.Fatalf("FromError(nil) = %v, %v; want <Code()=%s, Message()=%q, Err=nil>, true", s, ok, code, message) 144 } 145 } 146 147 type customError struct { 148 Code codes.Code 149 Message string 150 Details []*apb.Any 151 } 152 153 func (c customError) Error() string { 154 return fmt.Sprintf("rpc error: code = %s desc = %s", c.Code, c.Message) 155 } 156 157 func (c customError) GRPCStatus() *Status { 158 return status.FromProto(&spb.Status{ 159 Code: int32(c.Code), 160 Message: c.Message, 161 Details: c.Details, 162 }) 163 } 164 165 func (s) TestFromErrorImplementsInterface(t *testing.T) { 166 code, message := codes.Internal, "test description" 167 details := []*apb.Any{{ 168 TypeUrl: "testUrl", 169 Value: []byte("testValue"), 170 }} 171 err := customError{ 172 Code: code, 173 Message: message, 174 Details: details, 175 } 176 s, ok := FromError(err) 177 if !ok || s.Code() != code || s.Message() != message || s.Err() == nil { 178 t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q, Err()!=nil>, true", err, s, ok, code, message) 179 } 180 pd := s.Proto().GetDetails() 181 if len(pd) != 1 || !proto.Equal(pd[0], details[0]) { 182 t.Fatalf("s.Proto.GetDetails() = %v; want <Details()=%s>", pd, details) 183 } 184 } 185 186 func (s) TestFromErrorUnknownError(t *testing.T) { 187 code, message := codes.Unknown, "unknown error" 188 err := errors.New("unknown error") 189 s, ok := FromError(err) 190 if ok || s.Code() != code || s.Message() != message { 191 t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q>, false", err, s, ok, code, message) 192 } 193 } 194 195 func (s) TestConvertKnownError(t *testing.T) { 196 code, message := codes.Internal, "test description" 197 err := Error(code, message) 198 s := Convert(err) 199 if s.Code() != code || s.Message() != message { 200 t.Fatalf("Convert(%v) = %v; want <Code()=%s, Message()=%q>", err, s, code, message) 201 } 202 } 203 204 func (s) TestConvertUnknownError(t *testing.T) { 205 code, message := codes.Unknown, "unknown error" 206 err := errors.New("unknown error") 207 s := Convert(err) 208 if s.Code() != code || s.Message() != message { 209 t.Fatalf("Convert(%v) = %v; want <Code()=%s, Message()=%q>", err, s, code, message) 210 } 211 } 212 213 func (s) TestStatus_ErrorDetails(t *testing.T) { 214 tests := []struct { 215 code codes.Code 216 details []proto.Message 217 }{ 218 { 219 code: codes.NotFound, 220 details: nil, 221 }, 222 { 223 code: codes.NotFound, 224 details: []proto.Message{ 225 &epb.ResourceInfo{ 226 ResourceType: "book", 227 ResourceName: "projects/1234/books/5678", 228 Owner: "User", 229 }, 230 }, 231 }, 232 { 233 code: codes.Internal, 234 details: []proto.Message{ 235 &epb.DebugInfo{ 236 StackEntries: []string{ 237 "first stack", 238 "second stack", 239 }, 240 }, 241 }, 242 }, 243 { 244 code: codes.Unavailable, 245 details: []proto.Message{ 246 &epb.RetryInfo{ 247 RetryDelay: &dpb.Duration{Seconds: 60}, 248 }, 249 &epb.ResourceInfo{ 250 ResourceType: "book", 251 ResourceName: "projects/1234/books/5678", 252 Owner: "User", 253 }, 254 }, 255 }, 256 } 257 258 for _, tc := range tests { 259 s, err := New(tc.code, "").WithDetails(tc.details...) 260 if err != nil { 261 t.Fatalf("(%v).WithDetails(%+v) failed: %v", str(s), tc.details, err) 262 } 263 details := s.Details() 264 for i := range details { 265 if !proto.Equal(details[i].(proto.Message), tc.details[i]) { 266 t.Fatalf("(%v).Details()[%d] = %+v, want %+v", str(s), i, details[i], tc.details[i]) 267 } 268 } 269 } 270 } 271 272 func (s) TestStatus_WithDetails_Fail(t *testing.T) { 273 tests := []*Status{ 274 nil, 275 FromProto(nil), 276 New(codes.OK, ""), 277 } 278 for _, s := range tests { 279 if s, err := s.WithDetails(); err == nil || s != nil { 280 t.Fatalf("(%v).WithDetails(%+v) = %v, %v; want nil, non-nil", str(s), []proto.Message{}, s, err) 281 } 282 } 283 } 284 285 func (s) TestStatus_ErrorDetails_Fail(t *testing.T) { 286 tests := []struct { 287 s *Status 288 i []interface{} 289 }{ 290 { 291 nil, 292 nil, 293 }, 294 { 295 FromProto(nil), 296 nil, 297 }, 298 { 299 New(codes.OK, ""), 300 []interface{}{}, 301 }, 302 { 303 FromProto(&spb.Status{ 304 Code: int32(cpb.Code_CANCELLED), 305 Details: []*apb.Any{ 306 { 307 TypeUrl: "", 308 Value: []byte{}, 309 }, 310 mustMarshalAny(&epb.ResourceInfo{ 311 ResourceType: "book", 312 ResourceName: "projects/1234/books/5678", 313 Owner: "User", 314 }), 315 }, 316 }), 317 []interface{}{ 318 errors.New(`message type url "" is invalid`), 319 &epb.ResourceInfo{ 320 ResourceType: "book", 321 ResourceName: "projects/1234/books/5678", 322 Owner: "User", 323 }, 324 }, 325 }, 326 } 327 for _, tc := range tests { 328 got := tc.s.Details() 329 if !cmp.Equal(got, tc.i, cmp.Comparer(proto.Equal), cmp.Comparer(equalError)) { 330 t.Errorf("(%v).Details() = %+v, want %+v", str(tc.s), got, tc.i) 331 } 332 } 333 } 334 335 func equalError(x, y error) bool { 336 return x == y || (x != nil && y != nil && x.Error() == y.Error()) 337 } 338 339 func str(s *Status) string { 340 if s == nil { 341 return "nil" 342 } 343 if s.Proto() == nil { 344 return "<Code=OK>" 345 } 346 return fmt.Sprintf("<Code=%v, Message=%q, Details=%+v>", s.Code(), s.Message(), s.Details()) 347 } 348 349 // mustMarshalAny converts a protobuf message to an any. 350 func mustMarshalAny(msg proto.Message) *apb.Any { 351 any, err := ptypes.MarshalAny(msg) 352 if err != nil { 353 panic(fmt.Sprintf("ptypes.MarshalAny(%+v) failed: %v", msg, err)) 354 } 355 return any 356 } 357 358 func (s) TestFromContextError(t *testing.T) { 359 testCases := []struct { 360 in error 361 want *Status 362 }{ 363 {in: nil, want: New(codes.OK, "")}, 364 {in: context.DeadlineExceeded, want: New(codes.DeadlineExceeded, context.DeadlineExceeded.Error())}, 365 {in: context.Canceled, want: New(codes.Canceled, context.Canceled.Error())}, 366 {in: errors.New("other"), want: New(codes.Unknown, "other")}, 367 {in: fmt.Errorf("wrapped: %w", context.DeadlineExceeded), want: New(codes.DeadlineExceeded, "wrapped: "+context.DeadlineExceeded.Error())}, 368 {in: fmt.Errorf("wrapped: %w", context.Canceled), want: New(codes.Canceled, "wrapped: "+context.Canceled.Error())}, 369 } 370 for _, tc := range testCases { 371 got := FromContextError(tc.in) 372 if got.Code() != tc.want.Code() || got.Message() != tc.want.Message() { 373 t.Errorf("FromContextError(%v) = %v; want %v", tc.in, got, tc.want) 374 } 375 } 376 }