github.com/artisanhe/tools@v1.0.1-0.20210607022958-19a8fef2eb04/courier/transport_http/transform/scanner_test.go (about)

     1  package transform
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strconv"
     7  	"testing"
     8  
     9  	"github.com/go-courier/ptr"
    10  	"github.com/stretchr/testify/assert"
    11  
    12  	"github.com/artisanhe/tools/timelib"
    13  )
    14  
    15  type Gender int
    16  
    17  const (
    18  	GenderMale Gender = iota + 1
    19  	GenderFemale
    20  )
    21  
    22  func (g Gender) String() string {
    23  	switch g {
    24  	case GenderMale:
    25  		return "male"
    26  	case GenderFemale:
    27  		return "female"
    28  	}
    29  	return ""
    30  }
    31  
    32  func (g Gender) MarshalJSON() ([]byte, error) {
    33  	return []byte(g.String()), nil
    34  }
    35  
    36  func (g *Gender) UnmarshalJSON(data []byte) error {
    37  	s, _ := strconv.Unquote(string(data))
    38  	switch s {
    39  	case "male":
    40  		*g = GenderMale
    41  	case "female":
    42  		*g = GenderMale
    43  	default:
    44  		return fmt.Errorf("unknown gender")
    45  	}
    46  	return nil
    47  }
    48  
    49  func TestScanner(t *testing.T) {
    50  	tt := assert.New(t)
    51  
    52  	type Sub struct {
    53  		Name string `json:"name,omitempty"`
    54  	}
    55  
    56  	type TestModel struct {
    57  		Gender              Gender   `json:"gender" default:""`
    58  		Slice               []string `json:"slice"`
    59  		PtrString           *string  `json:"ptrString" default:""`
    60  		PtrStringUseInput   *string  `json:"ptrStringUseInput" default:"2"`
    61  		PtrStringUseDefault *string  `json:"ptrStringUseDefault" default:"2"`
    62  		RequiredString      *string  `json:"requiredString"`
    63  		Struct              Sub      `json:"struct"`
    64  		PtrStruct           *Sub     `json:"ptrStruct,omitempty"`
    65  	}
    66  
    67  	cases := []struct {
    68  		desc       string
    69  		model      TestModel
    70  		finalModel TestModel
    71  		valid      bool
    72  		msgMap     ErrMsgMap
    73  	}{
    74  		{
    75  			desc:  "normal",
    76  			valid: true,
    77  			model: TestModel{
    78  				PtrStringUseInput: ptr.String(""),
    79  				RequiredString:    ptr.String(""),
    80  			},
    81  			finalModel: TestModel{
    82  				PtrStringUseInput:   ptr.String(""),
    83  				PtrStringUseDefault: ptr.String("2"),
    84  				RequiredString:      ptr.String(""),
    85  			},
    86  		},
    87  	}
    88  
    89  	for _, tc := range cases {
    90  		tpe := reflect.TypeOf(tc.model)
    91  		rv := reflect.Indirect(reflect.ValueOf(&tc.model))
    92  
    93  		valid, msgMap := NewScanner().Validate(rv, tpe)
    94  
    95  		tt.Equal(tc.valid, valid, tc.desc)
    96  		tt.Equal(tc.msgMap, msgMap, tc.desc)
    97  		tt.Equal(tc.finalModel, tc.model, tc.desc)
    98  	}
    99  }
   100  
   101  type TestSubModel1 struct {
   102  	String string          `json:"string" default:"" validate:"@string[1,]"`
   103  	Slice  []TestSubModel2 `json:"slice"`
   104  }
   105  
   106  type TestSubModel2 struct {
   107  	String string `json:"string" default:"" validate:"@string[1,]"`
   108  }
   109  
   110  type TestModel2 struct {
   111  	a        string
   112  	Gender   Gender                 `json:"gender" default:"trans"`
   113  	SliceLen []string               `json:"slice_len" validate:"@array[1,]"`
   114  	Slice    []string               `json:"slice" validate:"@array[1,]:@string[1,]"`
   115  	Hook     string                 `json:"hook"`
   116  	Object   TestSubModel1          `json:"object"`
   117  	Time     timelib.MySQLTimestamp `json:"time"`
   118  }
   119  
   120  func (v TestModel2) ValidateHook() string {
   121  	return "hook failed"
   122  }
   123  
   124  func TestScannerWithValidateErrors(t *testing.T) {
   125  	tt := assert.New(t)
   126  
   127  	cases := []struct {
   128  		desc       string
   129  		model      TestModel2
   130  		finalModel TestModel2
   131  		valid      bool
   132  		msgMap     ErrMsgMap
   133  	}{
   134  		{
   135  			desc:  "default set failed",
   136  			valid: false,
   137  			model: TestModel2{
   138  				Slice: []string{"", ""},
   139  				Object: TestSubModel1{
   140  					Slice: []TestSubModel2{
   141  						{
   142  							String: "",
   143  						},
   144  					},
   145  				},
   146  			},
   147  			finalModel: TestModel2{
   148  				Slice: []string{"", ""},
   149  				Object: TestSubModel1{
   150  					Slice: []TestSubModel2{
   151  						{
   152  							String: "",
   153  						},
   154  					},
   155  				},
   156  			},
   157  			msgMap: ErrMsgMap{
   158  				"gender":                 "Gender can't set wrong default value trans",
   159  				"slice_len":              "切片元素个数不在[1, 0]范围内,当前个数:0",
   160  				"slice":                  "切片元素不满足校验[字符串长度不在[1, 1024]范围内,当前长度:0]",
   161  				"hook":                   "hook failed",
   162  				"time":                   "缺失必填字段",
   163  				"object.string":          "字符串长度不在[1, 1024]范围内,当前长度:0",
   164  				"object.slice[0].string": "字符串长度不在[1, 1024]范围内,当前长度:0",
   165  			},
   166  		},
   167  	}
   168  
   169  	for _, tc := range cases {
   170  		tpe := reflect.TypeOf(tc.model)
   171  		rv := reflect.Indirect(reflect.ValueOf(&tc.model))
   172  
   173  		valid, msgMap := NewScanner().Validate(rv, tpe)
   174  
   175  		tt.Equal(tc.valid, valid, tc.desc)
   176  		tt.Equal(tc.msgMap, msgMap, tc.desc)
   177  		tt.Equal(tc.finalModel, tc.model, tc.desc)
   178  	}
   179  }
   180  
   181  func TestMarshalAndValidate(t *testing.T) {
   182  	tt := assert.New(t)
   183  
   184  	{
   185  		cases := []struct {
   186  			desc             string
   187  			v                int
   188  			defaultValue     string
   189  			required         bool
   190  			tagValidate      string
   191  			tagErrMsg        string
   192  			errMsg           string
   193  			needToSetDefault bool
   194  		}{
   195  			{
   196  				desc:     "int required",
   197  				required: true,
   198  				errMsg:   ErrMsgForRequired,
   199  			},
   200  			{
   201  				desc:             "set int with default value",
   202  				defaultValue:     "1",
   203  				needToSetDefault: true,
   204  			},
   205  			{
   206  				desc:         "set int with default value failed",
   207  				defaultValue: "str",
   208  				errMsg:       fmt.Sprintf("%s can't set wrong default value %s", "int", "str"),
   209  			},
   210  		}
   211  
   212  		for _, tc := range cases {
   213  			tpe := reflect.TypeOf(tc.v)
   214  			rv := reflect.Indirect(reflect.ValueOf(&tc.v))
   215  
   216  			tt.Equal(tc.errMsg, MarshalAndValidate(rv, tpe, tc.defaultValue, tc.required, tc.tagValidate, tc.tagErrMsg), tc.desc)
   217  			if tc.needToSetDefault {
   218  				tt.Equal(tc.defaultValue, fmt.Sprintf("%v", tc.v))
   219  			}
   220  		}
   221  	}
   222  
   223  	{
   224  		cases := []struct {
   225  			desc             string
   226  			V                *string
   227  			defaultValue     string
   228  			required         bool
   229  			tagValidate      string
   230  			tagErrMsg        string
   231  			errMsg           string
   232  			needToSetDefault bool
   233  		}{
   234  			{
   235  				desc: "nil string skip",
   236  			},
   237  			{
   238  				desc:     "nil string required",
   239  				required: true,
   240  				errMsg:   ErrMsgForRequired,
   241  			},
   242  			{
   243  				desc:             "nil string should set default",
   244  				defaultValue:     "1",
   245  				needToSetDefault: true,
   246  			},
   247  			{
   248  				desc:             "nil string should set default",
   249  				defaultValue:     "1",
   250  				needToSetDefault: true,
   251  			},
   252  			{
   253  				desc:             "ptr string should key value",
   254  				V:                ptr.String(""),
   255  				defaultValue:     "1",
   256  				needToSetDefault: false,
   257  			},
   258  		}
   259  
   260  		for _, tc := range cases {
   261  			tpe := reflect.TypeOf(tc.V)
   262  			rv := reflect.Indirect(reflect.ValueOf(&tc)).FieldByName("V")
   263  
   264  			tt.Equal(tc.errMsg, MarshalAndValidate(rv, tpe, tc.defaultValue, tc.required, tc.tagValidate, tc.tagErrMsg), tc.desc)
   265  
   266  			if tc.needToSetDefault {
   267  				tt.Equal(&tc.defaultValue, tc.V, tc.desc)
   268  			}
   269  		}
   270  	}
   271  
   272  }