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  }