github.com/abhinav/git-fu@v0.6.1-0.20171029234004-54218d68c11b/git/rebase_test.go (about) 1 package git 2 3 import ( 4 "errors" 5 "testing" 6 7 "github.com/abhinav/git-pr/gateway" 8 "github.com/abhinav/git-pr/gateway/gatewaytest" 9 10 "github.com/golang/mock/gomock" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 ) 14 15 func TestBulkRebaser(t *testing.T) { 16 type deletion struct { 17 Checkout string 18 Delete string 19 } 20 21 type rebaseCall struct { 22 From string 23 To string 24 25 // Base() and Err() expected on the returned RebaseHandle. 26 WantBase string 27 WantErr string 28 } 29 30 type ontoCall struct { 31 Onto string 32 Rebases []rebaseCall 33 } 34 35 tests := []struct { 36 Desc string 37 Do []ontoCall 38 SetupGateway func(*gatewaytest.MockGit) 39 40 // ExpectRebases is a convenience option for setting up Rebase 41 // requests on the gateway that never fail. This may be omitted or 42 // partial if the test has more complex rebase setup in SetupGateway. 43 ExpectRebases []*gateway.RebaseRequest 44 45 // ExpectDeletions is a convenience option for setting up 46 // Checkout(parent), Delete(branch) in-order without any errors. This 47 // may be omitted or partial if the test has a more complex deletion 48 // setup in SetupGateway. 49 ExpectDeletions []deletion 50 51 WantErrors []string 52 }{ 53 { 54 Desc: "single rebase", 55 Do: []ontoCall{ 56 { 57 Onto: "master", 58 Rebases: []rebaseCall{ 59 { 60 From: "feature-1", 61 To: "feature-2", 62 WantBase: "git-pr/rebase/feature-2", 63 }, 64 }, 65 }, 66 }, 67 ExpectRebases: []*gateway.RebaseRequest{ 68 { 69 Onto: "master", 70 From: "feature-1", 71 Branch: "git-pr/rebase/feature-2", 72 }, 73 }, 74 ExpectDeletions: []deletion{ 75 {Checkout: "master", Delete: "git-pr/rebase/feature-2"}, 76 }, 77 }, 78 { 79 Desc: "rebase stack", 80 Do: []ontoCall{ 81 { 82 Onto: "origin/dev", 83 Rebases: []rebaseCall{ 84 { 85 From: "dev", 86 To: "feature-1", 87 WantBase: "git-pr/rebase/feature-1", 88 }, 89 { 90 From: "feature-1", 91 To: "feature-2", 92 WantBase: "git-pr/rebase/feature-2", 93 }, 94 { 95 From: "feature-2", 96 To: "feature-3", 97 WantBase: "git-pr/rebase/feature-3", 98 }, 99 { 100 From: "feature-3", 101 To: "feature-4", 102 WantBase: "git-pr/rebase/feature-4", 103 }, 104 }, 105 }, 106 }, 107 ExpectRebases: []*gateway.RebaseRequest{ 108 { 109 Onto: "origin/dev", 110 From: "dev", 111 Branch: "git-pr/rebase/feature-1", 112 }, 113 { 114 Onto: "git-pr/rebase/feature-1", 115 From: "feature-1", 116 Branch: "git-pr/rebase/feature-2", 117 }, 118 { 119 Onto: "git-pr/rebase/feature-2", 120 From: "feature-2", 121 Branch: "git-pr/rebase/feature-3", 122 }, 123 { 124 Onto: "git-pr/rebase/feature-3", 125 From: "feature-3", 126 Branch: "git-pr/rebase/feature-4", 127 }, 128 }, 129 ExpectDeletions: []deletion{ 130 { 131 Checkout: "git-pr/rebase/feature-3", 132 Delete: "git-pr/rebase/feature-4", 133 }, 134 { 135 Checkout: "git-pr/rebase/feature-2", 136 Delete: "git-pr/rebase/feature-3", 137 }, 138 { 139 Checkout: "git-pr/rebase/feature-1", 140 Delete: "git-pr/rebase/feature-2", 141 }, 142 { 143 Checkout: "origin/dev", 144 Delete: "git-pr/rebase/feature-1", 145 }, 146 }, 147 }, 148 { 149 Desc: "rebase failure", 150 Do: []ontoCall{ 151 { 152 Onto: "origin/master", 153 Rebases: []rebaseCall{ 154 { 155 From: "master", 156 To: "feature-1", 157 WantErr: "great sadness", 158 }, 159 { 160 From: "feature-1", 161 To: "feature-2", 162 WantErr: "great sadness", 163 }, 164 { 165 From: "feature-2", 166 To: "feature-3", 167 WantErr: "great sadness", 168 }, 169 }, 170 }, 171 { 172 Onto: "origin/master", 173 Rebases: []rebaseCall{ 174 { 175 From: "feature-3", 176 To: "feature-4", 177 WantBase: "git-pr/rebase/feature-4", 178 }, 179 }, 180 }, 181 }, 182 ExpectRebases: []*gateway.RebaseRequest{ 183 { 184 Onto: "origin/master", 185 From: "feature-3", 186 Branch: "git-pr/rebase/feature-4", 187 }, 188 }, 189 SetupGateway: func(git *gatewaytest.MockGit) { 190 git.EXPECT(). 191 Rebase(&gateway.RebaseRequest{ 192 Onto: "origin/master", 193 From: "master", 194 Branch: "git-pr/rebase/feature-1", 195 }). 196 Return(errors.New("great sadness")) 197 }, 198 ExpectDeletions: []deletion{ 199 {Checkout: "origin/master", Delete: "git-pr/rebase/feature-4"}, 200 {Checkout: "origin/master", Delete: "git-pr/rebase/feature-1"}, 201 }, 202 WantErrors: []string{"great sadness"}, 203 }, 204 { 205 Desc: "multiple rebase failures", 206 Do: []ontoCall{ 207 { 208 Onto: "origin/master", 209 Rebases: []rebaseCall{ 210 { 211 From: "master", 212 To: "feature-1", 213 WantErr: "feature 1 failed", 214 }, 215 }, 216 }, 217 { 218 Onto: "origin/master", 219 Rebases: []rebaseCall{ 220 { 221 From: "feature-1", 222 To: "feature-2", 223 WantErr: "feature 2 failed", 224 }, 225 }, 226 }, 227 { 228 Onto: "origin/master", 229 Rebases: []rebaseCall{ 230 { 231 From: "feature-2", 232 To: "feature-3", 233 WantErr: "feature 3 failed", 234 }, 235 }, 236 }, 237 }, 238 SetupGateway: func(git *gatewaytest.MockGit) { 239 git.EXPECT(). 240 Rebase(&gateway.RebaseRequest{ 241 Onto: "origin/master", 242 From: "master", 243 Branch: "git-pr/rebase/feature-1", 244 }). 245 Return(errors.New("feature 1 failed")) 246 247 git.EXPECT(). 248 Rebase(&gateway.RebaseRequest{ 249 Onto: "origin/master", 250 From: "feature-1", 251 Branch: "git-pr/rebase/feature-2", 252 }). 253 Return(errors.New("feature 2 failed")) 254 255 git.EXPECT(). 256 Rebase(&gateway.RebaseRequest{ 257 Onto: "origin/master", 258 From: "feature-2", 259 Branch: "git-pr/rebase/feature-3", 260 }). 261 Return(errors.New("feature 3 failed")) 262 }, 263 ExpectDeletions: []deletion{ 264 {Checkout: "origin/master", Delete: "git-pr/rebase/feature-3"}, 265 {Checkout: "origin/master", Delete: "git-pr/rebase/feature-2"}, 266 {Checkout: "origin/master", Delete: "git-pr/rebase/feature-1"}, 267 }, 268 WantErrors: []string{ 269 "feature 1 failed", 270 "feature 2 failed", 271 "feature 3 failed", 272 }, 273 }, 274 } 275 276 for _, tt := range tests { 277 t.Run(tt.Desc, func(t *testing.T) { 278 mockCtrl := gomock.NewController(t) 279 defer mockCtrl.Finish() 280 281 gw := gatewaytest.NewMockGit(mockCtrl) 282 if tt.SetupGateway != nil { 283 tt.SetupGateway(gw) 284 } 285 286 for _, req := range tt.ExpectRebases { 287 gw.EXPECT().Rebase(req).Return(nil) 288 } 289 290 var deletions []*gomock.Call 291 for _, x := range tt.ExpectDeletions { 292 deletions = append(deletions, 293 gw.EXPECT().Checkout(x.Checkout).Return(nil), 294 gw.EXPECT().DeleteBranch(x.Delete).Return(nil), 295 ) 296 } 297 gomock.InOrder(deletions...) 298 299 rebaser := NewBulkRebaser(gw) 300 rebaser.checkoutUniqueBranch = checkoutUniqueBranchAlwaysSuccessful 301 302 defer func() { 303 assert.NoError(t, rebaser.Cleanup(), 304 "cleanup failed") 305 }() 306 307 for _, ontoCall := range tt.Do { 308 h := rebaser.Onto(ontoCall.Onto) 309 for _, rebaseCall := range ontoCall.Rebases { 310 h = h.Rebase(rebaseCall.From, rebaseCall.To) 311 assert.Equal(t, rebaseCall.WantBase, h.Base()) 312 if rebaseCall.WantErr != "" { 313 err := h.Err() 314 if assert.Error(t, err) { 315 assert.Contains(t, err.Error(), rebaseCall.WantErr) 316 } 317 } 318 } 319 } 320 321 if len(tt.WantErrors) > 0 { 322 err := rebaser.Err() 323 require.Error(t, err, "expected failure") 324 for _, msg := range tt.WantErrors { 325 assert.Contains(t, err.Error(), msg) 326 } 327 return 328 } 329 330 require.NoError(t, rebaser.Err(), "expected success") 331 }) 332 } 333 } 334 335 func checkoutUniqueBranchAlwaysSuccessful( 336 _ gateway.Git, prefix string, _ string, 337 ) (string, error) { 338 return prefix, nil 339 }