code.gitea.io/gitea@v1.21.7/services/migrations/gitlab_test.go (about)

     1  // Copyright 2019 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package migrations
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"os"
    12  	"strconv"
    13  	"testing"
    14  	"time"
    15  
    16  	"code.gitea.io/gitea/modules/json"
    17  	base "code.gitea.io/gitea/modules/migration"
    18  
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/xanzy/go-gitlab"
    21  )
    22  
    23  func TestGitlabDownloadRepo(t *testing.T) {
    24  	// Skip tests if Gitlab token is not found
    25  	gitlabPersonalAccessToken := os.Getenv("GITLAB_READ_TOKEN")
    26  	if gitlabPersonalAccessToken == "" {
    27  		t.Skip("skipped test because GITLAB_READ_TOKEN was not in the environment")
    28  	}
    29  
    30  	resp, err := http.Get("https://gitlab.com/gitea/test_repo")
    31  	if err != nil || resp.StatusCode != http.StatusOK {
    32  		t.Skipf("Can't access test repo, skipping %s", t.Name())
    33  	}
    34  
    35  	downloader, err := NewGitlabDownloader(context.Background(), "https://gitlab.com", "gitea/test_repo", "", "", gitlabPersonalAccessToken)
    36  	if err != nil {
    37  		t.Fatalf("NewGitlabDownloader is nil: %v", err)
    38  	}
    39  	repo, err := downloader.GetRepoInfo()
    40  	assert.NoError(t, err)
    41  	// Repo Owner is blank in Gitlab Group repos
    42  	assertRepositoryEqual(t, &base.Repository{
    43  		Name:          "test_repo",
    44  		Owner:         "",
    45  		Description:   "Test repository for testing migration from gitlab to gitea",
    46  		CloneURL:      "https://gitlab.com/gitea/test_repo.git",
    47  		OriginalURL:   "https://gitlab.com/gitea/test_repo",
    48  		DefaultBranch: "master",
    49  	}, repo)
    50  
    51  	topics, err := downloader.GetTopics()
    52  	assert.NoError(t, err)
    53  	assert.True(t, len(topics) == 2)
    54  	assert.EqualValues(t, []string{"migration", "test"}, topics)
    55  
    56  	milestones, err := downloader.GetMilestones()
    57  	assert.NoError(t, err)
    58  	assertMilestonesEqual(t, []*base.Milestone{
    59  		{
    60  			Title:   "1.1.0",
    61  			Created: time.Date(2019, 11, 28, 8, 42, 44, 575000000, time.UTC),
    62  			Updated: timePtr(time.Date(2019, 11, 28, 8, 42, 44, 575000000, time.UTC)),
    63  			State:   "active",
    64  		},
    65  		{
    66  			Title:   "1.0.0",
    67  			Created: time.Date(2019, 11, 28, 8, 42, 30, 301000000, time.UTC),
    68  			Updated: timePtr(time.Date(2019, 11, 28, 15, 57, 52, 401000000, time.UTC)),
    69  			Closed:  timePtr(time.Date(2019, 11, 28, 15, 57, 52, 401000000, time.UTC)),
    70  			State:   "closed",
    71  		},
    72  	}, milestones)
    73  
    74  	labels, err := downloader.GetLabels()
    75  	assert.NoError(t, err)
    76  	assertLabelsEqual(t, []*base.Label{
    77  		{
    78  			Name:  "bug",
    79  			Color: "d9534f",
    80  		},
    81  		{
    82  			Name:  "confirmed",
    83  			Color: "d9534f",
    84  		},
    85  		{
    86  			Name:  "critical",
    87  			Color: "d9534f",
    88  		},
    89  		{
    90  			Name:  "discussion",
    91  			Color: "428bca",
    92  		},
    93  		{
    94  			Name:  "documentation",
    95  			Color: "f0ad4e",
    96  		},
    97  		{
    98  			Name:  "duplicate",
    99  			Color: "7f8c8d",
   100  		},
   101  		{
   102  			Name:  "enhancement",
   103  			Color: "5cb85c",
   104  		},
   105  		{
   106  			Name:  "suggestion",
   107  			Color: "428bca",
   108  		},
   109  		{
   110  			Name:  "support",
   111  			Color: "f0ad4e",
   112  		},
   113  	}, labels)
   114  
   115  	releases, err := downloader.GetReleases()
   116  	assert.NoError(t, err)
   117  	assertReleasesEqual(t, []*base.Release{
   118  		{
   119  			TagName:         "v0.9.99",
   120  			TargetCommitish: "0720a3ec57c1f843568298117b874319e7deee75",
   121  			Name:            "First Release",
   122  			Body:            "A test release",
   123  			Created:         time.Date(2019, 11, 28, 9, 9, 48, 840000000, time.UTC),
   124  			PublisherID:     1241334,
   125  			PublisherName:   "lafriks",
   126  		},
   127  	}, releases)
   128  
   129  	issues, isEnd, err := downloader.GetIssues(1, 2)
   130  	assert.NoError(t, err)
   131  	assert.False(t, isEnd)
   132  
   133  	assertIssuesEqual(t, []*base.Issue{
   134  		{
   135  			Number:     1,
   136  			Title:      "Please add an animated gif icon to the merge button",
   137  			Content:    "I just want the merge button to hurt my eyes a little. :stuck_out_tongue_closed_eyes:",
   138  			Milestone:  "1.0.0",
   139  			PosterID:   1241334,
   140  			PosterName: "lafriks",
   141  			State:      "closed",
   142  			Created:    time.Date(2019, 11, 28, 8, 43, 35, 459000000, time.UTC),
   143  			Updated:    time.Date(2019, 11, 28, 8, 46, 23, 304000000, time.UTC),
   144  			Labels: []*base.Label{
   145  				{
   146  					Name: "bug",
   147  				},
   148  				{
   149  					Name: "discussion",
   150  				},
   151  			},
   152  			Reactions: []*base.Reaction{
   153  				{
   154  					UserID:   1241334,
   155  					UserName: "lafriks",
   156  					Content:  "thumbsup",
   157  				},
   158  				{
   159  					UserID:   1241334,
   160  					UserName: "lafriks",
   161  					Content:  "open_mouth",
   162  				},
   163  			},
   164  			Closed: timePtr(time.Date(2019, 11, 28, 8, 46, 23, 275000000, time.UTC)),
   165  		},
   166  		{
   167  			Number:     2,
   168  			Title:      "Test issue",
   169  			Content:    "This is test issue 2, do not touch!",
   170  			Milestone:  "1.1.0",
   171  			PosterID:   1241334,
   172  			PosterName: "lafriks",
   173  			State:      "closed",
   174  			Created:    time.Date(2019, 11, 28, 8, 44, 46, 277000000, time.UTC),
   175  			Updated:    time.Date(2019, 11, 28, 8, 45, 44, 987000000, time.UTC),
   176  			Labels: []*base.Label{
   177  				{
   178  					Name: "duplicate",
   179  				},
   180  			},
   181  			Reactions: []*base.Reaction{
   182  				{
   183  					UserID:   1241334,
   184  					UserName: "lafriks",
   185  					Content:  "thumbsup",
   186  				},
   187  				{
   188  					UserID:   1241334,
   189  					UserName: "lafriks",
   190  					Content:  "thumbsdown",
   191  				},
   192  				{
   193  					UserID:   1241334,
   194  					UserName: "lafriks",
   195  					Content:  "laughing",
   196  				},
   197  				{
   198  					UserID:   1241334,
   199  					UserName: "lafriks",
   200  					Content:  "tada",
   201  				},
   202  				{
   203  					UserID:   1241334,
   204  					UserName: "lafriks",
   205  					Content:  "confused",
   206  				},
   207  				{
   208  					UserID:   1241334,
   209  					UserName: "lafriks",
   210  					Content:  "hearts",
   211  				},
   212  			},
   213  			Closed: timePtr(time.Date(2019, 11, 28, 8, 45, 44, 959000000, time.UTC)),
   214  		},
   215  	}, issues)
   216  
   217  	comments, _, err := downloader.GetComments(&base.Issue{
   218  		Number:       2,
   219  		ForeignIndex: 2,
   220  		Context:      gitlabIssueContext{IsMergeRequest: false},
   221  	})
   222  	assert.NoError(t, err)
   223  	assertCommentsEqual(t, []*base.Comment{
   224  		{
   225  			IssueIndex: 2,
   226  			PosterID:   1241334,
   227  			PosterName: "lafriks",
   228  			Created:    time.Date(2019, 11, 28, 8, 44, 52, 501000000, time.UTC),
   229  			Content:    "This is a comment",
   230  			Reactions:  nil,
   231  		},
   232  		{
   233  			IssueIndex: 2,
   234  			PosterID:   1241334,
   235  			PosterName: "lafriks",
   236  			Created:    time.Date(2019, 11, 28, 8, 45, 2, 329000000, time.UTC),
   237  			Content:    "changed milestone to %2",
   238  			Reactions:  nil,
   239  		},
   240  		{
   241  			IssueIndex: 2,
   242  			PosterID:   1241334,
   243  			PosterName: "lafriks",
   244  			Created:    time.Date(2019, 11, 28, 8, 45, 45, 7000000, time.UTC),
   245  			Content:    "closed",
   246  			Reactions:  nil,
   247  		},
   248  		{
   249  			IssueIndex: 2,
   250  			PosterID:   1241334,
   251  			PosterName: "lafriks",
   252  			Created:    time.Date(2019, 11, 28, 8, 45, 53, 501000000, time.UTC),
   253  			Content:    "A second comment",
   254  			Reactions:  nil,
   255  		},
   256  	}, comments)
   257  
   258  	prs, _, err := downloader.GetPullRequests(1, 1)
   259  	assert.NoError(t, err)
   260  	assertPullRequestsEqual(t, []*base.PullRequest{
   261  		{
   262  			Number:     4,
   263  			Title:      "Test branch",
   264  			Content:    "do not merge this PR",
   265  			Milestone:  "1.0.0",
   266  			PosterID:   1241334,
   267  			PosterName: "lafriks",
   268  			State:      "opened",
   269  			Created:    time.Date(2019, 11, 28, 15, 56, 54, 104000000, time.UTC),
   270  			Labels: []*base.Label{
   271  				{
   272  					Name: "bug",
   273  				},
   274  			},
   275  			Reactions: []*base.Reaction{{
   276  				UserID:   4575606,
   277  				UserName: "real6543",
   278  				Content:  "thumbsup",
   279  			}, {
   280  				UserID:   4575606,
   281  				UserName: "real6543",
   282  				Content:  "tada",
   283  			}},
   284  			PatchURL: "https://gitlab.com/gitea/test_repo/-/merge_requests/2.patch",
   285  			Head: base.PullRequestBranch{
   286  				Ref:       "feat/test",
   287  				CloneURL:  "https://gitlab.com/gitea/test_repo/-/merge_requests/2",
   288  				SHA:       "9f733b96b98a4175276edf6a2e1231489c3bdd23",
   289  				RepoName:  "test_repo",
   290  				OwnerName: "lafriks",
   291  			},
   292  			Base: base.PullRequestBranch{
   293  				Ref:       "master",
   294  				SHA:       "",
   295  				OwnerName: "lafriks",
   296  				RepoName:  "test_repo",
   297  			},
   298  			Closed:         nil,
   299  			Merged:         false,
   300  			MergedTime:     nil,
   301  			MergeCommitSHA: "",
   302  			ForeignIndex:   2,
   303  			Context:        gitlabIssueContext{IsMergeRequest: true},
   304  		},
   305  	}, prs)
   306  
   307  	rvs, err := downloader.GetReviews(&base.PullRequest{Number: 1, ForeignIndex: 1})
   308  	assert.NoError(t, err)
   309  	assertReviewsEqual(t, []*base.Review{
   310  		{
   311  			IssueIndex:   1,
   312  			ReviewerID:   4102996,
   313  			ReviewerName: "zeripath",
   314  			CreatedAt:    time.Date(2019, 11, 28, 16, 2, 8, 377000000, time.UTC),
   315  			State:        "APPROVED",
   316  		},
   317  		{
   318  			IssueIndex:   1,
   319  			ReviewerID:   527793,
   320  			ReviewerName: "axifive",
   321  			CreatedAt:    time.Date(2019, 11, 28, 16, 2, 8, 377000000, time.UTC),
   322  			State:        "APPROVED",
   323  		},
   324  	}, rvs)
   325  
   326  	rvs, err = downloader.GetReviews(&base.PullRequest{Number: 2, ForeignIndex: 2})
   327  	assert.NoError(t, err)
   328  	assertReviewsEqual(t, []*base.Review{
   329  		{
   330  			IssueIndex:   2,
   331  			ReviewerID:   4575606,
   332  			ReviewerName: "real6543",
   333  			CreatedAt:    time.Date(2020, 4, 19, 19, 24, 21, 108000000, time.UTC),
   334  			State:        "APPROVED",
   335  		},
   336  	}, rvs)
   337  }
   338  
   339  func gitlabClientMockSetup(t *testing.T) (*http.ServeMux, *httptest.Server, *gitlab.Client) {
   340  	// mux is the HTTP request multiplexer used with the test server.
   341  	mux := http.NewServeMux()
   342  
   343  	// server is a test HTTP server used to provide mock API responses.
   344  	server := httptest.NewServer(mux)
   345  
   346  	// client is the Gitlab client being tested.
   347  	client, err := gitlab.NewClient("", gitlab.WithBaseURL(server.URL))
   348  	if err != nil {
   349  		server.Close()
   350  		t.Fatalf("Failed to create client: %v", err)
   351  	}
   352  
   353  	return mux, server, client
   354  }
   355  
   356  func gitlabClientMockTeardown(server *httptest.Server) {
   357  	server.Close()
   358  }
   359  
   360  type reviewTestCase struct {
   361  	repoID, prID, reviewerID int
   362  	reviewerName             string
   363  	createdAt, updatedAt     *time.Time
   364  	expectedCreatedAt        time.Time
   365  }
   366  
   367  func convertTestCase(t reviewTestCase) (func(w http.ResponseWriter, r *http.Request), base.Review) {
   368  	var updatedAtField string
   369  	if t.updatedAt == nil {
   370  		updatedAtField = ""
   371  	} else {
   372  		updatedAtField = `"updated_at": "` + t.updatedAt.Format(time.RFC3339) + `",`
   373  	}
   374  
   375  	var createdAtField string
   376  	if t.createdAt == nil {
   377  		createdAtField = ""
   378  	} else {
   379  		createdAtField = `"created_at": "` + t.createdAt.Format(time.RFC3339) + `",`
   380  	}
   381  
   382  	handler := func(w http.ResponseWriter, r *http.Request) {
   383  		fmt.Fprint(w, `
   384  {
   385    "id": 5,
   386    "iid": `+strconv.Itoa(t.prID)+`,
   387    "project_id": `+strconv.Itoa(t.repoID)+`,
   388    "title": "Approvals API",
   389    "description": "Test",
   390    "state": "opened",
   391    `+createdAtField+`
   392    `+updatedAtField+`
   393    "merge_status": "cannot_be_merged",
   394    "approvals_required": 2,
   395    "approvals_left": 1,
   396    "approved_by": [
   397      {
   398        "user": {
   399          "name": "Administrator",
   400          "username": "`+t.reviewerName+`",
   401          "id": `+strconv.Itoa(t.reviewerID)+`,
   402          "state": "active",
   403          "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
   404          "web_url": "http://localhost:3000/root"
   405        }
   406      }
   407    ]
   408  }`)
   409  	}
   410  	review := base.Review{
   411  		IssueIndex:   int64(t.prID),
   412  		ReviewerID:   int64(t.reviewerID),
   413  		ReviewerName: t.reviewerName,
   414  		CreatedAt:    t.expectedCreatedAt,
   415  		State:        "APPROVED",
   416  	}
   417  
   418  	return handler, review
   419  }
   420  
   421  func TestGitlabGetReviews(t *testing.T) {
   422  	mux, server, client := gitlabClientMockSetup(t)
   423  	defer gitlabClientMockTeardown(server)
   424  
   425  	repoID := 1324
   426  
   427  	downloader := &GitlabDownloader{
   428  		ctx:    context.Background(),
   429  		client: client,
   430  		repoID: repoID,
   431  	}
   432  
   433  	createdAt := time.Date(2020, 4, 19, 19, 24, 21, 0, time.UTC)
   434  
   435  	for _, testCase := range []reviewTestCase{
   436  		{
   437  			repoID:            repoID,
   438  			prID:              1,
   439  			reviewerID:        801,
   440  			reviewerName:      "someone1",
   441  			createdAt:         nil,
   442  			updatedAt:         &createdAt,
   443  			expectedCreatedAt: createdAt,
   444  		},
   445  		{
   446  			repoID:            repoID,
   447  			prID:              2,
   448  			reviewerID:        802,
   449  			reviewerName:      "someone2",
   450  			createdAt:         &createdAt,
   451  			updatedAt:         nil,
   452  			expectedCreatedAt: createdAt,
   453  		},
   454  		{
   455  			repoID:            repoID,
   456  			prID:              3,
   457  			reviewerID:        803,
   458  			reviewerName:      "someone3",
   459  			createdAt:         nil,
   460  			updatedAt:         nil,
   461  			expectedCreatedAt: time.Now(),
   462  		},
   463  	} {
   464  		mock, review := convertTestCase(testCase)
   465  		mux.HandleFunc(fmt.Sprintf("/api/v4/projects/%d/merge_requests/%d/approvals", testCase.repoID, testCase.prID), mock)
   466  
   467  		id := int64(testCase.prID)
   468  		rvs, err := downloader.GetReviews(&base.Issue{Number: id, ForeignIndex: id})
   469  		assert.NoError(t, err)
   470  		assertReviewsEqual(t, []*base.Review{&review}, rvs)
   471  	}
   472  }
   473  
   474  func TestAwardsToReactions(t *testing.T) {
   475  	downloader := &GitlabDownloader{}
   476  	// yes gitlab can have duplicated reactions (https://gitlab.com/jaywink/socialhome/-/issues/24)
   477  	testResponse := `
   478  [
   479    {
   480      "name": "thumbsup",
   481      "user": {
   482        "id": 1241334,
   483        "username": "lafriks"
   484      }
   485    },
   486    {
   487      "name": "thumbsup",
   488      "user": {
   489        "id": 1241334,
   490        "username": "lafriks"
   491      }
   492    },
   493    {
   494      "name": "thumbsup",
   495      "user": {
   496        "id": 4575606,
   497        "username": "real6543"
   498      }
   499    }
   500  ]
   501  `
   502  	var awards []*gitlab.AwardEmoji
   503  	assert.NoError(t, json.Unmarshal([]byte(testResponse), &awards))
   504  
   505  	reactions := downloader.awardsToReactions(awards)
   506  	assert.EqualValues(t, []*base.Reaction{
   507  		{
   508  			UserName: "lafriks",
   509  			UserID:   1241334,
   510  			Content:  "thumbsup",
   511  		},
   512  		{
   513  			UserName: "real6543",
   514  			UserID:   4575606,
   515  			Content:  "thumbsup",
   516  		},
   517  	}, reactions)
   518  }
   519  
   520  func TestGitlabIIDResolver(t *testing.T) {
   521  	r := gitlabIIDResolver{}
   522  	r.recordIssueIID(1)
   523  	r.recordIssueIID(2)
   524  	r.recordIssueIID(3)
   525  	r.recordIssueIID(2)
   526  	assert.EqualValues(t, 4, r.generatePullRequestNumber(1))
   527  	assert.EqualValues(t, 13, r.generatePullRequestNumber(10))
   528  
   529  	assert.Panics(t, func() {
   530  		r := gitlabIIDResolver{}
   531  		r.recordIssueIID(1)
   532  		assert.EqualValues(t, 2, r.generatePullRequestNumber(1))
   533  		r.recordIssueIID(3) // the generation procedure has been started, it shouldn't accept any new issue IID, so it panics
   534  	})
   535  }