code.gitea.io/gitea@v1.22.3/tests/integration/api_helper_for_declarative_test.go (about)

     1  // Copyright 2019 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package integration
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"net/url"
    12  	"os"
    13  	"testing"
    14  	"time"
    15  
    16  	"code.gitea.io/gitea/models/auth"
    17  	"code.gitea.io/gitea/models/perm"
    18  	repo_model "code.gitea.io/gitea/models/repo"
    19  	"code.gitea.io/gitea/modules/json"
    20  	"code.gitea.io/gitea/modules/queue"
    21  	api "code.gitea.io/gitea/modules/structs"
    22  	"code.gitea.io/gitea/services/forms"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  )
    26  
    27  type APITestContext struct {
    28  	Reponame     string
    29  	Session      *TestSession
    30  	Token        string
    31  	Username     string
    32  	ExpectedCode int
    33  }
    34  
    35  func NewAPITestContext(t *testing.T, username, reponame string, scope ...auth.AccessTokenScope) APITestContext {
    36  	session := loginUser(t, username)
    37  	token := getTokenForLoggedInUser(t, session, scope...)
    38  	return APITestContext{
    39  		Session:  session,
    40  		Token:    token,
    41  		Username: username,
    42  		Reponame: reponame,
    43  	}
    44  }
    45  
    46  func (ctx APITestContext) GitPath() string {
    47  	return fmt.Sprintf("%s/%s.git", ctx.Username, ctx.Reponame)
    48  }
    49  
    50  func doAPICreateRepository(ctx APITestContext, empty bool, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
    51  	return func(t *testing.T) {
    52  		createRepoOption := &api.CreateRepoOption{
    53  			AutoInit:    !empty,
    54  			Description: "Temporary repo",
    55  			Name:        ctx.Reponame,
    56  			Private:     true,
    57  			Template:    true,
    58  			Gitignores:  "",
    59  			License:     "WTFPL",
    60  			Readme:      "Default",
    61  		}
    62  		req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos", createRepoOption).
    63  			AddTokenAuth(ctx.Token)
    64  		if ctx.ExpectedCode != 0 {
    65  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
    66  			return
    67  		}
    68  		resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
    69  
    70  		var repository api.Repository
    71  		DecodeJSON(t, resp, &repository)
    72  		if len(callback) > 0 {
    73  			callback[0](t, repository)
    74  		}
    75  	}
    76  }
    77  
    78  func doAPIEditRepository(ctx APITestContext, editRepoOption *api.EditRepoOption, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
    79  	return func(t *testing.T) {
    80  		req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)), editRepoOption).
    81  			AddTokenAuth(ctx.Token)
    82  		if ctx.ExpectedCode != 0 {
    83  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
    84  			return
    85  		}
    86  		resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
    87  
    88  		var repository api.Repository
    89  		DecodeJSON(t, resp, &repository)
    90  		if len(callback) > 0 {
    91  			callback[0](t, repository)
    92  		}
    93  	}
    94  }
    95  
    96  func doAPIAddCollaborator(ctx APITestContext, username string, mode perm.AccessMode) func(*testing.T) {
    97  	return func(t *testing.T) {
    98  		permission := "read"
    99  
   100  		if mode == perm.AccessModeAdmin {
   101  			permission = "admin"
   102  		} else if mode > perm.AccessModeRead {
   103  			permission = "write"
   104  		}
   105  		addCollaboratorOption := &api.AddCollaboratorOption{
   106  			Permission: &permission,
   107  		}
   108  		req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/collaborators/%s", ctx.Username, ctx.Reponame, username), addCollaboratorOption).
   109  			AddTokenAuth(ctx.Token)
   110  		if ctx.ExpectedCode != 0 {
   111  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   112  			return
   113  		}
   114  		ctx.Session.MakeRequest(t, req, http.StatusNoContent)
   115  	}
   116  }
   117  
   118  func doAPIForkRepository(ctx APITestContext, username string, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
   119  	return func(t *testing.T) {
   120  		createForkOption := &api.CreateForkOption{}
   121  		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks", username, ctx.Reponame), createForkOption).
   122  			AddTokenAuth(ctx.Token)
   123  		if ctx.ExpectedCode != 0 {
   124  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   125  			return
   126  		}
   127  		resp := ctx.Session.MakeRequest(t, req, http.StatusAccepted)
   128  		var repository api.Repository
   129  		DecodeJSON(t, resp, &repository)
   130  		if len(callback) > 0 {
   131  			callback[0](t, repository)
   132  		}
   133  	}
   134  }
   135  
   136  func doAPIGetRepository(ctx APITestContext, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
   137  	return func(t *testing.T) {
   138  		req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s", ctx.Username, ctx.Reponame)).
   139  			AddTokenAuth(ctx.Token)
   140  		if ctx.ExpectedCode != 0 {
   141  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   142  			return
   143  		}
   144  		resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
   145  
   146  		var repository api.Repository
   147  		DecodeJSON(t, resp, &repository)
   148  		if len(callback) > 0 {
   149  			callback[0](t, repository)
   150  		}
   151  	}
   152  }
   153  
   154  func doAPIDeleteRepository(ctx APITestContext) func(*testing.T) {
   155  	return func(t *testing.T) {
   156  		req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/%s", ctx.Username, ctx.Reponame)).
   157  			AddTokenAuth(ctx.Token)
   158  		if ctx.ExpectedCode != 0 {
   159  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   160  			return
   161  		}
   162  		ctx.Session.MakeRequest(t, req, http.StatusNoContent)
   163  	}
   164  }
   165  
   166  func doAPICreateUserKey(ctx APITestContext, keyname, keyFile string, callback ...func(*testing.T, api.PublicKey)) func(*testing.T) {
   167  	return func(t *testing.T) {
   168  		dataPubKey, err := os.ReadFile(keyFile + ".pub")
   169  		assert.NoError(t, err)
   170  		req := NewRequestWithJSON(t, "POST", "/api/v1/user/keys", &api.CreateKeyOption{
   171  			Title: keyname,
   172  			Key:   string(dataPubKey),
   173  		}).AddTokenAuth(ctx.Token)
   174  		if ctx.ExpectedCode != 0 {
   175  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   176  			return
   177  		}
   178  		resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
   179  		var publicKey api.PublicKey
   180  		DecodeJSON(t, resp, &publicKey)
   181  		if len(callback) > 0 {
   182  			callback[0](t, publicKey)
   183  		}
   184  	}
   185  }
   186  
   187  func doAPIDeleteUserKey(ctx APITestContext, keyID int64) func(*testing.T) {
   188  	return func(t *testing.T) {
   189  		req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/keys/%d", keyID)).
   190  			AddTokenAuth(ctx.Token)
   191  		if ctx.ExpectedCode != 0 {
   192  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   193  			return
   194  		}
   195  		ctx.Session.MakeRequest(t, req, http.StatusNoContent)
   196  	}
   197  }
   198  
   199  func doAPICreateDeployKey(ctx APITestContext, keyname, keyFile string, readOnly bool) func(*testing.T) {
   200  	return func(t *testing.T) {
   201  		dataPubKey, err := os.ReadFile(keyFile + ".pub")
   202  		assert.NoError(t, err)
   203  		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/keys", ctx.Username, ctx.Reponame), api.CreateKeyOption{
   204  			Title:    keyname,
   205  			Key:      string(dataPubKey),
   206  			ReadOnly: readOnly,
   207  		}).AddTokenAuth(ctx.Token)
   208  
   209  		if ctx.ExpectedCode != 0 {
   210  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   211  			return
   212  		}
   213  		ctx.Session.MakeRequest(t, req, http.StatusCreated)
   214  	}
   215  }
   216  
   217  func doAPICreatePullRequest(ctx APITestContext, owner, repo, baseBranch, headBranch string) func(*testing.T) (api.PullRequest, error) {
   218  	return func(t *testing.T) (api.PullRequest, error) {
   219  		req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner, repo), &api.CreatePullRequestOption{
   220  			Head:  headBranch,
   221  			Base:  baseBranch,
   222  			Title: fmt.Sprintf("create a pr from %s to %s", headBranch, baseBranch),
   223  		}).AddTokenAuth(ctx.Token)
   224  
   225  		expected := http.StatusCreated
   226  		if ctx.ExpectedCode != 0 {
   227  			expected = ctx.ExpectedCode
   228  		}
   229  		resp := ctx.Session.MakeRequest(t, req, expected)
   230  
   231  		decoder := json.NewDecoder(resp.Body)
   232  		pr := api.PullRequest{}
   233  		err := decoder.Decode(&pr)
   234  		return pr, err
   235  	}
   236  }
   237  
   238  func doAPIGetPullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) (api.PullRequest, error) {
   239  	return func(t *testing.T) (api.PullRequest, error) {
   240  		req := NewRequest(t, http.MethodGet, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner, repo, index)).
   241  			AddTokenAuth(ctx.Token)
   242  
   243  		expected := http.StatusOK
   244  		if ctx.ExpectedCode != 0 {
   245  			expected = ctx.ExpectedCode
   246  		}
   247  		resp := ctx.Session.MakeRequest(t, req, expected)
   248  
   249  		decoder := json.NewDecoder(resp.Body)
   250  		pr := api.PullRequest{}
   251  		err := decoder.Decode(&pr)
   252  		return pr, err
   253  	}
   254  }
   255  
   256  func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) {
   257  	return func(t *testing.T) {
   258  		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge", owner, repo, index)
   259  
   260  		var req *RequestWrapper
   261  		var resp *httptest.ResponseRecorder
   262  
   263  		for i := 0; i < 6; i++ {
   264  			req = NewRequestWithJSON(t, http.MethodPost, urlStr, &forms.MergePullRequestForm{
   265  				MergeMessageField: "doAPIMergePullRequest Merge",
   266  				Do:                string(repo_model.MergeStyleMerge),
   267  			}).AddTokenAuth(ctx.Token)
   268  
   269  			resp = ctx.Session.MakeRequest(t, req, NoExpectedStatus)
   270  
   271  			if resp.Code != http.StatusMethodNotAllowed {
   272  				break
   273  			}
   274  			err := api.APIError{}
   275  			DecodeJSON(t, resp, &err)
   276  			assert.EqualValues(t, "Please try again later", err.Message)
   277  			queue.GetManager().FlushAll(context.Background(), 5*time.Second)
   278  			<-time.After(1 * time.Second)
   279  		}
   280  
   281  		expected := ctx.ExpectedCode
   282  		if expected == 0 {
   283  			expected = http.StatusOK
   284  		}
   285  
   286  		if !assert.EqualValues(t, expected, resp.Code,
   287  			"Request: %s %s", req.Method, req.URL.String()) {
   288  			logUnexpectedResponse(t, resp)
   289  		}
   290  	}
   291  }
   292  
   293  func doAPIManuallyMergePullRequest(ctx APITestContext, owner, repo, commitID string, index int64) func(*testing.T) {
   294  	return func(t *testing.T) {
   295  		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge", owner, repo, index)
   296  		req := NewRequestWithJSON(t, http.MethodPost, urlStr, &forms.MergePullRequestForm{
   297  			Do:            string(repo_model.MergeStyleManuallyMerged),
   298  			MergeCommitID: commitID,
   299  		}).AddTokenAuth(ctx.Token)
   300  
   301  		if ctx.ExpectedCode != 0 {
   302  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   303  			return
   304  		}
   305  		ctx.Session.MakeRequest(t, req, http.StatusOK)
   306  	}
   307  }
   308  
   309  func doAPIAutoMergePullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) {
   310  	return func(t *testing.T) {
   311  		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge", owner, repo, index)
   312  		req := NewRequestWithJSON(t, http.MethodPost, urlStr, &forms.MergePullRequestForm{
   313  			MergeMessageField:      "doAPIMergePullRequest Merge",
   314  			Do:                     string(repo_model.MergeStyleMerge),
   315  			MergeWhenChecksSucceed: true,
   316  		}).AddTokenAuth(ctx.Token)
   317  
   318  		if ctx.ExpectedCode != 0 {
   319  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   320  			return
   321  		}
   322  		ctx.Session.MakeRequest(t, req, http.StatusOK)
   323  	}
   324  }
   325  
   326  func doAPICancelAutoMergePullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) {
   327  	return func(t *testing.T) {
   328  		req := NewRequest(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge", owner, repo, index)).
   329  			AddTokenAuth(ctx.Token)
   330  		if ctx.ExpectedCode != 0 {
   331  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   332  			return
   333  		}
   334  		ctx.Session.MakeRequest(t, req, http.StatusNoContent)
   335  	}
   336  }
   337  
   338  func doAPIGetBranch(ctx APITestContext, branch string, callback ...func(*testing.T, api.Branch)) func(*testing.T) {
   339  	return func(t *testing.T) {
   340  		req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/branches/%s", ctx.Username, ctx.Reponame, branch).
   341  			AddTokenAuth(ctx.Token)
   342  		if ctx.ExpectedCode != 0 {
   343  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   344  			return
   345  		}
   346  		resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
   347  
   348  		var branch api.Branch
   349  		DecodeJSON(t, resp, &branch)
   350  		if len(callback) > 0 {
   351  			callback[0](t, branch)
   352  		}
   353  	}
   354  }
   355  
   356  func doAPICreateFile(ctx APITestContext, treepath string, options *api.CreateFileOptions, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {
   357  	return func(t *testing.T) {
   358  		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", ctx.Username, ctx.Reponame, treepath), &options).
   359  			AddTokenAuth(ctx.Token)
   360  		if ctx.ExpectedCode != 0 {
   361  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   362  			return
   363  		}
   364  		resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
   365  
   366  		var contents api.FileResponse
   367  		DecodeJSON(t, resp, &contents)
   368  		if len(callback) > 0 {
   369  			callback[0](t, contents)
   370  		}
   371  	}
   372  }
   373  
   374  func doAPICreateOrganization(ctx APITestContext, options *api.CreateOrgOption, callback ...func(*testing.T, api.Organization)) func(t *testing.T) {
   375  	return func(t *testing.T) {
   376  		req := NewRequestWithJSON(t, "POST", "/api/v1/orgs", &options).
   377  			AddTokenAuth(ctx.Token)
   378  		if ctx.ExpectedCode != 0 {
   379  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   380  			return
   381  		}
   382  		resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
   383  
   384  		var contents api.Organization
   385  		DecodeJSON(t, resp, &contents)
   386  		if len(callback) > 0 {
   387  			callback[0](t, contents)
   388  		}
   389  	}
   390  }
   391  
   392  func doAPICreateOrganizationRepository(ctx APITestContext, orgName string, options *api.CreateRepoOption, callback ...func(*testing.T, api.Repository)) func(t *testing.T) {
   393  	return func(t *testing.T) {
   394  		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/repos", orgName), &options).
   395  			AddTokenAuth(ctx.Token)
   396  		if ctx.ExpectedCode != 0 {
   397  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   398  			return
   399  		}
   400  		resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
   401  
   402  		var contents api.Repository
   403  		DecodeJSON(t, resp, &contents)
   404  		if len(callback) > 0 {
   405  			callback[0](t, contents)
   406  		}
   407  	}
   408  }
   409  
   410  func doAPICreateOrganizationTeam(ctx APITestContext, orgName string, options *api.CreateTeamOption, callback ...func(*testing.T, api.Team)) func(t *testing.T) {
   411  	return func(t *testing.T) {
   412  		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/teams", orgName), &options).
   413  			AddTokenAuth(ctx.Token)
   414  		if ctx.ExpectedCode != 0 {
   415  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   416  			return
   417  		}
   418  		resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
   419  
   420  		var contents api.Team
   421  		DecodeJSON(t, resp, &contents)
   422  		if len(callback) > 0 {
   423  			callback[0](t, contents)
   424  		}
   425  	}
   426  }
   427  
   428  func doAPIAddUserToOrganizationTeam(ctx APITestContext, teamID int64, username string) func(t *testing.T) {
   429  	return func(t *testing.T) {
   430  		req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/teams/%d/members/%s", teamID, username)).
   431  			AddTokenAuth(ctx.Token)
   432  		if ctx.ExpectedCode != 0 {
   433  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   434  			return
   435  		}
   436  		ctx.Session.MakeRequest(t, req, http.StatusNoContent)
   437  	}
   438  }
   439  
   440  func doAPIAddRepoToOrganizationTeam(ctx APITestContext, teamID int64, orgName, repoName string) func(t *testing.T) {
   441  	return func(t *testing.T) {
   442  		req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/teams/%d/repos/%s/%s", teamID, orgName, repoName)).
   443  			AddTokenAuth(ctx.Token)
   444  		if ctx.ExpectedCode != 0 {
   445  			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
   446  			return
   447  		}
   448  		ctx.Session.MakeRequest(t, req, http.StatusNoContent)
   449  	}
   450  }