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 }