You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

api_repo_edit_test.go 15KB

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