/* * Tencent is pleased to support the open source community by making * 蓝鲸智云 - 配置平台 (BlueKing - CMDB) available. * Copyright (C) 2025 Tencent. All rights reserved. * Licensed under the MIT License (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://opensource.org/licenses/MIT * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the * specific language governing permissions and limitations under the License. * We undertake not to change the open source license (MIT license) applicable * to the current version of the project delivered to anyone in the future. */ package rest import ( "bytes" "io" "net/http" "net/url" "testing" "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "git.ifooth.com/common/pkg/rest/codec" ) // reqStruct for rest type reqStruct struct { Org string `json:"org" req:"path:org" in:"path=org"` Name string `json:"name" req:"query:name" in:"query=name"` Age int32 `json:"age" req:"form:age" in:"form=age"` Bool bool `json:"bool" req:"query:bool" in:"query=bool"` AgePtr *int `json:"agePtr" req:"header:age_ptr" in:"header=age_ptr"` SliceStr []string `json:"sliceStr" req:"query:slice_str" in:"query=slice_str"` Page int64 `json:"page"` } // newMockRequest creates a new mock request. func newMockRequest(t testing.TB, method string, header map[string]string, body io.Reader) *http.Request { req, err := http.NewRequest(method, "/{org}/vm/xxx?name=alices&age=20&age_ptr=21&slice_str=1&slice_str=2", body) req.SetPathValue("org", "myOrg") for k, v := range header { req.Header.Set(k, v) } req.Body = io.NopCloser(body) require.NoError(t, err) return req } func TestDecode(t *testing.T) { header := map[string]string{ "age_ptr": "21", } r := newMockRequest(t, http.MethodGet, header, nil) req, err := decodeReq[reqStruct](r) assert.NoError(t, err) assert.Equal(t, "myOrg", req.Org) assert.Equal(t, "alices", req.Name) assert.Equal(t, lo.ToPtr(21), req.AgePtr) assert.Equal(t, []string{"1", "2"}, req.SliceStr) } func TestFormDecode(t *testing.T) { header := map[string]string{ "age_ptr": "21", "Content-Type": "application/x-www-form-urlencoded", } formData := url.Values{} formData.Set("age", "20") r := newMockRequest(t, http.MethodPost, header, bytes.NewBufferString(formData.Encode())) req, err := decodeReq[reqStruct](r) assert.NoError(t, err) assert.Equal(t, "myOrg", req.Org) assert.Equal(t, "alices", req.Name) assert.Equal(t, int32(20), req.Age) assert.Equal(t, lo.ToPtr(21), req.AgePtr) assert.Equal(t, []string{"1", "2"}, req.SliceStr) } func TestJsonDecode(t *testing.T) { header := map[string]string{ "age_ptr": "21", "Content-Type": "application/json", } jsonData := `{"page": 64}` r := newMockRequest(t, http.MethodPost, header, bytes.NewBufferString(jsonData)) req, err := decodeReq[reqStruct](r) assert.NoError(t, err) assert.Equal(t, "myOrg", req.Org) assert.Equal(t, "alices", req.Name) assert.Equal(t, lo.ToPtr(21), req.AgePtr) assert.Equal(t, []string{"1", "2"}, req.SliceStr) assert.Equal(t, int64(64), req.Page) } func TestDecodeErr(t *testing.T) { header := map[string]string{ "age_ptr": "21", } // array not support type Req2 struct { SliceStr [1]string `json:"sliceStr" req:"query:slice_str"` } r := newMockRequest(t, http.MethodGet, header, nil) _, err := decodeReq[Req2](r) assert.ErrorIs(t, err, codec.ErrUnsupportedType) } func BenchmarkDecodeReq(b *testing.B) { for b.Loop() { r := newMockRequest(b, http.MethodGet, nil, nil) req, err := decodeReq[reqStruct](r) if err != nil { b.Fatal(err) } if req.Name != "alices" { b.Fatal("name not equal") } } }