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  }