github.com/moznion/go-optional@v0.11.1-0.20240312043125-6881072e44c1/option_test.go (about) 1 package optional 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 ) 11 12 func TestOption_IsNone(t *testing.T) { 13 assert.True(t, None[int]().IsNone()) 14 assert.False(t, Some[int](123).IsNone()) 15 16 var nilValue Option[int] = nil 17 assert.True(t, nilValue.IsNone()) 18 19 i := 0 20 assert.False(t, FromNillable[int](&i).IsNone()) 21 assert.True(t, FromNillable[int](nil).IsNone()) 22 } 23 24 func TestOption_IsSome(t *testing.T) { 25 assert.False(t, None[int]().IsSome()) 26 assert.True(t, Some[int](123).IsSome()) 27 28 var nilValue Option[int] = nil 29 assert.False(t, nilValue.IsSome()) 30 31 i := 0 32 assert.True(t, FromNillable[int](&i).IsSome()) 33 assert.False(t, FromNillable[int](nil).IsSome()) 34 } 35 36 func TestOption_Unwrap(t *testing.T) { 37 assert.Equal(t, "foo", Some[string]("foo").Unwrap()) 38 assert.Equal(t, "", None[string]().Unwrap()) 39 assert.Nil(t, None[*string]().Unwrap()) 40 41 i := 123 42 assert.Equal(t, i, FromNillable[int](&i).Unwrap()) 43 assert.Equal(t, 0, FromNillable[int](nil).Unwrap()) 44 assert.Equal(t, i, *PtrFromNillable[int](&i).Unwrap()) 45 assert.Nil(t, PtrFromNillable[int](nil).Unwrap()) 46 } 47 48 func TestOption_UnwrapAsPointer(t *testing.T) { 49 str := "foo" 50 refStr := &str 51 assert.EqualValues(t, &str, Some[string](str).UnwrapAsPtr()) 52 assert.EqualValues(t, &refStr, Some[*string](refStr).UnwrapAsPtr()) 53 assert.Nil(t, None[string]().UnwrapAsPtr()) 54 assert.Nil(t, None[*string]().UnwrapAsPtr()) 55 56 i := 123 57 assert.Equal(t, &i, FromNillable[int](&i).UnwrapAsPtr()) 58 assert.Nil(t, FromNillable[int](nil).UnwrapAsPtr()) 59 assert.Equal(t, &i, *PtrFromNillable[int](&i).UnwrapAsPtr()) 60 assert.Nil(t, PtrFromNillable[int](nil).UnwrapAsPtr()) 61 } 62 63 func TestOption_Take(t *testing.T) { 64 v, err := Some[int](123).Take() 65 assert.NoError(t, err) 66 assert.Equal(t, 123, v) 67 68 v, err = None[int]().Take() 69 assert.ErrorIs(t, err, ErrNoneValueTaken) 70 assert.Equal(t, 0, v) 71 } 72 73 func TestOption_TakeOr(t *testing.T) { 74 v := Some[int](123).TakeOr(666) 75 assert.Equal(t, 123, v) 76 77 v = None[int]().TakeOr(666) 78 assert.Equal(t, 666, v) 79 } 80 81 func TestOption_TakeOrElse(t *testing.T) { 82 v := Some[int](123).TakeOrElse(func() int { 83 return 666 84 }) 85 assert.Equal(t, 123, v) 86 87 v = None[int]().TakeOrElse(func() int { 88 return 666 89 }) 90 assert.Equal(t, 666, v) 91 } 92 93 func TestOption_Filter(t *testing.T) { 94 isEven := func(v int) bool { 95 return v%2 == 0 96 } 97 98 o := Some[int](2).Filter(isEven) 99 assert.True(t, o.IsSome()) 100 assert.Equal(t, 2, o[value]) 101 102 o = Some[int](1).Filter(isEven) 103 assert.True(t, o.IsNone()) 104 105 o = None[int]().Filter(isEven) 106 assert.True(t, o.IsNone()) 107 } 108 109 func TestMap(t *testing.T) { 110 some := Some[int](123) 111 mapped := Map(some, func(v int) string { 112 return fmt.Sprintf("%d", v) 113 }) 114 taken, err := mapped.Take() 115 assert.NoError(t, err) 116 assert.Equal(t, "123", taken) 117 118 none := None[int]() 119 mapped = Map(none, func(v int) string { 120 return fmt.Sprintf("%d", v) 121 }) 122 assert.True(t, mapped.IsNone()) 123 } 124 125 func TestMapOr(t *testing.T) { 126 some := Some[int](123) 127 mapped := MapOr(some, "666", func(v int) string { 128 return fmt.Sprintf("%d", v) 129 }) 130 assert.Equal(t, "123", mapped) 131 132 none := None[int]() 133 mapped = MapOr(none, "666", func(v int) string { 134 return fmt.Sprintf("%d", v) 135 }) 136 assert.Equal(t, "666", mapped) 137 } 138 139 func TestZip(t *testing.T) { 140 some1 := Some[int](123) 141 some2 := Some[string]("foo") 142 none := None[uint]() 143 144 zipped := Zip(some1, some2) 145 assert.True(t, zipped.IsSome()) 146 assert.Equal(t, Pair[int, string]{ 147 Value1: 123, 148 Value2: "foo", 149 }, zipped[value]) 150 151 assert.True(t, Zip(none, some1).IsNone()) 152 assert.True(t, Zip(some1, none).IsNone()) 153 } 154 155 func TestZipWith(t *testing.T) { 156 type Data struct { 157 A string 158 B int 159 } 160 161 some1 := Some[int](123) 162 some2 := Some[string]("foo") 163 164 zipped := ZipWith(some1, some2, func(v1 int, v2 string) Data { 165 return Data{ 166 A: v2, 167 B: v1, 168 } 169 }) 170 assert.True(t, zipped.IsSome()) 171 assert.Equal(t, Data{ 172 A: "foo", 173 B: 123, 174 }, zipped[value]) 175 176 assert.True(t, ZipWith(None[int](), some1, func(v1, v2 int) Data { 177 return Data{} 178 }).IsNone()) 179 assert.True(t, ZipWith(some1, None[int](), func(v1, v2 int) Data { 180 return Data{} 181 }).IsNone()) 182 } 183 184 func TestUnzip(t *testing.T) { 185 pair := Pair[int, string]{ 186 Value1: 123, 187 Value2: "foo", 188 } 189 190 o1, o2 := Unzip(Some[Pair[int, string]](pair)) 191 assert.Equal(t, 123, o1.TakeOr(0)) 192 assert.Equal(t, "foo", o2.TakeOr("")) 193 194 o1, o2 = Unzip(None[Pair[int, string]]()) 195 assert.True(t, o1.IsNone()) 196 assert.True(t, o2.IsNone()) 197 } 198 199 func TestUnzipWith(t *testing.T) { 200 type Data struct { 201 A string 202 B int 203 } 204 205 unzipper := func(d Data) (string, int) { 206 return d.A, d.B 207 } 208 209 o1, o2 := UnzipWith(Some[Data](Data{ 210 A: "foo", 211 B: 123, 212 }), unzipper) 213 assert.Equal(t, "foo", o1.TakeOr("")) 214 assert.Equal(t, 123, o2.TakeOr(0)) 215 216 o1, o2 = UnzipWith(None[Data](), unzipper) 217 assert.True(t, o1.IsNone()) 218 assert.True(t, o2.IsNone()) 219 } 220 221 func TestMapWithError(t *testing.T) { 222 some := Some[int](123) 223 mapped, err := MapWithError(some, func(v int) (string, error) { 224 return fmt.Sprintf("%d", v), nil 225 }) 226 assert.NoError(t, err) 227 taken, err := mapped.Take() 228 assert.NoError(t, err) 229 assert.Equal(t, "123", taken) 230 231 none := None[int]() 232 mapped, err = MapWithError(none, func(v int) (string, error) { 233 return fmt.Sprintf("%d", v), nil 234 }) 235 assert.NoError(t, err) 236 assert.True(t, mapped.IsNone()) 237 238 mapperError := errors.New("mapper error") 239 mapped, err = MapWithError(some, func(v int) (string, error) { 240 return "", mapperError 241 }) 242 assert.ErrorIs(t, err, mapperError) 243 assert.True(t, mapped.IsNone()) 244 } 245 246 func TestMapOrWithError(t *testing.T) { 247 some := Some[int](123) 248 mapped, err := MapOrWithError(some, "666", func(v int) (string, error) { 249 return fmt.Sprintf("%d", v), nil 250 }) 251 assert.NoError(t, err) 252 assert.Equal(t, "123", mapped) 253 254 none := None[int]() 255 mapped, err = MapOrWithError(none, "666", func(v int) (string, error) { 256 return fmt.Sprintf("%d", v), nil 257 }) 258 assert.NoError(t, err) 259 assert.Equal(t, "666", mapped) 260 261 mapperError := errors.New("mapper error") 262 mapped, err = MapOrWithError(some, "666", func(v int) (string, error) { 263 return "", mapperError 264 }) 265 assert.ErrorIs(t, err, mapperError) 266 assert.Equal(t, "", mapped) 267 } 268 269 func TestOption_IfSome(t *testing.T) { 270 callingValue := "" 271 Some("foo").IfSome(func(s string) { 272 callingValue = s 273 }) 274 assert.Equal(t, "foo", callingValue) 275 276 callingValue = "" 277 None[string]().IfSome(func(s string) { 278 callingValue = s 279 }) 280 assert.Equal(t, "", callingValue) 281 } 282 283 func TestOption_IfSomeWithError(t *testing.T) { 284 err := Some("foo").IfSomeWithError(func(s string) error { 285 return nil 286 }) 287 assert.NoError(t, err) 288 289 err = Some("foo").IfSomeWithError(func(s string) error { 290 return errors.New(s) 291 }) 292 assert.EqualError(t, err, "foo") 293 294 err = None[string]().IfSomeWithError(func(s string) error { 295 return errors.New(s) 296 }) 297 assert.NoError(t, err) 298 } 299 300 func TestOption_IfNone(t *testing.T) { 301 called := false 302 None[string]().IfNone(func() { 303 called = true 304 }) 305 assert.True(t, called) 306 307 called = false 308 Some("string").IfNone(func() { 309 called = true 310 }) 311 assert.False(t, called) 312 } 313 314 func TestOption_IfNoneWithError(t *testing.T) { 315 err := None[string]().IfNoneWithError(func() error { 316 return nil 317 }) 318 assert.NoError(t, err) 319 320 err = None[string]().IfNoneWithError(func() error { 321 return errors.New("err") 322 }) 323 assert.EqualError(t, err, "err") 324 325 err = Some("foo").IfNoneWithError(func() error { 326 return errors.New("err") 327 }) 328 assert.NoError(t, err) 329 } 330 331 func TestFlatMap(t *testing.T) { 332 some := Some[int](123) 333 mapped := FlatMap(some, func(v int) Option[string] { 334 return Some[string](fmt.Sprintf("%d", v)) 335 }) 336 taken, err := mapped.Take() 337 assert.NoError(t, err) 338 assert.Equal(t, "123", taken) 339 340 none := None[int]() 341 mapped = FlatMap(none, func(v int) Option[string] { 342 return Some[string](fmt.Sprintf("%d", v)) 343 }) 344 assert.True(t, mapped.IsNone()) 345 } 346 347 func TestFlatMapOr(t *testing.T) { 348 some := Some[int](123) 349 mapped := FlatMapOr(some, "666", func(v int) Option[string] { 350 return Some[string](fmt.Sprintf("%d", v)) 351 }) 352 assert.Equal(t, "123", mapped) 353 354 none := None[int]() 355 mapped = FlatMapOr(none, "666", func(v int) Option[string] { 356 return Some[string](fmt.Sprintf("%d", v)) 357 }) 358 assert.Equal(t, "666", mapped) 359 } 360 361 func TestFlatMapWithError(t *testing.T) { 362 some := Some[int](123) 363 mapped, err := FlatMapWithError(some, func(v int) (Option[string], error) { 364 return Some[string](fmt.Sprintf("%d", v)), nil 365 }) 366 assert.NoError(t, err) 367 taken, err := mapped.Take() 368 assert.NoError(t, err) 369 assert.Equal(t, "123", taken) 370 371 none := None[int]() 372 mapped, err = FlatMapWithError(none, func(v int) (Option[string], error) { 373 return Some[string](fmt.Sprintf("%d", v)), nil 374 }) 375 assert.NoError(t, err) 376 assert.True(t, mapped.IsNone()) 377 378 mapperError := errors.New("mapper error") 379 mapped, err = FlatMapWithError(some, func(v int) (Option[string], error) { 380 return Some[string](""), mapperError 381 }) 382 assert.ErrorIs(t, err, mapperError) 383 assert.True(t, mapped.IsNone()) 384 } 385 386 func TestFlatMapOrWithError(t *testing.T) { 387 some := Some[int](123) 388 mapped, err := FlatMapOrWithError(some, "666", func(v int) (Option[string], error) { 389 return Some[string](fmt.Sprintf("%d", v)), nil 390 }) 391 assert.NoError(t, err) 392 assert.Equal(t, "123", mapped) 393 394 none := None[int]() 395 mapped, err = FlatMapOrWithError(none, "666", func(v int) (Option[string], error) { 396 return Some[string](fmt.Sprintf("%d", v)), nil 397 }) 398 assert.NoError(t, err) 399 assert.Equal(t, "666", mapped) 400 401 mapperError := errors.New("mapper error") 402 mapped, err = FlatMapOrWithError(some, "666", func(v int) (Option[string], error) { 403 return Some[string](""), mapperError 404 }) 405 assert.ErrorIs(t, err, mapperError) 406 assert.Equal(t, "", mapped) 407 } 408 409 func TestOptionSerdeJSONForSomeValue(t *testing.T) { 410 { 411 type JSONStruct struct { 412 Val Option[int] `json:"val"` 413 } 414 415 some := Some[int](123) 416 jsonStruct := &JSONStruct{Val: some} 417 418 marshal, err := json.Marshal(jsonStruct) 419 assert.NoError(t, err) 420 assert.EqualValues(t, string(marshal), `{"val":123}`) 421 422 var unmarshalJSONStruct JSONStruct 423 err = json.Unmarshal(marshal, &unmarshalJSONStruct) 424 assert.NoError(t, err) 425 assert.EqualValues(t, jsonStruct, &unmarshalJSONStruct) 426 } 427 428 { 429 type JSONStruct struct { 430 Val Option[string] `json:"val"` 431 } 432 433 some := Some[string]("foobar") 434 jsonStruct := &JSONStruct{Val: some} 435 436 marshal, err := json.Marshal(jsonStruct) 437 assert.NoError(t, err) 438 assert.EqualValues(t, string(marshal), `{"val":"foobar"}`) 439 440 var unmarshalJSONStruct JSONStruct 441 err = json.Unmarshal(marshal, &unmarshalJSONStruct) 442 assert.NoError(t, err) 443 assert.EqualValues(t, jsonStruct, &unmarshalJSONStruct) 444 } 445 446 { 447 type JSONStruct struct { 448 Val Option[bool] `json:"val"` 449 } 450 451 some := Some[bool](false) 452 jsonStruct := &JSONStruct{Val: some} 453 454 marshal, err := json.Marshal(jsonStruct) 455 assert.NoError(t, err) 456 assert.EqualValues(t, string(marshal), `{"val":false}`) 457 458 var unmarshalJSONStruct JSONStruct 459 err = json.Unmarshal(marshal, &unmarshalJSONStruct) 460 assert.NoError(t, err) 461 assert.EqualValues(t, jsonStruct, &unmarshalJSONStruct) 462 } 463 464 { 465 type Inner struct { 466 B *bool `json:"b,omitempty"` 467 } 468 type JSONStruct struct { 469 Val Option[Inner] `json:"val"` 470 } 471 472 { 473 falsy := false 474 some := Some[Inner](Inner{ 475 B: &falsy, 476 }) 477 jsonStruct := &JSONStruct{Val: some} 478 479 marshal, err := json.Marshal(jsonStruct) 480 assert.NoError(t, err) 481 assert.EqualValues(t, string(marshal), `{"val":{"b":false}}`) 482 483 var unmarshalJSONStruct JSONStruct 484 err = json.Unmarshal(marshal, &unmarshalJSONStruct) 485 assert.NoError(t, err) 486 assert.EqualValues(t, jsonStruct, &unmarshalJSONStruct) 487 } 488 489 { 490 some := Some[Inner](Inner{ 491 B: nil, 492 }) 493 jsonStruct := &JSONStruct{Val: some} 494 495 marshal, err := json.Marshal(jsonStruct) 496 assert.NoError(t, err) 497 assert.EqualValues(t, string(marshal), `{"val":{}}`) 498 499 var unmarshalJSONStruct JSONStruct 500 err = json.Unmarshal(marshal, &unmarshalJSONStruct) 501 assert.NoError(t, err) 502 assert.EqualValues(t, jsonStruct, &unmarshalJSONStruct) 503 } 504 } 505 } 506 507 func TestOptionSerdeJSONForNoneValue(t *testing.T) { 508 type JSONStruct struct { 509 Val Option[int] `json:"val"` 510 } 511 some := None[int]() 512 jsonStruct := &JSONStruct{Val: some} 513 514 marshal, err := json.Marshal(jsonStruct) 515 assert.NoError(t, err) 516 assert.EqualValues(t, string(marshal), `{"val":null}`) 517 518 var unmarshalJSONStruct JSONStruct 519 err = json.Unmarshal(marshal, &unmarshalJSONStruct) 520 assert.NoError(t, err) 521 assert.EqualValues(t, jsonStruct, &unmarshalJSONStruct) 522 } 523 524 func TestOption_UnmarshalJSON_withEmptyJSONString(t *testing.T) { 525 type JSONStruct struct { 526 Val Option[int] `json:"val"` 527 } 528 529 var unmarshalJSONStruct JSONStruct 530 err := json.Unmarshal([]byte("{}"), &unmarshalJSONStruct) 531 assert.NoError(t, err) 532 assert.EqualValues(t, &JSONStruct{ 533 Val: None[int](), 534 }, &unmarshalJSONStruct) 535 } 536 537 func TestOption_MarshalJSON_shouldReturnErrorWhenInvalidJSONStructInputHasCome(t *testing.T) { 538 type JSONStruct struct { 539 Val Option[chan interface{}] `json:"val"` // chan type is unsupported on json marshaling 540 } 541 542 ch := make(chan interface{}) 543 some := Some[chan interface{}](ch) 544 jsonStruct := &JSONStruct{Val: some} 545 _, err := json.Marshal(jsonStruct) 546 assert.Error(t, err) 547 } 548 549 func TestOption_UnmarshalJSON_shouldReturnErrorWhenInvalidJSONStringInputHasCome(t *testing.T) { 550 type JSONStruct struct { 551 Val Option[int] `json:"val"` 552 } 553 554 var unmarshalJSONStruct JSONStruct 555 err := json.Unmarshal([]byte(`{"val":"__STRING__"}`), &unmarshalJSONStruct) 556 assert.Error(t, err) 557 } 558 559 func TestOption_MarshalJSON_shouldHandleOmitemptyCorrectly(t *testing.T) { 560 type JSONStruct struct { 561 NormalVal Option[string] `json:"normalVal"` 562 OmitemptyVal Option[string] `json:"omitemptyVal,omitempty"` // this should be omitted 563 } 564 565 none := None[string]() 566 jsonStruct := &JSONStruct{NormalVal: none, OmitemptyVal: none} 567 marshal, err := json.Marshal(jsonStruct) 568 assert.NoError(t, err) 569 assert.EqualValues(t, string(marshal), `{"normalVal":null}`) 570 } 571 572 type MyStringer struct { 573 } 574 575 func (m *MyStringer) String() string { 576 return "mystr" 577 } 578 579 func TestOption_String(t *testing.T) { 580 assert.Equal(t, "Some[123]", Some[int](123).String()) 581 assert.Equal(t, "None[]", None[int]().String()) 582 583 assert.Equal(t, "Some[mystr]", Some[*MyStringer](&MyStringer{}).String()) 584 assert.Equal(t, "None[]", None[*MyStringer]().String()) 585 } 586 587 func TestOption_Or(t *testing.T) { 588 fallback := Some[string]("fallback") 589 590 assert.EqualValues(t, Some[string]("actual").Or(fallback).Unwrap(), "actual") 591 assert.EqualValues(t, None[string]().Or(fallback).Unwrap(), "fallback") 592 } 593 594 func TestOption_OrElse(t *testing.T) { 595 fallbackFunc := func() Option[string] { return Some[string]("fallback") } 596 597 assert.EqualValues(t, Some[string]("actual").OrElse(fallbackFunc).Unwrap(), "actual") 598 assert.EqualValues(t, None[string]().OrElse(fallbackFunc).Unwrap(), "fallback") 599 }