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  }