sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/ghhook/ghhook_test.go (about) 1 /* 2 Copyright 2020 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 ghhook 18 19 import ( 20 "errors" 21 "flag" 22 "fmt" 23 "reflect" 24 "testing" 25 26 "sigs.k8s.io/prow/pkg/flagutil" 27 "sigs.k8s.io/prow/pkg/github" 28 ) 29 30 func TestGetOptions(t *testing.T) { 31 defArgs := map[string][]string{ 32 "--hmac-path": {"/fake/hmac-file"}, 33 "--hook-url": {"https://not-a-url"}, 34 "--repo": {"fake-org/fake-repo"}, 35 "--github-token-path": {"./testdata/token"}, 36 } 37 cases := []struct { 38 name string 39 args map[string][]string 40 expected func(*Options) 41 err bool 42 }{ 43 { 44 name: "reject empty --hmac-path", 45 args: map[string][]string{ 46 "--hmac-path": nil, 47 }, 48 err: true, 49 }, 50 { 51 name: "reject empty --hook-url", 52 args: map[string][]string{ 53 "--hook-url": nil, 54 }, 55 err: true, 56 }, 57 { 58 name: "empty --repo", 59 args: map[string][]string{ 60 "--repo": nil, 61 }, 62 err: true, 63 }, 64 { 65 name: "multi repo", 66 args: map[string][]string{ 67 "--repo": {"org1", "org2/repo"}, 68 }, 69 expected: func(o *Options) { 70 o.Repos = flagutil.NewStrings() 71 o.Repos.Set("org1") 72 o.Repos.Set("org2/repo") 73 }, 74 }, 75 { 76 name: "full flags", 77 args: map[string][]string{ 78 "--event": {"this", "that"}, 79 "--confirm": {"true"}, 80 }, 81 expected: func(o *Options) { 82 o.Events.Set("this") 83 o.Events.Set("that") 84 o.Confirm = true 85 }, 86 }, 87 } 88 89 for _, tc := range cases { 90 t.Run(tc.name, func(t *testing.T) { 91 var args []string 92 for k, v := range defArgs { 93 if _, ok := tc.args[k]; !ok { 94 tc.args[k] = v 95 } 96 } 97 98 for k, v := range tc.args { 99 for _, arg := range v { 100 args = append(args, k+"="+arg) 101 } 102 } 103 104 expected := Options{ 105 HMACPath: "/fake/hmac-file", 106 HookURL: "https://not-a-url", 107 Events: flagutil.NewStrings(github.AllHookEvents...), 108 } 109 expected.Repos.Set("fake-org/fake-repo") 110 111 if tc.expected != nil { 112 tc.expected(&expected) 113 } 114 115 o, err := GetOptions(flag.NewFlagSet("fake-flags", flag.ExitOnError), args) 116 if o != nil { // TODO(fejta): github.GitHubOptions not unit testable 117 o.GitHubOptions = flagutil.GitHubOptions{} 118 expected.GitHubOptions = flagutil.GitHubOptions{} 119 expected.GitHubHookClient = nil 120 o.GitHubHookClient = nil 121 } 122 switch { 123 case err != nil: 124 if !tc.err { 125 t.Errorf("unexpected error %s: %v", args, err) 126 } 127 case tc.err: 128 t.Error("failed to receive an error") 129 case !reflect.DeepEqual(*o, expected): 130 t.Errorf("%#v != actual %#v", expected, o) 131 } 132 }) 133 } 134 } 135 136 func TestFindHook(t *testing.T) { 137 const goal = "http://random-url" 138 number := 7 139 cases := []struct { 140 name string 141 hooks []github.Hook 142 expected *int 143 }{ 144 { 145 name: "nil on no match", 146 hooks: []github.Hook{{}, {}}, 147 }, 148 { 149 name: "return matched id", 150 hooks: []github.Hook{{ 151 ID: number, 152 Config: github.HookConfig{ 153 URL: goal, 154 }, 155 }}, 156 expected: &number, 157 }, 158 } 159 160 for _, tc := range cases { 161 actual := findHook(tc.hooks, goal) 162 if !reflect.DeepEqual(actual, tc.expected) { 163 t.Errorf("%s: expected %v != actual %v", tc.name, tc.expected, actual) 164 } 165 } 166 } 167 168 func TestReconcileHook(t *testing.T) { 169 const goal = "http://goal-url" 170 const targetId = 1000 171 secret := "ingredient" 172 j := "json" 173 cases := []struct { 174 name string 175 org string 176 hooks []github.Hook 177 expectCreate bool 178 expectEdit bool 179 expectDelete bool 180 err bool 181 }{ 182 { 183 name: "fail on list error", 184 org: "list-error", 185 err: true, 186 }, 187 { 188 name: "fail on create error", 189 org: "create-error", 190 err: true, 191 }, 192 { 193 name: "fail on edit error", 194 org: "edit-error", 195 hooks: []github.Hook{ 196 { 197 Config: github.HookConfig{ 198 URL: goal, 199 }, 200 }, 201 }, 202 err: true, 203 }, 204 { 205 name: "fail on delete error", 206 org: "delete-error", 207 hooks: []github.Hook{ 208 { 209 Config: github.HookConfig{ 210 URL: goal, 211 }, 212 }, 213 }, 214 err: true, 215 }, 216 { 217 name: "create when empty", 218 expectCreate: true, 219 }, 220 { 221 name: "create when no match", 222 hooks: []github.Hook{ 223 { 224 ID: targetId + 6666, 225 Config: github.HookConfig{ 226 URL: "http://random-url", 227 }, 228 }, 229 }, 230 expectCreate: true, 231 }, 232 { 233 name: "edit exiting item", 234 hooks: []github.Hook{ 235 { 236 ID: targetId, 237 Config: github.HookConfig{ 238 URL: goal, 239 }, 240 }, 241 }, 242 expectEdit: true, 243 }, 244 { 245 name: "delete exiting item", 246 hooks: []github.Hook{ 247 { 248 ID: targetId, 249 Config: github.HookConfig{ 250 URL: goal, 251 }, 252 }, 253 }, 254 expectDelete: true, 255 }, 256 } 257 258 for _, tc := range cases { 259 var created, edited, deleted *github.HookRequest 260 ch := changer{ 261 lister: func(org string) ([]github.Hook, error) { 262 if org == "list-error" { 263 return nil, errors.New("inject list error") 264 } 265 return tc.hooks, nil 266 }, 267 editor: func(org string, id int, req github.HookRequest) error { 268 if org == "edit-error" { 269 return errors.New("inject edit error") 270 } 271 if id != targetId { 272 return fmt.Errorf("id %d != expected %d", id, targetId) 273 } 274 edited = &req 275 return nil 276 }, 277 creator: func(org string, req github.HookRequest) (int, error) { 278 if org == "create-error" { 279 return 0, errors.New("inject create error") 280 } 281 if created != nil { 282 return 0, errors.New("already created") 283 } 284 created = &req 285 return targetId, nil 286 }, 287 deletor: func(org string, id int, req github.HookRequest) error { 288 if org == "delete-error" { 289 return errors.New("inject delete error") 290 } 291 if deleted != nil { 292 return errors.New("already deleted") 293 } 294 deleted = &req 295 return nil 296 }, 297 } 298 req := github.HookRequest{ 299 Name: "web", 300 Events: []string{"random"}, 301 Config: &github.HookConfig{ 302 URL: goal, 303 ContentType: &j, 304 Secret: &secret, 305 }, 306 } 307 308 err := reconcileHook(ch, tc.org, req, &Options{ShouldDelete: tc.expectDelete}) 309 switch { 310 case err != nil: 311 if !tc.err { 312 t.Errorf("unexpected error: %v", err) 313 } 314 case tc.err: 315 t.Error("failed to receive an error") 316 case tc.expectCreate && created == nil: 317 t.Error("failed to create") 318 case tc.expectEdit && edited == nil: 319 t.Error("failed to edit") 320 case tc.expectDelete && deleted == nil: 321 t.Error("failed to delete") 322 case created != nil && !reflect.DeepEqual(req, *created): 323 t.Errorf("created %#v != expected %#v", *created, req) 324 case edited != nil && !reflect.DeepEqual(req, *edited): 325 t.Errorf("edited %#v != expected %#v", *edited, req) 326 case deleted != nil && !reflect.DeepEqual(req, *deleted): 327 t.Errorf("deleted %#v != expected %#v", *deleted, req) 328 } 329 } 330 }