github.com/simpleiot/simpleiot@v0.18.3/data/encode_decode_test.go (about) 1 package data 2 3 import ( 4 "reflect" 5 "sort" 6 "strconv" 7 "testing" 8 ) 9 10 type testType struct { 11 ID string `node:"id"` 12 Parent string `node:"parent"` 13 Description string `point:"description"` 14 Count int `point:"count"` 15 Value float64 `point:"value"` 16 Value2 float32 `point:"value2"` 17 Role string `edgepoint:"role"` 18 Tombstone bool `edgepoint:"tombstone"` 19 } 20 21 var nodeEdgeTest = NodeEdge{ 22 ID: "123", 23 Parent: "456", 24 Type: "testType", 25 Points: []Point{ 26 {Type: "description", Text: "test type"}, 27 {Type: "count", Value: 120}, 28 {Type: "value", Value: 15.43}, 29 {Type: "value2", Value: 10}, 30 }, 31 EdgePoints: []Point{ 32 {Type: "role", Text: "admin"}, 33 {Type: "tombstone", Value: 1}, 34 }, 35 } 36 37 var testTypeData = testType{ 38 ID: "123", 39 Parent: "456", 40 Description: "test type", 41 Count: 120, 42 Value: 15.43, 43 Value2: 10, 44 Role: "admin", 45 Tombstone: true, 46 } 47 48 func TestDecode(t *testing.T) { 49 var out testType 50 51 err := Decode(NodeEdgeChildren{nodeEdgeTest, nil}, &out) 52 53 if err != nil { 54 t.Fatal("Error decoding: ", err) 55 } 56 57 if !reflect.DeepEqual(out, testTypeData) { 58 t.Errorf("Decode failed, exp: %v, got %v", testTypeData, out) 59 } 60 } 61 62 type testType2 struct { 63 ID string `node:"id"` 64 Parent string `node:"parent"` 65 Description int `point:"description"` 66 } 67 68 func TestDecodeTypeMismatch(t *testing.T) { 69 var out testType2 70 71 err := Decode(NodeEdgeChildren{nodeEdgeTest, nil}, &out) 72 73 if err != nil { 74 t.Fatal("Error decoding type mismatch test: ", err) 75 } 76 } 77 78 func TestEncode(t *testing.T) { 79 var out NodeEdge 80 81 out, err := Encode(testTypeData) 82 83 if err != nil { 84 t.Fatal("Error encoding: ", err) 85 } 86 87 if !reflect.DeepEqual(out, nodeEdgeTest) { 88 t.Errorf("Decode failed, exp: %v, got %v", nodeEdgeTest, out) 89 } 90 91 } 92 93 type TestType struct { 94 ID string `node:"id"` 95 Parent string `node:"parent"` 96 Description string `point:"description"` 97 } 98 99 func TestEncodeCase(t *testing.T) { 100 test := TestType{"123", "456", "hi there"} 101 102 ne, err := Encode(test) 103 104 if err != nil { 105 t.Fatal("encode failed: ", err) 106 } 107 108 if ne.Type != "testType" { 109 t.Error("expected testType, got: ", ne.Type) 110 } 111 } 112 113 type testTypeComplex struct { 114 ID string `node:"id"` 115 Parent string `node:"parent"` 116 Description string `point:"description"` 117 IPAddresses []string `point:"ipAddress"` 118 Location map[string]string `point:"location"` 119 Sensors map[string]int `point:"sensor"` 120 Nested TestType `point:"nested"` 121 ScheduledDays [7]bool `point:"scheduledDays"` 122 TestValues []int32 `edgepoint:"testValue"` 123 Tombstone bool `edgepoint:"tombstone"` 124 } 125 126 var testTypeComplexData = testTypeComplex{"123", "456", "hi there", 127 []string{"192.168.1.1", "127.0.0.1"}, 128 map[string]string{ 129 "hello": "world", 130 "goodbye": "cruel world", 131 }, 132 map[string]int{ 133 "temp1": 23, 134 "temp2": 40, 135 }, 136 TestType{"789", "456", "nested test type"}, 137 [7]bool{false, true, true, true, true, true, false}, 138 []int32{314, 1024}, 139 true, 140 } 141 142 var nodeEdgeTestComplex = NodeEdge{ 143 ID: "123", 144 Parent: "456", 145 Type: "testTypeComplex", 146 Points: []Point{ 147 {Type: "description", Text: "hi there"}, 148 {Type: "ipAddress", Key: "0", Text: "192.168.1.1"}, 149 {Type: "ipAddress", Key: "1", Text: "127.0.0.1"}, 150 {Type: "location", Key: "goodbye", Text: "cruel world"}, 151 {Type: "location", Key: "hello", Text: "world"}, 152 {Type: "nested", Key: "description", Text: "nested test type"}, 153 {Type: "nested", Key: "id", Text: "789"}, 154 {Type: "nested", Key: "parent", Text: "456"}, 155 {Type: "scheduledDays", Key: "0", Value: 0}, 156 {Type: "scheduledDays", Key: "1", Value: 1}, 157 {Type: "scheduledDays", Key: "2", Value: 1}, 158 {Type: "scheduledDays", Key: "3", Value: 1}, 159 {Type: "scheduledDays", Key: "4", Value: 1}, 160 {Type: "scheduledDays", Key: "5", Value: 1}, 161 {Type: "scheduledDays", Key: "6", Value: 0}, 162 {Type: "sensor", Key: "temp1", Value: 23}, 163 {Type: "sensor", Key: "temp2", Value: 40}, 164 }, 165 EdgePoints: []Point{ 166 {Type: "testValue", Key: "0", Value: 314}, 167 {Type: "testValue", Key: "1", Value: 1024}, 168 {Type: "tombstone", Value: 1}, 169 }, 170 } 171 172 type testTypePointers struct { 173 ID string `node:"id"` 174 Description *string `point:"description"` 175 IPAddresses []string `point:"ipAddress"` 176 NullStruct *TestType `point:"nullStruct"` 177 NullValue *float64 `point:"nullValue"` 178 NullEdge *int `edgepoint:"nullEdge"` 179 Value *float32 `edgepoint:"value"` 180 } 181 182 var testTypePointersNodeEdge = NodeEdge{ 183 ID: "nodeID", 184 Type: "testTypePointers", 185 Points: []Point{ 186 {Type: "description", Text: "testing 1, 2, 3"}, 187 {Type: "nullStruct", Key: "description", Tombstone: 1}, 188 {Type: "nullStruct", Key: "id", Tombstone: 1}, 189 {Type: "nullStruct", Key: "parent", Tombstone: 1}, 190 {Type: "nullValue", Tombstone: 1}, 191 }, 192 EdgePoints: []Point{ 193 {Type: "nullEdge", Tombstone: 1}, 194 {Type: "value", Value: 42}, 195 }, 196 } 197 198 func TestEncodeComplex(t *testing.T) { 199 ne, err := Encode(testTypeComplexData) 200 201 if err != nil { 202 t.Fatal("encode failed:", err) 203 } 204 sortPoints(ne.Points, ne.EdgePoints) 205 206 if !reflect.DeepEqual(ne, nodeEdgeTestComplex) { 207 t.Errorf("Decode failed, exp: %v, got %v", nodeEdgeTestComplex, ne) 208 } 209 } 210 211 func TestDecodeComplex(t *testing.T) { 212 var out testTypeComplex 213 214 err := Decode(NodeEdgeChildren{nodeEdgeTestComplex, nil}, &out) 215 216 if err != nil { 217 t.Fatal("Error decoding: ", err) 218 } 219 220 if !reflect.DeepEqual(out, testTypeComplexData) { 221 t.Errorf("Decode failed, exp: %v, got %v", testTypeComplexData, out) 222 } 223 } 224 225 type testX struct { 226 ID string `node:"id"` 227 Parent string `node:"parent"` 228 Description string `point:"description"` 229 TestYs []testY `child:"testY"` 230 } 231 232 type testY struct { 233 ID string `node:"id"` 234 Parent string `node:"parent"` 235 Description string `point:"description"` 236 Count int `point:"count"` 237 Role string `edgepoint:"role"` 238 TestZs []testZ `child:"testZ"` 239 TestYs []testY `child:"testY"` 240 } 241 242 type testZ struct { 243 ID string `node:"id"` 244 Parent string `node:"parent"` 245 Description string `point:"description"` 246 Count int `point:"count"` 247 Role string `edgepoint:"role"` 248 } 249 250 func TestDecodeWithChildren(t *testing.T) { 251 nX := NodeEdgeChildren{ 252 NodeEdge: NodeEdge{ 253 ID: "123", 254 Parent: "456", 255 Type: "testX", 256 Points: []Point{ 257 {Type: "description", Text: "test X type"}, 258 }, 259 EdgePoints: []Point{ 260 {Type: "role", Text: "admin"}, 261 {Type: "tombstone", Value: 1}, 262 }, 263 }, 264 Children: []NodeEdgeChildren{ 265 {NodeEdge{ 266 ID: "abc", 267 Parent: "123", 268 Type: "testY", 269 Points: []Point{ 270 {Type: "description", Text: "test Y1"}, 271 }, 272 EdgePoints: []Point{ 273 {Type: "role", Text: "user"}, 274 {Type: "tombstone", Value: 1}, 275 }, 276 }, 277 []NodeEdgeChildren{ 278 {NodeEdge{ 279 ID: "jkl", 280 Parent: "abc", 281 Type: "testY", 282 Points: []Point{ 283 {Type: "description", Text: "test Y2"}, 284 }, 285 EdgePoints: []Point{ 286 {Type: "role", Text: "user"}, 287 {Type: "tombstone", Value: 1}, 288 }, 289 }, nil}, 290 {NodeEdge{ 291 ID: "mno", 292 Parent: "abc", 293 Type: "testZ", 294 Points: []Point{ 295 {Type: "description", Text: "test Z1"}, 296 }, 297 EdgePoints: []Point{ 298 {Type: "role", Text: "user"}, 299 {Type: "tombstone", Value: 1}, 300 }, 301 }, nil}, 302 }, 303 }, 304 }, 305 } 306 307 var out testX 308 309 err := Decode(nX, &out) 310 311 if err != nil { 312 t.Fatal("Error decoding: ", err) 313 } 314 315 if out.ID != "123" { 316 t.Fatal("Decode failed, wrong ID") 317 } 318 319 if len(out.TestYs) < 1 { 320 t.Fatal("No TestYs") 321 } 322 323 if out.TestYs[0].ID != "abc" { 324 t.Fatal("Decode failed, wrong ID for TestYs[0]") 325 } 326 327 if len(out.TestYs[0].TestYs) < 1 { 328 t.Fatal("No TestYs.TestYs") 329 } 330 331 if len(out.TestYs[0].TestZs) < 1 { 332 t.Fatal("No TestYs.TestZs") 333 } 334 } 335 336 func TestDecodeTombstonePoint(t *testing.T) { 337 var ne = NodeEdge{ 338 Points: []Point{ 339 {Type: "ipAddress", Key: "0", Text: "192.168.1.1"}, 340 {Type: "ipAddress", Key: "1", Text: "127.0.0.1"}, 341 {Type: "ipAddress", Key: "2", Text: "127.0.0.2", Tombstone: 1}, 342 {Type: "location", Key: "goodbye", Text: "cruel world"}, 343 {Type: "location", Key: "hello", Text: "world"}, 344 {Type: "location", Key: "del", Text: "deleted entry", Tombstone: 1}, 345 {Type: "nested", Key: "fake", Text: "not a real field", Tombstone: 1}, 346 }, 347 } 348 349 var out testTypeComplex 350 out.Nested.Description = "decode should not change this value" 351 err := Decode(NodeEdgeChildren{ne, nil}, &out) 352 353 exp := testTypeComplex{ 354 Nested: TestType{ 355 Description: "decode should not change this value", 356 }, 357 IPAddresses: []string{"192.168.1.1", "127.0.0.1"}, 358 Location: map[string]string{ 359 "hello": "world", 360 "goodbye": "cruel world", 361 }, 362 } 363 364 if err != nil { 365 t.Fatal("Error decoding: ", err) 366 } 367 368 if !reflect.DeepEqual(out, exp) { 369 t.Errorf("Decode failed, exp: %v, got %v", exp, out) 370 } 371 } 372 373 func TestDecodeAllTombstonePointArray(t *testing.T) { 374 var ne = NodeEdge{ 375 Points: []Point{ 376 {Type: "ipAddress", Key: "0", Text: "192.168.1.1", Tombstone: 1}, 377 {Type: "ipAddress", Key: "1", Text: "127.0.0.1", Tombstone: 1}, 378 {Type: "ipAddress", Key: "2", Text: "127.0.0.2", Tombstone: 1}, 379 }, 380 } 381 382 var out testTypeComplex 383 err := Decode(NodeEdgeChildren{ne, nil}, &out) 384 385 if err != nil { 386 t.Fatal("Error decoding: ", err) 387 } 388 389 if len(out.IPAddresses) > 0 { 390 t.Error("Expected 0 IP address, got: ", len(out.IPAddresses)) 391 } 392 } 393 394 func TestEncodePointers(t *testing.T) { 395 str := "testing 1, 2, 3" 396 value := float32(42) 397 ne, err := Encode(testTypePointers{ 398 ID: "nodeID", 399 Description: &str, 400 Value: &value, 401 }) 402 403 if err != nil { 404 t.Fatal("encode failed:", err) 405 } 406 sortPoints(ne.Points, ne.EdgePoints) 407 408 if !reflect.DeepEqual(ne, testTypePointersNodeEdge) { 409 t.Errorf("Decode failed, exp: %v, got %v", testTypePointersNodeEdge, ne) 410 } 411 } 412 413 func TestDecodePointers(t *testing.T) { 414 desc := "Test description" 415 nullValue := 85.7 416 out := testTypePointers{ 417 ID: "123", 418 Description: &desc, 419 IPAddresses: []string{"127.0.0.1"}, 420 NullStruct: &TestType{ 421 Description: "hello there", 422 }, 423 NullValue: &nullValue, 424 } 425 err := Decode(NodeEdgeChildren{testTypePointersNodeEdge, nil}, &out) 426 427 desc = "testing 1, 2, 3" 428 value := float32(42) 429 exp := testTypePointers{ 430 ID: "nodeID", 431 Description: &desc, 432 IPAddresses: []string{"127.0.0.1"}, // unchanged 433 NullStruct: nil, // all fields are tombstone points 434 NullValue: nil, // tombstone point 435 NullEdge: nil, // tombstone point 436 Value: &value, 437 } 438 439 if err != nil { 440 t.Fatal("Error decoding: ", err) 441 } 442 443 if !reflect.DeepEqual(out, exp) { 444 t.Errorf("Decode failed, exp: %v, got %v", exp, out) 445 } 446 } 447 448 func TestDiffPoints(t *testing.T) { 449 before := testType{ 450 ID: "123", 451 Parent: "456", 452 Description: "test type", 453 Count: 120, 454 Value: 15.43, 455 Value2: 10, 456 Role: "admin", 457 Tombstone: true, 458 } 459 after := testType{ 460 ID: "0123", 461 Parent: "0456", 462 Description: "description changed", 463 Count: 110, 464 Value: 15.43, // unchanged 465 Value2: 10000000, 466 Role: "user", 467 Tombstone: false, 468 } 469 p, err := DiffPoints(before, after) 470 if err != nil { 471 t.Fatal("diff error:", err) 472 } 473 if len(p) != 3 { 474 t.Fatalf("expected 3 points; got %v", len(p)) 475 } 476 if p[0].Value != 0 || 477 p[0].Text != "description changed" || 478 p[0].Type != "description" || 479 p[0].Tombstone != 0 { 480 t.Errorf("generated point invalid; got %v", p[0]) 481 } 482 if p[1].Value != 110 || 483 p[1].Text != "" || 484 p[1].Type != "count" || 485 p[1].Tombstone != 0 { 486 t.Errorf("generated point invalid; got %v", p[1]) 487 } 488 if p[2].Value != 10000000 || 489 p[2].Text != "" || 490 p[2].Type != "value2" || 491 p[2].Tombstone != 0 { 492 t.Errorf("generated point invalid; got %v", p[2]) 493 } 494 } 495 496 func TestDiffPointsComplex(t *testing.T) { 497 // type testTypeComplex struct { 498 // ID string `node:"id"` 499 // Parent string `node:"parent"` 500 // Description string `point:"description"` 501 // IPAddresses []string `point:"ipAddress"` 502 // Location map[string]string `point:"location"` 503 // Sensors map[string]int `point:"sensor"` 504 // Nested TestType `point:"nested"` 505 // TestValues []int32 `edgepoint:"testValue"` 506 // Tombstone bool `edgepoint:"tombstone"` 507 // } 508 before := testTypeComplex{"123", "456", 509 "hi there", 510 []string{"192.168.1.1", "127.0.0.1"}, 511 map[string]string{ 512 "hello": "world", 513 "goodbye": "cruel world", 514 }, 515 map[string]int{ 516 "temp1": 23, 517 "temp2": 40, 518 }, 519 TestType{"789", "456", "nested test type"}, 520 [7]bool{false, true, true, true, true, true, false}, 521 []int32{314, 1024}, 522 true, 523 } 524 after := testTypeComplex{"0123", "0456", 525 "hi there", // unchanged 526 []string{"192.168.1.100"}, // index 0 updated; 1 deleted 527 map[string]string{ 528 "hello": "world!!!", // hello updated; goodbye deleted 529 "foo": "bar", // foo added 530 }, 531 map[string]int{ 532 "temp1": 23, 533 "temp2": 40, // unchanged 534 }, 535 TestType{"789", "456", "nested test type desc changed"}, 536 [7]bool{false, true, true, false, true, true, false}, 537 // ignore edgepoints 538 []int32{314, 1000, 2048, 4096}, 539 false, 540 } 541 p, err := DiffPoints(before, after) 542 if err != nil { 543 t.Fatal("diff error:", err) 544 } 545 sortPoints(p) 546 // log.Println(p) 547 if len(p) != 7 { 548 t.Fatalf("expected 7 points; got %v", len(p)) 549 } 550 if p[0].Value != 0 || 551 p[0].Text != "192.168.1.100" || 552 p[0].Key != "0" || 553 p[0].Type != "ipAddress" || 554 p[0].Tombstone != 0 { 555 t.Errorf("generated point invalid; got %v", p[0]) 556 } 557 if p[1].Value != 0 || 558 p[1].Text != "" || 559 p[1].Key != "1" || 560 p[1].Type != "ipAddress" || 561 p[1].Tombstone != 1 { 562 t.Errorf("generated point invalid; got %v", p[1]) 563 } 564 if p[2].Value != 0 || 565 p[2].Text != "bar" || 566 p[2].Key != "foo" || 567 p[2].Type != "location" || 568 p[2].Tombstone != 0 { 569 t.Errorf("generated point invalid; got %v", p[3]) 570 } 571 if p[3].Value != 0 || 572 p[3].Text != "" || 573 p[3].Key != "goodbye" || 574 p[3].Type != "location" || 575 p[3].Tombstone != 1 { 576 t.Errorf("generated point invalid; got %v", p[4]) 577 } 578 if p[4].Value != 0 || 579 p[4].Text != "world!!!" || 580 p[4].Key != "hello" || 581 p[4].Type != "location" || 582 p[4].Tombstone != 0 { 583 t.Errorf("generated point invalid; got %v", p[2]) 584 } 585 if p[5].Value != 0 || 586 p[5].Text != "nested test type desc changed" || 587 p[5].Key != "description" || 588 p[5].Type != "nested" || 589 p[5].Tombstone != 0 { 590 t.Errorf("generated point invalid; got %v", p[5]) 591 } 592 if p[6].Value != 0 || 593 p[6].Text != "" || 594 p[6].Key != "3" || 595 p[6].Type != "scheduledDays" || 596 p[6].Tombstone != 0 { 597 t.Errorf("generated point invalid; got %v", p[5]) 598 } 599 } 600 601 type SortablePoints []Point 602 603 func (sp SortablePoints) Len() int { 604 return len(sp) 605 } 606 607 // Sort by type, key, index, and then time 608 func (sp SortablePoints) Less(i, j int) bool { 609 if sp[i].Type < sp[j].Type { 610 return true 611 } 612 if sp[i].Type > sp[j].Type { 613 return false 614 } 615 616 // try to sort Key as int first (array), then as text 617 618 iInt, iErr := strconv.Atoi(sp[i].Key) 619 jInt, jErr := strconv.Atoi(sp[j].Key) 620 621 if iErr == nil && jErr == nil { 622 // we have ints, so do int sort 623 if iInt < jInt { 624 return true 625 } 626 627 if iInt > jInt { 628 return false 629 } 630 } else { 631 if sp[i].Key < sp[j].Key { 632 return true 633 } 634 if sp[i].Key > sp[j].Key { 635 return false 636 } 637 } 638 639 return sp[i].Time.Before(sp[j].Time) 640 } 641 642 func (sp SortablePoints) Swap(i, j int) { 643 sp[i], sp[j] = sp[j], sp[i] 644 } 645 646 func sortPoints(slices ...[]Point) { 647 for _, pts := range slices { 648 sort.Sort(SortablePoints(pts)) 649 } 650 }