gitea源码

api_pull_test.go 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "bytes"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "net/url"
  10. "strings"
  11. "testing"
  12. "time"
  13. auth_model "code.gitea.io/gitea/models/auth"
  14. issues_model "code.gitea.io/gitea/models/issues"
  15. "code.gitea.io/gitea/models/perm"
  16. repo_model "code.gitea.io/gitea/models/repo"
  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. "code.gitea.io/gitea/services/convert"
  22. "code.gitea.io/gitea/services/forms"
  23. "code.gitea.io/gitea/services/gitdiff"
  24. issue_service "code.gitea.io/gitea/services/issue"
  25. pull_service "code.gitea.io/gitea/services/pull"
  26. files_service "code.gitea.io/gitea/services/repository/files"
  27. "code.gitea.io/gitea/tests"
  28. "github.com/stretchr/testify/assert"
  29. )
  30. func TestAPIViewPulls(t *testing.T) {
  31. defer tests.PrepareTestEnv(t)()
  32. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
  33. owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  34. ctx := NewAPITestContext(t, "user2", repo.Name, auth_model.AccessTokenScopeReadRepository)
  35. req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls?state=all", owner.Name, repo.Name).
  36. AddTokenAuth(ctx.Token)
  37. resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
  38. var pulls []*api.PullRequest
  39. DecodeJSON(t, resp, &pulls)
  40. expectedLen := unittest.GetCount(t, &issues_model.Issue{RepoID: repo.ID}, unittest.Cond("is_pull = ?", true))
  41. assert.Len(t, pulls, expectedLen)
  42. assert.Len(t, pulls, 3)
  43. pull := pulls[0]
  44. assert.EqualValues(t, 1, pull.Poster.ID)
  45. assert.Len(t, pull.RequestedReviewers, 2)
  46. assert.Empty(t, pull.RequestedReviewersTeams)
  47. assert.EqualValues(t, 5, pull.RequestedReviewers[0].ID)
  48. assert.EqualValues(t, 6, pull.RequestedReviewers[1].ID)
  49. if assert.EqualValues(t, 5, pull.ID) {
  50. resp = ctx.Session.MakeRequest(t, NewRequest(t, "GET", pull.DiffURL), http.StatusOK)
  51. bs, err := io.ReadAll(resp.Body)
  52. assert.NoError(t, err)
  53. patch, err := gitdiff.ParsePatch(t.Context(), 1000, 5000, 10, bytes.NewReader(bs), "")
  54. assert.NoError(t, err)
  55. if assert.Len(t, patch.Files, 1) {
  56. assert.Equal(t, "File-WoW", patch.Files[0].Name)
  57. // FIXME: The old name should be empty if it's a file add type
  58. assert.Equal(t, "File-WoW", patch.Files[0].OldName)
  59. assert.Equal(t, 1, patch.Files[0].Addition)
  60. assert.Equal(t, 0, patch.Files[0].Deletion)
  61. assert.Equal(t, gitdiff.DiffFileAdd, patch.Files[0].Type)
  62. }
  63. t.Run(fmt.Sprintf("APIGetPullFiles_%d", pull.ID),
  64. doAPIGetPullFiles(ctx, pull, func(t *testing.T, files []*api.ChangedFile) {
  65. if assert.Len(t, files, 1) {
  66. assert.Equal(t, "File-WoW", files[0].Filename)
  67. assert.Empty(t, files[0].PreviousFilename)
  68. assert.Equal(t, 1, files[0].Additions)
  69. assert.Equal(t, 1, files[0].Changes)
  70. assert.Equal(t, 0, files[0].Deletions)
  71. assert.Equal(t, "added", files[0].Status)
  72. }
  73. }))
  74. }
  75. pull = pulls[1]
  76. assert.EqualValues(t, 1, pull.Poster.ID)
  77. assert.Len(t, pull.RequestedReviewers, 4)
  78. assert.Empty(t, pull.RequestedReviewersTeams)
  79. assert.EqualValues(t, 3, pull.RequestedReviewers[0].ID)
  80. assert.EqualValues(t, 4, pull.RequestedReviewers[1].ID)
  81. assert.EqualValues(t, 2, pull.RequestedReviewers[2].ID)
  82. assert.EqualValues(t, 5, pull.RequestedReviewers[3].ID)
  83. if assert.EqualValues(t, 2, pull.ID) {
  84. resp = ctx.Session.MakeRequest(t, NewRequest(t, "GET", pull.DiffURL), http.StatusOK)
  85. bs, err := io.ReadAll(resp.Body)
  86. assert.NoError(t, err)
  87. patch, err := gitdiff.ParsePatch(t.Context(), 1000, 5000, 10, bytes.NewReader(bs), "")
  88. assert.NoError(t, err)
  89. if assert.Len(t, patch.Files, 1) {
  90. assert.Equal(t, "README.md", patch.Files[0].Name)
  91. assert.Equal(t, "README.md", patch.Files[0].OldName)
  92. assert.Equal(t, 4, patch.Files[0].Addition)
  93. assert.Equal(t, 1, patch.Files[0].Deletion)
  94. assert.Equal(t, gitdiff.DiffFileChange, patch.Files[0].Type)
  95. }
  96. t.Run(fmt.Sprintf("APIGetPullFiles_%d", pull.ID),
  97. doAPIGetPullFiles(ctx, pull, func(t *testing.T, files []*api.ChangedFile) {
  98. if assert.Len(t, files, 1) {
  99. assert.Equal(t, "README.md", files[0].Filename)
  100. // FIXME: The PreviousFilename name should be the same as Filename if it's a file change
  101. assert.Empty(t, files[0].PreviousFilename)
  102. assert.Equal(t, 4, files[0].Additions)
  103. assert.Equal(t, 1, files[0].Deletions)
  104. assert.Equal(t, "changed", files[0].Status)
  105. }
  106. }))
  107. }
  108. pull = pulls[0]
  109. assert.EqualValues(t, 1, pull.Poster.ID)
  110. assert.Len(t, pull.RequestedReviewers, 2)
  111. assert.Empty(t, pull.RequestedReviewersTeams)
  112. assert.EqualValues(t, 5, pull.RequestedReviewers[0].ID)
  113. if assert.EqualValues(t, 5, pull.ID) {
  114. resp = ctx.Session.MakeRequest(t, NewRequest(t, "GET", pull.DiffURL), http.StatusOK)
  115. bs, err := io.ReadAll(resp.Body)
  116. assert.NoError(t, err)
  117. patch, err := gitdiff.ParsePatch(t.Context(), 1000, 5000, 10, bytes.NewReader(bs), "")
  118. assert.NoError(t, err)
  119. assert.Len(t, patch.Files, 1)
  120. t.Run(fmt.Sprintf("APIGetPullFiles_%d", pull.ID),
  121. doAPIGetPullFiles(ctx, pull, func(t *testing.T, files []*api.ChangedFile) {
  122. assert.Len(t, files, 1)
  123. }))
  124. }
  125. }
  126. func TestAPIViewPullsByBaseHead(t *testing.T) {
  127. defer tests.PrepareTestEnv(t)()
  128. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
  129. owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  130. ctx := NewAPITestContext(t, "user2", repo.Name, auth_model.AccessTokenScopeReadRepository)
  131. req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls/master/branch2", owner.Name, repo.Name).
  132. AddTokenAuth(ctx.Token)
  133. resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
  134. pull := &api.PullRequest{}
  135. DecodeJSON(t, resp, pull)
  136. assert.EqualValues(t, 3, pull.Index)
  137. assert.EqualValues(t, 2, pull.ID)
  138. req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls/master/branch-not-exist", owner.Name, repo.Name).
  139. AddTokenAuth(ctx.Token)
  140. ctx.Session.MakeRequest(t, req, http.StatusNotFound)
  141. }
  142. // TestAPIMergePullWIP ensures that we can't merge a WIP pull request
  143. func TestAPIMergePullWIP(t *testing.T) {
  144. defer tests.PrepareTestEnv(t)()
  145. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
  146. owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  147. pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{Status: issues_model.PullRequestStatusMergeable}, unittest.Cond("has_merged = ?", false))
  148. pr.LoadIssue(t.Context())
  149. issue_service.ChangeTitle(t.Context(), pr.Issue, owner, setting.Repository.PullRequest.WorkInProgressPrefixes[0]+" "+pr.Issue.Title)
  150. // force reload
  151. pr.LoadAttributes(t.Context())
  152. assert.Contains(t, pr.Issue.Title, setting.Repository.PullRequest.WorkInProgressPrefixes[0])
  153. session := loginUser(t, owner.Name)
  154. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  155. req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge", owner.Name, repo.Name, pr.Index), &forms.MergePullRequestForm{
  156. MergeMessageField: pr.Issue.Title,
  157. Do: string(repo_model.MergeStyleMerge),
  158. }).AddTokenAuth(token)
  159. MakeRequest(t, req, http.StatusMethodNotAllowed)
  160. }
  161. func TestAPICreatePullSuccess(t *testing.T) {
  162. defer tests.PrepareTestEnv(t)()
  163. repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
  164. // repo10 have code, pulls units.
  165. repo11 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 11})
  166. // repo11 only have code unit but should still create pulls
  167. owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID})
  168. owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID})
  169. session := loginUser(t, owner11.Name)
  170. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  171. req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &api.CreatePullRequestOption{
  172. Head: owner11.Name + ":master",
  173. Base: "master",
  174. Title: "create a failure pr",
  175. }).AddTokenAuth(token)
  176. MakeRequest(t, req, http.StatusCreated)
  177. MakeRequest(t, req, http.StatusUnprocessableEntity) // second request should fail
  178. }
  179. func TestAPICreatePullBasePermission(t *testing.T) {
  180. defer tests.PrepareTestEnv(t)()
  181. repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
  182. // repo10 have code, pulls units.
  183. repo11 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 11})
  184. // repo11 only have code unit but should still create pulls
  185. owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID})
  186. user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
  187. session := loginUser(t, user4.Name)
  188. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  189. opts := &api.CreatePullRequestOption{
  190. Head: repo11.OwnerName + ":master",
  191. Base: "master",
  192. Title: "create a failure pr",
  193. }
  194. req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &opts).AddTokenAuth(token)
  195. MakeRequest(t, req, http.StatusForbidden)
  196. // add user4 to be a collaborator to base repo
  197. ctx := NewAPITestContext(t, repo10.OwnerName, repo10.Name, auth_model.AccessTokenScopeWriteRepository)
  198. t.Run("AddUser4AsCollaborator", doAPIAddCollaborator(ctx, user4.Name, perm.AccessModeRead))
  199. // create again
  200. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &opts).AddTokenAuth(token)
  201. MakeRequest(t, req, http.StatusCreated)
  202. }
  203. func TestAPICreatePullHeadPermission(t *testing.T) {
  204. defer tests.PrepareTestEnv(t)()
  205. repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
  206. // repo10 have code, pulls units.
  207. repo11 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 11})
  208. // repo11 only have code unit but should still create pulls
  209. owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID})
  210. user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
  211. session := loginUser(t, user4.Name)
  212. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  213. opts := &api.CreatePullRequestOption{
  214. Head: repo11.OwnerName + ":master",
  215. Base: "master",
  216. Title: "create a failure pr",
  217. }
  218. req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &opts).AddTokenAuth(token)
  219. MakeRequest(t, req, http.StatusForbidden)
  220. // add user4 to be a collaborator to head repo with read permission
  221. ctx := NewAPITestContext(t, repo11.OwnerName, repo11.Name, auth_model.AccessTokenScopeWriteRepository)
  222. t.Run("AddUser4AsCollaboratorWithRead", doAPIAddCollaborator(ctx, user4.Name, perm.AccessModeRead))
  223. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &opts).AddTokenAuth(token)
  224. MakeRequest(t, req, http.StatusForbidden)
  225. // add user4 to be a collaborator to head repo with write permission
  226. t.Run("AddUser4AsCollaboratorWithWrite", doAPIAddCollaborator(ctx, user4.Name, perm.AccessModeWrite))
  227. req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &opts).AddTokenAuth(token)
  228. MakeRequest(t, req, http.StatusCreated)
  229. }
  230. func TestAPICreatePullSameRepoSuccess(t *testing.T) {
  231. defer tests.PrepareTestEnv(t)()
  232. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
  233. owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  234. session := loginUser(t, owner.Name)
  235. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  236. req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner.Name, repo.Name), &api.CreatePullRequestOption{
  237. Head: owner.Name + ":pr-to-update",
  238. Base: "master",
  239. Title: "successfully create a PR between branches of the same repository",
  240. }).AddTokenAuth(token)
  241. MakeRequest(t, req, http.StatusCreated)
  242. MakeRequest(t, req, http.StatusUnprocessableEntity) // second request should fail
  243. }
  244. func TestAPICreatePullWithFieldsSuccess(t *testing.T) {
  245. defer tests.PrepareTestEnv(t)()
  246. // repo10 have code, pulls units.
  247. repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
  248. owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID})
  249. // repo11 only have code unit but should still create pulls
  250. repo11 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 11})
  251. owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID})
  252. session := loginUser(t, owner11.Name)
  253. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  254. opts := &api.CreatePullRequestOption{
  255. Head: owner11.Name + ":master",
  256. Base: "master",
  257. Title: "create a failure pr",
  258. Body: "foobaaar",
  259. Milestone: 5,
  260. Assignees: []string{owner10.Name},
  261. Labels: []int64{5},
  262. }
  263. req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), opts).
  264. AddTokenAuth(token)
  265. res := MakeRequest(t, req, http.StatusCreated)
  266. pull := new(api.PullRequest)
  267. DecodeJSON(t, res, pull)
  268. assert.NotNil(t, pull.Milestone)
  269. assert.Equal(t, opts.Milestone, pull.Milestone.ID)
  270. if assert.Len(t, pull.Assignees, 1) {
  271. assert.Equal(t, opts.Assignees[0], owner10.Name)
  272. }
  273. assert.NotNil(t, pull.Labels)
  274. assert.Equal(t, opts.Labels[0], pull.Labels[0].ID)
  275. }
  276. func TestAPICreatePullWithFieldsFailure(t *testing.T) {
  277. defer tests.PrepareTestEnv(t)()
  278. // repo10 have code, pulls units.
  279. repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
  280. owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID})
  281. // repo11 only have code unit but should still create pulls
  282. repo11 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 11})
  283. owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID})
  284. session := loginUser(t, owner11.Name)
  285. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  286. opts := &api.CreatePullRequestOption{
  287. Head: owner11.Name + ":master",
  288. Base: "master",
  289. }
  290. req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), opts).
  291. AddTokenAuth(token)
  292. MakeRequest(t, req, http.StatusUnprocessableEntity)
  293. opts.Title = "is required"
  294. opts.Milestone = 666
  295. MakeRequest(t, req, http.StatusUnprocessableEntity)
  296. opts.Milestone = 5
  297. opts.Assignees = []string{"qweruqweroiuyqweoiruywqer"}
  298. MakeRequest(t, req, http.StatusUnprocessableEntity)
  299. opts.Assignees = []string{owner10.LoginName}
  300. opts.Labels = []int64{55555}
  301. MakeRequest(t, req, http.StatusUnprocessableEntity)
  302. opts.Labels = []int64{5}
  303. }
  304. func TestAPIEditPull(t *testing.T) {
  305. defer tests.PrepareTestEnv(t)()
  306. repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
  307. owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID})
  308. session := loginUser(t, owner10.Name)
  309. token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
  310. title := "create a success pr"
  311. req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &api.CreatePullRequestOption{
  312. Head: "develop",
  313. Base: "master",
  314. Title: title,
  315. }).AddTokenAuth(token)
  316. apiPull := new(api.PullRequest)
  317. resp := MakeRequest(t, req, http.StatusCreated)
  318. DecodeJSON(t, resp, apiPull)
  319. assert.Equal(t, "master", apiPull.Base.Name)
  320. newTitle := "edit a this pr"
  321. newBody := "edited body"
  322. req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, apiPull.Index), &api.EditPullRequestOption{
  323. Base: "feature/1",
  324. Title: newTitle,
  325. Body: &newBody,
  326. }).AddTokenAuth(token)
  327. resp = MakeRequest(t, req, http.StatusCreated)
  328. DecodeJSON(t, resp, apiPull)
  329. assert.Equal(t, "feature/1", apiPull.Base.Name)
  330. // check comment history
  331. pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: apiPull.ID})
  332. err := pull.LoadIssue(t.Context())
  333. assert.NoError(t, err)
  334. unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: pull.Issue.ID, OldTitle: title, NewTitle: newTitle})
  335. unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{IssueID: pull.Issue.ID, ContentText: newBody, IsFirstCreated: false})
  336. req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, pull.Index), &api.EditPullRequestOption{
  337. Base: "not-exist",
  338. }).AddTokenAuth(token)
  339. MakeRequest(t, req, http.StatusNotFound)
  340. }
  341. func doAPIGetPullFiles(ctx APITestContext, pr *api.PullRequest, callback func(*testing.T, []*api.ChangedFile)) func(*testing.T) {
  342. return func(t *testing.T) {
  343. req := NewRequest(t, http.MethodGet, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/files", ctx.Username, ctx.Reponame, pr.Index)).
  344. AddTokenAuth(ctx.Token)
  345. if ctx.ExpectedCode == 0 {
  346. ctx.ExpectedCode = http.StatusOK
  347. }
  348. resp := ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  349. files := make([]*api.ChangedFile, 0, 1)
  350. DecodeJSON(t, resp, &files)
  351. if callback != nil {
  352. callback(t, files)
  353. }
  354. }
  355. }
  356. func TestAPICommitPullRequest(t *testing.T) {
  357. defer tests.PrepareTestEnv(t)()
  358. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
  359. owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
  360. ctx := NewAPITestContext(t, "user2", repo.Name, auth_model.AccessTokenScopeReadRepository)
  361. mergedCommitSHA := "1a8823cd1a9549fde083f992f6b9b87a7ab74fb3"
  362. req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/commits/%s/pull", owner.Name, repo.Name, mergedCommitSHA).AddTokenAuth(ctx.Token)
  363. ctx.Session.MakeRequest(t, req, http.StatusOK)
  364. invalidCommitSHA := "abcd1234abcd1234abcd1234abcd1234abcd1234"
  365. req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/commits/%s/pull", owner.Name, repo.Name, invalidCommitSHA).AddTokenAuth(ctx.Token)
  366. ctx.Session.MakeRequest(t, req, http.StatusNotFound)
  367. }
  368. func TestAPIViewPullFilesWithHeadRepoDeleted(t *testing.T) {
  369. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  370. baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
  371. user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
  372. ctx := NewAPITestContext(t, "user1", baseRepo.Name, auth_model.AccessTokenScopeAll)
  373. doAPIForkRepository(ctx, "user2")(t)
  374. forkedRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ForkID: baseRepo.ID, OwnerName: "user1"})
  375. // add a new file to the forked repo
  376. addFileToForkedResp, err := files_service.ChangeRepoFiles(t.Context(), forkedRepo, user1, &files_service.ChangeRepoFilesOptions{
  377. Files: []*files_service.ChangeRepoFile{
  378. {
  379. Operation: "create",
  380. TreePath: "file_1.txt",
  381. ContentReader: strings.NewReader("file1"),
  382. },
  383. },
  384. Message: "add file1",
  385. OldBranch: "master",
  386. NewBranch: "fork-branch-1",
  387. Author: &files_service.IdentityOptions{
  388. GitUserName: user1.Name,
  389. GitUserEmail: user1.Email,
  390. },
  391. Committer: &files_service.IdentityOptions{
  392. GitUserName: user1.Name,
  393. GitUserEmail: user1.Email,
  394. },
  395. Dates: &files_service.CommitDateOptions{
  396. Author: time.Now(),
  397. Committer: time.Now(),
  398. },
  399. })
  400. assert.NoError(t, err)
  401. assert.NotEmpty(t, addFileToForkedResp)
  402. // create Pull
  403. pullIssue := &issues_model.Issue{
  404. RepoID: baseRepo.ID,
  405. Title: "Test pull-request-target-event",
  406. PosterID: user1.ID,
  407. Poster: user1,
  408. IsPull: true,
  409. }
  410. pullRequest := &issues_model.PullRequest{
  411. HeadRepoID: forkedRepo.ID,
  412. BaseRepoID: baseRepo.ID,
  413. HeadBranch: "fork-branch-1",
  414. BaseBranch: "master",
  415. HeadRepo: forkedRepo,
  416. BaseRepo: baseRepo,
  417. Type: issues_model.PullRequestGitea,
  418. }
  419. prOpts := &pull_service.NewPullRequestOptions{Repo: baseRepo, Issue: pullIssue, PullRequest: pullRequest}
  420. err = pull_service.NewPullRequest(t.Context(), prOpts)
  421. assert.NoError(t, err)
  422. pr := convert.ToAPIPullRequest(t.Context(), pullRequest, user1)
  423. ctx = NewAPITestContext(t, "user2", baseRepo.Name, auth_model.AccessTokenScopeAll)
  424. doAPIGetPullFiles(ctx, pr, func(t *testing.T, files []*api.ChangedFile) {
  425. if assert.Len(t, files, 1) {
  426. assert.Equal(t, "file_1.txt", files[0].Filename)
  427. assert.Empty(t, files[0].PreviousFilename)
  428. assert.Equal(t, 1, files[0].Additions)
  429. assert.Equal(t, 1, files[0].Changes)
  430. assert.Equal(t, 0, files[0].Deletions)
  431. assert.Equal(t, "added", files[0].Status)
  432. }
  433. })(t)
  434. // delete the head repository of the pull request
  435. forkCtx := NewAPITestContext(t, "user1", forkedRepo.Name, auth_model.AccessTokenScopeAll)
  436. doAPIDeleteRepository(forkCtx)(t)
  437. doAPIGetPullFiles(ctx, pr, func(t *testing.T, files []*api.ChangedFile) {
  438. if assert.Len(t, files, 1) {
  439. assert.Equal(t, "file_1.txt", files[0].Filename)
  440. assert.Empty(t, files[0].PreviousFilename)
  441. assert.Equal(t, 1, files[0].Additions)
  442. assert.Equal(t, 1, files[0].Changes)
  443. assert.Equal(t, 0, files[0].Deletions)
  444. assert.Equal(t, "added", files[0].Status)
  445. }
  446. })(t)
  447. })
  448. }