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 }