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

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