github.com/cloudwego/dynamicgo@v0.2.6-0.20240519101509-707f41b6b834/conv/p2j/conv_test.go (about) 1 package p2j 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "math" 9 "os" 10 "runtime" 11 "runtime/debug" 12 "testing" 13 "time" 14 15 "github.com/cloudwego/dynamicgo/conv" 16 "github.com/cloudwego/dynamicgo/internal/util_test" 17 "github.com/cloudwego/dynamicgo/meta" 18 "github.com/cloudwego/dynamicgo/proto" 19 "github.com/cloudwego/dynamicgo/testdata/kitex_gen/pb/base" 20 "github.com/cloudwego/dynamicgo/testdata/kitex_gen/pb/example2" 21 "github.com/stretchr/testify/assert" 22 "github.com/stretchr/testify/require" 23 goprotowire "google.golang.org/protobuf/encoding/protowire" 24 goproto "google.golang.org/protobuf/proto" 25 ) 26 27 var ( 28 debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == "" 29 ) 30 31 func TestMain(m *testing.M) { 32 go func() { 33 if !debugAsyncGC { 34 return 35 } 36 println("Begin GC looping...") 37 for { 38 runtime.GC() 39 debug.FreeOSMemory() 40 } 41 }() 42 time.Sleep(time.Millisecond) 43 m.Run() 44 } 45 46 const ( 47 exampleIDLPath = "testdata/idl/example2.proto" 48 exampleProtoPath = "testdata/data/example3_pb.bin" 49 exampleJSONPath = "testdata/data/example3req.json" 50 ) 51 52 func TestBuildData(t *testing.T) { 53 if err := saveExampleReqProtoBufData(); err != nil { 54 panic("build example3ProtoData failed") 55 } 56 } 57 58 func TestConvProto3JSON(t *testing.T) { 59 includeDirs := util_test.MustGitPath("testdata/idl/") // includeDirs is used to find the include files. 60 messageDesc := proto.FnRequest(proto.GetFnDescFromFile(exampleIDLPath, "ExampleMethod", proto.Options{}, includeDirs)) 61 //js := getExample2JSON() 62 cv := NewBinaryConv(conv.Options{}) 63 in := readExampleReqProtoBufData() 64 out, err := cv.Do(context.Background(), messageDesc, in) 65 if err != nil { 66 t.Fatal(err) 67 } 68 exp := example2.ExampleReq{} 69 // use kitex_util to check proto data validity 70 l := 0 71 dataLen := len(in) 72 for l < dataLen { 73 id, wtyp, tagLen := goprotowire.ConsumeTag(in) 74 if tagLen < 0 { 75 t.Fatal("proto data error format") 76 } 77 l += tagLen 78 in = in[tagLen:] 79 offset, err := exp.FastRead(in, int8(wtyp), int32(id)) 80 require.Nil(t, err) 81 in = in[offset:] 82 l += offset 83 } 84 if len(in) != 0 { 85 t.Fatal("proto data error format") 86 } 87 // check json data validity, convert it into act struct 88 var act example2.ExampleReq 89 require.Nil(t, json.Unmarshal([]byte(out), &act)) 90 assert.Equal(t, exp, act) 91 } 92 93 // construct ExampleReq Object 94 func constructExampleReqObject() *example2.ExampleReq { 95 req := example2.ExampleReq{} 96 req.Msg = "hello" 97 req.Subfix = math.MaxFloat64 98 req.InnerBase2 = &example2.InnerBase2{} 99 req.InnerBase2.Bool = true 100 req.InnerBase2.Uint32 = uint32(123) 101 req.InnerBase2.Uint64 = uint64(123) 102 req.InnerBase2.Double = float64(22.3) 103 req.InnerBase2.String_ = "hello_inner" 104 req.InnerBase2.ListInt32 = []int32{12, 13, 14, 15, 16, 17} 105 req.InnerBase2.MapStringString = map[string]string{"m1": "aaa", "m2": "bbb", "m3": "ccc", "m4": "ddd"} 106 req.InnerBase2.SetInt32 = []int32{200, 201, 202, 203, 204, 205} 107 req.InnerBase2.Foo = example2.FOO_FOO_A 108 req.InnerBase2.MapInt32String = map[int32]string{1: "aaa", 2: "bbb", 3: "ccc", 4: "ddd"} 109 req.InnerBase2.Binary = []byte{0x1, 0x2, 0x3, 0x4} 110 req.InnerBase2.MapUint32String = map[uint32]string{uint32(1): "u32aa", uint32(2): "u32bb", uint32(3): "u32cc", uint32(4): "u32dd"} 111 req.InnerBase2.MapUint64String = map[uint64]string{uint64(1): "u64aa", uint64(2): "u64bb", uint64(3): "u64cc", uint64(4): "u64dd"} 112 req.InnerBase2.MapInt64String = map[int64]string{int64(1): "64aaa", int64(2): "64bbb", int64(3): "64ccc", int64(4): "64ddd"} 113 req.InnerBase2.ListString = []string{"111", "222", "333", "44", "51", "6"} 114 req.InnerBase2.ListBase = []*base.Base{{ 115 LogID: "logId", 116 Caller: "caller", 117 Addr: "addr", 118 Client: "client", 119 TrafficEnv: &base.TrafficEnv{ 120 Open: false, 121 Env: "env", 122 }, 123 Extra: map[string]string{"1a": "aaa", "2a": "bbb", "3a": "ccc", "4a": "ddd"}, 124 }, { 125 LogID: "logId2", 126 Caller: "caller2", 127 Addr: "addr2", 128 Client: "client2", 129 TrafficEnv: &base.TrafficEnv{ 130 Open: true, 131 Env: "env2", 132 }, 133 Extra: map[string]string{"1a": "aaa2", "2a": "bbb2", "3a": "ccc2", "4a": "ddd2"}, 134 }} 135 req.InnerBase2.MapInt64Base = map[int64]*base.Base{int64(1): { 136 LogID: "logId", 137 Caller: "caller", 138 Addr: "addr", 139 Client: "client", 140 TrafficEnv: &base.TrafficEnv{ 141 Open: false, 142 Env: "env", 143 }, 144 Extra: map[string]string{"1a": "aaa", "2a": "bbb", "3a": "ccc", "4a": "ddd"}, 145 }, int64(2): { 146 LogID: "logId2", 147 Caller: "caller2", 148 Addr: "addr2", 149 Client: "client2", 150 TrafficEnv: &base.TrafficEnv{ 151 Open: true, 152 Env: "env2", 153 }, 154 Extra: map[string]string{"1a": "aaa2", "2a": "bbb2", "3a": "ccc2", "4a": "ddd2"}, 155 }} 156 req.InnerBase2.MapStringBase = map[string]*base.Base{"1": { 157 LogID: "logId", 158 Caller: "caller", 159 Addr: "addr", 160 Client: "client", 161 TrafficEnv: &base.TrafficEnv{ 162 Open: false, 163 Env: "env", 164 }, 165 Extra: map[string]string{"1a": "aaa", "2a": "bbb", "3a": "ccc", "4a": "ddd"}, 166 }, "2": { 167 LogID: "logId2", 168 Caller: "caller2", 169 Addr: "addr2", 170 Client: "client2", 171 TrafficEnv: &base.TrafficEnv{ 172 Open: true, 173 Env: "env2", 174 }, 175 Extra: map[string]string{"1a": "aaa2", "2a": "bbb2", "3a": "ccc2", "4a": "ddd2"}, 176 }} 177 req.InnerBase2.Base = &base.Base{} 178 req.InnerBase2.Base.LogID = "logId" 179 req.InnerBase2.Base.Caller = "caller" 180 req.InnerBase2.Base.Addr = "addr" 181 req.InnerBase2.Base.Client = "client" 182 req.InnerBase2.Base.TrafficEnv = &base.TrafficEnv{} 183 req.InnerBase2.Base.TrafficEnv.Open = false 184 req.InnerBase2.Base.TrafficEnv.Env = "env" 185 req.InnerBase2.Base.Extra = map[string]string{"1b": "aaa", "2b": "bbb", "3b": "ccc", "4b": "ddd"} 186 return &req 187 } 188 189 // marshal ExampleReq Object to ProtoBinary, and write binaryData into exampleProtoPath 190 func saveExampleReqProtoBufData() error { 191 req := constructExampleReqObject() 192 data, err := goproto.Marshal(req.ProtoReflect().Interface()) 193 if err != nil { 194 panic("goproto marshal data failed") 195 } 196 checkExist := func(path string) bool { 197 _, err := os.Stat(path) 198 if err != nil { 199 if os.IsExist(err) { 200 return true 201 } 202 return false 203 } 204 return true 205 } 206 var file *os.File 207 absoluteExampleProtoPath := util_test.MustGitPath(exampleProtoPath) 208 if checkExist(absoluteExampleProtoPath) == true { 209 if err := os.Remove(absoluteExampleProtoPath); err != nil { 210 panic("delete protoBinaryFile failed") 211 } 212 } 213 file, err = os.Create(absoluteExampleProtoPath) 214 if err != nil { 215 panic("create protoBinaryFile failed") 216 } 217 defer file.Close() 218 if _, err := file.Write(data); err != nil { 219 panic("write protoBinary data failed") 220 } 221 return nil 222 } 223 224 // read ProtoBuf's data in binary format from exampleProtoPath 225 func readExampleReqProtoBufData() []byte { 226 out, err := ioutil.ReadFile(util_test.MustGitPath(exampleProtoPath)) 227 if err != nil { 228 panic(err) 229 } 230 return out 231 } 232 233 // marshal ExampleReq Object to JsonBinary, and write binaryData into exampleJSONPath 234 func saveExampleReqJSONData() error { 235 req := constructExampleReqObject() 236 data, err := json.Marshal(req) 237 if err != nil { 238 panic(fmt.Sprintf("buildExampleJSONData failed, err: %v", err.Error())) 239 } 240 checkExist := func(path string) bool { 241 _, err := os.Stat(path) 242 if err != nil { 243 if os.IsExist(err) { 244 return true 245 } 246 return false 247 } 248 return true 249 } 250 var file *os.File 251 absoluteExampleJSONPath := util_test.MustGitPath(exampleJSONPath) 252 if checkExist(absoluteExampleJSONPath) == true { 253 if err := os.Remove(absoluteExampleJSONPath); err != nil { 254 panic("delete protoBinaryFile failed") 255 } 256 } 257 file, err = os.Create(absoluteExampleJSONPath) 258 if err != nil { 259 panic("create protoBinaryFile failed") 260 } 261 defer file.Close() 262 if _, err := file.WriteString(string(data)); err != nil { 263 panic("write protoJSONData failed") 264 } 265 return nil 266 } 267 268 // read JSON's data in binary format from exampleJSONPath 269 func readExampleReqJSONData() string { 270 out, err := ioutil.ReadFile(util_test.MustGitPath(exampleJSONPath)) 271 if err != nil { 272 panic(err) 273 } 274 return string(out) 275 } 276 277 func getExampleInt2Float() *proto.TypeDescriptor { 278 includeDirs := util_test.MustGitPath("testdata/idl/") // includeDirs is used to find the include files. 279 svc, err := proto.NewDescritorFromPath(context.Background(), util_test.MustGitPath(exampleIDLPath), includeDirs) 280 if err != nil { 281 panic(err) 282 } 283 return (*svc).LookupMethodByName("Int2FloatMethod").Output() 284 } 285 286 func TestInt2String(t *testing.T) { 287 cv := NewBinaryConv(conv.Options{}) 288 desc := getExampleInt2Float() 289 exp := example2.ExampleInt2Float{} 290 exp.Int32 = 1 291 exp.Int64 = 2 292 exp.Float64 = 3.14 293 exp.String_ = "hello" 294 exp.Subfix = 0.92653 295 ctx := context.Background() 296 in := make([]byte, exp.Size()) 297 exp.FastWrite(in) 298 299 out, err := cv.Do(ctx, desc, in) 300 require.NoError(t, err) 301 require.Equal(t, `{"Int32":1,"Float64":3.14,"String":"hello","Int64":2,"Subfix":0.92653}`, string(out)) 302 303 cv.opts.EnableValueMapping = false 304 out, err = cv.Do(ctx, desc, in) 305 require.NoError(t, err) 306 require.Equal(t, (`{"Int32":1,"Float64":3.14,"String":"hello","Int64":2,"Subfix":0.92653}`), string(out)) 307 308 cv.opts.String2Int64 = true 309 out, err = cv.Do(ctx, desc, in) 310 require.NoError(t, err) 311 require.Equal(t, (`{"Int32":1,"Float64":3.14,"String":"hello","Int64":"2","Subfix":0.92653}`), string(out)) 312 } 313 314 func getExampleReqPartialDesc() *proto.TypeDescriptor { 315 includeDirs := util_test.MustGitPath("testdata/idl/") // includeDirs is used to find the include files. 316 return proto.FnRequest(proto.GetFnDescFromFile(exampleIDLPath, "ExamplePartialMethod", proto.Options{}, includeDirs)) 317 } 318 319 func getExampleRespPartialDesc() *proto.TypeDescriptor { 320 includeDirs := util_test.MustGitPath("testdata/idl/") // includeDirs is used to find the include files. 321 return proto.FnResponse(proto.GetFnDescFromFile(exampleIDLPath, "ExamplePartialMethod2", proto.Options{}, includeDirs)) 322 } 323 324 // construct ExampleResp Object 325 func getExampleResp() *example2.ExampleResp { 326 resp := example2.ExampleResp{} 327 resp.Msg = "messagefist" 328 resp.RequiredField = "hello" 329 resp.BaseResp = &base.BaseResp{} 330 resp.BaseResp.StatusMessage = "status1" 331 resp.BaseResp.StatusCode = 32 332 resp.BaseResp.Extra = map[string]string{"1b": "aaa", "2b": "bbb", "3b": "ccc", "4b": "ddd"} 333 return &resp 334 } 335 336 func TestUnknowFields(t *testing.T) { 337 t.Run("top", func(t *testing.T) { 338 cv := NewBinaryConv(conv.Options{ 339 DisallowUnknownField: true, 340 }) 341 resp := getExampleResp() 342 data, err := goproto.Marshal(resp.ProtoReflect().Interface()) 343 if err != nil { 344 t.Fatal("marshal protobuf data failed") 345 } 346 partialRespDesc := getExampleRespPartialDesc() 347 _, err = cv.Do(context.Background(), partialRespDesc, data) 348 require.Error(t, err) 349 require.Equal(t, meta.ErrUnknownField, err.(meta.Error).Code.Behavior()) 350 }) 351 352 t.Run("nested", func(t *testing.T) { 353 cv := NewBinaryConv(conv.Options{ 354 DisallowUnknownField: true, 355 }) 356 partialReqDesc := getExampleReqPartialDesc() 357 in := readExampleReqProtoBufData() 358 _, err := cv.Do(context.Background(), partialReqDesc, in) 359 require.Error(t, err) 360 require.Equal(t, meta.ErrUnknownField, err.(meta.Error).Code.Behavior()) 361 }) 362 363 t.Run("skip top", func(t *testing.T) { 364 cv := NewBinaryConv(conv.Options{ 365 DisallowUnknownField: false, 366 }) 367 resp := getExampleResp() 368 data, err := goproto.Marshal(resp.ProtoReflect().Interface()) 369 if err != nil { 370 t.Fatal("marshal protobuf data failed") 371 } 372 partialRespDesc := getExampleRespPartialDesc() 373 _, err = cv.Do(context.Background(), partialRespDesc, data) 374 require.NoError(t, err) 375 }) 376 377 t.Run("skip nested", func(t *testing.T) { 378 cv := NewBinaryConv(conv.Options{ 379 DisallowUnknownField: false, 380 }) 381 partialReqDesc := getExampleReqPartialDesc() 382 in := readExampleReqProtoBufData() 383 _, err := cv.Do(context.Background(), partialReqDesc, in) 384 require.NoError(t, err) 385 }) 386 }