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  }