github.com/MontFerret/ferret@v0.18.0/pkg/runtime/values/helpers_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/values/types" 9 10 "github.com/MontFerret/ferret/pkg/runtime/core" 11 "github.com/MontFerret/ferret/pkg/runtime/values" 12 13 . "github.com/smartystreets/goconvey/convey" 14 ) 15 16 var CustomType = core.NewType("custom") 17 18 type CustomValue struct { 19 properties map[core.Value]core.Value 20 } 21 22 func (t *CustomValue) MarshalJSON() ([]byte, error) { 23 return nil, core.ErrNotImplemented 24 } 25 26 func (t *CustomValue) Type() core.Type { 27 return CustomType 28 } 29 30 func (t *CustomValue) String() string { 31 return "" 32 } 33 34 func (t *CustomValue) Compare(other core.Value) int64 { 35 return other.Compare(t) * -1 36 } 37 38 func (t *CustomValue) Unwrap() interface{} { 39 return t 40 } 41 42 func (t *CustomValue) Hash() uint64 { 43 return 0 44 } 45 46 func (t *CustomValue) Copy() core.Value { 47 return values.None 48 } 49 50 func (t *CustomValue) GetIn(ctx context.Context, path []core.Value) (core.Value, core.PathError) { 51 if len(path) == 0 { 52 return values.None, nil 53 } 54 55 propKey := path[0] 56 propValue, ok := t.properties[propKey] 57 58 if !ok { 59 return values.None, nil 60 } 61 62 if len(path) == 1 { 63 return propValue, nil 64 } 65 66 return values.GetIn(ctx, propValue, path[1:]) 67 } 68 69 func (t *CustomValue) SetIn(ctx context.Context, path []core.Value, value core.Value) core.PathError { 70 if len(path) == 0 { 71 return nil 72 } 73 74 propKey := path[0] 75 propValue, ok := t.properties[propKey] 76 77 if !ok { 78 return nil 79 } 80 81 if len(path) == 1 { 82 t.properties[propKey] = value 83 84 return nil 85 } 86 87 return values.SetIn(ctx, propValue, path[1:], value) 88 } 89 90 func TestHelpers(t *testing.T) { 91 Convey("Helpers", t, func() { 92 Convey("Getter", func() { 93 Convey("It should get a value by a given path", func() { 94 ct := &CustomValue{ 95 properties: map[core.Value]core.Value{ 96 values.NewString("foo"): values.NewInt(1), 97 values.NewString("bar"): &CustomValue{ 98 properties: map[core.Value]core.Value{ 99 values.NewString("qaz"): values.NewInt(2), 100 }, 101 }, 102 }, 103 } 104 105 ctx := context.Background() 106 107 foo, err := values.GetIn(ctx, ct, []core.Value{ 108 values.NewString("foo"), 109 }) 110 111 So(err, ShouldBeNil) 112 So(foo, ShouldEqual, values.NewInt(1)) 113 114 qaz, err := values.GetIn(ctx, ct, []core.Value{ 115 values.NewString("bar"), 116 values.NewString("qaz"), 117 }) 118 119 So(err, ShouldBeNil) 120 So(qaz, ShouldEqual, values.NewInt(2)) 121 }) 122 }) 123 124 Convey("Setter", func() { 125 Convey("It should get a value by a given path", func() { 126 ct := &CustomValue{ 127 properties: map[core.Value]core.Value{ 128 values.NewString("foo"): values.NewInt(1), 129 values.NewString("bar"): &CustomValue{ 130 properties: map[core.Value]core.Value{ 131 values.NewString("qaz"): values.NewInt(2), 132 }, 133 }, 134 }, 135 } 136 137 ctx := context.Background() 138 139 err := values.SetIn(ctx, ct, []core.Value{ 140 values.NewString("foo"), 141 }, values.NewInt(2)) 142 143 So(err, ShouldBeNil) 144 So(ct.properties[values.NewString("foo")], ShouldEqual, values.NewInt(2)) 145 146 err = values.SetIn(ctx, ct, []core.Value{ 147 values.NewString("bar"), 148 values.NewString("qaz"), 149 }, values.NewString("foobar")) 150 151 So(err, ShouldBeNil) 152 153 qaz, err := values.GetIn(ctx, ct, []core.Value{ 154 values.NewString("bar"), 155 values.NewString("qaz"), 156 }) 157 158 So(err, ShouldBeNil) 159 So(qaz, ShouldEqual, values.NewString("foobar")) 160 }) 161 }) 162 163 Convey("Parse", func() { 164 Convey("It should parse values", func() { 165 inputs := []struct { 166 Parsed core.Value 167 Raw interface{} 168 }{ 169 {Parsed: values.NewInt(1), Raw: int(1)}, 170 {Parsed: values.NewInt(1), Raw: int8(1)}, 171 {Parsed: values.NewInt(1), Raw: int16(1)}, 172 {Parsed: values.NewInt(1), Raw: int32(1)}, 173 {Parsed: values.NewInt(1), Raw: int64(1)}, 174 } 175 176 for _, input := range inputs { 177 out := values.Parse(input.Raw) 178 179 So(out.Type().ID(), ShouldEqual, input.Parsed.Type().ID()) 180 So(out.Unwrap(), ShouldEqual, input.Parsed.Unwrap()) 181 } 182 }) 183 }) 184 185 Convey("ToBoolean", func() { 186 Convey("Should convert values", func() { 187 inputs := [][]core.Value{ 188 { 189 values.None, 190 values.False, 191 }, 192 { 193 values.True, 194 values.True, 195 }, 196 { 197 values.False, 198 values.False, 199 }, 200 { 201 values.NewInt(1), 202 values.True, 203 }, 204 { 205 values.NewInt(0), 206 values.False, 207 }, 208 { 209 values.NewFloat(1), 210 values.True, 211 }, 212 { 213 values.NewFloat(0), 214 values.False, 215 }, 216 { 217 values.NewString("Foo"), 218 values.True, 219 }, 220 { 221 values.EmptyString, 222 values.False, 223 }, 224 { 225 values.NewCurrentDateTime(), 226 values.True, 227 }, 228 { 229 values.NewArray(1), 230 values.True, 231 }, 232 { 233 values.NewObject(), 234 values.True, 235 }, 236 { 237 values.NewBinary([]byte("")), 238 values.True, 239 }, 240 } 241 242 for _, pair := range inputs { 243 actual := values.ToBoolean(pair[0]) 244 expected := pair[1] 245 246 So(actual, ShouldEqual, expected) 247 } 248 }) 249 }) 250 251 Convey("ToFloat", func() { 252 Convey("Should convert Int", func() { 253 input := values.NewInt(100) 254 output := values.ToFloat(input) 255 256 So(output, ShouldEqual, values.NewFloat(100)) 257 }) 258 259 Convey("Should convert Float", func() { 260 input := values.NewFloat(100) 261 output := values.ToFloat(input) 262 263 So(output, ShouldEqual, values.NewFloat(100)) 264 }) 265 266 Convey("Should convert String", func() { 267 input := values.NewString("100.1") 268 output := values.ToFloat(input) 269 270 So(output, ShouldEqual, values.NewFloat(100.1)) 271 272 output2 := values.ToFloat(values.NewString("foobar")) 273 So(output2, ShouldEqual, values.ZeroFloat) 274 }) 275 276 Convey("Should convert Boolean", func() { 277 So(values.ToFloat(values.True), ShouldEqual, values.NewFloat(1)) 278 So(values.ToFloat(values.False), ShouldEqual, values.NewFloat(0)) 279 }) 280 281 Convey("Should convert Array with single item", func() { 282 So(values.ToFloat(values.NewArrayWith(values.NewFloat(1))), ShouldEqual, values.NewFloat(1)) 283 }) 284 285 Convey("Should convert Array with multiple items", func() { 286 arg := values.NewArrayWith(values.NewFloat(1), values.NewFloat(1)) 287 288 So(values.ToFloat(arg), ShouldEqual, values.NewFloat(2)) 289 }) 290 291 Convey("Should convert DateTime", func() { 292 dt := values.NewCurrentDateTime() 293 ts := dt.Time.Unix() 294 295 So(values.ToFloat(dt), ShouldEqual, values.NewFloat(float64(ts))) 296 }) 297 298 Convey("Should NOT convert other types", func() { 299 inputs := []core.Value{ 300 values.NewObject(), 301 values.NewBinary([]byte("")), 302 } 303 304 for _, input := range inputs { 305 So(values.ToFloat(input), ShouldEqual, values.ZeroFloat) 306 } 307 }) 308 }) 309 310 Convey("ToInt", func() { 311 Convey("Should convert Int", func() { 312 input := values.NewInt(100) 313 output := values.ToInt(input) 314 315 So(output, ShouldEqual, values.NewInt(100)) 316 }) 317 318 Convey("Should convert Float", func() { 319 input := values.NewFloat(100.1) 320 output := values.ToInt(input) 321 322 So(output, ShouldEqual, values.NewInt(100)) 323 }) 324 325 Convey("Should convert String", func() { 326 input := values.NewString("100") 327 output := values.ToInt(input) 328 329 So(output, ShouldEqual, values.NewInt(100)) 330 331 output2 := values.ToInt(values.NewString("foobar")) 332 So(output2, ShouldEqual, values.ZeroInt) 333 }) 334 335 Convey("Should convert Boolean", func() { 336 So(values.ToInt(values.True), ShouldEqual, values.NewInt(1)) 337 So(values.ToInt(values.False), ShouldEqual, values.NewInt(0)) 338 }) 339 340 Convey("Should convert Array with single item", func() { 341 So(values.ToInt(values.NewArrayWith(values.NewFloat(1))), ShouldEqual, values.NewInt(1)) 342 }) 343 344 Convey("Should convert Array with multiple items", func() { 345 arg := values.NewArrayWith(values.NewFloat(1), values.NewFloat(1)) 346 347 So(values.ToInt(arg), ShouldEqual, values.NewFloat(2)) 348 }) 349 350 Convey("Should convert DateTime", func() { 351 dt := values.NewCurrentDateTime() 352 ts := dt.Time.Unix() 353 354 So(values.ToInt(dt), ShouldEqual, values.NewInt(int(ts))) 355 }) 356 357 Convey("Should NOT convert other types", func() { 358 inputs := []core.Value{ 359 values.NewObject(), 360 values.NewBinary([]byte("")), 361 } 362 363 for _, input := range inputs { 364 So(values.ToInt(input), ShouldEqual, values.ZeroInt) 365 } 366 }) 367 }) 368 369 Convey("ToArray", func() { 370 Convey("Should convert primitives", func() { 371 dt := values.NewCurrentDateTime() 372 373 inputs := [][]core.Value{ 374 { 375 values.None, 376 values.NewArray(0), 377 }, 378 { 379 values.True, 380 values.NewArrayWith(values.True), 381 }, 382 { 383 values.NewInt(1), 384 values.NewArrayWith(values.NewInt(1)), 385 }, 386 { 387 values.NewFloat(1), 388 values.NewArrayWith(values.NewFloat(1)), 389 }, 390 { 391 values.NewString("foo"), 392 values.NewArrayWith(values.NewString("foo")), 393 }, 394 { 395 dt, 396 values.NewArrayWith(dt), 397 }, 398 } 399 400 for _, pairs := range inputs { 401 actual := values.ToArray(context.Background(), pairs[0]) 402 expected := pairs[1] 403 404 So(actual.Compare(expected), ShouldEqual, 0) 405 } 406 }) 407 408 Convey("Should create a copy of a given array", func() { 409 vals := []core.Value{ 410 values.NewInt(1), 411 values.NewInt(2), 412 values.NewInt(3), 413 values.NewInt(4), 414 values.NewArray(10), 415 values.NewObject(), 416 } 417 418 input := values.NewArrayWith(vals...) 419 arr := values.ToArray(context.Background(), input) 420 421 So(input == arr, ShouldBeFalse) 422 So(arr.Length() == input.Length(), ShouldBeTrue) 423 424 for idx := range vals { 425 expected := input.Get(values.NewInt(idx)) 426 actual := arr.Get(values.NewInt(idx)) 427 428 // same ref 429 So(actual == expected, ShouldBeTrue) 430 So(actual.Compare(expected), ShouldEqual, 0) 431 } 432 }) 433 434 Convey("Should convert object to an array", func() { 435 input := values.NewObjectWith( 436 values.NewObjectProperty("foo", values.NewString("bar")), 437 values.NewObjectProperty("baz", values.NewInt(1)), 438 values.NewObjectProperty("qaz", values.NewObject()), 439 ) 440 441 arr := values.ToArray(context.Background(), input).Sort() 442 443 So(arr.String(), ShouldEqual, "[1,\"bar\",{}]") 444 So(arr.Get(values.NewInt(2)) == input.MustGet("qaz"), ShouldBeTrue) 445 }) 446 }) 447 448 Convey("Unmarshal", func() { 449 Convey("Should deserialize object", func() { 450 input := map[string]interface{}{ 451 "foo": []string{ 452 "bar", 453 "qaz", 454 }, 455 } 456 json1, err := json.Marshal(input) 457 458 So(err, ShouldBeNil) 459 460 val, err := values.Unmarshal(json1) 461 462 So(err, ShouldBeNil) 463 So(val.Type(), ShouldResemble, types.Object) 464 465 json2, err := val.MarshalJSON() 466 467 So(err, ShouldBeNil) 468 So(json2, ShouldResemble, json1) 469 }) 470 }) 471 }) 472 }