code.gitea.io/gitea@v1.22.3/tests/integration/api_repo_edit_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 "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 repo_model "code.gitea.io/gitea/models/repo" 15 unit_model "code.gitea.io/gitea/models/unit" 16 "code.gitea.io/gitea/models/unittest" 17 user_model "code.gitea.io/gitea/models/user" 18 api "code.gitea.io/gitea/modules/structs" 19 20 "github.com/stretchr/testify/assert" 21 ) 22 23 // getRepoEditOptionFromRepo gets the options for an existing repo exactly as is 24 func getRepoEditOptionFromRepo(repo *repo_model.Repository) *api.EditRepoOption { 25 name := repo.Name 26 description := repo.Description 27 website := repo.Website 28 private := repo.IsPrivate 29 hasIssues := false 30 var internalTracker *api.InternalTracker 31 var externalTracker *api.ExternalTracker 32 if unit, err := repo.GetUnit(db.DefaultContext, unit_model.TypeIssues); err == nil { 33 config := unit.IssuesConfig() 34 hasIssues = true 35 internalTracker = &api.InternalTracker{ 36 EnableTimeTracker: config.EnableTimetracker, 37 AllowOnlyContributorsToTrackTime: config.AllowOnlyContributorsToTrackTime, 38 EnableIssueDependencies: config.EnableDependencies, 39 } 40 } else if unit, err := repo.GetUnit(db.DefaultContext, unit_model.TypeExternalTracker); err == nil { 41 config := unit.ExternalTrackerConfig() 42 hasIssues = true 43 externalTracker = &api.ExternalTracker{ 44 ExternalTrackerURL: config.ExternalTrackerURL, 45 ExternalTrackerFormat: config.ExternalTrackerFormat, 46 ExternalTrackerStyle: config.ExternalTrackerStyle, 47 ExternalTrackerRegexpPattern: config.ExternalTrackerRegexpPattern, 48 } 49 } 50 hasWiki := false 51 var externalWiki *api.ExternalWiki 52 if _, err := repo.GetUnit(db.DefaultContext, unit_model.TypeWiki); err == nil { 53 hasWiki = true 54 } else if unit, err := repo.GetUnit(db.DefaultContext, unit_model.TypeExternalWiki); err == nil { 55 hasWiki = true 56 config := unit.ExternalWikiConfig() 57 externalWiki = &api.ExternalWiki{ 58 ExternalWikiURL: config.ExternalWikiURL, 59 } 60 } 61 defaultBranch := repo.DefaultBranch 62 hasPullRequests := false 63 ignoreWhitespaceConflicts := false 64 allowMerge := false 65 allowRebase := false 66 allowRebaseMerge := false 67 allowSquash := false 68 allowFastForwardOnly := false 69 if unit, err := repo.GetUnit(db.DefaultContext, unit_model.TypePullRequests); err == nil { 70 config := unit.PullRequestsConfig() 71 hasPullRequests = true 72 ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts 73 allowMerge = config.AllowMerge 74 allowRebase = config.AllowRebase 75 allowRebaseMerge = config.AllowRebaseMerge 76 allowSquash = config.AllowSquash 77 allowFastForwardOnly = config.AllowFastForwardOnly 78 } 79 archived := repo.IsArchived 80 return &api.EditRepoOption{ 81 Name: &name, 82 Description: &description, 83 Website: &website, 84 Private: &private, 85 HasIssues: &hasIssues, 86 ExternalTracker: externalTracker, 87 InternalTracker: internalTracker, 88 HasWiki: &hasWiki, 89 ExternalWiki: externalWiki, 90 DefaultBranch: &defaultBranch, 91 HasPullRequests: &hasPullRequests, 92 IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts, 93 AllowMerge: &allowMerge, 94 AllowRebase: &allowRebase, 95 AllowRebaseMerge: &allowRebaseMerge, 96 AllowSquash: &allowSquash, 97 AllowFastForwardOnly: &allowFastForwardOnly, 98 Archived: &archived, 99 } 100 } 101 102 // getNewRepoEditOption Gets the options to change everything about an existing repo by adding to strings or changing 103 // the boolean 104 func getNewRepoEditOption(opts *api.EditRepoOption) *api.EditRepoOption { 105 // Gives a new property to everything 106 name := *opts.Name + "renamed" 107 description := "new description" 108 website := "http://wwww.newwebsite.com" 109 private := !*opts.Private 110 hasIssues := !*opts.HasIssues 111 hasWiki := !*opts.HasWiki 112 defaultBranch := "master" 113 hasPullRequests := !*opts.HasPullRequests 114 ignoreWhitespaceConflicts := !*opts.IgnoreWhitespaceConflicts 115 allowMerge := !*opts.AllowMerge 116 allowRebase := !*opts.AllowRebase 117 allowRebaseMerge := !*opts.AllowRebaseMerge 118 allowSquash := !*opts.AllowSquash 119 archived := !*opts.Archived 120 121 return &api.EditRepoOption{ 122 Name: &name, 123 Description: &description, 124 Website: &website, 125 Private: &private, 126 DefaultBranch: &defaultBranch, 127 HasIssues: &hasIssues, 128 HasWiki: &hasWiki, 129 HasPullRequests: &hasPullRequests, 130 IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts, 131 AllowMerge: &allowMerge, 132 AllowRebase: &allowRebase, 133 AllowRebaseMerge: &allowRebaseMerge, 134 AllowSquash: &allowSquash, 135 Archived: &archived, 136 } 137 } 138 139 func TestAPIRepoEdit(t *testing.T) { 140 onGiteaRun(t, func(t *testing.T, u *url.URL) { 141 bFalse, bTrue := false, true 142 143 user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo1 & repo16 144 org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) // owner of the repo3, is an org 145 user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) // owner of neither repos 146 repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) // public repo 147 repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // public repo 148 repo15 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 15}) // empty repo 149 repo16 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) // private repo 150 151 // Get user2's token 152 session := loginUser(t, user2.Name) 153 token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 154 // Get user4's token 155 session = loginUser(t, user4.Name) 156 token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) 157 158 // Test editing a repo1 which user2 owns, changing name and many properties 159 origRepoEditOption := getRepoEditOptionFromRepo(repo1) 160 repoEditOption := getNewRepoEditOption(origRepoEditOption) 161 req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo1.Name), &repoEditOption). 162 AddTokenAuth(token2) 163 resp := MakeRequest(t, req, http.StatusOK) 164 var repo api.Repository 165 DecodeJSON(t, resp, &repo) 166 assert.NotNil(t, repo) 167 // check response 168 assert.Equal(t, *repoEditOption.Name, repo.Name) 169 assert.Equal(t, *repoEditOption.Description, repo.Description) 170 assert.Equal(t, *repoEditOption.Website, repo.Website) 171 assert.Equal(t, *repoEditOption.Archived, repo.Archived) 172 // check repo1 from database 173 repo1edited := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 174 repo1editedOption := getRepoEditOptionFromRepo(repo1edited) 175 assert.Equal(t, *repoEditOption.Name, *repo1editedOption.Name) 176 assert.Equal(t, *repoEditOption.Description, *repo1editedOption.Description) 177 assert.Equal(t, *repoEditOption.Website, *repo1editedOption.Website) 178 assert.Equal(t, *repoEditOption.Archived, *repo1editedOption.Archived) 179 assert.Equal(t, *repoEditOption.Private, *repo1editedOption.Private) 180 assert.Equal(t, *repoEditOption.HasWiki, *repo1editedOption.HasWiki) 181 182 // Test editing repo1 to use internal issue and wiki (default) 183 *repoEditOption.HasIssues = true 184 repoEditOption.ExternalTracker = nil 185 repoEditOption.InternalTracker = &api.InternalTracker{ 186 EnableTimeTracker: false, 187 AllowOnlyContributorsToTrackTime: false, 188 EnableIssueDependencies: false, 189 } 190 *repoEditOption.HasWiki = true 191 repoEditOption.ExternalWiki = nil 192 url := fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, *repoEditOption.Name) 193 req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). 194 AddTokenAuth(token2) 195 resp = MakeRequest(t, req, http.StatusOK) 196 DecodeJSON(t, resp, &repo) 197 assert.NotNil(t, repo) 198 // check repo1 was written to database 199 repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 200 repo1editedOption = getRepoEditOptionFromRepo(repo1edited) 201 assert.True(t, *repo1editedOption.HasIssues) 202 assert.Nil(t, repo1editedOption.ExternalTracker) 203 assert.Equal(t, *repo1editedOption.InternalTracker, *repoEditOption.InternalTracker) 204 assert.True(t, *repo1editedOption.HasWiki) 205 assert.Nil(t, repo1editedOption.ExternalWiki) 206 207 // Test editing repo1 to use external issue and wiki 208 repoEditOption.ExternalTracker = &api.ExternalTracker{ 209 ExternalTrackerURL: "http://www.somewebsite.com", 210 ExternalTrackerFormat: "http://www.somewebsite.com/{user}/{repo}?issue={index}", 211 ExternalTrackerStyle: "alphanumeric", 212 } 213 repoEditOption.ExternalWiki = &api.ExternalWiki{ 214 ExternalWikiURL: "http://www.somewebsite.com", 215 } 216 req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). 217 AddTokenAuth(token2) 218 resp = MakeRequest(t, req, http.StatusOK) 219 DecodeJSON(t, resp, &repo) 220 assert.NotNil(t, repo) 221 // check repo1 was written to database 222 repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 223 repo1editedOption = getRepoEditOptionFromRepo(repo1edited) 224 assert.True(t, *repo1editedOption.HasIssues) 225 assert.Equal(t, *repo1editedOption.ExternalTracker, *repoEditOption.ExternalTracker) 226 assert.True(t, *repo1editedOption.HasWiki) 227 assert.Equal(t, *repo1editedOption.ExternalWiki, *repoEditOption.ExternalWiki) 228 229 repoEditOption.ExternalTracker.ExternalTrackerStyle = "regexp" 230 repoEditOption.ExternalTracker.ExternalTrackerRegexpPattern = `(\d+)` 231 req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). 232 AddTokenAuth(token2) 233 resp = MakeRequest(t, req, http.StatusOK) 234 DecodeJSON(t, resp, &repo) 235 assert.NotNil(t, repo) 236 repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 237 repo1editedOption = getRepoEditOptionFromRepo(repo1edited) 238 assert.True(t, *repo1editedOption.HasIssues) 239 assert.Equal(t, *repo1editedOption.ExternalTracker, *repoEditOption.ExternalTracker) 240 241 // Do some tests with invalid URL for external tracker and wiki 242 repoEditOption.ExternalTracker.ExternalTrackerURL = "htp://www.somewebsite.com" 243 req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). 244 AddTokenAuth(token2) 245 MakeRequest(t, req, http.StatusUnprocessableEntity) 246 repoEditOption.ExternalTracker.ExternalTrackerURL = "http://www.somewebsite.com" 247 repoEditOption.ExternalTracker.ExternalTrackerFormat = "http://www.somewebsite.com/{user/{repo}?issue={index}" 248 req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). 249 AddTokenAuth(token2) 250 MakeRequest(t, req, http.StatusUnprocessableEntity) 251 repoEditOption.ExternalTracker.ExternalTrackerFormat = "http://www.somewebsite.com/{user}/{repo}?issue={index}" 252 repoEditOption.ExternalWiki.ExternalWikiURL = "htp://www.somewebsite.com" 253 req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). 254 AddTokenAuth(token2) 255 MakeRequest(t, req, http.StatusUnprocessableEntity) 256 257 // Test small repo change through API with issue and wiki option not set; They shall not be touched. 258 *repoEditOption.Description = "small change" 259 repoEditOption.HasIssues = nil 260 repoEditOption.ExternalTracker = nil 261 repoEditOption.HasWiki = nil 262 repoEditOption.ExternalWiki = nil 263 req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). 264 AddTokenAuth(token2) 265 resp = MakeRequest(t, req, http.StatusOK) 266 DecodeJSON(t, resp, &repo) 267 assert.NotNil(t, repo) 268 // check repo1 was written to database 269 repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 270 repo1editedOption = getRepoEditOptionFromRepo(repo1edited) 271 assert.Equal(t, *repo1editedOption.Description, *repoEditOption.Description) 272 assert.True(t, *repo1editedOption.HasIssues) 273 assert.NotNil(t, *repo1editedOption.ExternalTracker) 274 assert.True(t, *repo1editedOption.HasWiki) 275 assert.NotNil(t, *repo1editedOption.ExternalWiki) 276 277 // reset repo in db 278 req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, *repoEditOption.Name), &origRepoEditOption). 279 AddTokenAuth(token2) 280 _ = MakeRequest(t, req, http.StatusOK) 281 282 // Test editing a non-existing repo 283 name := "repodoesnotexist" 284 req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, name), &api.EditRepoOption{Name: &name}). 285 AddTokenAuth(token2) 286 _ = MakeRequest(t, req, http.StatusNotFound) 287 288 // Test editing repo16 by user4 who does not have write access 289 origRepoEditOption = getRepoEditOptionFromRepo(repo16) 290 repoEditOption = getNewRepoEditOption(origRepoEditOption) 291 req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name), &repoEditOption). 292 AddTokenAuth(token4) 293 MakeRequest(t, req, http.StatusNotFound) 294 295 // Tests a repo with no token given so will fail 296 origRepoEditOption = getRepoEditOptionFromRepo(repo16) 297 repoEditOption = getNewRepoEditOption(origRepoEditOption) 298 req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name), &repoEditOption) 299 _ = MakeRequest(t, req, http.StatusNotFound) 300 301 // Test using access token for a private repo that the user of the token owns 302 origRepoEditOption = getRepoEditOptionFromRepo(repo16) 303 repoEditOption = getNewRepoEditOption(origRepoEditOption) 304 req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name), &repoEditOption). 305 AddTokenAuth(token2) 306 _ = MakeRequest(t, req, http.StatusOK) 307 // reset repo in db 308 req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, *repoEditOption.Name), &origRepoEditOption). 309 AddTokenAuth(token2) 310 _ = MakeRequest(t, req, http.StatusOK) 311 312 // Test making a repo public that is private 313 repo16 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) 314 assert.True(t, repo16.IsPrivate) 315 repoEditOption = &api.EditRepoOption{ 316 Private: &bFalse, 317 } 318 url = fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo16.Name) 319 req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). 320 AddTokenAuth(token2) 321 _ = MakeRequest(t, req, http.StatusOK) 322 repo16 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) 323 assert.False(t, repo16.IsPrivate) 324 // Make it private again 325 repoEditOption.Private = &bTrue 326 req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption). 327 AddTokenAuth(token2) 328 _ = MakeRequest(t, req, http.StatusOK) 329 330 // Test to change empty repo 331 assert.False(t, repo15.IsArchived) 332 url = fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo15.Name) 333 req = NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{ 334 Archived: &bTrue, 335 }).AddTokenAuth(token2) 336 _ = MakeRequest(t, req, http.StatusOK) 337 repo15 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 15}) 338 assert.True(t, repo15.IsArchived) 339 req = NewRequestWithJSON(t, "PATCH", url, &api.EditRepoOption{ 340 Archived: &bFalse, 341 }).AddTokenAuth(token2) 342 _ = MakeRequest(t, req, http.StatusOK) 343 344 // Test using org repo "org3/repo3" where user2 is a collaborator 345 origRepoEditOption = getRepoEditOptionFromRepo(repo3) 346 repoEditOption = getNewRepoEditOption(origRepoEditOption) 347 req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", org3.Name, repo3.Name), &repoEditOption). 348 AddTokenAuth(token2) 349 MakeRequest(t, req, http.StatusOK) 350 // reset repo in db 351 req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", org3.Name, *repoEditOption.Name), &origRepoEditOption). 352 AddTokenAuth(token2) 353 _ = MakeRequest(t, req, http.StatusOK) 354 355 // Test using org repo "org3/repo3" with no user token 356 origRepoEditOption = getRepoEditOptionFromRepo(repo3) 357 repoEditOption = getNewRepoEditOption(origRepoEditOption) 358 req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", org3.Name, repo3.Name), &repoEditOption) 359 MakeRequest(t, req, http.StatusNotFound) 360 361 // Test using repo "user2/repo1" where user4 is a NOT collaborator 362 origRepoEditOption = getRepoEditOptionFromRepo(repo1) 363 repoEditOption = getNewRepoEditOption(origRepoEditOption) 364 req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo1.Name), &repoEditOption). 365 AddTokenAuth(token4) 366 MakeRequest(t, req, http.StatusForbidden) 367 }) 368 }