controlclient_test.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package controlclient
  4. import (
  5. "io"
  6. "reflect"
  7. "slices"
  8. "testing"
  9. "tailscale.com/types/netmap"
  10. "tailscale.com/types/persist"
  11. )
  12. func fieldsOf(t reflect.Type) (fields []string) {
  13. for i := range t.NumField() {
  14. if name := t.Field(i).Name; name != "_" {
  15. fields = append(fields, name)
  16. }
  17. }
  18. return
  19. }
  20. func TestStatusEqual(t *testing.T) {
  21. // Verify that the Equal method stays in sync with reality
  22. equalHandles := []string{"Err", "URL", "NetMap", "Persist", "state"}
  23. if have := fieldsOf(reflect.TypeFor[Status]()); !reflect.DeepEqual(have, equalHandles) {
  24. t.Errorf("Status.Equal check might be out of sync\nfields: %q\nhandled: %q\n",
  25. have, equalHandles)
  26. }
  27. tests := []struct {
  28. a, b *Status
  29. want bool
  30. }{
  31. {
  32. &Status{},
  33. nil,
  34. false,
  35. },
  36. {
  37. nil,
  38. &Status{},
  39. false,
  40. },
  41. {
  42. nil,
  43. nil,
  44. true,
  45. },
  46. {
  47. &Status{},
  48. &Status{},
  49. true,
  50. },
  51. {
  52. &Status{},
  53. &Status{state: StateAuthenticated},
  54. false,
  55. },
  56. }
  57. for i, tt := range tests {
  58. got := tt.a.Equal(tt.b)
  59. if got != tt.want {
  60. t.Errorf("%d. Equal = %v; want %v", i, got, tt.want)
  61. }
  62. }
  63. }
  64. // tests [canSkipStatus].
  65. func TestCanSkipStatus(t *testing.T) {
  66. st := new(Status)
  67. nm1 := &netmap.NetworkMap{}
  68. nm2 := &netmap.NetworkMap{}
  69. tests := []struct {
  70. name string
  71. s1, s2 *Status
  72. want bool
  73. }{
  74. {
  75. name: "nil-s2",
  76. s1: st,
  77. s2: nil,
  78. want: false,
  79. },
  80. {
  81. name: "equal",
  82. s1: st,
  83. s2: st,
  84. want: false,
  85. },
  86. {
  87. name: "s1-error",
  88. s1: &Status{Err: io.EOF, NetMap: nm1},
  89. s2: &Status{NetMap: nm2},
  90. want: false,
  91. },
  92. {
  93. name: "s1-url",
  94. s1: &Status{URL: "foo", NetMap: nm1},
  95. s2: &Status{NetMap: nm2},
  96. want: false,
  97. },
  98. {
  99. name: "s1-persist-diff",
  100. s1: &Status{Persist: new(persist.Persist).View(), NetMap: nm1},
  101. s2: &Status{NetMap: nm2},
  102. want: false,
  103. },
  104. {
  105. name: "s1-state-diff",
  106. s1: &Status{state: 123, NetMap: nm1},
  107. s2: &Status{NetMap: nm2},
  108. want: false,
  109. },
  110. {
  111. name: "s1-no-netmap1",
  112. s1: &Status{NetMap: nil},
  113. s2: &Status{NetMap: nm2},
  114. want: false,
  115. },
  116. {
  117. name: "s1-no-netmap2",
  118. s1: &Status{NetMap: nm1},
  119. s2: &Status{NetMap: nil},
  120. want: false,
  121. },
  122. {
  123. name: "skip",
  124. s1: &Status{NetMap: nm1},
  125. s2: &Status{NetMap: nm2},
  126. want: true,
  127. },
  128. }
  129. for _, tt := range tests {
  130. t.Run(tt.name, func(t *testing.T) {
  131. if got := canSkipStatus(tt.s1, tt.s2); got != tt.want {
  132. t.Errorf("canSkipStatus = %v, want %v", got, tt.want)
  133. }
  134. })
  135. }
  136. want := []string{"Err", "URL", "NetMap", "Persist", "state"}
  137. if f := fieldsOf(reflect.TypeFor[Status]()); !slices.Equal(f, want) {
  138. t.Errorf("Status fields = %q; this code was only written to handle fields %q", f, want)
  139. }
  140. }