code.gitea.io/gitea@v1.22.3/tests/integration/api_repo_test.go (about) 1 // Copyright 2017 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package integration 5 6 import ( 7 "fmt" 8 "net/http" 9 "net/url" 10 "testing" 11 12 auth_model "code.gitea.io/gitea/models/auth" 13 "code.gitea.io/gitea/models/db" 14 access_model "code.gitea.io/gitea/models/perm/access" 15 repo_model "code.gitea.io/gitea/models/repo" 16 unit_model "code.gitea.io/gitea/models/unit" 17 "code.gitea.io/gitea/models/unittest" 18 user_model "code.gitea.io/gitea/models/user" 19 "code.gitea.io/gitea/modules/setting" 20 api "code.gitea.io/gitea/modules/structs" 21 repo_service "code.gitea.io/gitea/services/repository" 22 "code.gitea.io/gitea/tests" 23 24 "github.com/stretchr/testify/assert" 25 ) 26 27 func TestAPIUserReposNotLogin(t *testing.T) { 28 defer tests.PrepareTestEnv(t)() 29 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 30 31 req := NewRequestf(t, "GET", "/api/v1/users/%s/repos", user.Name) 32 resp := MakeRequest(t, req, http.StatusOK) 33 34 var apiRepos []api.Repository 35 DecodeJSON(t, resp, &apiRepos) 36 expectedLen := unittest.GetCount(t, repo_model.Repository{OwnerID: user.ID}, 37 unittest.Cond("is_private = ?", false)) 38 assert.Len(t, apiRepos, expectedLen) 39 for _, repo := range apiRepos { 40 assert.EqualValues(t, user.ID, repo.Owner.ID) 41 assert.False(t, repo.Private) 42 } 43 } 44 45 func TestAPIUserReposWithWrongToken(t *testing.T) { 46 defer tests.PrepareTestEnv(t)() 47 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 48 wrongToken := fmt.Sprintf("Bearer %s", "wrong_token") 49 req := NewRequestf(t, "GET", "/api/v1/users/%s/repos", user.Name). 50 AddTokenAuth(wrongToken) 51 resp := MakeRequest(t, req, http.StatusUnauthorized) 52 53 assert.Contains(t, resp.Body.String(), "user does not exist") 54 } 55 56 func TestAPISearchRepo(t *testing.T) { 57 defer tests.PrepareTestEnv(t)() 58 const keyword = "test" 59 60 req := NewRequestf(t, "GET", "/api/v1/repos/search?q=%s", keyword) 61 resp := MakeRequest(t, req, http.StatusOK) 62 63 var body api.SearchResults 64 DecodeJSON(t, resp, &body) 65 assert.NotEmpty(t, body.Data) 66 for _, repo := range body.Data { 67 assert.Contains(t, repo.Name, keyword) 68 assert.False(t, repo.Private) 69 } 70 71 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15}) 72 user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 16}) 73 org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 18}) 74 user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20}) 75 orgUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 17}) 76 77 oldAPIDefaultNum := setting.API.DefaultPagingNum 78 defer func() { 79 setting.API.DefaultPagingNum = oldAPIDefaultNum 80 }() 81 setting.API.DefaultPagingNum = 10 82 83 // Map of expected results, where key is user for login 84 type expectedResults map[*user_model.User]struct { 85 count int 86 repoOwnerID int64 87 repoName string 88 includesPrivate bool 89 } 90 91 testCases := []struct { 92 name, requestURL string 93 expectedResults 94 }{ 95 { 96 name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50&private=false", expectedResults: expectedResults{ 97 nil: {count: 35}, 98 user: {count: 35}, 99 user2: {count: 35}, 100 }, 101 }, 102 { 103 name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10&private=false", expectedResults: expectedResults{ 104 nil: {count: 10}, 105 user: {count: 10}, 106 user2: {count: 10}, 107 }, 108 }, 109 { 110 name: "RepositoriesDefault", requestURL: "/api/v1/repos/search?default&private=false", expectedResults: expectedResults{ 111 nil: {count: 10}, 112 user: {count: 10}, 113 user2: {count: 10}, 114 }, 115 }, 116 { 117 name: "RepositoriesByName", requestURL: fmt.Sprintf("/api/v1/repos/search?q=%s&private=false", "big_test_"), expectedResults: expectedResults{ 118 nil: {count: 7, repoName: "big_test_"}, 119 user: {count: 7, repoName: "big_test_"}, 120 user2: {count: 7, repoName: "big_test_"}, 121 }, 122 }, 123 { 124 name: "RepositoriesByName", requestURL: fmt.Sprintf("/api/v1/repos/search?q=%s&private=false", "user2/big_test_"), expectedResults: expectedResults{ 125 user2: {count: 2, repoName: "big_test_"}, 126 }, 127 }, 128 { 129 name: "RepositoriesAccessibleAndRelatedToUser", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user.ID), expectedResults: expectedResults{ 130 nil: {count: 5}, 131 user: {count: 9, includesPrivate: true}, 132 user2: {count: 6, includesPrivate: true}, 133 }, 134 }, 135 { 136 name: "RepositoriesAccessibleAndRelatedToUser2", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user2.ID), expectedResults: expectedResults{ 137 nil: {count: 1}, 138 user: {count: 2, includesPrivate: true}, 139 user2: {count: 2, includesPrivate: true}, 140 user4: {count: 1}, 141 }, 142 }, 143 { 144 name: "RepositoriesAccessibleAndRelatedToUser3", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", org3.ID), expectedResults: expectedResults{ 145 nil: {count: 1}, 146 user: {count: 4, includesPrivate: true}, 147 user2: {count: 3, includesPrivate: true}, 148 org3: {count: 4, includesPrivate: true}, 149 }, 150 }, 151 { 152 name: "RepositoriesOwnedByOrganization", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", orgUser.ID), expectedResults: expectedResults{ 153 nil: {count: 1, repoOwnerID: orgUser.ID}, 154 user: {count: 2, repoOwnerID: orgUser.ID, includesPrivate: true}, 155 user2: {count: 1, repoOwnerID: orgUser.ID}, 156 }, 157 }, 158 {name: "RepositoriesAccessibleAndRelatedToUser4", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user4.ID), expectedResults: expectedResults{ 159 nil: {count: 3}, 160 user: {count: 4, includesPrivate: true}, 161 user4: {count: 7, includesPrivate: true}, 162 }}, 163 {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeSource", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "source"), expectedResults: expectedResults{ 164 nil: {count: 0}, 165 user: {count: 1, includesPrivate: true}, 166 user4: {count: 1, includesPrivate: true}, 167 }}, 168 {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "fork"), expectedResults: expectedResults{ 169 nil: {count: 1}, 170 user: {count: 1}, 171 user4: {count: 2, includesPrivate: true}, 172 }}, 173 {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork/Exclusive", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s&exclusive=1", user4.ID, "fork"), expectedResults: expectedResults{ 174 nil: {count: 1}, 175 user: {count: 1}, 176 user4: {count: 2, includesPrivate: true}, 177 }}, 178 {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeMirror", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "mirror"), expectedResults: expectedResults{ 179 nil: {count: 2}, 180 user: {count: 2}, 181 user4: {count: 4, includesPrivate: true}, 182 }}, 183 {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeMirror/Exclusive", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s&exclusive=1", user4.ID, "mirror"), expectedResults: expectedResults{ 184 nil: {count: 1}, 185 user: {count: 1}, 186 user4: {count: 2, includesPrivate: true}, 187 }}, 188 {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeCollaborative", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "collaborative"), expectedResults: expectedResults{ 189 nil: {count: 0}, 190 user: {count: 1, includesPrivate: true}, 191 user4: {count: 1, includesPrivate: true}, 192 }}, 193 } 194 195 for _, testCase := range testCases { 196 t.Run(testCase.name, func(t *testing.T) { 197 for userToLogin, expected := range testCase.expectedResults { 198 var testName string 199 var userID int64 200 var token string 201 if userToLogin != nil && userToLogin.ID > 0 { 202 testName = fmt.Sprintf("LoggedUser%d", userToLogin.ID) 203 session := loginUser(t, userToLogin.Name) 204 token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) 205 userID = userToLogin.ID 206 } else { 207 testName = "AnonymousUser" 208 _ = emptyTestSession(t) 209 } 210 211 t.Run(testName, func(t *testing.T) { 212 request := NewRequest(t, "GET", testCase.requestURL). 213 AddTokenAuth(token) 214 response := MakeRequest(t, request, http.StatusOK) 215 216 var body api.SearchResults 217 DecodeJSON(t, response, &body) 218 219 repoNames := make([]string, 0, len(body.Data)) 220 for _, repo := range body.Data { 221 repoNames = append(repoNames, fmt.Sprintf("%d:%s:%t", repo.ID, repo.FullName, repo.Private)) 222 } 223 assert.Len(t, repoNames, expected.count) 224 for _, repo := range body.Data { 225 r := getRepo(t, repo.ID) 226 hasAccess, err := access_model.HasAnyUnitAccess(db.DefaultContext, userID, r) 227 assert.NoError(t, err, "Error when checking if User: %d has access to %s: %v", userID, repo.FullName, err) 228 assert.True(t, hasAccess, "User: %d does not have access to %s", userID, repo.FullName) 229 230 assert.NotEmpty(t, repo.Name) 231 assert.Equal(t, repo.Name, r.Name) 232 233 if len(expected.repoName) > 0 { 234 assert.Contains(t, repo.Name, expected.repoName) 235 } 236 237 if expected.repoOwnerID > 0 { 238 assert.Equal(t, expected.repoOwnerID, repo.Owner.ID) 239 } 240 241 if !expected.includesPrivate { 242 assert.False(t, repo.Private, "User: %d not expecting private repository: %s", userID, repo.FullName) 243 } 244 } 245 }) 246 } 247 }) 248 } 249 } 250 251 var repoCache = make(map[int64]*repo_model.Repository) 252 253 func getRepo(t *testing.T, repoID int64) *repo_model.Repository { 254 if _, ok := repoCache[repoID]; !ok { 255 repoCache[repoID] = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}) 256 } 257 return repoCache[repoID] 258 } 259 260 func TestAPIViewRepo(t *testing.T) { 261 defer tests.PrepareTestEnv(t)() 262 263 var repo api.Repository 264 265 req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1") 266 resp := MakeRequest(t, req, http.StatusOK) 267 DecodeJSON(t, resp, &repo) 268 assert.EqualValues(t, 1, repo.ID) 269 assert.EqualValues(t, "repo1", repo.Name) 270 assert.EqualValues(t, 2, repo.Releases) 271 assert.EqualValues(t, 1, repo.OpenIssues) 272 assert.EqualValues(t, 3, repo.OpenPulls) 273 274 req = NewRequest(t, "GET", "/api/v1/repos/user12/repo10") 275 resp = MakeRequest(t, req, http.StatusOK) 276 DecodeJSON(t, resp, &repo) 277 assert.EqualValues(t, 10, repo.ID) 278 assert.EqualValues(t, "repo10", repo.Name) 279 assert.EqualValues(t, 1, repo.OpenPulls) 280 assert.EqualValues(t, 1, repo.Forks) 281 282 req = NewRequest(t, "GET", "/api/v1/repos/user5/repo4") 283 resp = MakeRequest(t, req, http.StatusOK) 284 DecodeJSON(t, resp, &repo) 285 assert.EqualValues(t, 4, repo.ID) 286 assert.EqualValues(t, "repo4", repo.Name) 287 assert.EqualValues(t, 1, repo.Stars) 288 } 289 290 func TestAPIOrgRepos(t *testing.T) { 291 defer tests.PrepareTestEnv(t)() 292 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 293 user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) 294 org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) 295 // org3 is an Org. Check their repos. 296 sourceOrg := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) 297 298 expectedResults := map[*user_model.User]struct { 299 count int 300 includesPrivate bool 301 }{ 302 user: {count: 1}, 303 user: {count: 3, includesPrivate: true}, 304 user2: {count: 3, includesPrivate: true}, 305 org3: {count: 1}, 306 } 307 308 for userToLogin, expected := range expectedResults { 309 testName := fmt.Sprintf("LoggedUser%d", userToLogin.ID) 310 session := loginUser(t, userToLogin.Name) 311 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrganization) 312 313 t.Run(testName, func(t *testing.T) { 314 req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", sourceOrg.Name). 315 AddTokenAuth(token) 316 resp := MakeRequest(t, req, http.StatusOK) 317 318 var apiRepos []*api.Repository 319 DecodeJSON(t, resp, &apiRepos) 320 assert.Len(t, apiRepos, expected.count) 321 for _, repo := range apiRepos { 322 if !expected.includesPrivate { 323 assert.False(t, repo.Private) 324 } 325 } 326 }) 327 } 328 } 329 330 // See issue #28483. Tests to make sure we consider more than just code unit-enabled repositories. 331 func TestAPIOrgReposWithCodeUnitDisabled(t *testing.T) { 332 defer tests.PrepareTestEnv(t)() 333 repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: "repo21"}) 334 org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo21.OwnerID}) 335 336 // Disable code repository unit. 337 var units []unit_model.Type 338 units = append(units, unit_model.TypeCode) 339 340 if err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo21, nil, units); err != nil { 341 assert.Fail(t, "should have been able to delete code repository unit; failed to %v", err) 342 } 343 assert.False(t, repo21.UnitEnabled(db.DefaultContext, unit_model.TypeCode)) 344 345 session := loginUser(t, "user2") 346 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrganization) 347 348 req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", org3.Name). 349 AddTokenAuth(token) 350 351 resp := MakeRequest(t, req, http.StatusOK) 352 var apiRepos []*api.Repository 353 DecodeJSON(t, resp, &apiRepos) 354 355 var repoNames []string 356 for _, r := range apiRepos { 357 repoNames = append(repoNames, r.Name) 358 } 359 360 assert.Contains(t, repoNames, repo21.Name) 361 } 362 363 func TestAPIGetRepoByIDUnauthorized(t *testing.T) { 364 defer tests.PrepareTestEnv(t)() 365 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) 366 session := loginUser(t, user.Name) 367 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) 368 req := NewRequest(t, "GET", "/api/v1/repositories/2"). 369 AddTokenAuth(token) 370 MakeRequest(t, req, http.StatusNotFound) 371 } 372 373 func TestAPIRepoMigrate(t *testing.T) { 374 testCases := []struct { 375 ctxUserID, userID int64 376 cloneURL, repoName string 377 expectedStatus int 378 }{ 379 {ctxUserID: 1, userID: 2, cloneURL: "https://github.com/go-gitea/test_repo.git", repoName: "git-admin", expectedStatus: http.StatusCreated}, 380 {ctxUserID: 2, userID: 2, cloneURL: "https://github.com/go-gitea/test_repo.git", repoName: "git-own", expectedStatus: http.StatusCreated}, 381 {ctxUserID: 2, userID: 1, cloneURL: "https://github.com/go-gitea/test_repo.git", repoName: "git-bad", expectedStatus: http.StatusForbidden}, 382 {ctxUserID: 2, userID: 3, cloneURL: "https://github.com/go-gitea/test_repo.git", repoName: "git-org", expectedStatus: http.StatusCreated}, 383 {ctxUserID: 2, userID: 6, cloneURL: "https://github.com/go-gitea/test_repo.git", repoName: "git-bad-org", expectedStatus: http.StatusForbidden}, 384 {ctxUserID: 2, userID: 3, cloneURL: "https://localhost:3000/user/test_repo.git", repoName: "private-ip", expectedStatus: http.StatusUnprocessableEntity}, 385 {ctxUserID: 2, userID: 3, cloneURL: "https://10.0.0.1/user/test_repo.git", repoName: "private-ip", expectedStatus: http.StatusUnprocessableEntity}, 386 } 387 388 defer tests.PrepareTestEnv(t)() 389 for _, testCase := range testCases { 390 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) 391 session := loginUser(t, user.Name) 392 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 393 req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate", &api.MigrateRepoOptions{ 394 CloneAddr: testCase.cloneURL, 395 RepoOwnerID: testCase.userID, 396 RepoName: testCase.repoName, 397 }).AddTokenAuth(token) 398 resp := MakeRequest(t, req, NoExpectedStatus) 399 if resp.Code == http.StatusUnprocessableEntity { 400 respJSON := map[string]string{} 401 DecodeJSON(t, resp, &respJSON) 402 switch respJSON["message"] { 403 case "Remote visit addressed rate limitation.": 404 t.Log("test hit github rate limitation") 405 case "You can not import from disallowed hosts.": 406 assert.EqualValues(t, "private-ip", testCase.repoName) 407 default: 408 assert.FailNow(t, "unexpected error '%v' on url '%s'", respJSON["message"], testCase.cloneURL) 409 } 410 } else { 411 assert.EqualValues(t, testCase.expectedStatus, resp.Code) 412 } 413 } 414 } 415 416 func TestAPIRepoMigrateConflict(t *testing.T) { 417 onGiteaRun(t, testAPIRepoMigrateConflict) 418 } 419 420 func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) { 421 username := "user2" 422 baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) 423 424 u.Path = baseAPITestContext.GitPath() 425 426 t.Run("Existing", func(t *testing.T) { 427 httpContext := baseAPITestContext 428 429 httpContext.Reponame = "repo-tmp-17" 430 t.Run("CreateRepo", doAPICreateRepository(httpContext, false)) 431 432 user, err := user_model.GetUserByName(db.DefaultContext, httpContext.Username) 433 assert.NoError(t, err) 434 userID := user.ID 435 436 cloneURL := "https://github.com/go-gitea/test_repo.git" 437 438 req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate", 439 &api.MigrateRepoOptions{ 440 CloneAddr: cloneURL, 441 RepoOwnerID: userID, 442 RepoName: httpContext.Reponame, 443 }). 444 AddTokenAuth(httpContext.Token) 445 resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict) 446 respJSON := map[string]string{} 447 DecodeJSON(t, resp, &respJSON) 448 assert.Equal(t, "The repository with the same name already exists.", respJSON["message"]) 449 }) 450 } 451 452 // mirror-sync must fail with "400 (Bad Request)" when an attempt is made to 453 // sync a non-mirror repository. 454 func TestAPIMirrorSyncNonMirrorRepo(t *testing.T) { 455 defer tests.PrepareTestEnv(t)() 456 457 session := loginUser(t, "user2") 458 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 459 460 var repo api.Repository 461 req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1") 462 resp := MakeRequest(t, req, http.StatusOK) 463 DecodeJSON(t, resp, &repo) 464 assert.False(t, repo.Mirror) 465 466 req = NewRequestf(t, "POST", "/api/v1/repos/user2/repo1/mirror-sync"). 467 AddTokenAuth(token) 468 resp = MakeRequest(t, req, http.StatusBadRequest) 469 errRespJSON := map[string]string{} 470 DecodeJSON(t, resp, &errRespJSON) 471 assert.Equal(t, "Repository is not a mirror", errRespJSON["message"]) 472 } 473 474 func TestAPIOrgRepoCreate(t *testing.T) { 475 testCases := []struct { 476 ctxUserID int64 477 orgName, repoName string 478 expectedStatus int 479 }{ 480 {ctxUserID: 1, orgName: "org3", repoName: "repo-admin", expectedStatus: http.StatusCreated}, 481 {ctxUserID: 2, orgName: "org3", repoName: "repo-own", expectedStatus: http.StatusCreated}, 482 {ctxUserID: 2, orgName: "org6", repoName: "repo-bad-org", expectedStatus: http.StatusForbidden}, 483 {ctxUserID: 28, orgName: "org3", repoName: "repo-creator", expectedStatus: http.StatusCreated}, 484 {ctxUserID: 28, orgName: "org6", repoName: "repo-not-creator", expectedStatus: http.StatusForbidden}, 485 } 486 487 defer tests.PrepareTestEnv(t)() 488 for _, testCase := range testCases { 489 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) 490 session := loginUser(t, user.Name) 491 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization, auth_model.AccessTokenScopeWriteRepository) 492 req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos", testCase.orgName), &api.CreateRepoOption{ 493 Name: testCase.repoName, 494 }).AddTokenAuth(token) 495 MakeRequest(t, req, testCase.expectedStatus) 496 } 497 } 498 499 func TestAPIRepoCreateConflict(t *testing.T) { 500 onGiteaRun(t, testAPIRepoCreateConflict) 501 } 502 503 func testAPIRepoCreateConflict(t *testing.T, u *url.URL) { 504 username := "user2" 505 baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) 506 507 u.Path = baseAPITestContext.GitPath() 508 509 t.Run("Existing", func(t *testing.T) { 510 httpContext := baseAPITestContext 511 512 httpContext.Reponame = "repo-tmp-17" 513 t.Run("CreateRepo", doAPICreateRepository(httpContext, false)) 514 515 req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos", 516 &api.CreateRepoOption{ 517 Name: httpContext.Reponame, 518 }). 519 AddTokenAuth(httpContext.Token) 520 resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict) 521 respJSON := map[string]string{} 522 DecodeJSON(t, resp, &respJSON) 523 assert.Equal(t, "The repository with the same name already exists.", respJSON["message"]) 524 }) 525 } 526 527 func TestAPIRepoTransfer(t *testing.T) { 528 testCases := []struct { 529 ctxUserID int64 530 newOwner string 531 teams *[]int64 532 expectedStatus int 533 }{ 534 // Disclaimer for test story: "user1" is an admin, "user2" is normal user and part of in owner team of org "org3" 535 // Transfer to a user with teams in another org should fail 536 {ctxUserID: 1, newOwner: "org3", teams: &[]int64{5}, expectedStatus: http.StatusForbidden}, 537 // Transfer to a user with non-existent team IDs should fail 538 {ctxUserID: 1, newOwner: "user2", teams: &[]int64{2}, expectedStatus: http.StatusUnprocessableEntity}, 539 // Transfer should go through 540 {ctxUserID: 1, newOwner: "org3", teams: &[]int64{2}, expectedStatus: http.StatusAccepted}, 541 // Let user transfer it back to himself 542 {ctxUserID: 2, newOwner: "user2", expectedStatus: http.StatusAccepted}, 543 // And revert transfer 544 {ctxUserID: 2, newOwner: "org3", teams: &[]int64{2}, expectedStatus: http.StatusAccepted}, 545 // Cannot start transfer to an existing repo 546 {ctxUserID: 2, newOwner: "org3", teams: nil, expectedStatus: http.StatusUnprocessableEntity}, 547 // Start transfer, repo is now in pending transfer mode 548 {ctxUserID: 2, newOwner: "org6", teams: nil, expectedStatus: http.StatusCreated}, 549 } 550 551 defer tests.PrepareTestEnv(t)() 552 553 // create repo to move 554 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) 555 session := loginUser(t, user.Name) 556 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) 557 repoName := "moveME" 558 apiRepo := new(api.Repository) 559 req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos", &api.CreateRepoOption{ 560 Name: repoName, 561 Description: "repo move around", 562 Private: false, 563 Readme: "Default", 564 AutoInit: true, 565 }).AddTokenAuth(token) 566 resp := MakeRequest(t, req, http.StatusCreated) 567 DecodeJSON(t, resp, apiRepo) 568 569 // start testing 570 for _, testCase := range testCases { 571 user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) 572 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID}) 573 session = loginUser(t, user.Name) 574 token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 575 req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer", repo.OwnerName, repo.Name), &api.TransferRepoOption{ 576 NewOwner: testCase.newOwner, 577 TeamIDs: testCase.teams, 578 }).AddTokenAuth(token) 579 MakeRequest(t, req, testCase.expectedStatus) 580 } 581 582 // cleanup 583 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID}) 584 _ = repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repo.ID) 585 } 586 587 func transfer(t *testing.T) *repo_model.Repository { 588 // create repo to move 589 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 590 session := loginUser(t, user.Name) 591 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) 592 repoName := "moveME" 593 apiRepo := new(api.Repository) 594 req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos", &api.CreateRepoOption{ 595 Name: repoName, 596 Description: "repo move around", 597 Private: false, 598 Readme: "Default", 599 AutoInit: true, 600 }).AddTokenAuth(token) 601 602 resp := MakeRequest(t, req, http.StatusCreated) 603 DecodeJSON(t, resp, apiRepo) 604 605 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID}) 606 req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer", repo.OwnerName, repo.Name), &api.TransferRepoOption{ 607 NewOwner: "user4", 608 }).AddTokenAuth(token) 609 MakeRequest(t, req, http.StatusCreated) 610 611 return repo 612 } 613 614 func TestAPIAcceptTransfer(t *testing.T) { 615 defer tests.PrepareTestEnv(t)() 616 617 repo := transfer(t) 618 619 // try to accept with not authorized user 620 session := loginUser(t, "user2") 621 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) 622 req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject", repo.OwnerName, repo.Name)). 623 AddTokenAuth(token) 624 MakeRequest(t, req, http.StatusForbidden) 625 626 // try to accept repo that's not marked as transferred 627 req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/accept", "user2", "repo1")). 628 AddTokenAuth(token) 629 MakeRequest(t, req, http.StatusNotFound) 630 631 // accept transfer 632 session = loginUser(t, "user4") 633 token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) 634 635 req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/accept", repo.OwnerName, repo.Name)). 636 AddTokenAuth(token) 637 resp := MakeRequest(t, req, http.StatusAccepted) 638 apiRepo := new(api.Repository) 639 DecodeJSON(t, resp, apiRepo) 640 assert.Equal(t, "user4", apiRepo.Owner.UserName) 641 } 642 643 func TestAPIRejectTransfer(t *testing.T) { 644 defer tests.PrepareTestEnv(t)() 645 646 repo := transfer(t) 647 648 // try to reject with not authorized user 649 session := loginUser(t, "user2") 650 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 651 req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject", repo.OwnerName, repo.Name)). 652 AddTokenAuth(token) 653 MakeRequest(t, req, http.StatusForbidden) 654 655 // try to reject repo that's not marked as transferred 656 req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject", "user2", "repo1")). 657 AddTokenAuth(token) 658 MakeRequest(t, req, http.StatusNotFound) 659 660 // reject transfer 661 session = loginUser(t, "user4") 662 token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 663 664 req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject", repo.OwnerName, repo.Name)). 665 AddTokenAuth(token) 666 resp := MakeRequest(t, req, http.StatusOK) 667 apiRepo := new(api.Repository) 668 DecodeJSON(t, resp, apiRepo) 669 assert.Equal(t, "user2", apiRepo.Owner.UserName) 670 } 671 672 func TestAPIGenerateRepo(t *testing.T) { 673 defer tests.PrepareTestEnv(t)() 674 675 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) 676 session := loginUser(t, user.Name) 677 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 678 679 templateRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 44}) 680 681 // user 682 repo := new(api.Repository) 683 req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/generate", templateRepo.OwnerName, templateRepo.Name), &api.GenerateRepoOption{ 684 Owner: user.Name, 685 Name: "new-repo", 686 Description: "test generate repo", 687 Private: false, 688 GitContent: true, 689 }).AddTokenAuth(token) 690 resp := MakeRequest(t, req, http.StatusCreated) 691 DecodeJSON(t, resp, repo) 692 693 assert.Equal(t, "new-repo", repo.Name) 694 695 // org 696 req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/generate", templateRepo.OwnerName, templateRepo.Name), &api.GenerateRepoOption{ 697 Owner: "org3", 698 Name: "new-repo", 699 Description: "test generate repo", 700 Private: false, 701 GitContent: true, 702 }).AddTokenAuth(token) 703 resp = MakeRequest(t, req, http.StatusCreated) 704 DecodeJSON(t, resp, repo) 705 706 assert.Equal(t, "new-repo", repo.Name) 707 } 708 709 func TestAPIRepoGetReviewers(t *testing.T) { 710 defer tests.PrepareTestEnv(t)() 711 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 712 session := loginUser(t, user.Name) 713 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) 714 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 715 716 req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/reviewers", user.Name, repo.Name). 717 AddTokenAuth(token) 718 resp := MakeRequest(t, req, http.StatusOK) 719 var reviewers []*api.User 720 DecodeJSON(t, resp, &reviewers) 721 if assert.Len(t, reviewers, 3) { 722 assert.ElementsMatch(t, []int64{1, 4, 11}, []int64{reviewers[0].ID, reviewers[1].ID, reviewers[2].ID}) 723 } 724 } 725 726 func TestAPIRepoGetAssignees(t *testing.T) { 727 defer tests.PrepareTestEnv(t)() 728 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 729 session := loginUser(t, user.Name) 730 token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) 731 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 732 733 req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/assignees", user.Name, repo.Name). 734 AddTokenAuth(token) 735 resp := MakeRequest(t, req, http.StatusOK) 736 var assignees []*api.User 737 DecodeJSON(t, resp, &assignees) 738 assert.Len(t, assignees, 1) 739 }