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