github.com/MontFerret/ferret@v0.18.0/pkg/runtime/values/array_test.go (about)

     1  package values_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"testing"
     7  
     8  	"github.com/MontFerret/ferret/pkg/runtime/core"
     9  	"github.com/MontFerret/ferret/pkg/runtime/values"
    10  	"github.com/MontFerret/ferret/pkg/runtime/values/types"
    11  
    12  	. "github.com/smartystreets/goconvey/convey"
    13  )
    14  
    15  func TestArray(t *testing.T) {
    16  	Convey("#constructor", t, func() {
    17  		Convey("Should create an empty array", func() {
    18  			arr := values.NewArray(10)
    19  
    20  			So(arr.Length(), ShouldEqual, 0)
    21  		})
    22  
    23  		Convey("Should create an array, from passed values", func() {
    24  			arr := values.NewArrayWith(
    25  				values.NewInt(1),
    26  				values.NewInt(2),
    27  				values.NewInt(3),
    28  			)
    29  
    30  			So(arr.Length(), ShouldEqual, 3)
    31  		})
    32  	})
    33  
    34  	Convey(".MarshalJSON", t, func() {
    35  		Convey("Should serialize empty array", func() {
    36  			arr := values.NewArray(10)
    37  			marshaled, err := arr.MarshalJSON()
    38  
    39  			So(err, ShouldBeNil)
    40  
    41  			So(string(marshaled), ShouldEqual, "[]")
    42  		})
    43  
    44  		Convey("Should serialize full array", func() {
    45  			arr := values.NewArrayWith(
    46  				values.NewInt(1),
    47  				values.NewInt(2),
    48  				values.NewInt(3),
    49  			)
    50  			marshaled, err := json.Marshal(arr)
    51  
    52  			So(err, ShouldBeNil)
    53  
    54  			So(string(marshaled), ShouldEqual, "[1,2,3]")
    55  		})
    56  	})
    57  
    58  	Convey(".Type", t, func() {
    59  		Convey("Should return type", func() {
    60  			arr := values.NewArray(1)
    61  
    62  			So(arr.Type().Equals(types.Array), ShouldBeTrue)
    63  		})
    64  	})
    65  
    66  	Convey(".Unwrap", t, func() {
    67  		Convey("Should return a an array of unwrapped values", func() {
    68  			arr := values.NewArrayWith(
    69  				values.ZeroInt,
    70  				values.ZeroInt,
    71  			)
    72  
    73  			for _, val := range arr.Unwrap().([]interface{}) {
    74  				So(val, ShouldHaveSameTypeAs, 0)
    75  			}
    76  		})
    77  	})
    78  
    79  	Convey(".String", t, func() {
    80  		Convey("Should return a string representation ", func() {
    81  			arr := values.NewArrayWith(values.ZeroInt, values.ZeroInt)
    82  
    83  			So(arr.String(), ShouldEqual, "[0,0]")
    84  		})
    85  	})
    86  
    87  	Convey(".Compare", t, func() {
    88  		Convey("It should return 1 for all non-array and non-object values", func() {
    89  			arr := values.NewArrayWith(values.ZeroInt, values.ZeroInt)
    90  
    91  			So(arr.Compare(values.None), ShouldEqual, 1)
    92  			So(arr.Compare(values.ZeroInt), ShouldEqual, 1)
    93  			So(arr.Compare(values.ZeroFloat), ShouldEqual, 1)
    94  			So(arr.Compare(values.EmptyString), ShouldEqual, 1)
    95  		})
    96  
    97  		Convey("It should return -1 for all object values", func() {
    98  			arr := values.NewArrayWith(values.ZeroInt, values.ZeroInt)
    99  			obj := values.NewObject()
   100  
   101  			So(arr.Compare(obj), ShouldEqual, -1)
   102  		})
   103  
   104  		Convey("It should return 0 when both arrays are empty", func() {
   105  			arr1 := values.NewArray(1)
   106  			arr2 := values.NewArray(1)
   107  
   108  			So(arr1.Compare(arr2), ShouldEqual, 0)
   109  		})
   110  
   111  		Convey("It should return 1 when other array is empty", func() {
   112  			arr1 := values.NewArrayWith(values.ZeroFloat)
   113  			arr2 := values.NewArray(1)
   114  
   115  			So(arr1.Compare(arr2), ShouldEqual, 1)
   116  		})
   117  
   118  		Convey("It should return 1 when values are bigger", func() {
   119  			arr1 := values.NewArrayWith(values.NewInt(1))
   120  			arr2 := values.NewArrayWith(values.ZeroInt)
   121  
   122  			So(arr1.Compare(arr2), ShouldEqual, 1)
   123  		})
   124  
   125  		Convey("It should return 0 when arrays are equal", func() {
   126  			Convey("When only simple types are nested", func() {
   127  				arr1 := values.NewArrayWith(
   128  					values.NewInt(0), values.NewString("str"),
   129  				)
   130  				arr2 := values.NewArrayWith(
   131  					values.NewInt(0), values.NewString("str"),
   132  				)
   133  
   134  				So(arr1.Compare(arr2), ShouldEqual, 0)
   135  			})
   136  
   137  			Convey("When object and array are nested at the same time", func() {
   138  				arr1 := values.NewArrayWith(
   139  					values.NewObjectWith(
   140  						values.NewObjectProperty("one", values.NewInt(1)),
   141  					),
   142  					values.NewArrayWith(
   143  						values.NewInt(2),
   144  					),
   145  				)
   146  				arr2 := values.NewArrayWith(
   147  					values.NewObjectWith(
   148  						values.NewObjectProperty("one", values.NewInt(1)),
   149  					),
   150  					values.NewArrayWith(
   151  						values.NewInt(2),
   152  					),
   153  				)
   154  
   155  				So(arr1.Compare(arr2), ShouldEqual, 0)
   156  			})
   157  
   158  			Convey("When only objects are nested", func() {
   159  				arr1 := values.NewArrayWith(
   160  					values.NewObjectWith(
   161  						values.NewObjectProperty("one", values.NewInt(1)),
   162  					),
   163  				)
   164  				arr2 := values.NewArrayWith(
   165  					values.NewObjectWith(
   166  						values.NewObjectProperty("one", values.NewInt(1)),
   167  					),
   168  				)
   169  
   170  				So(arr1.Compare(arr2), ShouldEqual, 0)
   171  			})
   172  
   173  			Convey("When only arrays are nested", func() {
   174  				arr1 := values.NewArrayWith(
   175  					values.NewArrayWith(
   176  						values.NewInt(2),
   177  					),
   178  				)
   179  				arr2 := values.NewArrayWith(
   180  					values.NewArrayWith(
   181  						values.NewInt(2),
   182  					),
   183  				)
   184  
   185  				So(arr1.Compare(arr2), ShouldEqual, 0)
   186  			})
   187  
   188  			Convey("When simple and complex types at the same time", func() {
   189  				arr1 := values.NewArrayWith(
   190  					values.NewInt(0),
   191  					values.NewObjectWith(
   192  						values.NewObjectProperty("one", values.NewInt(1)),
   193  					),
   194  					values.NewArrayWith(
   195  						values.NewInt(2),
   196  					),
   197  				)
   198  				arr2 := values.NewArrayWith(
   199  					values.NewInt(0),
   200  					values.NewObjectWith(
   201  						values.NewObjectProperty("one", values.NewInt(1)),
   202  					),
   203  					values.NewArrayWith(
   204  						values.NewInt(2),
   205  					),
   206  				)
   207  
   208  				So(arr1.Compare(arr2), ShouldEqual, 0)
   209  			})
   210  
   211  			Convey("When custom complex type", func() {
   212  				arr1 := values.NewArrayWith(
   213  					values.NewObjectWith(
   214  						values.NewObjectProperty(
   215  							"arr", values.NewArrayWith(values.NewObject()),
   216  						),
   217  					),
   218  				)
   219  				arr2 := values.NewArrayWith(
   220  					values.NewObjectWith(
   221  						values.NewObjectProperty(
   222  							"arr", values.NewArrayWith(values.NewObject()),
   223  						),
   224  					),
   225  				)
   226  
   227  				So(arr1.Compare(arr2), ShouldEqual, 0)
   228  			})
   229  		})
   230  	})
   231  
   232  	Convey(".Hash", t, func() {
   233  		Convey("It should calculate hash of non-empty array", func() {
   234  			arr := values.NewArrayWith(
   235  				values.NewInt(1),
   236  				values.NewInt(2),
   237  				values.NewInt(3),
   238  			)
   239  
   240  			h := arr.Hash()
   241  
   242  			So(h, ShouldBeGreaterThan, 0)
   243  		})
   244  
   245  		Convey("It should calculate hash of empty array", func() {
   246  			arr := values.NewArrayWith()
   247  
   248  			h := arr.Hash()
   249  
   250  			So(h, ShouldBeGreaterThan, 0)
   251  		})
   252  
   253  		Convey("Hash sum should be consistent", func() {
   254  			arr := values.NewArrayWith(
   255  				values.True,
   256  				values.NewInt(1),
   257  				values.NewFloat(1.1),
   258  				values.NewString("foobar"),
   259  				values.NewCurrentDateTime(),
   260  				values.NewArrayWith(values.NewInt(1), values.True),
   261  				values.NewObjectWith(values.NewObjectProperty("foo", values.NewString("bar"))),
   262  			)
   263  
   264  			h1 := arr.Hash()
   265  			h2 := arr.Hash()
   266  
   267  			So(h1, ShouldEqual, h2)
   268  		})
   269  	})
   270  
   271  	Convey(".Length", t, func() {
   272  		Convey("Should return 0 when empty", func() {
   273  			arr := values.NewArray(1)
   274  
   275  			So(arr.Length(), ShouldEqual, 0)
   276  		})
   277  
   278  		Convey("Should return greater than 0 when not empty", func() {
   279  			arr := values.NewArrayWith(values.ZeroInt, values.ZeroInt)
   280  
   281  			So(arr.Length(), ShouldEqual, 2)
   282  		})
   283  	})
   284  
   285  	Convey(".ForEach", t, func() {
   286  		Convey("Should iterate over elements", func() {
   287  			arr := values.NewArrayWith(
   288  				values.NewInt(1),
   289  				values.NewInt(2),
   290  				values.NewInt(3),
   291  			)
   292  			counter := 0
   293  
   294  			arr.ForEach(func(value core.Value, idx int) bool {
   295  				counter++
   296  
   297  				return true
   298  			})
   299  
   300  			So(counter, ShouldEqual, arr.Length())
   301  		})
   302  
   303  		Convey("Should not iterate when empty", func() {
   304  			arr := values.NewArrayWith()
   305  			counter := 0
   306  
   307  			arr.ForEach(func(value core.Value, idx int) bool {
   308  				counter++
   309  
   310  				return true
   311  			})
   312  
   313  			So(counter, ShouldEqual, arr.Length())
   314  		})
   315  
   316  		Convey("Should break iteration when false returned", func() {
   317  			arr := values.NewArrayWith(
   318  				values.NewInt(1),
   319  				values.NewInt(2),
   320  				values.NewInt(3),
   321  				values.NewInt(4),
   322  				values.NewInt(5),
   323  			)
   324  			threshold := 3
   325  			counter := 0
   326  
   327  			arr.ForEach(func(value core.Value, idx int) bool {
   328  				counter++
   329  
   330  				return value.Compare(values.NewInt(threshold)) == -1
   331  			})
   332  
   333  			So(counter, ShouldEqual, threshold)
   334  		})
   335  	})
   336  
   337  	Convey(".Get", t, func() {
   338  		Convey("Should return item by index", func() {
   339  			arr := values.NewArrayWith(
   340  				values.NewInt(1),
   341  				values.NewInt(2),
   342  				values.NewInt(3),
   343  				values.NewInt(4),
   344  				values.NewInt(5),
   345  			)
   346  
   347  			el := arr.Get(1)
   348  
   349  			So(el.Compare(values.NewInt(2)), ShouldEqual, 0)
   350  		})
   351  
   352  		Convey("Should return None when no items", func() {
   353  			arr := values.NewArrayWith()
   354  
   355  			el := arr.Get(1)
   356  
   357  			So(el.Compare(values.None), ShouldEqual, 0)
   358  		})
   359  	})
   360  
   361  	Convey(".Set", t, func() {
   362  		Convey("Should set item by index", func() {
   363  			arr := values.NewArrayWith(values.ZeroInt)
   364  
   365  			err := arr.Set(0, values.NewInt(1))
   366  
   367  			So(err, ShouldBeNil)
   368  			So(arr.Length(), ShouldEqual, 1)
   369  			So(arr.Get(0).Compare(values.NewInt(1)), ShouldEqual, 0)
   370  		})
   371  
   372  		Convey("Should return an error when index is out of bounds", func() {
   373  			arr := values.NewArray(10)
   374  
   375  			err := arr.Set(0, values.NewInt(1))
   376  
   377  			So(err, ShouldNotBeNil)
   378  			So(arr.Length(), ShouldEqual, 0)
   379  		})
   380  	})
   381  
   382  	Convey(".Push", t, func() {
   383  		Convey("Should add an item", func() {
   384  			arr := values.NewArray(10)
   385  
   386  			src := []core.Value{
   387  				values.ZeroInt,
   388  				values.ZeroInt,
   389  				values.ZeroInt,
   390  				values.ZeroInt,
   391  				values.ZeroInt,
   392  			}
   393  
   394  			for _, val := range src {
   395  				arr.Push(val)
   396  			}
   397  
   398  			So(arr.Length(), ShouldEqual, len(src))
   399  		})
   400  	})
   401  
   402  	Convey(".Slice", t, func() {
   403  		Convey("Should return a slice", func() {
   404  			arr := values.NewArrayWith(
   405  				values.NewInt(0),
   406  				values.NewInt(1),
   407  				values.NewInt(2),
   408  				values.NewInt(3),
   409  				values.NewInt(4),
   410  				values.NewInt(5),
   411  			)
   412  
   413  			s := arr.Slice(0, 1)
   414  
   415  			So(s.Length(), ShouldEqual, 1)
   416  			So(s.Get(0).Compare(values.ZeroInt), ShouldEqual, 0)
   417  
   418  			s2 := arr.Slice(2, arr.Length())
   419  
   420  			So(s2.Length(), ShouldEqual, arr.Length()-2)
   421  		})
   422  	})
   423  
   424  	Convey(".Insert", t, func() {
   425  		Convey("Should insert an item in the middle of an array", func() {
   426  			arr := values.NewArrayWith(
   427  				values.NewInt(0),
   428  				values.NewInt(1),
   429  				values.NewInt(2),
   430  				values.NewInt(3),
   431  				values.NewInt(4),
   432  				values.NewInt(5),
   433  			)
   434  
   435  			lenBefore := arr.Length()
   436  
   437  			arr.Insert(3, values.NewInt(100))
   438  
   439  			lenAfter := arr.Length()
   440  
   441  			So(lenAfter, ShouldBeGreaterThan, lenBefore)
   442  			So(arr.Get(3), ShouldEqual, 100)
   443  		})
   444  	})
   445  
   446  	Convey(".RemoveAt", t, func() {
   447  		Convey("Should remove an item from the middle", func() {
   448  			arr := values.NewArrayWith(
   449  				values.NewInt(0),
   450  				values.NewInt(1),
   451  				values.NewInt(2),
   452  				values.NewInt(3),
   453  				values.NewInt(4),
   454  				values.NewInt(5),
   455  			)
   456  
   457  			lenBefore := arr.Length()
   458  
   459  			arr.RemoveAt(3)
   460  
   461  			lenAfter := arr.Length()
   462  
   463  			So(lenAfter, ShouldBeLessThan, lenBefore)
   464  			So(arr.Get(3), ShouldEqual, 4)
   465  		})
   466  
   467  		Convey("Should remove an item from the end", func() {
   468  			arr := values.NewArrayWith(
   469  				values.NewInt(0),
   470  				values.NewInt(1),
   471  				values.NewInt(2),
   472  				values.NewInt(3),
   473  				values.NewInt(4),
   474  				values.NewInt(5),
   475  			)
   476  
   477  			lenBefore := arr.Length()
   478  
   479  			arr.RemoveAt(5)
   480  
   481  			lenAfter := arr.Length()
   482  
   483  			So(lenAfter, ShouldBeLessThan, lenBefore)
   484  			So(lenAfter, ShouldEqual, 5)
   485  			So(arr.Get(4), ShouldEqual, 4)
   486  		})
   487  
   488  		Convey("Should remove an item from the beginning", func() {
   489  			arr := values.NewArrayWith(
   490  				values.NewInt(0),
   491  				values.NewInt(1),
   492  				values.NewInt(2),
   493  				values.NewInt(3),
   494  				values.NewInt(4),
   495  				values.NewInt(5),
   496  			)
   497  
   498  			lenBefore := arr.Length()
   499  
   500  			arr.RemoveAt(0)
   501  
   502  			lenAfter := arr.Length()
   503  
   504  			So(lenAfter, ShouldBeLessThan, lenBefore)
   505  			So(arr.Get(0), ShouldEqual, 1)
   506  		})
   507  	})
   508  
   509  	Convey(".Clone", t, func() {
   510  		Convey("Cloned array should be equal to source array", func() {
   511  			arr := values.NewArrayWith(
   512  				values.NewInt(0),
   513  				values.NewObjectWith(
   514  					values.NewObjectProperty("one", values.NewInt(1)),
   515  				),
   516  				values.NewArrayWith(
   517  					values.NewInt(2),
   518  				),
   519  			)
   520  
   521  			clone := arr.Clone().(*values.Array)
   522  
   523  			So(arr.Length(), ShouldEqual, clone.Length())
   524  			So(arr.Compare(clone), ShouldEqual, 0)
   525  		})
   526  
   527  		Convey("Cloned array should be independent of the source array", func() {
   528  			arr := values.NewArrayWith(
   529  				values.NewInt(0),
   530  				values.NewInt(1),
   531  				values.NewInt(2),
   532  				values.NewInt(3),
   533  				values.NewInt(4),
   534  				values.NewInt(5),
   535  			)
   536  
   537  			clone := arr.Clone().(*values.Array)
   538  
   539  			arr.Push(values.NewInt(6))
   540  
   541  			So(arr.Length(), ShouldNotEqual, clone.Length())
   542  			So(arr.Compare(clone), ShouldNotEqual, 0)
   543  		})
   544  
   545  		Convey("Cloned array must contain copies of the nested objects", func() {
   546  			arr := values.NewArrayWith(
   547  				values.NewArrayWith(
   548  					values.NewInt(0),
   549  					values.NewInt(1),
   550  					values.NewInt(2),
   551  					values.NewInt(3),
   552  					values.NewInt(4),
   553  				),
   554  			)
   555  
   556  			clone := arr.Clone().(*values.Array)
   557  
   558  			nestedInArr := arr.Get(values.NewInt(0)).(*values.Array)
   559  			nestedInArr.Push(values.NewInt(5))
   560  
   561  			nestedInClone := clone.Get(values.NewInt(0)).(*values.Array)
   562  
   563  			So(nestedInArr.Compare(nestedInClone), ShouldNotEqual, 0)
   564  		})
   565  	})
   566  
   567  	Convey(".GetIn", t, func() {
   568  
   569  		ctx := context.Background()
   570  
   571  		Convey("Should return the same as .Get when input is correct", func() {
   572  
   573  			Convey("Should return item by key", func() {
   574  				key := values.NewInt(0)
   575  				arr := values.NewArrayWith(
   576  					values.NewInt(0),
   577  				)
   578  
   579  				el, err := arr.GetIn(ctx, []core.Value{key})
   580  				elGet := arr.Get(key)
   581  
   582  				So(err, ShouldBeNil)
   583  				So(el.Compare(elGet), ShouldEqual, 0)
   584  			})
   585  
   586  			Convey("Should return None when no items", func() {
   587  				key := values.NewInt(0)
   588  				arr := values.NewArray(0)
   589  
   590  				el, err := arr.GetIn(ctx, []core.Value{key})
   591  				elGet := arr.Get(key)
   592  
   593  				So(err, ShouldBeNil)
   594  				So(el.Compare(elGet), ShouldEqual, 0)
   595  			})
   596  		})
   597  
   598  		Convey("Should error when input is not correct", func() {
   599  
   600  			Convey("Should error when path[0] is not an int", func() {
   601  				arr := values.NewArray(0)
   602  				path := []core.Value{values.NewString("")}
   603  
   604  				el, err := arr.GetIn(ctx, path)
   605  
   606  				So(err, ShouldBeError)
   607  				So(el.Compare(values.None), ShouldEqual, 0)
   608  			})
   609  
   610  			Convey("Should error when first received item is not a Getter and len(path) > 1", func() {
   611  				key := values.NewInt(0)
   612  				arr := values.NewArrayWith(
   613  					values.NewInt(1),
   614  				)
   615  				path := []core.Value{key, key}
   616  
   617  				el, err := arr.GetIn(ctx, path)
   618  
   619  				So(err, ShouldBeError)
   620  				So(el.Compare(values.None), ShouldEqual, 0)
   621  			})
   622  		})
   623  
   624  		Convey("Should return None when len(path) == 0", func() {
   625  			arr := values.NewArrayWith(
   626  				values.NewInt(1),
   627  			)
   628  
   629  			el, err := arr.GetIn(ctx, nil)
   630  
   631  			So(err, ShouldBeNil)
   632  			So(el.Compare(values.None), ShouldEqual, 0)
   633  		})
   634  
   635  		Convey("Should call the nested Getter", func() {
   636  			key := values.NewInt(0)
   637  			arr := values.NewArrayWith(
   638  				values.NewObjectWith(
   639  					values.NewObjectProperty("foo", key),
   640  				),
   641  			)
   642  
   643  			el, err := arr.GetIn(ctx, []core.Value{
   644  				key,                     // obj[0]
   645  				values.NewString("foo"), // obj[0].foo
   646  			})
   647  
   648  			So(err, ShouldBeNil)
   649  			So(el.Compare(key), ShouldEqual, 0)
   650  		})
   651  	})
   652  }