github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/defaults/defaults_test.go (about) 1 package defaults 2 3 import ( 4 "reflect" 5 "testing" 6 "time" 7 8 "github.com/bingoohuang/gg/pkg/defaults/internal/fixture" 9 ) 10 11 type ( 12 MyInt int 13 MyInt8 int8 14 MyInt16 int16 15 MyInt32 int32 16 MyInt64 int64 17 MyUint uint 18 MyUint8 uint8 19 MyUint16 uint16 20 MyUint32 uint32 21 MyUint64 uint64 22 MyUintptr uintptr 23 MyFloat32 float32 24 MyFloat64 float64 25 MyBool bool 26 MyString string 27 MyMap map[string]int 28 MySlice []int 29 ) 30 31 type Sample struct { 32 Int int `default:"1"` 33 Int8 int8 `default:"8"` 34 Int16 int16 `default:"16"` 35 Int32 int32 `default:"32"` 36 Int64 int64 `default:"64"` 37 Uint uint `default:"1"` 38 Uint8 uint8 `default:"8"` 39 Uint16 uint16 `default:"16"` 40 Uint32 uint32 `default:"32"` 41 Uint64 uint64 `default:"64"` 42 Uintptr uintptr `default:"1"` 43 Float32 float32 `default:"1.32"` 44 Float64 float64 `default:"1.64"` 45 BoolTrue bool `default:"true"` 46 BoolFalse bool `default:"false"` 47 String string `default:"hello"` 48 Duration time.Duration `default:"10s"` 49 50 Struct Struct `default:"{}"` 51 StructPtr *Struct `default:"{}"` 52 Map map[string]int `default:"{}"` 53 Slice []string `default:"[]"` 54 55 MyInt MyInt `default:"1"` 56 MyInt8 MyInt8 `default:"8"` 57 MyInt16 MyInt16 `default:"16"` 58 MyInt32 MyInt32 `default:"32"` 59 MyInt64 MyInt64 `default:"64"` 60 MyUint MyUint `default:"1"` 61 MyUint8 MyUint8 `default:"8"` 62 MyUint16 MyUint16 `default:"16"` 63 MyUint32 MyUint32 `default:"32"` 64 MyUint64 MyUint64 `default:"64"` 65 MyUintptr MyUintptr `default:"1"` 66 MyFloat32 MyFloat32 `default:"1.32"` 67 MyFloat64 MyFloat64 `default:"1.64"` 68 MyBoolTrue MyBool `default:"true"` 69 MyBoolFalse MyBool `default:"false"` 70 MyString MyString `default:"hello"` 71 MyMap MyMap `default:"{}"` 72 MySlice MySlice `default:"[]"` 73 74 StructWithJSON Struct `default:"{\"Foo\": 123}"` 75 StructPtrWithJSON *Struct `default:"{\"Foo\": 123}"` 76 MapWithJSON map[string]int `default:"{\"foo\": 123}"` 77 SliceWithJSON []string `default:"[\"foo\"]"` 78 79 Empty string `default:""` 80 81 NoDefault *string `default:"-"` 82 NoDefaultStruct Struct `default:"-"` 83 84 MapWithNoTag map[string]int 85 SliceWithNoTag []string 86 StructPtrWithNoTag *Struct 87 StructWithNoTag Struct 88 DeepSliceOfStructsWithNoTag [][][]Struct 89 90 NonInitialString string `default:"foo"` 91 NonInitialSlice []int `default:"[123]"` 92 NonInitialStruct Struct `default:"{}"` 93 NonInitialStructPtr *Struct `default:"{}"` 94 } 95 96 type Struct struct { 97 Emmbeded `default:"{}"` 98 99 Foo int 100 Bar int 101 WithDefault string `default:"foo"` 102 } 103 104 func (s *Struct) SetDefaults() { 105 s.Bar = 456 106 } 107 108 type Emmbeded struct { 109 Int int `default:"1"` 110 } 111 112 func TestMustSet(t *testing.T) { 113 sample := &Sample{ 114 NonInitialString: "string", 115 NonInitialSlice: []int{1, 2, 3}, 116 NonInitialStruct: Struct{Foo: 123}, 117 NonInitialStructPtr: &Struct{Foo: 123}, 118 DeepSliceOfStructsWithNoTag: [][][]Struct{{{{Foo: 123}}}}, 119 } 120 121 MustSet(sample) 122 go func() { 123 if err := recover(); err != nil { 124 t.Fatalf("it should not panic error: %v", err) 125 } 126 }() 127 t.Log("it works.") 128 } 129 130 func TestInit(t *testing.T) { 131 sample := &Sample{ 132 NonInitialString: "string", 133 NonInitialSlice: []int{1, 2, 3}, 134 NonInitialStruct: Struct{Foo: 123}, 135 NonInitialStructPtr: &Struct{Foo: 123}, 136 DeepSliceOfStructsWithNoTag: [][][]Struct{{{{Foo: 123}}}}, 137 } 138 139 if err := Set(sample); err != nil { 140 t.Fatalf("it should not return an error: %v", err) 141 } 142 143 nonPtrVal := 1 144 145 if err := Set(nonPtrVal); err == nil { 146 t.Fatalf("it should return an error when used for a non-pointer type") 147 } 148 if err := Set(&nonPtrVal); err == nil { 149 t.Fatalf("it should return an error when used for a non-pointer type") 150 } 151 152 Set(&fixture.Sample{}) // should not panic 153 154 t.Run("scalar types", func(t *testing.T) { 155 if sample.Int != 1 { 156 t.Errorf("it should initialize int") 157 } 158 if sample.Int8 != 8 { 159 t.Errorf("it should initialize int8") 160 } 161 if sample.Int16 != 16 { 162 t.Errorf("it should initialize int16") 163 } 164 if sample.Int32 != 32 { 165 t.Errorf("it should initialize int32") 166 } 167 if sample.Int64 != 64 { 168 t.Errorf("it should initialize int64") 169 } 170 if sample.Uint != 1 { 171 t.Errorf("it should initialize uint") 172 } 173 if sample.Uint8 != 8 { 174 t.Errorf("it should initialize uint8") 175 } 176 if sample.Uint16 != 16 { 177 t.Errorf("it should initialize uint16") 178 } 179 if sample.Uint32 != 32 { 180 t.Errorf("it should initialize uint32") 181 } 182 if sample.Uint64 != 64 { 183 t.Errorf("it should initialize uint64") 184 } 185 if sample.Uintptr != 1 { 186 t.Errorf("it should initialize uintptr") 187 } 188 if sample.Float32 != 1.32 { 189 t.Errorf("it should initialize float32") 190 } 191 if sample.Float64 != 1.64 { 192 t.Errorf("it should initialize float64") 193 } 194 if sample.BoolTrue != true { 195 t.Errorf("it should initialize bool (true)") 196 } 197 if sample.BoolFalse != false { 198 t.Errorf("it should initialize bool (false)") 199 } 200 if sample.String != "hello" { 201 t.Errorf("it should initialize string") 202 } 203 }) 204 205 t.Run("complex types", func(t *testing.T) { 206 if sample.StructPtr == nil { 207 t.Errorf("it should initialize struct pointer") 208 } 209 if sample.Map == nil { 210 t.Errorf("it should initialize map") 211 } 212 if sample.Slice == nil { 213 t.Errorf("it should initialize slice") 214 } 215 }) 216 217 t.Run("aliased types", func(t *testing.T) { 218 if sample.MyInt != 1 { 219 t.Errorf("it should initialize int") 220 } 221 if sample.MyInt8 != 8 { 222 t.Errorf("it should initialize int8") 223 } 224 if sample.MyInt16 != 16 { 225 t.Errorf("it should initialize int16") 226 } 227 if sample.MyInt32 != 32 { 228 t.Errorf("it should initialize int32") 229 } 230 if sample.MyInt64 != 64 { 231 t.Errorf("it should initialize int64") 232 } 233 if sample.MyUint != 1 { 234 t.Errorf("it should initialize uint") 235 } 236 if sample.MyUint8 != 8 { 237 t.Errorf("it should initialize uint8") 238 } 239 if sample.MyUint16 != 16 { 240 t.Errorf("it should initialize uint16") 241 } 242 if sample.MyUint32 != 32 { 243 t.Errorf("it should initialize uint32") 244 } 245 if sample.MyUint64 != 64 { 246 t.Errorf("it should initialize uint64") 247 } 248 if sample.MyUintptr != 1 { 249 t.Errorf("it should initialize uintptr") 250 } 251 if sample.MyFloat32 != 1.32 { 252 t.Errorf("it should initialize float32") 253 } 254 if sample.MyFloat64 != 1.64 { 255 t.Errorf("it should initialize float64") 256 } 257 if sample.MyBoolTrue != true { 258 t.Errorf("it should initialize bool (true)") 259 } 260 if sample.MyBoolFalse != false { 261 t.Errorf("it should initialize bool (false)") 262 } 263 if sample.MyString != "hello" { 264 t.Errorf("it should initialize string") 265 } 266 267 if sample.MyMap == nil { 268 t.Errorf("it should initialize map") 269 } 270 if sample.MySlice == nil { 271 t.Errorf("it should initialize slice") 272 } 273 }) 274 275 t.Run("nested", func(t *testing.T) { 276 if sample.Struct.WithDefault != "foo" { 277 t.Errorf("it should set default on inner field in struct") 278 } 279 if sample.StructPtr == nil || sample.StructPtr.WithDefault != "foo" { 280 t.Errorf("it should set default on inner field in struct pointer") 281 } 282 if sample.Struct.Emmbeded.Int != 1 { 283 t.Errorf("it should set default on an emmbeded struct") 284 } 285 }) 286 287 t.Run("complex types with json", func(t *testing.T) { 288 if sample.StructWithJSON.Foo != 123 { 289 t.Errorf("it should initialize struct with json") 290 } 291 if sample.StructPtrWithJSON == nil || sample.StructPtrWithJSON.Foo != 123 { 292 t.Errorf("it should initialize struct pointer with json") 293 } 294 if sample.MapWithJSON["foo"] != 123 { 295 t.Errorf("it should initialize map with json") 296 } 297 if len(sample.SliceWithJSON) == 0 || sample.SliceWithJSON[0] != "foo" { 298 t.Errorf("it should initialize slice with json") 299 } 300 301 t.Run("invalid json", func(t *testing.T) { 302 if err := Set(&struct { 303 I []int `default:"[!]"` 304 }{}); err == nil { 305 t.Errorf("it should return error") 306 } 307 308 if err := Set(&struct { 309 I map[string]int `default:"{1}"` 310 }{}); err == nil { 311 t.Errorf("it should return error") 312 } 313 314 if err := Set(&struct { 315 S struct { 316 I []int 317 } `default:"{!}"` 318 }{}); err == nil { 319 t.Errorf("it should return error") 320 } 321 322 if err := Set(&struct { 323 S struct { 324 I []int `default:"[!]"` 325 } 326 }{}); err == nil { 327 t.Errorf("it should return error") 328 } 329 }) 330 }) 331 332 t.Run("Setter interface", func(t *testing.T) { 333 if sample.Struct.Bar != 456 { 334 t.Errorf("it should initialize struct") 335 } 336 if sample.StructPtr == nil || sample.StructPtr.Bar != 456 { 337 t.Errorf("it should initialize struct pointer") 338 } 339 }) 340 341 t.Run("non-initial value", func(t *testing.T) { 342 if sample.NonInitialString != "string" { 343 t.Errorf("it should not override non-initial value") 344 } 345 if !reflect.DeepEqual(sample.NonInitialSlice, []int{1, 2, 3}) { 346 t.Errorf("it should not override non-initial value") 347 } 348 if !reflect.DeepEqual(sample.NonInitialStruct, Struct{Emmbeded: Emmbeded{Int: 1}, Foo: 123, Bar: 456, WithDefault: "foo"}) { 349 t.Errorf("it should not override non-initial value but set defaults for fields") 350 } 351 if !reflect.DeepEqual(sample.NonInitialStructPtr, &Struct{Emmbeded: Emmbeded{Int: 1}, Foo: 123, Bar: 456, WithDefault: "foo"}) { 352 t.Errorf("it should not override non-initial value but set defaults for fields") 353 } 354 }) 355 356 t.Run("no tag", func(t *testing.T) { 357 if sample.MapWithNoTag != nil { 358 t.Errorf("it should not initialize pointer type (map)") 359 } 360 if sample.SliceWithNoTag != nil { 361 t.Errorf("it should not initialize pointer type (slice)") 362 } 363 if sample.StructPtrWithNoTag != nil { 364 t.Errorf("it should not initialize pointer type (struct)") 365 } 366 if sample.StructWithNoTag.WithDefault != "foo" { 367 t.Errorf("it should automatically recurse into a struct even without a tag") 368 } 369 if !reflect.DeepEqual(sample.DeepSliceOfStructsWithNoTag, [][][]Struct{{{{Emmbeded: Emmbeded{Int: 1}, Foo: 123, Bar: 456, WithDefault: "foo"}}}}) { 370 t.Errorf("it should automatically recurse into a slice of structs even without a tag") 371 } 372 }) 373 374 t.Run("opt-out", func(t *testing.T) { 375 if sample.NoDefault != nil { 376 t.Errorf("it should not be set") 377 } 378 if sample.NoDefaultStruct.WithDefault != "" { 379 t.Errorf("it should not initialize a struct with default values") 380 } 381 }) 382 } 383 384 func TestCanUpdate(t *testing.T) { 385 type st struct{ Int int } 386 387 var myStructPtr *st 388 389 pairs := map[interface{}]bool{ 390 0: true, 391 123: false, 392 float64(0): true, 393 float64(123): false, 394 "": true, 395 "string": false, 396 false: true, 397 true: false, 398 st{}: true, 399 st{Int: 123}: false, 400 myStructPtr: true, 401 &st{}: false, 402 } 403 for input, expect := range pairs { 404 typ := reflect.TypeOf(input).String() 405 output := CanUpdate(input) 406 if output != expect { 407 t.Errorf("CanUpdate(%+v) returns %v, expected %v, type:%s", input, output, expect, typ) 408 } 409 } 410 }