github.com/MontFerret/ferret@v0.18.0/pkg/runtime/values/object_test.go (about) 1 package values_test 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/MontFerret/ferret/pkg/runtime/core" 8 "github.com/MontFerret/ferret/pkg/runtime/values" 9 "github.com/MontFerret/ferret/pkg/runtime/values/types" 10 11 . "github.com/smartystreets/goconvey/convey" 12 ) 13 14 func TestObject(t *testing.T) { 15 Convey("#constructor", t, func() { 16 Convey("Should create an empty object", func() { 17 obj := values.NewObject() 18 19 So(obj.Length(), ShouldEqual, 0) 20 }) 21 22 Convey("Should create an object, from passed values", func() { 23 obj := values.NewObjectWith( 24 values.NewObjectProperty("none", values.None), 25 values.NewObjectProperty("boolean", values.False), 26 values.NewObjectProperty("int", values.NewInt(1)), 27 values.NewObjectProperty("float", values.Float(1)), 28 values.NewObjectProperty("string", values.NewString("1")), 29 values.NewObjectProperty("array", values.NewArray(10)), 30 values.NewObjectProperty("object", values.NewObject()), 31 ) 32 33 So(obj.Length(), ShouldEqual, 7) 34 }) 35 }) 36 37 Convey(".MarshalJSON", t, func() { 38 Convey("Should serialize an empty object", func() { 39 obj := values.NewObject() 40 marshaled, err := obj.MarshalJSON() 41 42 So(err, ShouldBeNil) 43 44 So(string(marshaled), ShouldEqual, "{}") 45 }) 46 47 Convey("Should serialize full object", func() { 48 obj := values.NewObjectWith( 49 values.NewObjectProperty("none", values.None), 50 values.NewObjectProperty("boolean", values.False), 51 values.NewObjectProperty("int", values.NewInt(1)), 52 values.NewObjectProperty("float", values.Float(1)), 53 values.NewObjectProperty("string", values.NewString("1")), 54 values.NewObjectProperty("array", values.NewArray(10)), 55 values.NewObjectProperty("object", values.NewObject()), 56 ) 57 marshaled, err := obj.MarshalJSON() 58 59 So(err, ShouldBeNil) 60 61 So(string(marshaled), ShouldEqual, "{\"array\":[],\"boolean\":false,\"float\":1,\"int\":1,\"none\":null,\"object\":{},\"string\":\"1\"}") 62 }) 63 }) 64 65 Convey(".Type", t, func() { 66 Convey("Should return type", func() { 67 obj := values.NewObject() 68 69 So(obj.Type().Equals(types.Object), ShouldBeTrue) 70 }) 71 }) 72 73 Convey(".Unwrap", t, func() { 74 Convey("Should return an unwrapped items", func() { 75 obj := values.NewObjectWith( 76 values.NewObjectProperty("foo", values.NewString("foo")), 77 values.NewObjectProperty("bar", values.NewString("bar")), 78 ) 79 80 for _, val := range obj.Unwrap().(map[string]interface{}) { 81 So(val, ShouldHaveSameTypeAs, "") 82 } 83 }) 84 }) 85 86 Convey(".String", t, func() { 87 Convey("Should return a string representation ", func() { 88 obj := values.NewObjectWith( 89 values.NewObjectProperty("foo", values.NewString("bar")), 90 ) 91 92 So(obj.String(), ShouldEqual, "{\"foo\":\"bar\"}") 93 }) 94 }) 95 96 Convey(".Compare", t, func() { 97 Convey("It should return 1 for all non-object values", func() { 98 arr := []core.Value{ 99 values.None, 100 values.False, 101 values.NewInt(1), 102 values.Float(1), 103 values.NewString("1"), 104 values.NewArray(10), 105 } 106 obj := values.NewObject() 107 108 for _, val := range arr { 109 So(obj.Compare(val), ShouldEqual, 1) 110 } 111 }) 112 113 Convey("It should return -1 for all object values", func() { 114 arr := values.NewArrayWith(values.ZeroInt, values.ZeroInt) 115 obj := values.NewObject() 116 117 So(arr.Compare(obj), ShouldEqual, -1) 118 }) 119 120 Convey("It should return 0 when both objects are empty", func() { 121 obj1 := values.NewObject() 122 obj2 := values.NewObject() 123 124 So(obj1.Compare(obj2), ShouldEqual, 0) 125 }) 126 127 Convey("It should return 0 when both objects are equal (independent of key order)", func() { 128 obj1 := values.NewObjectWith( 129 values.NewObjectProperty("foo", values.NewString("foo")), 130 values.NewObjectProperty("bar", values.NewString("bar")), 131 ) 132 obj2 := values.NewObjectWith( 133 values.NewObjectProperty("foo", values.NewString("foo")), 134 values.NewObjectProperty("bar", values.NewString("bar")), 135 ) 136 137 So(obj1.Compare(obj1), ShouldEqual, 0) 138 So(obj2.Compare(obj2), ShouldEqual, 0) 139 So(obj1.Compare(obj2), ShouldEqual, 0) 140 So(obj2.Compare(obj1), ShouldEqual, 0) 141 }) 142 143 Convey("It should return 1 when other array is empty", func() { 144 obj1 := values.NewObjectWith(values.NewObjectProperty("foo", values.NewString("bar"))) 145 obj2 := values.NewObject() 146 147 So(obj1.Compare(obj2), ShouldEqual, 1) 148 }) 149 150 Convey("It should return 1 when values are bigger", func() { 151 obj1 := values.NewObjectWith(values.NewObjectProperty("foo", values.NewFloat(3))) 152 obj2 := values.NewObjectWith(values.NewObjectProperty("foo", values.NewFloat(2))) 153 154 So(obj1.Compare(obj2), ShouldEqual, 1) 155 }) 156 157 Convey("It should return 1 when values are less", func() { 158 obj1 := values.NewObjectWith(values.NewObjectProperty("foo", values.NewFloat(1))) 159 obj2 := values.NewObjectWith(values.NewObjectProperty("foo", values.NewFloat(2))) 160 161 So(obj1.Compare(obj2), ShouldEqual, -1) 162 }) 163 164 Convey("ArangoDB compatibility", func() { 165 Convey("It should return 1 when {a:1} and {b:2}", func() { 166 obj1 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(1))) 167 obj2 := values.NewObjectWith(values.NewObjectProperty("b", values.NewInt(2))) 168 169 So(obj1.Compare(obj2), ShouldEqual, 1) 170 }) 171 172 Convey("It should return 0 when {a:1} and {a:1}", func() { 173 obj1 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(1))) 174 obj2 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(1))) 175 176 So(obj1.Compare(obj2), ShouldEqual, 0) 177 }) 178 179 Convey("It should return 0 {a:1, c:2} and {c:2, a:1}", func() { 180 obj1 := values.NewObjectWith( 181 values.NewObjectProperty("a", values.NewInt(1)), 182 values.NewObjectProperty("c", values.NewInt(2)), 183 ) 184 obj2 := values.NewObjectWith( 185 values.NewObjectProperty("c", values.NewInt(2)), 186 values.NewObjectProperty("a", values.NewInt(1)), 187 ) 188 189 So(obj1.Compare(obj2), ShouldEqual, 0) 190 }) 191 192 Convey("It should return -1 when {a:1} and {a:2}", func() { 193 obj1 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(1))) 194 obj2 := values.NewObjectWith(values.NewObjectProperty("a", values.NewInt(2))) 195 196 So(obj1.Compare(obj2), ShouldEqual, -1) 197 }) 198 199 Convey("It should return 1 when {a:1, c:2} and {c:2, b:2}", func() { 200 obj1 := values.NewObjectWith( 201 values.NewObjectProperty("a", values.NewInt(1)), 202 values.NewObjectProperty("c", values.NewInt(2)), 203 ) 204 obj2 := values.NewObjectWith( 205 values.NewObjectProperty("c", values.NewInt(2)), 206 values.NewObjectProperty("b", values.NewInt(2)), 207 ) 208 209 So(obj1.Compare(obj2), ShouldEqual, 1) 210 }) 211 212 Convey("It should return 1 {a:1, c:3} and {c:2, a:1}", func() { 213 obj1 := values.NewObjectWith( 214 values.NewObjectProperty("a", values.NewInt(1)), 215 values.NewObjectProperty("c", values.NewInt(3)), 216 ) 217 obj2 := values.NewObjectWith( 218 values.NewObjectProperty("c", values.NewInt(2)), 219 values.NewObjectProperty("a", values.NewInt(1)), 220 ) 221 222 So(obj1.Compare(obj2), ShouldEqual, 1) 223 }) 224 }) 225 }) 226 227 Convey(".Hash", t, func() { 228 Convey("It should calculate hash of non-empty object", func() { 229 v := values.NewObjectWith( 230 values.NewObjectProperty("foo", values.NewString("bar")), 231 values.NewObjectProperty("faz", values.NewInt(1)), 232 values.NewObjectProperty("qaz", values.True), 233 ) 234 235 h := v.Hash() 236 237 So(h, ShouldBeGreaterThan, 0) 238 }) 239 240 Convey("It should calculate hash of empty object", func() { 241 v := values.NewObject() 242 243 h := v.Hash() 244 245 So(h, ShouldBeGreaterThan, 0) 246 }) 247 248 Convey("Hash sum should be consistent", func() { 249 v := values.NewObjectWith( 250 values.NewObjectProperty("boolean", values.True), 251 values.NewObjectProperty("int", values.NewInt(1)), 252 values.NewObjectProperty("float", values.NewFloat(1.1)), 253 values.NewObjectProperty("string", values.NewString("foobar")), 254 values.NewObjectProperty("datetime", values.NewCurrentDateTime()), 255 values.NewObjectProperty("array", values.NewArrayWith(values.NewInt(1), values.True)), 256 values.NewObjectProperty("object", values.NewObjectWith(values.NewObjectProperty("foo", values.NewString("bar")))), 257 ) 258 259 h1 := v.Hash() 260 h2 := v.Hash() 261 262 So(h1, ShouldEqual, h2) 263 }) 264 }) 265 266 Convey(".Length", t, func() { 267 Convey("Should return 0 when empty", func() { 268 obj := values.NewObject() 269 270 So(obj.Length(), ShouldEqual, 0) 271 }) 272 273 Convey("Should return greater than 0 when not empty", func() { 274 obj := values.NewObjectWith( 275 values.NewObjectProperty("foo", values.ZeroInt), 276 values.NewObjectProperty("bar", values.ZeroInt), 277 ) 278 279 So(obj.Length(), ShouldEqual, 2) 280 }) 281 }) 282 283 Convey(".ForEach", t, func() { 284 Convey("Should iterate over elements", func() { 285 obj := values.NewObjectWith( 286 values.NewObjectProperty("foo", values.ZeroInt), 287 values.NewObjectProperty("bar", values.ZeroInt), 288 ) 289 counter := 0 290 291 obj.ForEach(func(value core.Value, key string) bool { 292 counter++ 293 294 return true 295 }) 296 297 So(counter, ShouldEqual, obj.Length()) 298 }) 299 300 Convey("Should not iterate when empty", func() { 301 obj := values.NewObject() 302 counter := 0 303 304 obj.ForEach(func(value core.Value, key string) bool { 305 counter++ 306 307 return true 308 }) 309 310 So(counter, ShouldEqual, obj.Length()) 311 }) 312 313 Convey("Should break iteration when false returned", func() { 314 obj := values.NewObjectWith( 315 values.NewObjectProperty("1", values.NewInt(1)), 316 values.NewObjectProperty("2", values.NewInt(2)), 317 values.NewObjectProperty("3", values.NewInt(3)), 318 values.NewObjectProperty("4", values.NewInt(4)), 319 values.NewObjectProperty("5", values.NewInt(5)), 320 ) 321 threshold := 3 322 counter := 0 323 324 obj.ForEach(func(value core.Value, key string) bool { 325 counter++ 326 327 return counter < threshold 328 }) 329 330 So(counter, ShouldEqual, threshold) 331 }) 332 }) 333 334 Convey(".Get", t, func() { 335 Convey("Should return item by key", func() { 336 obj := values.NewObjectWith( 337 values.NewObjectProperty("foo", values.NewInt(1)), 338 values.NewObjectProperty("bar", values.NewInt(2)), 339 values.NewObjectProperty("qaz", values.NewInt(3)), 340 ) 341 342 el, _ := obj.Get("foo") 343 344 So(el.Compare(values.NewInt(1)), ShouldEqual, 0) 345 }) 346 347 Convey("Should return None when no items", func() { 348 obj := values.NewObject() 349 350 el, _ := obj.Get("foo") 351 352 So(el.Compare(values.None), ShouldEqual, 0) 353 }) 354 }) 355 356 Convey(".Set", t, func() { 357 Convey("Should set item by index", func() { 358 obj := values.NewObject() 359 360 obj.Set("foo", values.NewInt(1)) 361 362 So(obj.Length(), ShouldEqual, 1) 363 364 v, _ := obj.Get("foo") 365 So(v.Compare(values.NewInt(1)), ShouldEqual, 0) 366 }) 367 }) 368 369 Convey(".Clone", t, func() { 370 Convey("Cloned object should be equal to source object", func() { 371 obj := values.NewObjectWith( 372 values.NewObjectProperty("one", values.NewInt(1)), 373 values.NewObjectProperty("two", values.NewInt(2)), 374 ) 375 376 clone := obj.Clone().(*values.Object) 377 378 So(obj.Compare(clone), ShouldEqual, 0) 379 }) 380 381 Convey("Cloned object should be independent of the source object", func() { 382 obj := values.NewObjectWith( 383 values.NewObjectProperty("one", values.NewInt(1)), 384 values.NewObjectProperty("two", values.NewInt(2)), 385 ) 386 387 clone := obj.Clone().(*values.Object) 388 389 obj.Remove(values.NewString("one")) 390 391 So(obj.Compare(clone), ShouldNotEqual, 0) 392 }) 393 394 Convey("Cloned object must contain copies of the nested objects", func() { 395 obj := values.NewObjectWith( 396 values.NewObjectProperty( 397 "arr", values.NewArrayWith(values.NewInt(1)), 398 ), 399 ) 400 401 clone := obj.Clone().(*values.Object) 402 403 nestedInObj, _ := obj.Get(values.NewString("arr")) 404 nestedInObjArr := nestedInObj.(*values.Array) 405 nestedInObjArr.Push(values.NewInt(2)) 406 407 nestedInClone, _ := clone.Get(values.NewString("arr")) 408 nestedInCloneArr := nestedInClone.(*values.Array) 409 410 So(nestedInObjArr.Compare(nestedInCloneArr), ShouldNotEqual, 0) 411 }) 412 }) 413 414 Convey(".GetIn", t, func() { 415 416 ctx := context.Background() 417 418 Convey("Should return the same as .Get when input is correct", func() { 419 420 Convey("Should return item by key", func() { 421 key := values.NewString("foo") 422 obj := values.NewObjectWith( 423 values.NewObjectProperty(key.String(), values.NewInt(1)), 424 ) 425 426 el, err := obj.GetIn(ctx, []core.Value{key}) 427 elGet, _ := obj.Get(key) 428 429 So(err, ShouldBeNil) 430 So(el.Compare(elGet), ShouldEqual, 0) 431 }) 432 433 Convey("Should return None when no items", func() { 434 key := values.NewString("foo") 435 obj := values.NewObject() 436 437 el, err := obj.GetIn(ctx, []core.Value{key}) 438 elGet, _ := obj.Get(key) 439 440 So(err, ShouldBeNil) 441 So(el.Compare(elGet), ShouldEqual, 0) 442 }) 443 }) 444 445 Convey("Should error when input is not correct", func() { 446 447 Convey("Should return None when path[0] is not a string", func() { 448 obj := values.NewObject() 449 path := []core.Value{values.NewInt(0)} 450 451 el, err := obj.GetIn(ctx, path) 452 453 So(err, ShouldBeNil) 454 So(el, ShouldNotBeNil) 455 So(el.Type().String(), ShouldEqual, types.None.String()) 456 }) 457 458 Convey("Should error when first received item is not a Getter and len(path) > 1", func() { 459 key := values.NewString("foo") 460 obj := values.NewObjectWith( 461 values.NewObjectProperty(key.String(), values.NewInt(1)), 462 ) 463 path := []core.Value{key, key} 464 465 el, err := obj.GetIn(ctx, path) 466 467 So(err, ShouldBeError) 468 So(el.Compare(values.None), ShouldEqual, 0) 469 }) 470 }) 471 472 Convey("Should return None when len(path) == 0", func() { 473 obj := values.NewObject() 474 475 el, err := obj.GetIn(ctx, nil) 476 477 So(err, ShouldBeNil) 478 So(el.Compare(values.None), ShouldEqual, 0) 479 }) 480 481 Convey("Should call the nested Getter", func() { 482 key := values.NewString("foo") 483 obj := values.NewObjectWith( 484 values.NewObjectProperty(key.String(), values.NewArrayWith(key)), 485 ) 486 487 el, err := obj.GetIn(ctx, []core.Value{ 488 key, // obj.foo 489 values.NewInt(0), // obj.foo[0] 490 }) 491 492 So(err, ShouldBeNil) 493 So(el.Compare(key), ShouldEqual, 0) 494 }) 495 }) 496 }