client_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. /*
  2. Copyright 2017 Google Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package client_test
  14. import (
  15. "fmt"
  16. "reflect"
  17. "sort"
  18. "testing"
  19. "time"
  20. "context"
  21. "github.com/golang/protobuf/proto"
  22. "notabug.org/themusicgod1/gnmi/client"
  23. fclient "notabug.org/themusicgod1/gnmi/client/fake"
  24. )
  25. const (
  26. defaultQuery = "*"
  27. )
  28. func testImpl(ctx context.Context, d client.Destination) (client.Impl, error) {
  29. if len(d.Addrs) > 0 && d.Addrs[0] == "error" {
  30. return nil, fmt.Errorf("error")
  31. }
  32. return fclient.New(ctx, d)
  33. }
  34. func TestRegister(t *testing.T) {
  35. // In case some other test forgot, clean out registered impls.
  36. client.ResetRegisteredImpls()
  37. // Verify Reset
  38. if got := client.RegisteredImpls(); got != nil {
  39. t.Fatalf("client.ResetRegisteredImpls() failed: got %v want nil", got)
  40. }
  41. // Clean out what we registered.
  42. defer client.ResetRegisteredImpls()
  43. // Registered names must not be reused unless you expect an error in duplicate
  44. tests := []struct {
  45. desc string
  46. name string
  47. f client.InitImpl
  48. clientType []string
  49. rErr bool
  50. nErr bool
  51. }{{
  52. desc: "Missing Impl",
  53. name: "foo",
  54. rErr: true,
  55. }, {
  56. desc: "No registration, unspecified client",
  57. nErr: true,
  58. }, {
  59. desc: "Name only",
  60. name: "foo",
  61. rErr: true,
  62. }, {
  63. desc: "Valid Client",
  64. name: "bar",
  65. f: testImpl,
  66. clientType: []string{"bar"},
  67. nErr: false,
  68. }, {
  69. desc: "Unspecified client with prior registeration",
  70. nErr: false,
  71. }, {
  72. desc: "Duplicate Registration",
  73. name: "bar",
  74. f: testImpl,
  75. clientType: []string{"bar"},
  76. rErr: true,
  77. }, {
  78. desc: "Unknown Registration",
  79. name: "foobar",
  80. f: testImpl,
  81. clientType: []string{"zbaz"},
  82. nErr: true,
  83. }, {
  84. desc: "Multiple clients, one valid",
  85. clientType: []string{"zbaz", "bar"},
  86. nErr: false,
  87. }}
  88. for _, tt := range tests {
  89. t.Run(tt.desc, func(t *testing.T) {
  90. if tt.name != "" {
  91. err := client.Register(tt.name, tt.f)
  92. switch {
  93. case tt.rErr && err == nil:
  94. t.Fatalf("Register(%q, %v) unexpected success", tt.name, tt.f)
  95. case !tt.rErr && err != nil:
  96. t.Fatalf("Register(%q, %v) failed: %v", tt.name, tt.f, err)
  97. case tt.rErr && err != nil:
  98. return
  99. }
  100. }
  101. err := client.New().Subscribe(context.Background(), client.Query{
  102. Type: client.Once,
  103. Addrs: []string{"fake"},
  104. Queries: []client.Path{{"*"}},
  105. NotificationHandler: func(client.Notification) error { return nil },
  106. }, tt.clientType...)
  107. switch {
  108. case tt.nErr && err == nil:
  109. t.Fatalf("Subscribe() unexpected success")
  110. case !tt.nErr && err != nil:
  111. t.Fatalf("Subscribe() failed: %v", err)
  112. case tt.nErr && err != nil:
  113. return
  114. }
  115. })
  116. }
  117. }
  118. func TestRegisterHangingImpl(t *testing.T) {
  119. // This test makes sure that a hanging client.NewImpl (due to blocked
  120. // InitImpl, e.g. waiting for timeout) doesn't prevent other client.NewImpl
  121. // calls from blocking too. This may happen due to a global mutex in
  122. // register.go
  123. // In case some other test forgot, clean out registered impls.
  124. client.ResetRegisteredImpls()
  125. // Clean out what we registered.
  126. defer client.ResetRegisteredImpls()
  127. blocked := make(chan struct{})
  128. client.Register("blocking", func(ctx context.Context, _ client.Destination) (client.Impl, error) {
  129. close(blocked)
  130. // Block until test returns.
  131. <-ctx.Done()
  132. return nil, nil
  133. })
  134. client.Register("regular", func(context.Context, client.Destination) (client.Impl, error) {
  135. return nil, nil
  136. })
  137. ctx, cancel := context.WithCancel(context.Background())
  138. defer cancel()
  139. connected := make(chan string, 1)
  140. go func() {
  141. client.NewImpl(ctx, client.Destination{}, "blocking")
  142. connected <- "blocking"
  143. }()
  144. go func() {
  145. // Wait for blocking Impl to start blocking.
  146. <-blocked
  147. client.NewImpl(ctx, client.Destination{}, "regular")
  148. connected <- "regular"
  149. }()
  150. select {
  151. case got := <-connected:
  152. if got != "regular" {
  153. t.Errorf(`connected Impl %q, want "regular"`, got)
  154. }
  155. case <-time.After(5 * time.Second):
  156. t.Error("blocking InitImpl prevents regular InitImpl from connecting (waiter 5s)")
  157. }
  158. }
  159. func TestQuery(t *testing.T) {
  160. tests := []struct {
  161. desc string
  162. in client.Query
  163. wantPath []client.Path
  164. err bool
  165. client []string
  166. }{{
  167. desc: "Empty Query",
  168. in: client.Query{},
  169. err: true,
  170. wantPath: []client.Path{{defaultQuery}},
  171. }, {
  172. desc: "No Addr",
  173. wantPath: []client.Path{{defaultQuery}},
  174. in: client.Query{
  175. Queries: []client.Path{{"foo", "bar"}, {"a", "b"}},
  176. Type: client.Once,
  177. },
  178. err: true,
  179. }, {
  180. desc: "No Target",
  181. wantPath: []client.Path{{defaultQuery}},
  182. in: client.Query{
  183. Addrs: []string{"fake addr"},
  184. Queries: []client.Path{{"foo", "bar"}, {"a", "b"}},
  185. Type: client.Once,
  186. },
  187. err: true,
  188. }, {
  189. desc: "No Type",
  190. wantPath: []client.Path{{defaultQuery}},
  191. in: client.Query{
  192. Addrs: []string{"fake addr"},
  193. Target: "",
  194. Queries: []client.Path{{"foo", "bar"}, {"a", "b"}},
  195. },
  196. err: true,
  197. }, {
  198. desc: "No Queries",
  199. wantPath: []client.Path{{defaultQuery}},
  200. in: client.Query{
  201. Addrs: []string{"fake addr"},
  202. Target: "",
  203. Type: client.Once,
  204. },
  205. err: true,
  206. }, {
  207. desc: "Both handlers set",
  208. wantPath: []client.Path{{"foo", "bar"}, {"a", "b"}},
  209. in: client.Query{
  210. Addrs: []string{"fake addr"},
  211. Target: "",
  212. Queries: []client.Path{{"foo", "bar"}, {"a", "b"}},
  213. Type: client.Once,
  214. NotificationHandler: func(_ client.Notification) error { return nil },
  215. ProtoHandler: func(_ proto.Message) error { return nil },
  216. },
  217. err: true,
  218. }, {
  219. desc: "Valid Query",
  220. wantPath: []client.Path{{"foo", "bar"}, {"a", "b"}},
  221. in: client.Query{
  222. Addrs: []string{"fake addr"},
  223. Target: "",
  224. Queries: []client.Path{{"foo", "bar"}, {"a", "b"}},
  225. Type: client.Once,
  226. NotificationHandler: func(_ client.Notification) error { return nil },
  227. },
  228. }, {
  229. desc: "Password contains forbidden characters",
  230. in: client.Query{
  231. Credentials: &client.Credentials{Password: "\n"},
  232. },
  233. err: true,
  234. }, {
  235. desc: "Username contains forbidden characters",
  236. in: client.Query{
  237. Credentials: &client.Credentials{Username: "\n"},
  238. },
  239. err: true,
  240. }}
  241. for _, tt := range tests {
  242. t.Run(tt.desc, func(t *testing.T) {
  243. err := tt.in.Validate()
  244. switch {
  245. case err != nil && tt.err:
  246. return
  247. case err != nil && !tt.err:
  248. t.Errorf("Validate() failed: %v", err)
  249. return
  250. case err == nil && tt.err:
  251. t.Errorf("Validate() expected error.")
  252. return
  253. }
  254. if !reflect.DeepEqual(tt.wantPath, tt.in.Queries) {
  255. t.Errorf("Validate() failed: got %v, want %v", tt.in.Queries, tt.wantPath)
  256. }
  257. })
  258. }
  259. }
  260. func TestLeaves(t *testing.T) {
  261. tests := []struct {
  262. desc string
  263. in client.Leaves
  264. want client.Leaves
  265. }{{
  266. desc: "sorted",
  267. in: client.Leaves{
  268. {TS: time.Unix(0, 0), Path: client.Path{"a"}, Val: 1},
  269. {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1},
  270. {TS: time.Unix(0, 0), Path: client.Path{"a", "b", "c"}, Val: 1},
  271. },
  272. want: client.Leaves{
  273. {TS: time.Unix(0, 0), Path: client.Path{"a"}, Val: 1},
  274. {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1},
  275. {TS: time.Unix(0, 0), Path: client.Path{"a", "b", "c"}, Val: 1},
  276. },
  277. }, {
  278. desc: "unsorted",
  279. in: client.Leaves{
  280. {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 1},
  281. {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1},
  282. {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 1},
  283. },
  284. want: client.Leaves{
  285. {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1},
  286. {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 1},
  287. {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 1},
  288. },
  289. }, {
  290. desc: "stable",
  291. in: client.Leaves{
  292. {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 1},
  293. {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 2},
  294. {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1},
  295. {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 2},
  296. {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 1},
  297. {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 3},
  298. },
  299. want: client.Leaves{
  300. {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1},
  301. {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 2},
  302. {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 1},
  303. {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 1},
  304. {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 2},
  305. {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 3},
  306. },
  307. }, {
  308. desc: "nil path",
  309. in: client.Leaves{
  310. {TS: time.Unix(0, 0), Val: 1},
  311. {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1},
  312. {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 2},
  313. {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 1},
  314. {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 3},
  315. },
  316. want: client.Leaves{
  317. {TS: time.Unix(0, 0), Val: 1},
  318. {TS: time.Unix(0, 0), Path: client.Path{"a", "b"}, Val: 1},
  319. {TS: time.Unix(0, 0), Path: client.Path{"b", "b", "c"}, Val: 1},
  320. {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 2},
  321. {TS: time.Unix(0, 0), Path: client.Path{"c"}, Val: 3},
  322. },
  323. }}
  324. for _, tt := range tests {
  325. t.Run(tt.desc, func(t *testing.T) {
  326. got := make(client.Leaves, len(tt.in))
  327. copy(got, tt.in)
  328. sort.Sort(got)
  329. if !reflect.DeepEqual(got, tt.want) {
  330. t.Fatalf("sort.Sort(%v) failed: got %v, want %v", tt.in, got, tt.want)
  331. }
  332. })
  333. }
  334. }
  335. func TestPath(t *testing.T) {
  336. tests := []struct {
  337. desc string
  338. in client.Path
  339. cmp client.Path
  340. want bool
  341. }{{
  342. desc: "same",
  343. in: client.Path{"a", "b", "c"},
  344. cmp: client.Path{"a", "b", "c"},
  345. want: true,
  346. }, {
  347. desc: "different length",
  348. in: client.Path{"a", "b", "c"},
  349. cmp: client.Path{"a", "b"},
  350. want: false,
  351. }, {
  352. desc: "different",
  353. in: client.Path{"a", "b", "c"},
  354. cmp: client.Path{"a", "b", "d"},
  355. want: false,
  356. }}
  357. for _, tt := range tests {
  358. t.Run(tt.desc, func(t *testing.T) {
  359. if got := tt.in.Equal(tt.cmp); got != tt.want {
  360. t.Fatalf("%+v.Equal(%+v) failed: got %v, want %v", tt.in, tt.cmp, got, tt.want)
  361. }
  362. })
  363. }
  364. }
  365. func TestNewType(t *testing.T) {
  366. tests := []struct {
  367. desc string
  368. in string
  369. want client.Type
  370. }{{
  371. desc: "Unknown",
  372. in: "foo",
  373. want: client.Unknown,
  374. }, {
  375. desc: "Once",
  376. in: "once",
  377. want: client.Once,
  378. }, {
  379. desc: "Stream",
  380. in: "stream",
  381. want: client.Stream,
  382. }, {
  383. desc: "Poll",
  384. in: "poll",
  385. want: client.Poll,
  386. }}
  387. for _, tt := range tests {
  388. t.Run(tt.desc, func(t *testing.T) {
  389. if got := client.NewType(tt.in); got != tt.want {
  390. t.Fatalf("client.NewType(%+v) failed: got %v, want %v", tt.in, got, tt.want)
  391. }
  392. })
  393. }
  394. }
  395. func TestError(t *testing.T) {
  396. want := "foo"
  397. e := client.NewError(want)
  398. if got := e.Error(); got != want {
  399. t.Errorf("client.NewError(%q) failed: got %v, want %v", want, got, want)
  400. }
  401. }
  402. func TestClientUpdatesAfterClose(t *testing.T) {
  403. client.ResetRegisteredImpls()
  404. fake := fakeStreamingClient{ch: make(chan struct{})}
  405. client.Register("fake", func(context.Context, client.Destination) (client.Impl, error) {
  406. return fake, nil
  407. })
  408. c := &client.BaseClient{}
  409. done := make(chan struct{})
  410. go func() {
  411. defer close(done)
  412. err := c.Subscribe(context.Background(), client.Query{
  413. Addrs: []string{"fake"},
  414. Type: client.Stream,
  415. Queries: []client.Path{{"*"}},
  416. NotificationHandler: func(client.Notification) error { return nil },
  417. }, "fake")
  418. if err != nil {
  419. t.Errorf("Subscribe(): %v", err)
  420. }
  421. }()
  422. for i := 0; i < 10; i++ {
  423. select {
  424. case fake.ch <- struct{}{}:
  425. case <-done:
  426. t.Fatal("Subscribe returned before close")
  427. }
  428. }
  429. c.Close()
  430. var updatesAfterClose int
  431. loop:
  432. for {
  433. select {
  434. case fake.ch <- struct{}{}:
  435. updatesAfterClose++
  436. case <-done:
  437. break loop
  438. }
  439. }
  440. if updatesAfterClose > 1 {
  441. t.Errorf("got %d updates after Close, expect at most 1", updatesAfterClose)
  442. }
  443. }
  444. type fakeStreamingClient struct {
  445. client.Impl
  446. ch chan struct{}
  447. }
  448. func (f fakeStreamingClient) Subscribe(context.Context, client.Query) error {
  449. return nil
  450. }
  451. func (f fakeStreamingClient) Recv() error {
  452. <-f.ch
  453. return nil
  454. }
  455. func (f fakeStreamingClient) Close() error { return nil }
  456. // Once client will run the query and once complete you can act on the
  457. // returned tree.
  458. func ExampleClient_Once() {
  459. q := client.Query{
  460. Addrs: []string{"127.0.0.1:1234"},
  461. Target: "dev",
  462. Queries: []client.Path{{"*"}},
  463. Type: client.Once,
  464. }
  465. c := client.New()
  466. defer c.Close()
  467. err := c.Subscribe(context.Background(), q)
  468. if err != nil {
  469. fmt.Println(err)
  470. return
  471. }
  472. for _, v := range c.Leaves() {
  473. fmt.Printf("%v: %v\n", v.Path, v.Val)
  474. }
  475. }
  476. // Poll client is like Once client, but can be re-triggered via Poll to
  477. // re-execute the query.
  478. func ExampleClient_Poll() {
  479. q := client.Query{
  480. Addrs: []string{"127.0.0.1:1234"},
  481. Target: "dev",
  482. Queries: []client.Path{{"*"}},
  483. Type: client.Poll,
  484. }
  485. c := client.New()
  486. defer c.Close()
  487. err := c.Subscribe(context.Background(), q)
  488. if err != nil {
  489. fmt.Println(err)
  490. return
  491. }
  492. for _, v := range c.Leaves() {
  493. fmt.Printf("%v: %v\n", v.Path, v.Val)
  494. }
  495. err = c.Poll() // Poll allows the underyling Query to keep running
  496. if err != nil {
  497. fmt.Println(err)
  498. return
  499. }
  500. for _, v := range c.Leaves() {
  501. fmt.Printf("%v: %v\n", v.Path, v.Val)
  502. }
  503. }
  504. // Stream client returns the current state for the query and keeps running
  505. // until closed or the underlying connection breaks.
  506. func ExampleClient_Stream() {
  507. q := client.Query{
  508. Addrs: []string{"127.0.0.1:1234"},
  509. Target: "dev",
  510. Queries: []client.Path{{"*"}},
  511. Type: client.Stream,
  512. NotificationHandler: func(n client.Notification) error {
  513. switch nn := n.(type) {
  514. case client.Connected:
  515. fmt.Println("client is connected")
  516. case client.Sync:
  517. fmt.Println("client is synced")
  518. case client.Update, client.Delete:
  519. fmt.Printf("update: %+v\n", nn)
  520. case client.Error:
  521. fmt.Printf("error: %v\n", nn)
  522. }
  523. return nil
  524. },
  525. }
  526. c := client.New()
  527. defer c.Close()
  528. // Note that Subscribe will block.
  529. err := c.Subscribe(context.Background(), q)
  530. if err != nil {
  531. fmt.Println(err)
  532. }
  533. }