hash_test.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  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 hash
  14. import (
  15. "reflect"
  16. "strings"
  17. "testing"
  18. "k8s.io/api/core/v1"
  19. )
  20. func TestConfigMapHash(t *testing.T) {
  21. cases := []struct {
  22. desc string
  23. cm *v1.ConfigMap
  24. hash string
  25. err string
  26. }{
  27. // empty map
  28. {"empty data", &v1.ConfigMap{Data: map[string]string{}, BinaryData: map[string][]byte{}}, "42745tchd9", ""},
  29. // one key
  30. {"one key", &v1.ConfigMap{Data: map[string]string{"one": ""}}, "9g67k2htb6", ""},
  31. // three keys (tests sorting order)
  32. {"three keys", &v1.ConfigMap{Data: map[string]string{"two": "2", "one": "", "three": "3"}}, "f5h7t85m9b", ""},
  33. // empty binary data map
  34. {"empty binary data", &v1.ConfigMap{BinaryData: map[string][]byte{}}, "dk855m5d49", ""},
  35. // one key with binary data
  36. {"one key with binary data", &v1.ConfigMap{BinaryData: map[string][]byte{"one": []byte("")}}, "mk79584b8c", ""},
  37. // three keys with binary data (tests sorting order)
  38. {"three keys with binary data", &v1.ConfigMap{BinaryData: map[string][]byte{"two": []byte("2"), "one": []byte(""), "three": []byte("3")}}, "t458mc6db2", ""},
  39. // two keys, one with string and another with binary data
  40. {"two keys with one each", &v1.ConfigMap{Data: map[string]string{"one": ""}, BinaryData: map[string][]byte{"two": []byte("")}}, "698h7c7t9m", ""},
  41. }
  42. for _, c := range cases {
  43. h, err := ConfigMapHash(c.cm)
  44. if SkipRest(t, c.desc, err, c.err) {
  45. continue
  46. }
  47. if c.hash != h {
  48. t.Errorf("case %q, expect hash %q but got %q", c.desc, c.hash, h)
  49. }
  50. }
  51. }
  52. func TestSecretHash(t *testing.T) {
  53. cases := []struct {
  54. desc string
  55. secret *v1.Secret
  56. hash string
  57. err string
  58. }{
  59. // empty map
  60. {"empty data", &v1.Secret{Type: "my-type", Data: map[string][]byte{}}, "t75bgf6ctb", ""},
  61. // one key
  62. {"one key", &v1.Secret{Type: "my-type", Data: map[string][]byte{"one": []byte("")}}, "74bd68bm66", ""},
  63. // three keys (tests sorting order)
  64. {"three keys", &v1.Secret{Type: "my-type", Data: map[string][]byte{"two": []byte("2"), "one": []byte(""), "three": []byte("3")}}, "dgcb6h9tmk", ""},
  65. }
  66. for _, c := range cases {
  67. h, err := SecretHash(c.secret)
  68. if SkipRest(t, c.desc, err, c.err) {
  69. continue
  70. }
  71. if c.hash != h {
  72. t.Errorf("case %q, expect hash %q but got %q", c.desc, c.hash, h)
  73. }
  74. }
  75. }
  76. func TestEncodeConfigMap(t *testing.T) {
  77. cases := []struct {
  78. desc string
  79. cm *v1.ConfigMap
  80. expect string
  81. err string
  82. }{
  83. // empty map
  84. {"empty data", &v1.ConfigMap{Data: map[string]string{}}, `{"data":{},"kind":"ConfigMap","name":""}`, ""},
  85. // one key
  86. {"one key", &v1.ConfigMap{Data: map[string]string{"one": ""}}, `{"data":{"one":""},"kind":"ConfigMap","name":""}`, ""},
  87. // three keys (tests sorting order)
  88. {"three keys", &v1.ConfigMap{Data: map[string]string{"two": "2", "one": "", "three": "3"}}, `{"data":{"one":"","three":"3","two":"2"},"kind":"ConfigMap","name":""}`, ""},
  89. // empty binary map
  90. {"empty data", &v1.ConfigMap{BinaryData: map[string][]byte{}}, `{"data":null,"kind":"ConfigMap","name":""}`, ""},
  91. // one key with binary data
  92. {"one key", &v1.ConfigMap{BinaryData: map[string][]byte{"one": []byte("")}}, `{"binaryData":{"one":""},"data":null,"kind":"ConfigMap","name":""}`, ""},
  93. // three keys with binary data (tests sorting order)
  94. {"three keys", &v1.ConfigMap{BinaryData: map[string][]byte{"two": []byte("2"), "one": []byte(""), "three": []byte("3")}}, `{"binaryData":{"one":"","three":"Mw==","two":"Mg=="},"data":null,"kind":"ConfigMap","name":""}`, ""},
  95. // two keys, one string and one binary values
  96. {"two keys with one each", &v1.ConfigMap{Data: map[string]string{"one": ""}, BinaryData: map[string][]byte{"two": []byte("")}}, `{"binaryData":{"two":""},"data":{"one":""},"kind":"ConfigMap","name":""}`, ""},
  97. }
  98. for _, c := range cases {
  99. s, err := encodeConfigMap(c.cm)
  100. if SkipRest(t, c.desc, err, c.err) {
  101. continue
  102. }
  103. if s != c.expect {
  104. t.Errorf("case %q, expect %q but got %q from encode %#v", c.desc, c.expect, s, c.cm)
  105. }
  106. }
  107. }
  108. func TestEncodeSecret(t *testing.T) {
  109. cases := []struct {
  110. desc string
  111. secret *v1.Secret
  112. expect string
  113. err string
  114. }{
  115. // empty map
  116. {"empty data", &v1.Secret{Type: "my-type", Data: map[string][]byte{}}, `{"data":{},"kind":"Secret","name":"","type":"my-type"}`, ""},
  117. // one key
  118. {"one key", &v1.Secret{Type: "my-type", Data: map[string][]byte{"one": []byte("")}}, `{"data":{"one":""},"kind":"Secret","name":"","type":"my-type"}`, ""},
  119. // three keys (tests sorting order) - note json.Marshal base64 encodes the values because they come in as []byte
  120. {"three keys", &v1.Secret{Type: "my-type", Data: map[string][]byte{"two": []byte("2"), "one": []byte(""), "three": []byte("3")}}, `{"data":{"one":"","three":"Mw==","two":"Mg=="},"kind":"Secret","name":"","type":"my-type"}`, ""},
  121. }
  122. for _, c := range cases {
  123. s, err := encodeSecret(c.secret)
  124. if SkipRest(t, c.desc, err, c.err) {
  125. continue
  126. }
  127. if s != c.expect {
  128. t.Errorf("case %q, expect %q but got %q from encode %#v", c.desc, c.expect, s, c.secret)
  129. }
  130. }
  131. }
  132. func TestHash(t *testing.T) {
  133. // hash the empty string to be sure that sha256 is being used
  134. expect := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
  135. sum := hash("")
  136. if expect != sum {
  137. t.Errorf("expected hash %q but got %q", expect, sum)
  138. }
  139. }
  140. // warn devs who change types that they might have to update a hash function
  141. // not perfect, as it only checks the number of top-level fields
  142. func TestTypeStability(t *testing.T) {
  143. errfmt := `case %q, expected %d fields but got %d
  144. Depending on the field(s) you added, you may need to modify the hash function for this type.
  145. To guide you: the hash function targets fields that comprise the contents of objects,
  146. not their metadata (e.g. the Data of a ConfigMap, but nothing in ObjectMeta).
  147. `
  148. cases := []struct {
  149. typeName string
  150. obj interface{}
  151. expect int
  152. }{
  153. {"ConfigMap", v1.ConfigMap{}, 4},
  154. {"Secret", v1.Secret{}, 5},
  155. }
  156. for _, c := range cases {
  157. val := reflect.ValueOf(c.obj)
  158. if num := val.NumField(); c.expect != num {
  159. t.Errorf(errfmt, c.typeName, c.expect, num)
  160. }
  161. }
  162. }
  163. // SkipRest returns true if there was a non-nil error or if we expected an error that didn't happen,
  164. // and logs the appropriate error on the test object.
  165. // The return value indicates whether we should skip the rest of the test case due to the error result.
  166. func SkipRest(t *testing.T, desc string, err error, contains string) bool {
  167. if err != nil {
  168. if len(contains) == 0 {
  169. t.Errorf("case %q, expect nil error but got %q", desc, err.Error())
  170. } else if !strings.Contains(err.Error(), contains) {
  171. t.Errorf("case %q, expect error to contain %q but got %q", desc, contains, err.Error())
  172. }
  173. return true
  174. } else if len(contains) > 0 {
  175. t.Errorf("case %q, expect error to contain %q but got nil error", desc, contains)
  176. return true
  177. }
  178. return false
  179. }