github.com/aristanetworks/goarista@v0.0.0-20240514173732-cca2755bbd44/gnmi/client/client_test.go (about)

     1  // Copyright (c) 2023 Arista Networks, Inc.
     2  // Use of this source code is governed by the Apache License 2.0
     3  // that can be found in the COPYING file.
     4  
     5  package client
     6  
     7  import (
     8  	"testing"
     9  
    10  	"github.com/aristanetworks/goarista/gnmi"
    11  	"github.com/aristanetworks/goarista/test"
    12  	pb "github.com/openconfig/gnmi/proto/gnmi"
    13  )
    14  
    15  func TestNewGetRequest(t *testing.T) {
    16  	testCases := map[string]struct {
    17  		pathParam *reqParams
    18  		exp       *pb.GetRequest
    19  	}{
    20  		"ascii-cli": {
    21  			pathParam: &reqParams{
    22  				encoding: "ascii",
    23  				origin:   "cli",
    24  				paths:    []string{"show version"},
    25  			},
    26  			exp: &pb.GetRequest{
    27  				Encoding: pb.Encoding_ASCII,
    28  				Type:     pb.GetRequest_ALL,
    29  				Path: []*pb.Path{{
    30  					Origin:  "cli",
    31  					Element: []string{"show version"},
    32  					Elem: []*pb.PathElem{{
    33  						Name: "show version",
    34  					}},
    35  				},
    36  				},
    37  			},
    38  		},
    39  		"default-cli": {
    40  			pathParam: &reqParams{
    41  				origin: "cli",
    42  				paths:  []string{"show version"},
    43  			},
    44  			exp: &pb.GetRequest{
    45  				Encoding: pb.Encoding_JSON,
    46  				Type:     pb.GetRequest_ALL,
    47  				Path: []*pb.Path{{
    48  					Origin:  "cli",
    49  					Element: []string{"show version"},
    50  					Elem: []*pb.PathElem{{
    51  						Name: "show version",
    52  					}},
    53  				},
    54  				},
    55  			},
    56  		},
    57  		"default-non-cli": {
    58  			pathParam: &reqParams{paths: []string{"show version"}},
    59  			exp: &pb.GetRequest{
    60  				Encoding: pb.Encoding_JSON,
    61  				Type:     pb.GetRequest_ALL,
    62  				Path: []*pb.Path{{
    63  					Element: []string{"show version"},
    64  					Elem: []*pb.PathElem{{
    65  						Name: "show version",
    66  					}},
    67  				},
    68  				},
    69  			},
    70  		},
    71  		"multiple-paths": {
    72  			pathParam: &reqParams{
    73  				encoding: "ascii",
    74  				origin:   "cli",
    75  				paths:    []string{"show version", "show running-config", "show history"},
    76  			},
    77  			exp: &pb.GetRequest{
    78  				Encoding: pb.Encoding_ASCII,
    79  				Type:     pb.GetRequest_ALL,
    80  				Path: []*pb.Path{
    81  					{
    82  						Origin:  "cli",
    83  						Element: []string{"show version"},
    84  						Elem: []*pb.PathElem{{
    85  							Name: "show version",
    86  						}},
    87  					},
    88  					{
    89  						Origin:  "cli",
    90  						Element: []string{"show running-config"},
    91  						Elem: []*pb.PathElem{{
    92  							Name: "show running-config",
    93  						}},
    94  					},
    95  					{
    96  						Origin:  "cli",
    97  						Element: []string{"show history"},
    98  						Elem: []*pb.PathElem{{
    99  							Name: "show history",
   100  						}},
   101  					}},
   102  			},
   103  		},
   104  	}
   105  
   106  	for name, tc := range testCases {
   107  		got, err := newGetRequest(*tc.pathParam, "all")
   108  		if err != nil {
   109  			t.Fatalf("ERROR!\n%s: got error: %s, but expect no error\n", name, err.Error())
   110  		}
   111  		if !test.DeepEqual(got, tc.exp) {
   112  			t.Fatalf("ERROR!\nTest Case: %s\nGot: %s,\nWant %s\n", name, got, tc.exp)
   113  		}
   114  	}
   115  }
   116  
   117  func TestNewSubscribeOptions(t *testing.T) {
   118  	testCases := map[string]struct {
   119  		pathParam *reqParams
   120  		exp       *gnmi.SubscribeOptions
   121  	}{
   122  		"core": {
   123  			pathParam: &reqParams{
   124  				target: "target",
   125  				origin: "cli",
   126  				paths:  []string{"show version"},
   127  			},
   128  			exp: &gnmi.SubscribeOptions{
   129  				Paths:  gnmi.SplitPaths([]string{"show version"}),
   130  				Origin: "cli",
   131  				Target: "target",
   132  			},
   133  		},
   134  		"multi-paths": {
   135  			pathParam: &reqParams{
   136  				target: "target",
   137  				origin: "cli",
   138  				paths:  []string{"show version", "show running-config", "show history"},
   139  			},
   140  			exp: &gnmi.SubscribeOptions{
   141  				Paths: gnmi.SplitPaths(
   142  					[]string{"show version", "show running-config", "show history"},
   143  				),
   144  				Target: "target",
   145  				Origin: "cli",
   146  			},
   147  		},
   148  	}
   149  
   150  	for name, tc := range testCases {
   151  		got, err := newSubscribeOptions(*tc.pathParam, nil, new(gnmi.SubscribeOptions))
   152  		if err != nil {
   153  			t.Fatalf("ERROR!\n%s: got error: %s, but expect no error\n", name, err.Error())
   154  		}
   155  		if !test.DeepEqual(got, tc.exp) {
   156  			t.Fatalf("ERROR!\nTest Case: %s\nGot: %+v,\nWant %+v\n", name, got, tc.exp)
   157  		}
   158  	}
   159  }
   160  
   161  // subcribe request does not support encoding
   162  // test that it throws an error if encoding is given
   163  func TestEncodingSubscribeOptions(t *testing.T) {
   164  	testCases := map[string]struct {
   165  		pathParam *reqParams
   166  	}{
   167  		"ASCII": {
   168  			&reqParams{
   169  				encoding: "ASCII",
   170  				origin:   "cli",
   171  				paths:    []string{"show version"},
   172  			},
   173  		},
   174  		"bytes": {
   175  			&reqParams{
   176  				encoding: "bytes",
   177  				origin:   "cli",
   178  				target:   "target",
   179  				paths:    []string{"show version"},
   180  			},
   181  		},
   182  		"json": {
   183  			&reqParams{
   184  				encoding: "json",
   185  				paths:    []string{"show version"},
   186  			},
   187  		},
   188  		"json_ietf": {
   189  			&reqParams{
   190  				encoding: "json_ietf",
   191  				origin:   "cli",
   192  				paths:    []string{"show version"},
   193  			},
   194  		},
   195  		"proto": {
   196  			&reqParams{
   197  				encoding: "proto",
   198  				origin:   "OpenConfig",
   199  				target:   "whatever",
   200  				paths:    []string{"show version"},
   201  			},
   202  		},
   203  		"dot": {
   204  			&reqParams{
   205  				encoding: ".",
   206  				paths:    []string{"show version"},
   207  			},
   208  		},
   209  	}
   210  
   211  	for name, tc := range testCases {
   212  		_, err := newSubscribeOptions(*tc.pathParam, nil, nil)
   213  		if err == nil {
   214  			t.Fatalf("ERROR!\n%s: got no error, but expect an error\n", name)
   215  		}
   216  	}
   217  }
   218  
   219  func TestNewSetOperations(t *testing.T) {
   220  	testCases := map[string]struct {
   221  		args []string
   222  		exp  *gnmi.Operation
   223  	}{
   224  		"update": {
   225  			args: []string{"update", "origin=cli", "target=target", "path", "100"},
   226  			exp: &gnmi.Operation{
   227  				Type:   "update",
   228  				Origin: "cli",
   229  				Target: "target",
   230  				Val:    "100",
   231  				Path:   gnmi.SplitPath("path"),
   232  			},
   233  		},
   234  		"replace": {
   235  			args: []string{"replace", "origin=cli", "target=target", "path", "100"},
   236  			exp: &gnmi.Operation{
   237  				Type:   "replace",
   238  				Origin: "cli",
   239  				Target: "target",
   240  				Val:    "100",
   241  				Path:   gnmi.SplitPath("path"),
   242  			},
   243  		},
   244  		"delete": {
   245  			args: []string{"delete", "origin=cli", "target=target", "path"},
   246  			exp: &gnmi.Operation{
   247  				Type:   "delete",
   248  				Origin: "cli",
   249  				Target: "target",
   250  				Path:   gnmi.SplitPath("path"),
   251  			},
   252  		},
   253  		"union_replace": {
   254  			args: []string{"union_replace", "origin=cli", "target=target", "path", "100"},
   255  			exp: &gnmi.Operation{
   256  				Type:   "union_replace",
   257  				Origin: "cli",
   258  				Target: "target",
   259  				Val:    "100",
   260  				Path:   gnmi.SplitPath("path"),
   261  			},
   262  		},
   263  	}
   264  
   265  	for name, tc := range testCases {
   266  		_, got, err := newSetOperation(0, tc.args, "")
   267  		if err != nil {
   268  			t.Fatalf("ERROR!\n%s: got error: %s, but expect no error\n", name, err.Error())
   269  		}
   270  		if !test.DeepEqual(got, tc.exp) {
   271  			t.Fatalf("ERROR!\nTest Case: %s\nGot: %+v,\nWant %+v\n", name, got, tc.exp)
   272  		}
   273  	}
   274  }
   275  
   276  // update|replace|delete|union_replace request does not support encoding
   277  // test that it throws an error if encoding is given
   278  func TestEncodingNewSetOperations(t *testing.T) {
   279  	testCases := map[string]struct {
   280  		args []string
   281  	}{
   282  		// update
   283  		"dot_update":       {[]string{"update", "encoding=.", "/", "val"}},
   284  		"ASCII_update":     {[]string{"update", "encoding=ascii", "origin=cli", "/", "val"}},
   285  		"bytes_update":     {[]string{"update", "encoding=bytes", "/", "val"}},
   286  		"json_update":      {[]string{"update", "encoding=json", "target=what", "/", "val"}},
   287  		"json_ieft_update": {[]string{"update", "encoding=json_ietf", "/", "val"}},
   288  		"proto_update": {
   289  			[]string{
   290  				"update",
   291  				"encoding=proto",
   292  				"target=h",
   293  				"origin=a",
   294  				"/",
   295  				"val"},
   296  		},
   297  
   298  		// replace
   299  		"dot_replace":       {[]string{"replace", "encoding=.", "/", "val"}},
   300  		"ASCII_replace":     {[]string{"replace", "encoding=ascii", "origin=cli", "/", "val"}},
   301  		"bytes_replace":     {[]string{"replace", "encoding=bytes", "/", "val"}},
   302  		"json_replace":      {[]string{"replace", "encoding=json", "target=what", "/", "val"}},
   303  		"json_ieft_replace": {[]string{"replace", "encoding=json_ietf", "/", "val"}},
   304  		"proto_replace": {
   305  			[]string{
   306  				"replace",
   307  				"encoding=proto",
   308  				"target=h",
   309  				"origin=a",
   310  				"/",
   311  				"val"},
   312  		},
   313  
   314  		// delete
   315  		"dot_delete":       {[]string{"delete", "encoding=.", "/"}},
   316  		"ASCII_delete":     {[]string{"delete", "encoding=ascii", "origin=cli", "/"}},
   317  		"bytes_delete":     {[]string{"delete", "encoding=bytes", "/"}},
   318  		"json_delete":      {[]string{"delete", "encoding=json", "target=what", "/"}},
   319  		"json_ieft_delete": {[]string{"delete", "encoding=json_ietf", "/"}},
   320  		"proto_delete": {
   321  			[]string{
   322  				"delete",
   323  				"encoding=proto",
   324  				"target=h",
   325  				"origin=a",
   326  				"/"}},
   327  
   328  		// union_replace
   329  		"dot_union_replace": {[]string{"union_replace", "encoding=.", "/", "val"}},
   330  		"ASCII_union_replace": {
   331  			[]string{"union_replace", "encoding=ascii", "origin=cli", "/", "val"}},
   332  		"bytes_union_replace": {[]string{"union_replace", "encoding=bytes", "/", "val"}},
   333  		"json_union_replace": {
   334  			[]string{"union_replace", "encoding=json", "target=what", "/", "val"}},
   335  		"json_ieft_union_replace": {[]string{"union_replace", "encoding=json_ietf", "/", "val"}},
   336  		"proto_union_replace": {
   337  			[]string{
   338  				"union_replace",
   339  				"encoding=proto",
   340  				"target=h",
   341  				"origin=a",
   342  				"/",
   343  				"val"},
   344  		},
   345  	}
   346  	for name, tc := range testCases {
   347  		_, _, err := newSetOperation(0, tc.args, "")
   348  		if err == nil {
   349  			t.Fatalf("ERROR!\n%s: got no error, but expect an error\n", name)
   350  		}
   351  	}
   352  }
   353  
   354  // update|replace|union_replace operation needs a value
   355  // test that it throws an error if missing
   356  func TestMissingValueNewSetOperations(t *testing.T) {
   357  	testCases := map[string]struct {
   358  		args []string
   359  	}{
   360  		// update
   361  		"update":        {[]string{"update", "/"}},
   362  		"update_origin": {[]string{"update", "origin=cli", "/"}},
   363  		"update_target": {[]string{"update", "target=what", "/"}},
   364  		"update_both":   {[]string{"update", "target=h", "origin=a", "/"}},
   365  
   366  		// replace
   367  		"replace":        {[]string{"replace", "/"}},
   368  		"replace_origin": {[]string{"replace", "origin=cli", "/"}},
   369  		"replace_target": {[]string{"replace", "target=what", "/"}},
   370  		"replace_both":   {[]string{"replace", "target=h", "origin=a", "/"}},
   371  
   372  		// union_replace
   373  		"union_replace":        {[]string{"union_replace", "/"}},
   374  		"union_replace_origin": {[]string{"union_replace", "origin=cli", "/"}},
   375  		"union_replace_target": {[]string{"union_replace", "target=what", "/"}},
   376  		"union_replace_both":   {[]string{"union_replace", "target=h", "origin=a", "/"}},
   377  	}
   378  
   379  	for name, tc := range testCases {
   380  		_, _, err := newSetOperation(0, tc.args, "")
   381  		if err == nil {
   382  			t.Fatalf("ERROR!\n%s: got no error, but expect an error\n", name)
   383  		}
   384  	}
   385  }
   386  
   387  // update|replace|delete|union_replace needs a path
   388  // test that it throws an error if missing
   389  func TestMissingPathsNewSetOperations(t *testing.T) {
   390  	testCases := map[string]struct {
   391  		args []string
   392  	}{
   393  		// update
   394  		"update":        {[]string{"update"}},
   395  		"update_origin": {[]string{"update", "origin=cli"}},
   396  		"update_target": {[]string{"update", "target=what"}},
   397  		"update_both":   {[]string{"update", "target=h", "origin=a"}},
   398  
   399  		// replace
   400  		"replace":        {[]string{"replace"}},
   401  		"replace_origin": {[]string{"replace", "origin=cli"}},
   402  		"replace_target": {[]string{"replace", "target=what"}},
   403  		"replace_both":   {[]string{"replace", "target=h", "origin=a"}},
   404  
   405  		// delete
   406  		"delete":        {[]string{"delete"}},
   407  		"delete_origin": {[]string{"delete", "origin=OpenConfig"}},
   408  		"delete_target": {[]string{"delete", "target=target"}},
   409  		"delete_both":   {[]string{"delete", "target=target", "origin=origin"}},
   410  
   411  		// union_replace
   412  		"union_replace":        {[]string{"union_replace"}},
   413  		"union_replace_origin": {[]string{"union_replace", "origin=cli"}},
   414  		"union_replace_target": {[]string{"union_replace", "target=what"}},
   415  		"union_replace_both":   {[]string{"union_replace", "target=h", "origin=a"}},
   416  	}
   417  
   418  	for name, tc := range testCases {
   419  		_, _, err := newSetOperation(0, tc.args, "")
   420  		if err == nil {
   421  			t.Fatalf("ERROR!\n%s: got no error, but expect an error\n", name)
   422  		}
   423  	}
   424  }
   425  
   426  // for update|replace|delete|union_replace
   427  // test that it stops parsing at the correct args position
   428  func TestArgsPosNewSetOperations(t *testing.T) {
   429  	testCases := map[string]struct {
   430  		args []string
   431  	}{
   432  		// update
   433  		"update":        {[]string{"update", "/", "hi", "hi"}},
   434  		"update_origin": {[]string{"update", "origin=cli", "/", "hi", "hi"}},
   435  		"update_target": {[]string{"update", "target=what", "/", "hi", "hi"}},
   436  		"update_both":   {[]string{"update", "target=h", "origin=a", "/", "hi", "hi"}},
   437  
   438  		// replace
   439  		"replace":        {[]string{"replace", "/", "hi", "hi"}},
   440  		"replace_origin": {[]string{"replace", "origin=cli", "/", "hi", "hi"}},
   441  		"replace_target": {[]string{"replace", "target=what", "/", "hi", "hi"}},
   442  		"replace_both":   {[]string{"replace", "target=h", "origin=a", "/", "hi", "hi"}},
   443  
   444  		// delete
   445  		"delete":        {[]string{"delete", "/", "update"}},
   446  		"delete_origin": {[]string{"delete", "origin=cli", "/", "next operation"}},
   447  		"delete_target": {[]string{"delete", "target=what", "/", "next operation"}},
   448  		"delete_both":   {[]string{"delete", "origin=cli", "target=tar", "/", "next operation"}},
   449  
   450  		// union_replace
   451  		"union_replace":        {[]string{"union_replace", "/", "hi", "hi"}},
   452  		"union_replace_origin": {[]string{"union_replace", "origin=cli", "/", "hi", "hi"}},
   453  		"union_replace_target": {[]string{"union_replace", "target=what", "/", "hi", "hi"}},
   454  		"union_replace_both": {
   455  			[]string{"union_replace", "target=h", "origin=a", "/", "hi", "hi"}},
   456  	}
   457  	for name, tc := range testCases {
   458  		pos, _, err := newSetOperation(0, tc.args, "")
   459  		expectedPos := len(tc.args) - 2
   460  		if err != nil {
   461  			t.Fatalf("ERROR!\n%s: got error: %s, but expect no error\n", name, err.Error())
   462  		}
   463  		if pos != expectedPos {
   464  			t.Fatalf("ERROR!\n%s: got pos = %d, but expect pos = %d\n", name, pos, expectedPos)
   465  		}
   466  	}
   467  }