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