github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/label_sync/main_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "encoding/json" 21 "reflect" 22 "strings" 23 "testing" 24 "time" 25 ) 26 27 // Tests for getting data from GitHub are not needed: 28 // The would have to use real API point or test stubs 29 30 // Test syncLabels(config *Configuration, curr *RepoLabels) (updates RepoUpdates, err error) 31 // Input: Configuration list and Current labels list on multiple repos 32 // Output: list of wanted label updates (update due to name or color) addition due to missing labels 33 // This is main testing for this program 34 func TestSyncLabels(t *testing.T) { 35 var testcases = []struct { 36 name string 37 config Configuration 38 current RepoLabels 39 expectedUpdates RepoUpdates 40 expectedError bool 41 now time.Time 42 }{ 43 { 44 name: "All empty", 45 }, 46 { 47 name: "Duplicate wanted label", 48 config: Configuration{Labels: []Label{ 49 {Name: "lab1", Description: "Test Label 1", Color: "deadbe"}, 50 {Name: "lab1", Description: "Test Label 1", Color: "befade"}, 51 }}, 52 expectedError: true, 53 }, 54 { 55 name: "Required label has non unique labels when downcased", 56 config: Configuration{Labels: []Label{ 57 {Name: "lab1", Description: "Test Label 1", Color: "deadbe"}, 58 {Name: "LAB1", Description: "Test Label 2", Color: "deadbe"}, 59 }}, 60 expectedError: true, 61 }, 62 { 63 name: "Duplicate label on repo1", 64 current: RepoLabels{ 65 "repo1": { 66 {Name: "lab1", Description: "Test Label 1", Color: "deadbe"}, 67 {Name: "lab1", Description: "Test Label 1", Color: "befade"}, 68 }, 69 }, 70 expectedError: true, 71 }, 72 { 73 name: "Non unique label on repo1 when downcased", 74 current: RepoLabels{ 75 "repo1": { 76 {Name: "lab1", Description: "Test Label 1", Color: "deadbe"}, 77 {Name: "LAB1", Description: "Test Label 2", Color: "deadbe"}, 78 }, 79 }, 80 expectedError: true, 81 }, 82 { 83 name: "Non unique label but on different repos - allowed", 84 current: RepoLabels{ 85 "repo1": {{Name: "lab1", Description: "Test Label 1", Color: "deadbe"}}, 86 "repo2": {{Name: "lab1", Description: "Test Label 1", Color: "deadbe"}}, 87 }, 88 }, 89 { 90 name: "Repo has exactly all wanted labels", 91 config: Configuration{Labels: []Label{ 92 {Name: "lab1", Description: "Test Label 1", Color: "deadbe"}, 93 }}, 94 current: RepoLabels{ 95 "repo1": { 96 {Name: "lab1", Description: "Test Label 1", Color: "deadbe"}, 97 }, 98 }, 99 }, 100 { 101 name: "Repo has label with wrong color", 102 config: Configuration{Labels: []Label{ 103 {Name: "lab1", Description: "Test Label 1", Color: "deadbe"}, 104 }}, 105 current: RepoLabels{ 106 "repo1": { 107 {Name: "lab1", Description: "Test Label 1", Color: "bebeef"}, 108 }, 109 }, 110 expectedUpdates: RepoUpdates{ 111 "repo1": { 112 {Why: "change", Current: &Label{Name: "lab1", Description: "Test Label 1", Color: "deadbe"}, Wanted: &Label{Name: "lab1", Description: "Test Label 1", Color: "deadbe"}}, 113 }, 114 }, 115 }, 116 { 117 name: "Repo has label with wrong description", 118 config: Configuration{Labels: []Label{ 119 {Name: "lab1", Description: "Test Label 1", Color: "deadbe"}, 120 }}, 121 current: RepoLabels{ 122 "repo1": { 123 {Name: "lab1", Description: "Test Label 5", Color: "deadbe"}, 124 }, 125 }, 126 expectedUpdates: RepoUpdates{ 127 "repo1": { 128 {Why: "change", Current: &Label{Name: "lab1", Description: "Test Label 1", Color: "deadbe"}, Wanted: &Label{Name: "lab1", Description: "Test Label 1", Color: "deadbe"}}, 129 }, 130 }, 131 }, 132 { 133 name: "Repo has label with wrong name (different case)", 134 config: Configuration{Labels: []Label{ 135 {Name: "Lab1", Description: "Test Label 1", Color: "deadbe"}, 136 }}, 137 current: RepoLabels{ 138 "repo1": { 139 {Name: "laB1", Description: "Test Label 1", Color: "deadbe"}, 140 }, 141 }, 142 expectedUpdates: RepoUpdates{ 143 "repo1": { 144 {Why: "rename", Wanted: &Label{Name: "Lab1", Description: "Test Label 1", Color: "deadbe"}, Current: &Label{Name: "laB1", Description: "Test Label 1", Color: "deadbe"}}, 145 }, 146 }, 147 }, 148 { 149 name: "old name", 150 config: Configuration{Labels: []Label{ 151 {Name: "current", Description: "Test Label 1", Color: "blue", Previously: []Label{{Name: "old", Description: "Test Label 1", Color: "gray"}}}, 152 }}, 153 current: RepoLabels{ 154 "no current": {{Name: "old", Description: "Test Label 1", Color: "much gray"}}, 155 "has current": { 156 {Name: "old", Description: "Test Label 1", Color: "gray"}, 157 {Name: "current", Description: "Test Label 1", Color: "blue"}, 158 }, 159 }, 160 expectedUpdates: RepoUpdates{ 161 "no current": { 162 {Why: "rename", Current: &Label{Name: "old", Description: "Test Label 1", Color: "much gray"}, Wanted: &Label{Name: "current", Description: "Test Label 1", Color: "blue"}}, 163 }, 164 "has current": { 165 {Why: "migrate", Current: &Label{Name: "old", Description: "Test Label 1", Color: "gray"}, Wanted: &Label{Name: "current", Description: "Test Label 1", Color: "blue"}}, 166 }, 167 }, 168 }, 169 { 170 name: "Repo is missing a label", 171 config: Configuration{Labels: []Label{ 172 {Name: "Lab1", Description: "Test Label 1", Color: "deadbe"}, 173 }}, 174 current: RepoLabels{ 175 "repo1": {}, 176 }, 177 expectedUpdates: RepoUpdates{ 178 "repo1": { 179 {Why: "missing", Wanted: &Label{Name: "Lab1", Description: "Test Label 1", Color: "deadbe"}}, 180 }, 181 }, 182 }, 183 { 184 name: "Repo is missing multiple labels, and expected labels order is changed", 185 config: Configuration{Labels: []Label{ 186 {Name: "Lab1", Description: "Test Label 1", Color: "deadbe"}, 187 {Name: "Lab2", Description: "Test Label 2", Color: "000000"}, 188 {Name: "Lab3", Description: "Test Label 3", Color: "ffffff"}, 189 }}, 190 current: RepoLabels{ 191 "repo1": {}, 192 "repo2": {{Name: "Lab2", Description: "Test Label 2", Color: "000000"}}, 193 }, 194 expectedUpdates: RepoUpdates{ 195 "repo2": { 196 {Why: "missing", Wanted: &Label{Name: "Lab3", Description: "Test Label 3", Color: "ffffff"}}, 197 {Why: "missing", Wanted: &Label{Name: "Lab1", Description: "Test Label 1", Color: "deadbe"}}, 198 }, 199 "repo1": { 200 {Why: "missing", Wanted: &Label{Color: "000000", Name: "Lab2", Description: "Test Label 2"}}, 201 {Why: "missing", Wanted: &Label{Name: "Lab3", Description: "Test Label 3", Color: "ffffff"}}, 202 {Why: "missing", Wanted: &Label{Name: "Lab1", Description: "Test Label 1", Color: "deadbe"}}, 203 }, 204 }, 205 }, 206 { 207 name: "Multiple repos complex case", 208 config: Configuration{Labels: []Label{ 209 {Name: "priority/P0", Description: "P0 Priority", Color: "ff0000"}, 210 {Name: "lgtm", Description: "LGTM", Color: "00ff00"}, 211 }}, 212 current: RepoLabels{ 213 "repo1": { 214 {Name: "Priority/P0", Description: "P0 Priority", Color: "ee3333"}, 215 {Name: "LGTM", Description: "LGTM", Color: "00ff00"}, 216 }, 217 "repo2": { 218 {Name: "priority/P0", Description: "P0 Priority", Color: "ee3333"}, 219 {Name: "lgtm", Description: "LGTM", Color: "00ff00"}, 220 }, 221 "repo3": { 222 {Name: "PRIORITY/P0", Description: "P0 Priority", Color: "ff0000"}, 223 {Name: "lgtm", Description: "LGTM", Color: "0000ff"}, 224 }, 225 "repo4": { 226 {Name: "priority/P0", Description: "P0 Priority", Color: "ff0000"}, 227 }, 228 "repo5": { 229 {Name: "lgtm", Description: "LGTM", Color: "00ff00"}, 230 }, 231 }, 232 expectedUpdates: RepoUpdates{ 233 "repo1": { 234 {Why: "rename", Wanted: &Label{Name: "priority/P0", Description: "P0 Priority", Color: "ff0000"}, Current: &Label{Name: "Priority/P0", Description: "P0 Priority", Color: "ee3333"}}, 235 {Why: "rename", Wanted: &Label{Name: "lgtm", Description: "LGTM", Color: "00ff00"}, Current: &Label{Name: "LGTM", Description: "LGTM", Color: "00ff00"}}, 236 }, 237 "repo2": { 238 {Why: "change", Current: &Label{Name: "priority/P0", Description: "P0 Priority", Color: "ff0000"}, Wanted: &Label{Name: "priority/P0", Description: "P0 Priority", Color: "ff0000"}}, 239 }, 240 "repo3": { 241 {Why: "rename", Wanted: &Label{Name: "priority/P0", Description: "P0 Priority", Color: "ff0000"}, Current: &Label{Name: "PRIORITY/P0", Description: "P0 Priority", Color: "ff0000"}}, 242 {Why: "change", Current: &Label{Name: "lgtm", Description: "LGTM", Color: "00ff00"}, Wanted: &Label{Name: "lgtm", Description: "LGTM", Color: "00ff00"}}, 243 }, 244 "repo4": { 245 {Why: "missing", Wanted: &Label{Name: "lgtm", Description: "LGTM", Color: "00ff00"}}, 246 }, 247 "repo5": { 248 {Why: "missing", Wanted: &Label{Name: "priority/P0", Description: "P0 Priority", Color: "ff0000"}}, 249 }, 250 }, 251 }, 252 } 253 254 // Do tests 255 for _, tc := range testcases { 256 actualUpdates, err := syncLabels(tc.config, tc.current) 257 if err == nil && tc.expectedError { 258 t.Errorf("%s: failed to raise error", tc.name) 259 } else if err != nil && !tc.expectedError { 260 t.Errorf("%s: unexpected error: %v", tc.name, err) 261 } else if !tc.expectedError && !equalUpdates(actualUpdates, tc.expectedUpdates, t) { 262 t.Errorf("%s: expected updates:\n%+v\ngot:\n%+v", tc.name, tc.expectedUpdates, actualUpdates) 263 } 264 } 265 } 266 267 // This is needed to compare Update sets, two update sets are equal 268 // only if their maps have the same lists (but order can be different) 269 // Using standard `reflect.DeepEqual` for entire structures makes tests flaky 270 func equalUpdates(updates1, updates2 RepoUpdates, t *testing.T) bool { 271 if len(updates1) != len(updates2) { 272 t.Errorf("ERROR: expected and actual update sets have different repo sets") 273 return false 274 } 275 // Iterate per repository differences 276 for repo, list1 := range updates1 { 277 list2, ok := updates2[repo] 278 if !ok || len(list1) != len(list2) { 279 t.Errorf("ERROR: expected and actual update lists for repo %s have different lengths", repo) 280 return false 281 } 282 items1 := make(map[string]bool) 283 for _, item := range list1 { 284 j, err := json.Marshal(item) 285 if err != nil { 286 t.Errorf("ERROR: internal test error: unable to json.Marshal test item: %+v", item) 287 return false 288 } 289 items1[string(j)] = true 290 } 291 items2 := make(map[string]bool) 292 for _, item := range list2 { 293 j, err := json.Marshal(item) 294 if err != nil { 295 t.Errorf("ERROR: internal test error: unable to json.Marshal test item: %+v", item) 296 return false 297 } 298 items2[string(j)] = true 299 } 300 // Iterate list of label differences 301 for key := range items1 { 302 _, ok := items2[key] 303 if !ok { 304 t.Errorf("ERROR: difference: repo: %s, key: %s not found", repo, key) 305 return false 306 } 307 } 308 } 309 return true 310 } 311 312 // Test loading YAML file (labels.yaml) 313 func TestLoadYAML(t *testing.T) { 314 d := time.Date(2017, 1, 1, 13, 0, 0, 0, time.UTC) 315 var testcases = []struct { 316 path string 317 expected Configuration 318 ok bool 319 errMsg string 320 }{ 321 { 322 path: "labels_example.yaml", 323 expected: Configuration{Labels: []Label{ 324 {Name: "lgtm", Description: "LGTM", Color: "green"}, 325 {Name: "priority/P0", Description: "P0 Priority", Color: "red", Previously: []Label{{Name: "P0", Description: "P0 Priority", Color: "blue"}}}, 326 {Name: "dead-label", Description: "Delete Me :)", DeleteAfter: &d}, 327 }}, 328 ok: true, 329 }, 330 { 331 path: "syntax_error_example.yaml", 332 expected: Configuration{}, 333 ok: false, 334 errMsg: "error converting", 335 }, 336 { 337 path: "no_such_file.yaml", 338 expected: Configuration{}, 339 ok: false, 340 errMsg: "no such file", 341 }, 342 } 343 for i, tc := range testcases { 344 actual, err := LoadConfig(tc.path) 345 errNil := (err == nil) 346 if errNil != tc.ok { 347 t.Errorf("TestLoadYAML: test case number %d, expected ok: %v, got %v (error=%v)", i+1, tc.ok, err == nil, err) 348 } 349 if !errNil && !strings.Contains(err.Error(), tc.errMsg) { 350 t.Errorf("TestLoadYAML: test case number %d, expected error '%v' to contain '%v'", i+1, err.Error(), tc.errMsg) 351 } 352 if errNil && !reflect.DeepEqual(*actual, tc.expected) { 353 t.Errorf("TestLoadYAML: test case number %d, expected labels %v, got %v", i+1, tc.expected, actual) 354 } 355 } 356 }