github.com/cloudwego/dynamicgo@v0.2.6-0.20240519101509-707f41b6b834/conv/j2t/conv_test.go (about) 1 /** 2 * Copyright 2023 CloudWeGo Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package j2t 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/base64" 23 "encoding/json" 24 "fmt" 25 "io/ioutil" 26 "math" 27 stdh "net/http" 28 "net/url" 29 "os" 30 "runtime" 31 "runtime/debug" 32 "strconv" 33 "strings" 34 "testing" 35 "time" 36 "unsafe" 37 38 sjson "github.com/bytedance/sonic/ast" 39 "github.com/cloudwego/dynamicgo/conv" 40 "github.com/cloudwego/dynamicgo/http" 41 "github.com/cloudwego/dynamicgo/internal/native" 42 "github.com/cloudwego/dynamicgo/internal/native/types" 43 "github.com/cloudwego/dynamicgo/internal/rt" 44 "github.com/cloudwego/dynamicgo/meta" 45 "github.com/cloudwego/dynamicgo/testdata/kitex_gen/example3" 46 "github.com/cloudwego/dynamicgo/testdata/kitex_gen/null" 47 "github.com/cloudwego/dynamicgo/testdata/sample" 48 "github.com/cloudwego/dynamicgo/thrift" 49 "github.com/cloudwego/dynamicgo/thrift/annotation" 50 "github.com/cloudwego/dynamicgo/thrift/base" 51 "github.com/stretchr/testify/require" 52 ) 53 54 var ( 55 debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == "" 56 ) 57 58 func TestMain(m *testing.M) { 59 go func() { 60 if !debugAsyncGC { 61 return 62 } 63 println("Begin GC looping...") 64 for { 65 runtime.GC() 66 debug.FreeOSMemory() 67 } 68 }() 69 time.Sleep(time.Millisecond) 70 annotation.InitAGWAnnos() 71 m.Run() 72 } 73 74 const ( 75 exampleIDLPath = "../../testdata/idl/example3.thrift" 76 nullIDLPath = "../../testdata/idl/null.thrift" 77 exampleJSON = "../../testdata/data/example3req.json" 78 nullJSON = "../../testdata/data/null_pass.json" 79 nullerrJSON = "../../testdata/data/null_err.json" 80 ) 81 82 83 func TestCases(t *testing.T) { 84 var tests = []struct{ 85 name string 86 idl string 87 includes map[string]string 88 js string 89 opt conv.Options 90 want interface{} 91 err error 92 }{ 93 { 94 name: "int2double_vm", 95 idl: 96 `struct Req { 97 1: optional double body (api.js_conv=""), 98 } 99 100 service SVR { 101 void Method(1: Req req) 102 } 103 `, 104 includes: nil, 105 js: `{"body":"-1"}`, 106 opt: conv.Options{}, 107 want: map[string]interface{}{ 108 "body": float64(-1), 109 }, 110 }, 111 { 112 name: "int2double", 113 idl: 114 `struct Req { 115 1: optional double body, 116 } 117 118 service SVR { 119 void Method(1: Req req) 120 } 121 `, 122 includes: nil, 123 js: `{"body":-2}`, 124 opt: conv.Options{EnableValueMapping: true}, 125 want: map[string]interface{}{ 126 "body": float64(-2), 127 }, 128 }, 129 } 130 for _, c := range tests { 131 t.Run(c.name, func(t *testing.T) { 132 ctx := context.Background() 133 svc, err := thrift.NewDefaultOptions().NewDescritorFromContent(ctx, "a.thrift", c.idl, c.includes, false) 134 if err != nil { 135 t.Fatal(err) 136 } 137 desc := svc.Functions()["Method"].Request().Struct().FieldById(1).Type() 138 cv := NewBinaryConv(conv.Options{ 139 EnableValueMapping: true, 140 }) 141 out, err := cv.Do(ctx, desc, []byte(c.js)) 142 if err != nil { 143 if c.err == nil || c.err.Error() == err.Error() { 144 t.Fatal(err) 145 } 146 } 147 v, err := thrift.NewBinaryProtocol(out).ReadAnyWithDesc(desc, false, false, false, true) 148 if err != nil { 149 if c.err == nil || c.err.Error() == err.Error() { 150 t.Fatal(err) 151 } 152 } 153 require.Equal(t, c.want, v) 154 }) 155 } 156 } 157 158 159 func TestConvJSON2Thrift(t *testing.T) { 160 desc := getExampleDesc() 161 data := getExampleData() 162 cv := NewBinaryConv(conv.Options{}) 163 ctx := context.Background() 164 out, err := cv.Do(ctx, desc, data) 165 require.Nil(t, err) 166 exp := example3.NewExampleReq() 167 err = json.Unmarshal(data, exp) 168 require.Nil(t, err) 169 act := example3.NewExampleReq() 170 _, err = act.FastRead(out) 171 require.Nil(t, err) 172 require.Equal(t, exp, act) 173 } 174 175 func TestConvHTTP2Thrift(t *testing.T) { 176 desc := getExampleDesc() 177 data := getExampleData() 178 exp := example3.NewExampleReq() 179 err := json.Unmarshal(data, exp) 180 require.Nil(t, err) 181 req := getExampleReq(exp, true, data) 182 cv := NewBinaryConv(conv.Options{ 183 EnableHttpMapping: true, 184 }) 185 ctx := context.Background() 186 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 187 out, err := cv.Do(ctx, desc, data) 188 require.NoError(t, err) 189 190 act := example3.NewExampleReq() 191 _, err = act.FastRead(out) 192 require.Nil(t, err) 193 require.Equal(t, exp, act) 194 } 195 196 func getExampleDesc() *thrift.TypeDescriptor { 197 opts := thrift.Options{} 198 svc, err := opts.NewDescritorFromPath(context.Background(), exampleIDLPath) 199 if err != nil { 200 panic(err) 201 } 202 return svc.Functions()["ExampleMethod"].Request().Struct().FieldById(1).Type() 203 } 204 205 func getErrorExampleDesc() *thrift.TypeDescriptor { 206 opts := thrift.Options{} 207 svc, err := opts.NewDescritorFromPath(context.Background(), exampleIDLPath) 208 if err != nil { 209 panic(err) 210 } 211 return svc.Functions()["ErrorMethod"].Request().Struct().FieldById(1).Type() 212 } 213 214 func getExampleInt2FloatDesc() *thrift.TypeDescriptor { 215 opts := thrift.Options{} 216 svc, err := opts.NewDescritorFromPath(context.Background(), exampleIDLPath) 217 if err != nil { 218 panic(err) 219 } 220 return svc.Functions()["Int2FloatMethod"].Request().Struct().FieldById(1).Type() 221 } 222 223 func getExampleJSONStringDesc() *thrift.TypeDescriptor { 224 opts := thrift.Options{} 225 svc, err := opts.NewDescritorFromPath(context.Background(), exampleIDLPath) 226 if err != nil { 227 panic(err) 228 } 229 return svc.Functions()["JSONStringMethod"].Request().Struct().FieldById(1).Type() 230 } 231 232 func getExampleFallbackDesc() *thrift.TypeDescriptor { 233 opts := thrift.Options{} 234 svc, err := opts.NewDescritorFromPath(context.Background(), exampleIDLPath) 235 if err != nil { 236 panic(err) 237 } 238 return svc.Functions()["FallbackMethod"].Request().Struct().FieldById(1).Type() 239 } 240 241 func getExampleDescByName(method string, req bool, opts thrift.Options) *thrift.TypeDescriptor { 242 svc, err := opts.NewDescritorFromPath(context.Background(), exampleIDLPath) 243 if err != nil { 244 panic(err) 245 } 246 if req { 247 return svc.Functions()[method].Request().Struct().Fields()[0].Type() 248 } else { 249 return svc.Functions()[method].Response().Struct().Fields()[0].Type() 250 } 251 } 252 253 func getExampleData() []byte { 254 out, err := ioutil.ReadFile(exampleJSON) 255 if err != nil { 256 panic(err) 257 } 258 return out 259 } 260 261 func getExampleReq(exp *example3.ExampleReq, setIs bool, body []byte) *http.HTTPRequest { 262 f := -1.00001 263 x := true 264 q := []string{"1", "2", "3"} 265 p := "<>" 266 is := "abcd" 267 uv := url.Values{ 268 "query": []string{strings.Join(q, ",")}, 269 } 270 if setIs { 271 uv.Add("inner_query", is) 272 exp.InnerBase.InnerQuery = is 273 exp.InnerBase.ListInnerBase[0].InnerQuery = is 274 exp.InnerBase.MapStringInnerBase["innerx"].InnerQuery = is 275 } 276 277 uri := "http://localhost:8888/root?" + uv.Encode() 278 hr, err := stdh.NewRequest("POST", uri, bytes.NewBuffer(body)) 279 if err != nil { 280 panic(err) 281 } 282 hr.Header.Set("Content-Type", "application/json") 283 req, err := http.NewHTTPRequestFromStdReq(hr) 284 if err != nil { 285 panic(err) 286 } 287 req.Params.Set("path", p) 288 req.Request.Header.Add("heeader", strconv.FormatBool(x)) 289 req.AddCookie(&stdh.Cookie{Name: "cookie", Value: strconv.FormatFloat(f, 'f', -1, 64)}) 290 if setIs { 291 req.Request.Header.Add("inner_string", is) 292 exp.InnerBase.ListInnerBase[0].String_ = is 293 exp.InnerBase.MapStringInnerBase["innerx"].String_ = is 294 exp.InnerBase.String_ = is 295 } 296 exp.Path = p 297 exp.Query = q 298 exp.Header = &x 299 exp.Cookie = &f 300 exp.RawUri = uri 301 return req 302 } 303 304 func getExampleJSONStringReq(exp *example3.ExampleJSONString) *http.HTTPRequest { 305 j := `{"a":"1","b":2}` 306 x := "{}" 307 a := `["1","2","3"]` 308 b := `[1,2,3]` 309 c := `{"1":"1","2":"2","3":"3"}` 310 311 qs := url.Values{} 312 qs.Add("query", j) 313 qs.Add("query2", a) 314 hr, err := stdh.NewRequest("POST", "http://localhost:8888/root?"+qs.Encode(), bytes.NewBuffer(nil)) 315 if err != nil { 316 panic(err) 317 } 318 req := &http.HTTPRequest{ 319 Request: hr, 320 } 321 req.AddCookie(&stdh.Cookie{Name: "cookie", Value: x}) 322 req.AddCookie(&stdh.Cookie{Name: "cookie2", Value: b}) 323 req.Request.Header.Set("header", j) 324 req.Request.Header.Set("header2", c) 325 326 _ = json.Unmarshal([]byte(j), &exp.Query) 327 _ = json.Unmarshal([]byte(a), &exp.Query2) 328 _ = json.Unmarshal([]byte(x), &exp.Cookie) 329 _ = json.Unmarshal([]byte(b), &exp.Cookie2) 330 _ = json.Unmarshal([]byte(j), &exp.Header) 331 _ = json.Unmarshal([]byte(c), &exp.Header2) 332 return req 333 } 334 335 func getNullDesc() *thrift.TypeDescriptor { 336 opts := thrift.Options{} 337 svc, err := opts.NewDescritorFromPath(context.Background(), nullIDLPath) 338 if err != nil { 339 panic(err) 340 } 341 return svc.Functions()["NullTest"].Request().Struct().FieldById(1).Type() 342 } 343 344 func getNullData() []byte { 345 out, err := ioutil.ReadFile(nullJSON) 346 if err != nil { 347 panic(err) 348 } 349 return out 350 } 351 352 func getNullErrData() []byte { 353 out, err := ioutil.ReadFile(nullerrJSON) 354 if err != nil { 355 panic(err) 356 } 357 return out 358 } 359 360 func TestWriteDefault(t *testing.T) { 361 desc := getExampleDesc() 362 data := []byte(`{"Path":"<>"}`) 363 exp := example3.NewExampleReq() 364 data2 := []byte(`{"Path":"<>","Base":{}}`) 365 exp.InnerBase = sample.GetEmptyInnerBase3() 366 err := json.Unmarshal(data2, exp) 367 require.Nil(t, err) 368 req := getExampleReq(exp, false, data) 369 cv := NewBinaryConv(conv.Options{ 370 WriteDefaultField: true, 371 EnableHttpMapping: true, 372 }) 373 ctx := context.Background() 374 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 375 out, err := cv.Do(ctx, desc, data) 376 require.Nil(t, err) 377 act := example3.NewExampleReq() 378 _, err = act.FastRead(out) 379 require.Nil(t, err) 380 require.Equal(t, exp, act) 381 } 382 383 func TestWriteRequired(t *testing.T) { 384 desc := getExampleDesc() 385 data := []byte(`{}`) 386 t.Run("JSON", func(t *testing.T) { 387 exp := example3.NewExampleReq() 388 data2 := []byte(`{"Path":""}`) 389 err := json.Unmarshal(data2, exp) 390 require.Nil(t, err) 391 cv := NewBinaryConv(conv.Options{ 392 WriteRequireField: true, 393 }) 394 ctx := context.Background() 395 out, err := cv.Do(ctx, desc, data) 396 require.Nil(t, err) 397 act := example3.NewExampleReq() 398 _, err = act.FastRead(out) 399 require.Nil(t, err) 400 require.Equal(t, exp, act) 401 }) 402 t.Run("http-mapping", func(t *testing.T) { 403 exp := example3.NewExampleReq() 404 exp.InnerBase = sample.GetEmptyInnerBase3() 405 data2 := []byte(`{"Path":"","Base":{}}`) 406 err := json.Unmarshal(data2, exp) 407 require.Nil(t, err) 408 hr, err := stdh.NewRequest("POST", "http://localhost:8888/root", bytes.NewBuffer(data)) 409 require.NoError(t, err) 410 exp.RawUri = hr.URL.String() 411 req, err := http.NewHTTPRequestFromStdReq(hr) 412 require.NoError(t, err) 413 cv := NewBinaryConv(conv.Options{ 414 WriteRequireField: true, 415 WriteDefaultField: true, 416 EnableHttpMapping: true, 417 }) 418 ctx := context.Background() 419 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 420 out, err := cv.Do(ctx, desc, data) 421 require.Nil(t, err) 422 act := example3.NewExampleReq() 423 _, err = act.FastRead(out) 424 require.Nil(t, err) 425 require.Equal(t, exp, act) 426 }) 427 } 428 429 func TestBodyFallbackToHttp(t *testing.T) { 430 desc := getExampleDesc() 431 data := []byte(`{}`) 432 hr, err := stdh.NewRequest("POST", "http://localhost:8888/root?Msg=a&Subfix=1", bytes.NewBuffer(nil)) 433 hr.Header.Set("Base", `{"LogID":"c"}`) 434 hr.Header.Set("Path", `b`) 435 // NOTICE: optional field will be ignored 436 hr.Header.Set("Extra", `{"x":"y"}`) 437 require.NoError(t, err) 438 req := &http.HTTPRequest{ 439 Request: hr, 440 BodyMap: map[string]string{ 441 "InnerBase": `{"Bool":true}`, 442 }, 443 } 444 445 t.Run("write default", func(t *testing.T) { 446 edata := []byte(`{"Base":{"LogID":"c"},"Subfix":1,"Path":"b","InnerBase":{"Bool":true}}`) 447 exp := example3.NewExampleReq() 448 exp.InnerBase = sample.GetEmptyInnerBase3() 449 exp.RawUri = req.GetUri() 450 err = json.Unmarshal(edata, exp) 451 require.Nil(t, err) 452 cv := NewBinaryConv(conv.Options{ 453 EnableHttpMapping: true, 454 WriteDefaultField: true, 455 ReadHttpValueFallback: true, 456 TracebackRequredOrRootFields: true, 457 }) 458 ctx := context.Background() 459 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 460 out, err := cv.Do(ctx, desc, data) 461 require.NoError(t, err) 462 act := example3.NewExampleReq() 463 _, err = act.FastRead(out) 464 require.Nil(t, err) 465 require.Equal(t, exp, act) 466 }) 467 468 t.Run("not write default", func(t *testing.T) { 469 edata := []byte(`{"Base":{"LogID":"c"},"Subfix":1,"Path":"b","InnerBase":{"Bool":true}}`) 470 exp := example3.NewExampleReq() 471 exp.RawUri = req.GetUri() 472 err = json.Unmarshal(edata, exp) 473 require.Nil(t, err) 474 cv := NewBinaryConv(conv.Options{ 475 WriteRequireField: true, 476 EnableHttpMapping: true, 477 WriteDefaultField: false, 478 ReadHttpValueFallback: true, 479 TracebackRequredOrRootFields: true, 480 }) 481 ctx := context.Background() 482 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 483 out, err := cv.Do(ctx, desc, data) 484 require.NoError(t, err) 485 act := example3.NewExampleReq() 486 _, err = act.FastRead(out) 487 require.Nil(t, err) 488 require.Equal(t, exp, act) 489 }) 490 } 491 492 func TestRequireness(t *testing.T) { 493 desc := getErrorExampleDesc() 494 data := []byte(`{}`) 495 cv := NewBinaryConv(conv.Options{ 496 EnableHttpMapping: true, 497 }) 498 ctx := context.Background() 499 req := http.NewHTTPRequest() 500 req.Request, _ = stdh.NewRequest("GET", "root?query=abc", bytes.NewBuffer(nil)) 501 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 502 out, err := cv.Do(ctx, desc, data) 503 require.Nil(t, err) 504 act := example3.NewExampleError() 505 _, err = act.FastRead(out) 506 require.Nil(t, err) 507 require.Equal(t, req.URL.Query().Get("query"), act.Query) 508 } 509 510 func TestNullJSON2Thrift(t *testing.T) { 511 desc := getNullDesc() 512 data := getNullData() 513 cv := NewBinaryConv(conv.Options{}) 514 ctx := context.Background() 515 out, err := cv.Do(ctx, desc, data) 516 require.Nil(t, err) 517 exp := null.NewNullStruct() 518 err = json.Unmarshal(data, exp) 519 require.Nil(t, err) 520 521 var m = map[string]interface{}{} 522 err = json.Unmarshal(data, &m) 523 require.Nil(t, err) 524 fmt.Printf("%#v", m) 525 526 act := null.NewNullStruct() 527 _, err = act.FastRead(out) 528 require.Nil(t, err) 529 // require.Equal(t, exp, act) 530 } 531 532 func TestApiBody(t *testing.T) { 533 desc := getExampleDescByName("ApiBodyMethod", true, thrift.Options{}) 534 data := []byte(`{"code":1024,"InnerCode":{}}`) 535 cv := NewBinaryConv(conv.Options{ 536 EnableHttpMapping: true, 537 WriteDefaultField: true, 538 ReadHttpValueFallback: true, 539 TracebackRequredOrRootFields: true, 540 }) 541 ctx := context.Background() 542 req, err := stdh.NewRequest("POST", "http://localhost:8888/root", bytes.NewBuffer(data)) 543 require.Nil(t, err) 544 req.Header.Set("Content-Type", "application/json") 545 r, err := http.NewHTTPRequestFromStdReq(req) 546 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, r) 547 out, err := cv.Do(ctx, desc, data) 548 require.Nil(t, err) 549 act := example3.NewExampleApiBody() 550 _, err = act.FastRead(out) 551 require.Nil(t, err) 552 require.Equal(t, int64(1024), act.Code) 553 require.Equal(t, int16(1024), act.Code2) 554 require.Equal(t, int64(1024), act.InnerCode.C1) 555 require.Equal(t, int16(0), act.InnerCode.C2) 556 } 557 558 func TestError(t *testing.T) { 559 desc := getExampleDesc() 560 561 t.Run("ERR_NULL_REQUIRED", func(t *testing.T) { 562 desc := getNullDesc() 563 data := getNullErrData() 564 cv := NewBinaryConv(conv.Options{}) 565 ctx := context.Background() 566 _, err := cv.Do(ctx, desc, data) 567 require.Error(t, err) 568 msg := err.Error() 569 require.Equal(t, meta.ErrMissRequiredField, err.(meta.Error).Code.Behavior()) 570 require.True(t, strings.Contains(msg, "missing required field 3")) 571 cv2 := NewBinaryConv(conv.Options{ 572 WriteRequireField: true, 573 }) 574 _, err2 := cv2.Do(context.Background(), desc, data) 575 require.NoError(t, err2) 576 // require.True(t, strings.Contains(msg, "near "+strconv.Itoa(strings.Index(string(data), `"Null3": null`)+13))) 577 }) 578 579 t.Run("INVALID_CHAR", func(t *testing.T) { 580 data := `{xx}` 581 cv := NewBinaryConv(conv.Options{}) 582 ctx := context.Background() 583 _, err := cv.Do(ctx, desc, []byte(data)) 584 require.Error(t, err) 585 msg := err.Error() 586 require.Equal(t, meta.ErrRead, err.(meta.Error).Code.Behavior()) 587 require.True(t, strings.Contains(msg, "invalid char 'x' for state J2T_OBJ_0")) 588 // require.True(t, strings.Contains(msg, "near 2")) 589 }) 590 591 t.Run("ERR_INVALID_NUMBER_FMT", func(t *testing.T) { 592 desc := getExampleInt2FloatDesc() 593 data := []byte(`{"Float64":1.x1}`) 594 cv := NewBinaryConv(conv.Options{ 595 EnableValueMapping: true, 596 }) 597 ctx := context.Background() 598 _, err := cv.Do(ctx, desc, data) 599 require.Error(t, err) 600 msg := err.Error() 601 require.Equal(t, meta.ErrConvert, err.(meta.Error).Code.Behavior()) 602 require.True(t, strings.Contains(msg, "unexpected number type")) 603 // require.True(t, strings.Contains(msg, "near 41")) 604 }) 605 606 t.Run("ERR_UNSUPPORT_THRIFT_TYPE", func(t *testing.T) { 607 desc := getErrorExampleDesc() 608 data := []byte(`{"MapInnerBaseInnerBase":{"a":"a"}`) 609 cv := NewBinaryConv(conv.Options{}) 610 ctx := context.Background() 611 _, err := cv.Do(ctx, desc, data) 612 require.Error(t, err) 613 msg := err.Error() 614 require.Equal(t, meta.ErrUnsupportedType, err.(meta.Error).Code.Behavior()) 615 require.True(t, strings.Contains(msg, "unsupported thrift type STRUCT")) 616 // require.True(t, strings.Contains(msg, "near 32")) 617 }) 618 619 t.Run("ERR_DISMATCH_TYPE", func(t *testing.T) { 620 data := getExampleData() 621 n, err := sjson.NewSearcher(string(data)).GetByPath() 622 require.Nil(t, err) 623 exist, err := n.Set("code_code", sjson.NewString("1.1")) 624 require.True(t, exist) 625 require.Nil(t, err) 626 data, err = n.MarshalJSON() 627 require.Nil(t, err) 628 cv := NewBinaryConv(conv.Options{}) 629 ctx := context.Background() 630 _, err = cv.Do(ctx, desc, data) 631 require.Error(t, err) 632 msg := err.Error() 633 require.Equal(t, meta.ErrDismatchType, err.(meta.Error).Code.Behavior()) 634 require.True(t, strings.Contains(msg, "expect type I64 but got type 11")) 635 }) 636 637 t.Run("ERR_UNKNOWN_FIELD", func(t *testing.T) { 638 desc := getErrorExampleDesc() 639 data := []byte(`{"UnknownField":"1"}`) 640 cv := NewBinaryConv(conv.Options{ 641 DisallowUnknownField: true, 642 }) 643 ctx := context.Background() 644 _, err := cv.Do(ctx, desc, data) 645 require.Error(t, err) 646 msg := err.Error() 647 require.Equal(t, meta.ErrUnknownField, err.(meta.Error).Code.Behavior()) 648 require.True(t, strings.Contains(msg, "unknown field 'UnknownField'")) 649 }) 650 651 t.Run("ERR_UNKNOWN_FIELD", func(t *testing.T) { 652 desc := getErrorExampleDesc() 653 data := []byte(`{"UnknownField":"1"}`) 654 cv := NewBinaryConv(conv.Options{ 655 DisallowUnknownField: true, 656 }) 657 ctx := context.Background() 658 _, err := cv.Do(ctx, desc, data) 659 require.Error(t, err) 660 msg := err.Error() 661 require.Equal(t, meta.ErrUnknownField, err.(meta.Error).Code.Behavior()) 662 require.True(t, strings.Contains(msg, "unknown field 'UnknownField'")) 663 }) 664 665 t.Run("ERR_DECODE_BASE64", func(t *testing.T) { 666 desc := getErrorExampleDesc() 667 data := []byte(`{"Base64":"xxx"}`) 668 cv := NewBinaryConv(conv.Options{}) 669 ctx := context.Background() 670 _, err := cv.Do(ctx, desc, data) 671 require.Error(t, err) 672 msg := err.Error() 673 require.Equal(t, meta.ErrRead, err.(meta.Error).Code.Behavior()) 674 require.True(t, strings.Contains(msg, "decode base64 error: ")) 675 }) 676 677 t.Run("ERR_RECURSE_EXCEED_MAX", func(t *testing.T) { 678 desc := getExampleInt2FloatDesc() 679 src := []byte(`{}`) 680 cv := NewBinaryConv(conv.Options{}) 681 ctx := context.Background() 682 buf := make([]byte, 0, 1) 683 mock := MockConv{ 684 sp: types.MAX_RECURSE + 1, 685 reqsCache: 1, 686 keyCache: 1, 687 dcap: 800, 688 } 689 err := mock.do(&cv, ctx, src, desc, &buf, nil, true) 690 require.Error(t, err) 691 msg := err.Error() 692 require.Equal(t, meta.ErrStackOverflow, err.(meta.Error).Code.Behavior()) 693 require.True(t, strings.Contains(msg, "stack "+strconv.Itoa(types.MAX_RECURSE+1)+" overflow")) 694 }) 695 } 696 697 func TestFloat2Int(t *testing.T) { 698 t.Run("double2int", func(t *testing.T) { 699 desc := getExampleInt2FloatDesc() 700 data := []byte(`{"Int32":2.229e+2}`) 701 cv := NewBinaryConv(conv.Options{}) 702 ctx := context.Background() 703 out, err := cv.Do(ctx, desc, data) 704 require.NoError(t, err) 705 exp := example3.NewExampleInt2Float() 706 _, err = exp.FastRead(out) 707 require.Nil(t, err) 708 require.Equal(t, exp.Int32, int32(222)) 709 }) 710 t.Run("int2double", func(t *testing.T) { 711 desc := getExampleInt2FloatDesc() 712 data := []byte(`{"Float64":` + strconv.Itoa(math.MaxInt64) + `}`) 713 cv := NewBinaryConv(conv.Options{}) 714 ctx := context.Background() 715 out, err := cv.Do(ctx, desc, data) 716 require.NoError(t, err) 717 exp := example3.NewExampleInt2Float() 718 _, err = exp.FastRead(out) 719 require.Nil(t, err) 720 require.Equal(t, exp.Float64, float64(math.MaxInt64)) 721 }) 722 } 723 724 type MockConv struct { 725 sp int 726 reqsCache int 727 keyCache int 728 dcap int 729 fc int 730 } 731 732 func (mock *MockConv) Do(self *BinaryConv, ctx context.Context, desc *thrift.TypeDescriptor, jbytes []byte) (tbytes []byte, err error) { 733 buf := conv.NewBytes() 734 735 var req http.RequestGetter 736 if self.opts.EnableHttpMapping { 737 reqi := ctx.Value(conv.CtxKeyHTTPRequest) 738 if reqi != nil { 739 reqi, ok := reqi.(http.RequestGetter) 740 if !ok { 741 return nil, newError(meta.ErrInvalidParam, "invalid http.RequestGetter", nil) 742 } 743 req = reqi 744 } else { 745 return nil, newError(meta.ErrInvalidParam, "EnableHttpMapping but no http response in context", nil) 746 } 747 } 748 749 err = mock.do(self, ctx, jbytes, desc, buf, req, true) 750 751 if err == nil && len(*buf) > 0 { 752 tbytes = make([]byte, len(*buf)) 753 copy(tbytes, *buf) 754 } 755 756 conv.FreeBytes(buf) 757 return 758 } 759 760 func (mock MockConv) do(self *BinaryConv, ctx context.Context, src []byte, desc *thrift.TypeDescriptor, buf *[]byte, req http.RequestGetter, top bool) (err error) { 761 flags := toFlags(self.opts) 762 jp := rt.Mem2Str(src) 763 tmp := make([]byte, 0, mock.dcap) 764 fsm := &types.J2TStateMachine{ 765 SP: mock.sp, 766 JT: types.JsonState{ 767 Dbuf: *(**byte)(unsafe.Pointer(&tmp)), 768 Dcap: mock.dcap, 769 }, 770 ReqsCache: make([]byte, 0, mock.reqsCache), 771 KeyCache: make([]byte, 0, mock.keyCache), 772 SM: types.StateMachine{}, 773 VT: [types.MAX_RECURSE]types.J2TState{}, 774 FieldCache: make([]int32, 0, mock.fc), 775 } 776 fsm.Init(0, unsafe.Pointer(desc)) 777 if mock.sp != 0 { 778 fsm.SP = mock.sp 779 } 780 781 exec: 782 ret := native.J2T_FSM(fsm, buf, &jp, flags) 783 if ret != 0 { 784 cont, e := self.handleError(ctx, fsm, buf, src, req, ret, top) 785 if cont && e == nil { 786 goto exec 787 } 788 err = e 789 goto ret 790 } 791 792 ret: 793 types.FreeJ2TStateMachine(fsm) 794 runtime.KeepAlive(desc) 795 return 796 } 797 798 func TestStateMachineOOM(t *testing.T) { 799 desc := getExampleInt2FloatDesc() 800 src := []byte(`{"\u4e2d\u6587":"\u4e2d\u6587", "Int32":222}`) 801 println(((uint8)([]byte(`中文`)[5]))) 802 f := desc.Struct().FieldByKey("中文") 803 require.NotNil(t, f) 804 cv := NewBinaryConv(conv.Options{}) 805 ctx := context.Background() 806 buf := make([]byte, 0, 1) 807 mock := MockConv{ 808 sp: 0, 809 reqsCache: 1, 810 keyCache: 1, 811 dcap: 800, 812 } 813 err := mock.do(&cv, ctx, src, desc, &buf, nil, true) 814 require.Nil(t, err) 815 exp := example3.NewExampleInt2Float() 816 err = json.Unmarshal(src, exp) 817 require.NoError(t, err) 818 act := example3.NewExampleInt2Float() 819 _, err = act.FastRead(buf) 820 require.Nil(t, err) 821 require.Equal(t, exp, act) 822 823 t.Run("field cache OOM", func(t *testing.T) { 824 desc := getExampleDesc() 825 data := []byte(`{}`) 826 hr, err := stdh.NewRequest("POST", "http://localhost:8888/root?Msg=a&Subfix=1", bytes.NewBuffer(nil)) 827 hr.Header.Set("Base", `{"LogID":"c"}`) 828 hr.Header.Set("Path", `b`) 829 // NOTICE: optional field will be ignored 830 hr.Header.Set("Extra", `{"x":"y"}`) 831 require.NoError(t, err) 832 req := &http.HTTPRequest{ 833 Request: hr, 834 BodyMap: map[string]string{ 835 "InnerBase": `{"Bool":true}`, 836 }, 837 } 838 edata := []byte(`{"Base":{"LogID":"c"},"Subfix":1,"Path":"b","InnerBase":{"Bool":true}}`) 839 exp := example3.NewExampleReq() 840 exp.RawUri = req.GetUri() 841 err = json.Unmarshal(edata, exp) 842 require.Nil(t, err) 843 exp.RawUri = req.GetUri() 844 mock := MockConv{ 845 sp: 1, 846 reqsCache: 1, 847 keyCache: 1, 848 dcap: 800, 849 fc: 0, 850 } 851 cv := NewBinaryConv(conv.Options{ 852 EnableHttpMapping: true, 853 WriteRequireField: true, 854 ReadHttpValueFallback: true, 855 TracebackRequredOrRootFields: true, 856 }) 857 ctx := context.Background() 858 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 859 out, err := mock.Do(&cv, ctx, desc, data) 860 require.NoError(t, err) 861 act := example3.NewExampleReq() 862 _, err = act.FastRead(out) 863 require.Nil(t, err) 864 require.Equal(t, exp, act) 865 }) 866 } 867 868 func TestEmptyConvHTTP2Thrift(t *testing.T) { 869 desc := getExampleDesc() 870 data := []byte(``) 871 exp := example3.NewExampleReq() 872 req := getExampleReq(exp, false, data) 873 cv := NewBinaryConv(conv.Options{ 874 EnableHttpMapping: true, 875 }) 876 ctx := context.Background() 877 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 878 out, err := cv.Do(ctx, desc, data) 879 require.NoError(t, err) 880 881 act := example3.NewExampleReq() 882 _, err = act.FastRead(out) 883 require.Nil(t, err) 884 require.Equal(t, exp, act) 885 } 886 887 func TestThriftRequestBase(t *testing.T) { 888 desc := getExampleDescByName("ExampleMethod", true, thrift.Options{ 889 // NOTICE: must set options.EnableThriftBase to true 890 EnableThriftBase: true, 891 }) 892 cv := NewBinaryConv(conv.Options{ 893 EnableThriftBase: true, 894 WriteDefaultField: true, 895 }) 896 ctx := context.Background() 897 b := base.NewBase() 898 b.Caller = "caller" 899 b.Extra = map[string]string{ 900 "key": "value", 901 } 902 b.TrafficEnv = &base.TrafficEnv{ 903 Env: "env", 904 } 905 ctx = context.WithValue(ctx, conv.CtxKeyThriftReqBase, b) 906 app, err := json.Marshal(b) 907 require.NoError(t, err) 908 data := getExampleData() 909 910 t.Run("context base", func(t *testing.T) { 911 root, _ := sjson.NewSearcher(string(data)).GetByPath() 912 _, err := root.Unset("Base") 913 require.NoError(t, err) 914 str, _ := root.Raw() 915 in := []byte(str) 916 out, err := cv.Do(ctx, desc, in) 917 require.NoError(t, err) 918 act := example3.NewExampleReq() 919 _, err = act.FastRead(out) 920 921 exp := example3.NewExampleReq() 922 _, err = root.Set("Base", sjson.NewRaw(string(app))) 923 require.NoError(t, err) 924 str, _ = root.Raw() 925 err = json.Unmarshal([]byte(str), exp) 926 require.Nil(t, err) 927 require.Equal(t, exp, act) 928 }) 929 930 // NOTICE: when both body and context base are set, body base will be used 931 t.Run("ctx + json base", func(t *testing.T) { 932 out, err := cv.Do(ctx, desc, data) 933 require.NoError(t, err) 934 act := example3.NewExampleReq() 935 _, err = act.FastRead(out) 936 exp := example3.NewExampleReq() 937 err = json.Unmarshal(data, exp) 938 require.Nil(t, err) 939 require.Equal(t, exp, act) 940 }) 941 } 942 943 func TestString2Int(t *testing.T) { 944 desc := getExampleInt2FloatDesc() 945 cv := NewBinaryConv(conv.Options{ 946 String2Int64: true, 947 }) 948 t.Run("converting", func(t *testing.T) { 949 data := []byte(`{"Int32":"", "Float64":"1.1", "中文": 123.3}`) 950 cv.SetOptions(conv.Options{ 951 EnableValueMapping: true, 952 }) 953 ctx := context.Background() 954 out, err := cv.Do(ctx, desc, data) 955 require.Nil(t, err) 956 exp := example3.NewExampleInt2Float() 957 exp.Int32 = 0 958 exp.Float64 = 1.1 959 exp.String_ = "123.3" 960 act := example3.NewExampleInt2Float() 961 _, err = act.FastRead(out) 962 require.Nil(t, err) 963 require.Equal(t, exp, act) 964 }) 965 966 t.Run("no-converting", func(t *testing.T) { 967 data := []byte(`{"Int32":222, "Float64":1.1, "中文": "123.3"}`) 968 cv.SetOptions(conv.Options{ 969 EnableValueMapping: true, 970 }) 971 ctx := context.Background() 972 out, err := cv.Do(ctx, desc, data) 973 require.Nil(t, err) 974 exp := example3.NewExampleInt2Float() 975 exp.Int32 = 222 976 exp.Float64 = 1.1 977 exp.String_ = "123.3" 978 act := example3.NewExampleInt2Float() 979 _, err = act.FastRead(out) 980 require.Nil(t, err) 981 require.Equal(t, exp, act) 982 }) 983 984 t.Run("option Int64AsString", func(t *testing.T) { 985 data := []byte(`{"Int32":"222","Int64":"333", "Float64":"1.1"}`) 986 cv.SetOptions(conv.Options{ 987 String2Int64: true, 988 }) 989 ctx := context.Background() 990 out, err := cv.Do(ctx, desc, data) 991 require.Nil(t, err) 992 exp := example3.NewExampleInt2Float() 993 exp.Int64 = 333 994 exp.Int32 = 222 995 exp.Float64 = 1.1 996 act := example3.NewExampleInt2Float() 997 _, err = act.FastRead(out) 998 require.Nil(t, err) 999 require.Equal(t, exp, act) 1000 }) 1001 } 1002 1003 func TestJSONString(t *testing.T) { 1004 desc := getExampleJSONStringDesc() 1005 data := []byte(``) 1006 exp := example3.NewExampleJSONString() 1007 req := getExampleJSONStringReq(exp) 1008 cv := NewBinaryConv(conv.Options{ 1009 EnableHttpMapping: true, 1010 }) 1011 ctx := context.Background() 1012 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 1013 out, err := cv.Do(ctx, desc, data) 1014 require.NoError(t, err) 1015 1016 act := example3.NewExampleJSONString() 1017 _, err = act.FastRead(out) 1018 require.Nil(t, err) 1019 require.Equal(t, exp, act) 1020 } 1021 1022 func TestHttpConvError(t *testing.T) { 1023 desc := getErrorExampleDesc() 1024 t.Run("nil required", func(t *testing.T) { 1025 data := []byte(`{}`) 1026 hr, err := stdh.NewRequest("GET", "http://localhost", nil) 1027 require.Nil(t, err) 1028 req := &http.HTTPRequest{ 1029 Request: hr, 1030 } 1031 cv := NewBinaryConv(conv.Options{ 1032 EnableHttpMapping: true, 1033 }) 1034 ctx := context.Background() 1035 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 1036 _, err = cv.Do(ctx, desc, data) 1037 require.Error(t, err) 1038 require.Equal(t, meta.ErrMissRequiredField, err.(meta.Error).Code.Behavior()) 1039 }) 1040 1041 t.Run("write default", func(t *testing.T) { 1042 data := []byte(`{}`) 1043 hr, err := stdh.NewRequest("GET", "http://localhost?query=a", nil) 1044 require.Nil(t, err) 1045 hr.Header = stdh.Header{} 1046 req := &http.HTTPRequest{ 1047 Request: hr, 1048 } 1049 cv := NewBinaryConv(conv.Options{ 1050 EnableHttpMapping: true, 1051 WriteDefaultField: true, 1052 }) 1053 ctx := context.Background() 1054 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 1055 out, err := cv.Do(ctx, desc, data) 1056 require.NoError(t, err) 1057 var exp = example3.NewExampleError() 1058 _, err = exp.FastRead(out) 1059 require.Nil(t, err) 1060 require.Equal(t, "a", exp.Query) 1061 require.Equal(t, "", exp.Header) 1062 }) 1063 1064 t.Run("dismathed type", func(t *testing.T) { 1065 data := []byte(`{}`) 1066 hr, err := stdh.NewRequest("GET", "http://localhost?query=a&q2=1.5", nil) 1067 require.Nil(t, err) 1068 req := &http.HTTPRequest{ 1069 Request: hr, 1070 } 1071 cv := NewBinaryConv(conv.Options{ 1072 EnableHttpMapping: true, 1073 }) 1074 ctx := context.Background() 1075 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 1076 _, err = cv.Do(ctx, desc, data) 1077 require.Error(t, err) 1078 require.Equal(t, meta.ErrConvert, err.(meta.Error).Code.Behavior()) 1079 }) 1080 } 1081 1082 func TestHttpMappingFallback(t *testing.T) { 1083 desc := getExampleFallbackDesc() 1084 data := []byte(`{"Msg":"hello","Heeader":"world"}`) 1085 t.Run("fallback", func(t *testing.T) { 1086 hr, err := stdh.NewRequest("GET", "http://localhost?query=a", nil) 1087 req := &http.HTTPRequest{ 1088 Request: hr, 1089 } 1090 cv := NewBinaryConv(conv.Options{ 1091 EnableHttpMapping: true, 1092 ReadHttpValueFallback: true, 1093 }) 1094 ctx := context.Background() 1095 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 1096 out, err := cv.Do(ctx, desc, data) 1097 require.NoError(t, err) 1098 exp := example3.NewExampleFallback() 1099 exp.Msg = "hello" 1100 exp.Heeader = "world" 1101 act := example3.NewExampleFallback() 1102 _, err = act.FastRead(out) 1103 require.Nil(t, err) 1104 require.Equal(t, exp, act) 1105 }) 1106 t.Run("not fallback", func(t *testing.T) { 1107 hr, err := stdh.NewRequest("GET", "http://localhost?A=a", nil) 1108 hr.Header.Set("heeader", "中文") 1109 req := &http.HTTPRequest{ 1110 Request: hr, 1111 } 1112 cv := NewBinaryConv(conv.Options{ 1113 EnableHttpMapping: true, 1114 }) 1115 ctx := context.Background() 1116 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 1117 out, err := cv.Do(ctx, desc, data) 1118 require.NoError(t, err) 1119 exp := example3.NewExampleFallback() 1120 exp.Msg = "a" 1121 exp.Heeader = "中文" 1122 act := example3.NewExampleFallback() 1123 _, err = act.FastRead(out) 1124 require.Nil(t, err) 1125 require.Equal(t, exp, act) 1126 }) 1127 } 1128 1129 func TestPostFormBody(t *testing.T) { 1130 desc := getExampleDescByName("PostFormMethod", true, thrift.Options{}) 1131 data := url.Values{ 1132 "form": []string{"b"}, 1133 "JSON": []string{`{"a":"中文","b":1}`}, 1134 "inner_form": []string{"1"}, 1135 } 1136 t.Run("fallback", func(t *testing.T) { 1137 exp := example3.NewExamplePostForm() 1138 exp.Query = "a" 1139 exp.Form = "b" 1140 exp.JSON = &example3.InnerJSON{ 1141 A: "中文", 1142 B: 1, 1143 InnerForm: 0, 1144 } 1145 cv := NewBinaryConv(conv.Options{ 1146 WriteDefaultField: true, 1147 EnableHttpMapping: true, 1148 ReadHttpValueFallback: true, 1149 TracebackRequredOrRootFields: true, 1150 }) 1151 ctx := context.Background() 1152 sr, err := stdh.NewRequest("POST", "http://localhost?query=a", strings.NewReader(data.Encode())) 1153 require.NoError(t, err) 1154 sr.Header.Set("Content-Type", "application/x-www-form-urlencoded") 1155 req, err := http.NewHTTPRequestFromStdReq(sr) 1156 require.NoError(t, err) 1157 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 1158 out, err := cv.Do(ctx, desc, []byte(`{}`)) 1159 require.Nil(t, err) 1160 act := example3.NewExamplePostForm() 1161 _, err = act.FastRead(out) 1162 require.Nil(t, err) 1163 require.Equal(t, exp, act) 1164 }) 1165 t.Run("no fallback", func(t *testing.T) { 1166 exp := example3.NewExamplePostForm() 1167 exp.Query = "a" 1168 exp.Form = "b" 1169 // exp.JSON = &example3.InnerJSON{ 1170 // A: "中文", 1171 // B: 1, 1172 // InnerForm: 1, 1173 // } //NOTICE: not set since conv data is nil, thus no fallback 1174 cv := NewBinaryConv(conv.Options{ 1175 WriteDefaultField: false, 1176 EnableHttpMapping: true, 1177 }) 1178 ctx := context.Background() 1179 sr, err := stdh.NewRequest("POST", "http://localhost?query=a", strings.NewReader(data.Encode())) 1180 require.NoError(t, err) 1181 sr.Header.Set("Content-Type", "application/x-www-form-urlencoded") 1182 req, err := http.NewHTTPRequestFromStdReq(sr) 1183 require.NoError(t, err) 1184 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 1185 out, err := cv.Do(ctx, desc, []byte(``)) 1186 require.Nil(t, err) 1187 act := example3.NewExamplePostForm() 1188 _, err = act.FastRead(out) 1189 require.Nil(t, err) 1190 require.Equal(t, exp, act) 1191 }) 1192 } 1193 1194 func TestAGWDynamicBody(t *testing.T) { 1195 desc := getExampleDescByName("DynamicStructMethod", true, thrift.Options{}) 1196 exp := example3.NewExampleDynamicStruct() 1197 exp.Query = "1" 1198 exp.JSON = "[1,2,3]" 1199 exp.InnerStruct = &example3.InnerStruct{ 1200 InnerJSON: `{"a":"中文","b":1}`, 1201 Must: "2", 1202 } 1203 t.Run("no http-mapping", func(t *testing.T) { 1204 data := `{"Query":"1","json":[1,2,3],"inner_struct":{"inner_json":{"a":"中文","b":1},"Must":"2"}}` 1205 cv := NewBinaryConv(conv.Options{ 1206 EnableValueMapping: true, 1207 EnableHttpMapping: false, 1208 WriteRequireField: true, 1209 ReadHttpValueFallback: true, 1210 }) 1211 ctx := context.Background() 1212 out, err := cv.Do(ctx, desc, []byte(data)) 1213 require.Nil(t, err) 1214 act := example3.NewExampleDynamicStruct() 1215 _, err = act.FastRead(out) 1216 require.Nil(t, err) 1217 require.Equal(t, exp, act) 1218 }) 1219 t.Run("http-mapping", func(t *testing.T) { 1220 data := `{"json":[1,2,3],"inner_struct":{"inner_json":{"a":"中文","b":1}}}` 1221 cv := NewBinaryConv(conv.Options{ 1222 EnableValueMapping: true, 1223 EnableHttpMapping: true, 1224 WriteRequireField: true, 1225 ReadHttpValueFallback: true, 1226 TracebackRequredOrRootFields: true, 1227 }) 1228 ctx := context.Background() 1229 req, err := stdh.NewRequest("GET", "http://localhost?query=1&Must=2", nil) 1230 require.NoError(t, err) 1231 rr, err := http.NewHTTPRequestFromStdReq(req) 1232 require.NoError(t, err) 1233 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, rr) 1234 out, err := cv.Do(ctx, desc, []byte(data)) 1235 require.Nil(t, err) 1236 act := example3.NewExampleDynamicStruct() 1237 _, err = act.FastRead(out) 1238 require.Nil(t, err) 1239 require.Equal(t, exp, act) 1240 }) 1241 } 1242 1243 func TestNobodyRequiredFields(t *testing.T) { 1244 path := "a/b/main.thrift" 1245 content := ` 1246 namespace go kitex.test.server 1247 struct Base { 1248 1: required string required_field 1249 } 1250 1251 service InboxService { 1252 string ExampleMethod(1: Base req) 1253 } 1254 ` 1255 includes := map[string]string{ 1256 path: content, 1257 } 1258 p, err := thrift.NewDescritorFromContent(context.Background(), path, content, includes, true) 1259 if err != nil { 1260 t.Fatal(err) 1261 } 1262 desc := p.Functions()["ExampleMethod"].Request().Struct().Fields()[0].Type() 1263 cv := NewBinaryConv(conv.Options{ 1264 EnableHttpMapping: true, 1265 WriteRequireField: true, 1266 ReadHttpValueFallback: true, 1267 }) 1268 ctx := context.Background() 1269 req, err := http.NewHTTPRequestFromUrl("GET", "http://localhost?required_field=1", nil) 1270 require.NoError(t, err) 1271 ctx = context.WithValue(ctx, conv.CtxKeyHTTPRequest, req) 1272 out, err := cv.Do(ctx, desc, nil) 1273 require.Nil(t, err) 1274 fmt.Printf("%+v", out) 1275 } 1276 1277 func TestBase64Decode(t *testing.T) { 1278 desc := getExampleDescByName("Base64BinaryMethod", true, thrift.Options{}) 1279 t.Run("base64 decode", func(t *testing.T) { 1280 exp := example3.NewExampleBase64Binary() 1281 exp.Binary = []byte("hello") 1282 in, err := json.Marshal(exp) 1283 require.Nil(t, err) 1284 cv := NewBinaryConv(conv.Options{ 1285 EnableHttpMapping: true, 1286 NoBase64Binary: false, 1287 }) 1288 req, err := http.NewHTTPRequestFromUrl("GET", "http://localhost", nil) 1289 require.NoError(t, err) 1290 req.Request.Header.Set("Binary2", base64.StdEncoding.EncodeToString([]byte("world"))) 1291 ctx := context.WithValue(context.Background(), conv.CtxKeyHTTPRequest, req) 1292 out, err := cv.Do(ctx, desc, in) 1293 require.Nil(t, err) 1294 1295 act := example3.NewExampleBase64Binary() 1296 _, err = act.FastRead(out) 1297 require.Nil(t, err) 1298 require.Equal(t, []byte("hello"), act.Binary) 1299 require.Equal(t, []byte("world"), act.Binary2) 1300 }) 1301 1302 t.Run("no base64 decode", func(t *testing.T) { 1303 in := []byte(`{"Binary":"hello"}`) 1304 cv := NewBinaryConv(conv.Options{ 1305 EnableHttpMapping: true, 1306 NoBase64Binary: true, 1307 }) 1308 req, err := http.NewHTTPRequestFromUrl("GET", "http://localhost", nil) 1309 require.NoError(t, err) 1310 req.Request.Header.Set("Binary2", "world") 1311 ctx := context.WithValue(context.Background(), conv.CtxKeyHTTPRequest, req) 1312 out, err := cv.Do(ctx, desc, in) 1313 require.Nil(t, err) 1314 1315 act := example3.NewExampleBase64Binary() 1316 _, err = act.FastRead(out) 1317 require.Nil(t, err) 1318 require.Equal(t, []byte("hello"), act.Binary) 1319 require.Equal(t, []byte("world"), act.Binary2) 1320 }) 1321 } 1322 1323 func TestDefaultValue(t *testing.T) { 1324 desc := getExampleDescByName("DefaultValueMethod", true, thrift.Options{ 1325 UseDefaultValue: true, 1326 }) 1327 in := []byte(`{}`) 1328 t.Run("default value", func(t *testing.T) { 1329 cv := NewBinaryConv(conv.Options{ 1330 WriteDefaultField: true, 1331 EnableHttpMapping: false, 1332 }) 1333 out, err := cv.Do(context.Background(), desc, in) 1334 require.Nil(t, err) 1335 act := &example3.ExampleDefaultValue{} 1336 _, err = act.FastRead(out) 1337 require.Nil(t, err) 1338 exp := example3.NewExampleDefaultValue() 1339 require.Equal(t, exp, act) 1340 }) 1341 t.Run("default value + http mapping", func(t *testing.T) { 1342 cv := NewBinaryConv(conv.Options{ 1343 WriteDefaultField: true, 1344 EnableHttpMapping: true, 1345 }) 1346 req, err := http.NewHTTPRequestFromUrl("GET", "http://localhost", nil) 1347 require.NoError(t, err) 1348 ctx := context.WithValue(context.Background(), conv.CtxKeyHTTPRequest, req) 1349 out, err := cv.Do(ctx, desc, in) 1350 require.Nil(t, err) 1351 act := &example3.ExampleDefaultValue{} 1352 _, err = act.FastRead(out) 1353 require.Nil(t, err) 1354 1355 exp := example3.NewExampleDefaultValue() 1356 require.Equal(t, exp, act) 1357 }) 1358 t.Run("zero value", func(t *testing.T) { 1359 desc := getExampleDescByName("DefaultValueMethod", true, thrift.Options{ 1360 UseDefaultValue: false, 1361 }) 1362 cv := NewBinaryConv(conv.Options{ 1363 WriteDefaultField: true, 1364 EnableHttpMapping: false, 1365 }) 1366 req, err := http.NewHTTPRequestFromUrl("GET", "http://localhost", nil) 1367 require.NoError(t, err) 1368 ctx := context.WithValue(context.Background(), conv.CtxKeyHTTPRequest, req) 1369 out, err := cv.Do(ctx, desc, in) 1370 require.Nil(t, err) 1371 act := &example3.ExampleDefaultValue{} 1372 _, err = act.FastRead(out) 1373 require.Nil(t, err) 1374 1375 exp := &example3.ExampleDefaultValue{} 1376 require.Equal(t, exp, act) 1377 }) 1378 } 1379 1380 func TestOptionalDefaultValue(t *testing.T) { 1381 desc := getExampleDescByName("OptionalDefaultValueMethod", true, thrift.Options{ 1382 SetOptionalBitmap: true, 1383 UseDefaultValue: true, 1384 }) 1385 in := []byte(`{}`) 1386 t.Run("write default + write optional", func(t *testing.T) { 1387 cv := NewBinaryConv(conv.Options{ 1388 WriteRequireField: true, 1389 WriteDefaultField: true, 1390 EnableHttpMapping: false, 1391 WriteOptionalField: true, 1392 }) 1393 out, err := cv.Do(context.Background(), desc, in) 1394 require.Nil(t, err) 1395 act := &example3.ExampleOptionalDefaultValue{} 1396 _, err = act.FastRead(out) 1397 require.Nil(t, err) 1398 exp := example3.NewExampleOptionalDefaultValue() 1399 exp.E = new(string) 1400 exp.F = new(string) 1401 require.Equal(t, exp, act) 1402 }) 1403 t.Run("not write optional", func(t *testing.T) { 1404 cv := NewBinaryConv(conv.Options{ 1405 WriteRequireField: true, 1406 WriteDefaultField: false, 1407 EnableHttpMapping: false, 1408 }) 1409 out, err := cv.Do(context.Background(), desc, in) 1410 require.Nil(t, err) 1411 act := &example3.ExampleOptionalDefaultValue{} 1412 _, err = act.FastRead(out) 1413 require.Nil(t, err) 1414 exp := example3.NewExampleOptionalDefaultValue() 1415 exp.A = "" 1416 exp.C = 0 1417 require.Equal(t, exp, act) 1418 }) 1419 t.Run("write default", func(t *testing.T) { 1420 cv := NewBinaryConv(conv.Options{ 1421 WriteRequireField: false, 1422 WriteDefaultField: true, 1423 EnableHttpMapping: false, 1424 }) 1425 _, err := cv.Do(context.Background(), desc, in) 1426 require.Error(t, err) 1427 }) 1428 t.Run("write default + http mapping", func(t *testing.T) { 1429 in := []byte(`{"B":1}`) 1430 cv := NewBinaryConv(conv.Options{ 1431 WriteRequireField: false, 1432 WriteDefaultField: true, 1433 EnableHttpMapping: true, 1434 }) 1435 req, err := http.NewHTTPRequestFromUrl("GET", "http://localhost", nil) 1436 require.NoError(t, err) 1437 ctx := context.WithValue(context.Background(), conv.CtxKeyHTTPRequest, req) 1438 _, err = cv.Do(ctx, desc, in) 1439 require.Error(t, err) 1440 }) 1441 t.Run("write default + write optional + http mapping", func(t *testing.T) { 1442 cv := NewBinaryConv(conv.Options{ 1443 WriteRequireField: true, 1444 WriteDefaultField: true, 1445 EnableHttpMapping: true, 1446 WriteOptionalField: true, 1447 }) 1448 req, err := http.NewHTTPRequestFromUrl("GET", "http://localhost", nil) 1449 require.NoError(t, err) 1450 ctx := context.WithValue(context.Background(), conv.CtxKeyHTTPRequest, req) 1451 out, err := cv.Do(ctx, desc, in) 1452 require.Nil(t, err) 1453 act := &example3.ExampleOptionalDefaultValue{} 1454 _, err = act.FastRead(out) 1455 require.Nil(t, err) 1456 exp := example3.NewExampleOptionalDefaultValue() 1457 exp.E = new(string) 1458 exp.F = new(string) 1459 require.Equal(t, exp, act) 1460 }) 1461 } 1462 1463 func TestNoBodyStruct(t *testing.T) { 1464 desc := getExampleDescByName("NoBodyStructMethod", true, thrift.Options{ 1465 UseDefaultValue: true, 1466 }) 1467 in := []byte(`{}`) 1468 req, err := http.NewHTTPRequestFromUrl("GET", "http://localhost?b=1", nil) 1469 require.NoError(t, err) 1470 cv := NewBinaryConv(conv.Options{ 1471 EnableHttpMapping: true, 1472 }) 1473 ctx := context.WithValue(context.Background(), conv.CtxKeyHTTPRequest, req) 1474 out, err := cv.Do(ctx, desc, in) 1475 require.Nil(t, err) 1476 act := &example3.ExampleNoBodyStruct{} 1477 _, err = act.FastRead(out) 1478 require.Nil(t, err) 1479 exp := example3.NewExampleNoBodyStruct() 1480 exp.NoBodyStruct = example3.NewNoBodyStruct() 1481 B := int32(1) 1482 exp.NoBodyStruct.B = &B 1483 require.Equal(t, exp, act) 1484 } 1485 1486 func TestSimpleArgs(t *testing.T) { 1487 cv := NewBinaryConv(conv.Options{}) 1488 1489 t.Run("string", func(t *testing.T) { 1490 desc := getExampleDescByName("String", true, thrift.Options{}) 1491 p := thrift.NewBinaryProtocolBuffer() 1492 p.WriteString("hello") 1493 exp := p.Buf 1494 out, err := cv.Do(context.Background(), desc, []byte(`"hello"`)) 1495 require.NoError(t, err) 1496 require.Equal(t, exp, out) 1497 }) 1498 1499 t.Run("no quoted string", func(t *testing.T) { 1500 desc := getExampleDescByName("String", true, thrift.Options{}) 1501 p := thrift.NewBinaryProtocolBuffer() 1502 p.WriteString(`hel\lo`) 1503 exp := p.Buf 1504 out, err := cv.Do(context.Background(), desc, []byte(`hel\lo`)) 1505 require.NoError(t, err) 1506 require.Equal(t, exp, out) 1507 }) 1508 1509 t.Run("int", func(t *testing.T) { 1510 desc := getExampleDescByName("I64", true, thrift.Options{}) 1511 p := thrift.NewBinaryProtocolBuffer() 1512 p.WriteI64(math.MaxInt64) 1513 exp := p.Buf 1514 out, err := cv.Do(context.Background(), desc, []byte(strconv.Itoa(math.MaxInt64))) 1515 require.NoError(t, err) 1516 require.Equal(t, exp, out) 1517 }) 1518 }