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_helper_for_declarative_test.go 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "context"
  6. "fmt"
  7. "net/http"
  8. "net/http/httptest"
  9. "net/url"
  10. "os"
  11. "testing"
  12. "time"
  13. "code.gitea.io/gitea/models/perm"
  14. repo_model "code.gitea.io/gitea/models/repo"
  15. "code.gitea.io/gitea/modules/json"
  16. "code.gitea.io/gitea/modules/queue"
  17. api "code.gitea.io/gitea/modules/structs"
  18. "code.gitea.io/gitea/services/forms"
  19. "github.com/stretchr/testify/assert"
  20. )
  21. type APITestContext struct {
  22. Reponame string
  23. Session *TestSession
  24. Token string
  25. Username string
  26. ExpectedCode int
  27. }
  28. func NewAPITestContext(t *testing.T, username, reponame string) APITestContext {
  29. session := loginUser(t, username)
  30. token := getTokenForLoggedInUser(t, session)
  31. return APITestContext{
  32. Session: session,
  33. Token: token,
  34. Username: username,
  35. Reponame: reponame,
  36. }
  37. }
  38. func (ctx APITestContext) GitPath() string {
  39. return fmt.Sprintf("%s/%s.git", ctx.Username, ctx.Reponame)
  40. }
  41. func doAPICreateRepository(ctx APITestContext, empty bool, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
  42. return func(t *testing.T) {
  43. createRepoOption := &api.CreateRepoOption{
  44. AutoInit: !empty,
  45. Description: "Temporary repo",
  46. Name: ctx.Reponame,
  47. Private: true,
  48. Template: true,
  49. Gitignores: "",
  50. License: "WTFPL",
  51. Readme: "Default",
  52. }
  53. req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos?token="+ctx.Token, createRepoOption)
  54. if ctx.ExpectedCode != 0 {
  55. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  56. return
  57. }
  58. resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
  59. var repository api.Repository
  60. DecodeJSON(t, resp, &repository)
  61. if len(callback) > 0 {
  62. callback[0](t, repository)
  63. }
  64. }
  65. }
  66. func doAPIEditRepository(ctx APITestContext, editRepoOption *api.EditRepoOption, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
  67. return func(t *testing.T) {
  68. req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), ctx.Token), editRepoOption)
  69. if ctx.ExpectedCode != 0 {
  70. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  71. return
  72. }
  73. resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
  74. var repository api.Repository
  75. DecodeJSON(t, resp, &repository)
  76. if len(callback) > 0 {
  77. callback[0](t, repository)
  78. }
  79. }
  80. }
  81. func doAPIAddCollaborator(ctx APITestContext, username string, mode perm.AccessMode) func(*testing.T) {
  82. return func(t *testing.T) {
  83. permission := "read"
  84. if mode == perm.AccessModeAdmin {
  85. permission = "admin"
  86. } else if mode > perm.AccessModeRead {
  87. permission = "write"
  88. }
  89. addCollaboratorOption := &api.AddCollaboratorOption{
  90. Permission: &permission,
  91. }
  92. req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/collaborators/%s?token=%s", ctx.Username, ctx.Reponame, username, ctx.Token), addCollaboratorOption)
  93. if ctx.ExpectedCode != 0 {
  94. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  95. return
  96. }
  97. ctx.Session.MakeRequest(t, req, http.StatusNoContent)
  98. }
  99. }
  100. func doAPIForkRepository(ctx APITestContext, username string, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
  101. return func(t *testing.T) {
  102. createForkOption := &api.CreateForkOption{}
  103. req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks?token=%s", username, ctx.Reponame, ctx.Token), createForkOption)
  104. if ctx.ExpectedCode != 0 {
  105. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  106. return
  107. }
  108. resp := ctx.Session.MakeRequest(t, req, http.StatusAccepted)
  109. var repository api.Repository
  110. DecodeJSON(t, resp, &repository)
  111. if len(callback) > 0 {
  112. callback[0](t, repository)
  113. }
  114. }
  115. }
  116. func doAPIGetRepository(ctx APITestContext, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
  117. return func(t *testing.T) {
  118. urlStr := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", ctx.Username, ctx.Reponame, ctx.Token)
  119. req := NewRequest(t, "GET", urlStr)
  120. if ctx.ExpectedCode != 0 {
  121. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  122. return
  123. }
  124. resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
  125. var repository api.Repository
  126. DecodeJSON(t, resp, &repository)
  127. if len(callback) > 0 {
  128. callback[0](t, repository)
  129. }
  130. }
  131. }
  132. func doAPIDeleteRepository(ctx APITestContext) func(*testing.T) {
  133. return func(t *testing.T) {
  134. urlStr := fmt.Sprintf("/api/v1/repos/%s/%s?token=%s", ctx.Username, ctx.Reponame, ctx.Token)
  135. req := NewRequest(t, "DELETE", urlStr)
  136. if ctx.ExpectedCode != 0 {
  137. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  138. return
  139. }
  140. ctx.Session.MakeRequest(t, req, http.StatusNoContent)
  141. }
  142. }
  143. func doAPICreateUserKey(ctx APITestContext, keyname, keyFile string, callback ...func(*testing.T, api.PublicKey)) func(*testing.T) {
  144. return func(t *testing.T) {
  145. urlStr := fmt.Sprintf("/api/v1/user/keys?token=%s", ctx.Token)
  146. dataPubKey, err := os.ReadFile(keyFile + ".pub")
  147. assert.NoError(t, err)
  148. req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateKeyOption{
  149. Title: keyname,
  150. Key: string(dataPubKey),
  151. })
  152. if ctx.ExpectedCode != 0 {
  153. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  154. return
  155. }
  156. resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
  157. var publicKey api.PublicKey
  158. DecodeJSON(t, resp, &publicKey)
  159. if len(callback) > 0 {
  160. callback[0](t, publicKey)
  161. }
  162. }
  163. }
  164. func doAPIDeleteUserKey(ctx APITestContext, keyID int64) func(*testing.T) {
  165. return func(t *testing.T) {
  166. urlStr := fmt.Sprintf("/api/v1/user/keys/%d?token=%s", keyID, ctx.Token)
  167. req := NewRequest(t, "DELETE", urlStr)
  168. if ctx.ExpectedCode != 0 {
  169. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  170. return
  171. }
  172. ctx.Session.MakeRequest(t, req, http.StatusNoContent)
  173. }
  174. }
  175. func doAPICreateDeployKey(ctx APITestContext, keyname, keyFile string, readOnly bool) func(*testing.T) {
  176. return func(t *testing.T) {
  177. urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", ctx.Username, ctx.Reponame, ctx.Token)
  178. dataPubKey, err := os.ReadFile(keyFile + ".pub")
  179. assert.NoError(t, err)
  180. req := NewRequestWithJSON(t, "POST", urlStr, api.CreateKeyOption{
  181. Title: keyname,
  182. Key: string(dataPubKey),
  183. ReadOnly: readOnly,
  184. })
  185. if ctx.ExpectedCode != 0 {
  186. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  187. return
  188. }
  189. ctx.Session.MakeRequest(t, req, http.StatusCreated)
  190. }
  191. }
  192. func doAPICreatePullRequest(ctx APITestContext, owner, repo, baseBranch, headBranch string) func(*testing.T) (api.PullRequest, error) {
  193. return func(t *testing.T) (api.PullRequest, error) {
  194. urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s",
  195. owner, repo, ctx.Token)
  196. req := NewRequestWithJSON(t, http.MethodPost, urlStr, &api.CreatePullRequestOption{
  197. Head: headBranch,
  198. Base: baseBranch,
  199. Title: fmt.Sprintf("create a pr from %s to %s", headBranch, baseBranch),
  200. })
  201. expected := http.StatusCreated
  202. if ctx.ExpectedCode != 0 {
  203. expected = ctx.ExpectedCode
  204. }
  205. resp := ctx.Session.MakeRequest(t, req, expected)
  206. decoder := json.NewDecoder(resp.Body)
  207. pr := api.PullRequest{}
  208. err := decoder.Decode(&pr)
  209. return pr, err
  210. }
  211. }
  212. func doAPIGetPullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) (api.PullRequest, error) {
  213. return func(t *testing.T) (api.PullRequest, error) {
  214. urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d?token=%s",
  215. owner, repo, index, ctx.Token)
  216. req := NewRequest(t, http.MethodGet, urlStr)
  217. expected := http.StatusOK
  218. if ctx.ExpectedCode != 0 {
  219. expected = ctx.ExpectedCode
  220. }
  221. resp := ctx.Session.MakeRequest(t, req, expected)
  222. decoder := json.NewDecoder(resp.Body)
  223. pr := api.PullRequest{}
  224. err := decoder.Decode(&pr)
  225. return pr, err
  226. }
  227. }
  228. func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) {
  229. return func(t *testing.T) {
  230. urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge?token=%s",
  231. owner, repo, index, ctx.Token)
  232. var req *http.Request
  233. var resp *httptest.ResponseRecorder
  234. for i := 0; i < 6; i++ {
  235. req = NewRequestWithJSON(t, http.MethodPost, urlStr, &forms.MergePullRequestForm{
  236. MergeMessageField: "doAPIMergePullRequest Merge",
  237. Do: string(repo_model.MergeStyleMerge),
  238. })
  239. resp = ctx.Session.MakeRequest(t, req, NoExpectedStatus)
  240. if resp.Code != http.StatusMethodNotAllowed {
  241. break
  242. }
  243. err := api.APIError{}
  244. DecodeJSON(t, resp, &err)
  245. assert.EqualValues(t, "Please try again later", err.Message)
  246. queue.GetManager().FlushAll(context.Background(), 5*time.Second)
  247. <-time.After(1 * time.Second)
  248. }
  249. expected := ctx.ExpectedCode
  250. if expected == 0 {
  251. expected = http.StatusOK
  252. }
  253. if !assert.EqualValues(t, expected, resp.Code,
  254. "Request: %s %s", req.Method, req.URL.String()) {
  255. logUnexpectedResponse(t, resp)
  256. }
  257. }
  258. }
  259. func doAPIManuallyMergePullRequest(ctx APITestContext, owner, repo, commitID string, index int64) func(*testing.T) {
  260. return func(t *testing.T) {
  261. urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge?token=%s",
  262. owner, repo, index, ctx.Token)
  263. req := NewRequestWithJSON(t, http.MethodPost, urlStr, &forms.MergePullRequestForm{
  264. Do: string(repo_model.MergeStyleManuallyMerged),
  265. MergeCommitID: commitID,
  266. })
  267. if ctx.ExpectedCode != 0 {
  268. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  269. return
  270. }
  271. ctx.Session.MakeRequest(t, req, http.StatusOK)
  272. }
  273. }
  274. func doAPIAutoMergePullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) {
  275. return func(t *testing.T) {
  276. urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge?token=%s",
  277. owner, repo, index, ctx.Token)
  278. req := NewRequestWithJSON(t, http.MethodPost, urlStr, &forms.MergePullRequestForm{
  279. MergeMessageField: "doAPIMergePullRequest Merge",
  280. Do: string(repo_model.MergeStyleMerge),
  281. MergeWhenChecksSucceed: true,
  282. })
  283. if ctx.ExpectedCode != 0 {
  284. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  285. return
  286. }
  287. ctx.Session.MakeRequest(t, req, 200)
  288. }
  289. }
  290. func doAPICancelAutoMergePullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) {
  291. return func(t *testing.T) {
  292. urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge?token=%s",
  293. owner, repo, index, ctx.Token)
  294. req := NewRequest(t, http.MethodDelete, urlStr)
  295. if ctx.ExpectedCode != 0 {
  296. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  297. return
  298. }
  299. ctx.Session.MakeRequest(t, req, 204)
  300. }
  301. }
  302. func doAPIGetBranch(ctx APITestContext, branch string, callback ...func(*testing.T, api.Branch)) func(*testing.T) {
  303. return func(t *testing.T) {
  304. req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/branches/%s?token=%s", ctx.Username, ctx.Reponame, branch, ctx.Token)
  305. if ctx.ExpectedCode != 0 {
  306. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  307. return
  308. }
  309. resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
  310. var branch api.Branch
  311. DecodeJSON(t, resp, &branch)
  312. if len(callback) > 0 {
  313. callback[0](t, branch)
  314. }
  315. }
  316. }
  317. func doAPICreateFile(ctx APITestContext, treepath string, options *api.CreateFileOptions, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {
  318. return func(t *testing.T) {
  319. url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", ctx.Username, ctx.Reponame, treepath, ctx.Token)
  320. req := NewRequestWithJSON(t, "POST", url, &options)
  321. if ctx.ExpectedCode != 0 {
  322. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  323. return
  324. }
  325. resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
  326. var contents api.FileResponse
  327. DecodeJSON(t, resp, &contents)
  328. if len(callback) > 0 {
  329. callback[0](t, contents)
  330. }
  331. }
  332. }
  333. func doAPICreateOrganization(ctx APITestContext, options *api.CreateOrgOption, callback ...func(*testing.T, api.Organization)) func(t *testing.T) {
  334. return func(t *testing.T) {
  335. url := fmt.Sprintf("/api/v1/orgs?token=%s", ctx.Token)
  336. req := NewRequestWithJSON(t, "POST", url, &options)
  337. if ctx.ExpectedCode != 0 {
  338. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  339. return
  340. }
  341. resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
  342. var contents api.Organization
  343. DecodeJSON(t, resp, &contents)
  344. if len(callback) > 0 {
  345. callback[0](t, contents)
  346. }
  347. }
  348. }
  349. func doAPICreateOrganizationRepository(ctx APITestContext, orgName string, options *api.CreateRepoOption, callback ...func(*testing.T, api.Repository)) func(t *testing.T) {
  350. return func(t *testing.T) {
  351. url := fmt.Sprintf("/api/v1/orgs/%s/repos?token=%s", orgName, ctx.Token)
  352. req := NewRequestWithJSON(t, "POST", url, &options)
  353. if ctx.ExpectedCode != 0 {
  354. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  355. return
  356. }
  357. resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
  358. var contents api.Repository
  359. DecodeJSON(t, resp, &contents)
  360. if len(callback) > 0 {
  361. callback[0](t, contents)
  362. }
  363. }
  364. }
  365. func doAPICreateOrganizationTeam(ctx APITestContext, orgName string, options *api.CreateTeamOption, callback ...func(*testing.T, api.Team)) func(t *testing.T) {
  366. return func(t *testing.T) {
  367. url := fmt.Sprintf("/api/v1/orgs/%s/teams?token=%s", orgName, ctx.Token)
  368. req := NewRequestWithJSON(t, "POST", url, &options)
  369. if ctx.ExpectedCode != 0 {
  370. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  371. return
  372. }
  373. resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
  374. var contents api.Team
  375. DecodeJSON(t, resp, &contents)
  376. if len(callback) > 0 {
  377. callback[0](t, contents)
  378. }
  379. }
  380. }
  381. func doAPIAddUserToOrganizationTeam(ctx APITestContext, teamID int64, username string) func(t *testing.T) {
  382. return func(t *testing.T) {
  383. url := fmt.Sprintf("/api/v1/teams/%d/members/%s?token=%s", teamID, username, ctx.Token)
  384. req := NewRequest(t, "PUT", url)
  385. if ctx.ExpectedCode != 0 {
  386. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  387. return
  388. }
  389. ctx.Session.MakeRequest(t, req, http.StatusNoContent)
  390. }
  391. }
  392. func doAPIAddRepoToOrganizationTeam(ctx APITestContext, teamID int64, orgName, repoName string) func(t *testing.T) {
  393. return func(t *testing.T) {
  394. url := fmt.Sprintf("/api/v1/teams/%d/repos/%s/%s?token=%s", teamID, orgName, repoName, ctx.Token)
  395. req := NewRequest(t, "PUT", url)
  396. if ctx.ExpectedCode != 0 {
  397. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  398. return
  399. }
  400. ctx.Session.MakeRequest(t, req, http.StatusNoContent)
  401. }
  402. }