gitea源码

api_helper_for_declarative_test.go 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "fmt"
  6. "net/http"
  7. "net/http/httptest"
  8. "net/url"
  9. "os"
  10. "testing"
  11. "time"
  12. "code.gitea.io/gitea/models/auth"
  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, scope ...auth.AccessTokenScope) APITestContext {
  29. session := loginUser(t, username)
  30. if len(scope) == 0 {
  31. // FIXME: legacy logic: no scope means all
  32. scope = []auth.AccessTokenScope{auth.AccessTokenScopeAll}
  33. }
  34. token := getTokenForLoggedInUser(t, session, scope...)
  35. return APITestContext{
  36. Session: session,
  37. Token: token,
  38. Username: username,
  39. Reponame: reponame,
  40. }
  41. }
  42. func (ctx APITestContext) GitPath() string {
  43. return fmt.Sprintf("%s/%s.git", ctx.Username, ctx.Reponame)
  44. }
  45. func doAPICreateRepository(ctx APITestContext, empty bool, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
  46. return func(t *testing.T) {
  47. createRepoOption := &api.CreateRepoOption{
  48. AutoInit: !empty,
  49. Description: "Temporary repo",
  50. Name: ctx.Reponame,
  51. Private: true,
  52. Template: true,
  53. Gitignores: "",
  54. License: "WTFPL",
  55. Readme: "Default",
  56. }
  57. req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos", createRepoOption).
  58. AddTokenAuth(ctx.Token)
  59. if ctx.ExpectedCode != 0 {
  60. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  61. return
  62. }
  63. resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
  64. var repository api.Repository
  65. DecodeJSON(t, resp, &repository)
  66. if len(callback) > 0 {
  67. callback[0](t, repository)
  68. }
  69. }
  70. }
  71. func doAPIEditRepository(ctx APITestContext, editRepoOption *api.EditRepoOption, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
  72. return func(t *testing.T) {
  73. req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/repos/%s/%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)), editRepoOption).
  74. AddTokenAuth(ctx.Token)
  75. if ctx.ExpectedCode != 0 {
  76. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  77. return
  78. }
  79. resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
  80. var repository api.Repository
  81. DecodeJSON(t, resp, &repository)
  82. if len(callback) > 0 {
  83. callback[0](t, repository)
  84. }
  85. }
  86. }
  87. func doAPIAddCollaborator(ctx APITestContext, username string, mode perm.AccessMode) func(*testing.T) {
  88. return func(t *testing.T) {
  89. permission := "read"
  90. if mode == perm.AccessModeAdmin {
  91. permission = "admin"
  92. } else if mode > perm.AccessModeRead {
  93. permission = "write"
  94. }
  95. addCollaboratorOption := &api.AddCollaboratorOption{
  96. Permission: &permission,
  97. }
  98. req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/collaborators/%s", ctx.Username, ctx.Reponame, username), addCollaboratorOption).
  99. AddTokenAuth(ctx.Token)
  100. if ctx.ExpectedCode != 0 {
  101. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  102. return
  103. }
  104. ctx.Session.MakeRequest(t, req, http.StatusNoContent)
  105. }
  106. }
  107. func doAPIForkRepository(ctx APITestContext, username string, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
  108. return func(t *testing.T) {
  109. createForkOption := &api.CreateForkOption{}
  110. req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks", username, ctx.Reponame), createForkOption).
  111. AddTokenAuth(ctx.Token)
  112. if ctx.ExpectedCode != 0 {
  113. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  114. return
  115. }
  116. resp := ctx.Session.MakeRequest(t, req, http.StatusAccepted)
  117. var repository api.Repository
  118. DecodeJSON(t, resp, &repository)
  119. if len(callback) > 0 {
  120. callback[0](t, repository)
  121. }
  122. }
  123. }
  124. func doAPIGetRepository(ctx APITestContext, callback ...func(*testing.T, api.Repository)) func(*testing.T) {
  125. return func(t *testing.T) {
  126. req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s", ctx.Username, ctx.Reponame)).
  127. AddTokenAuth(ctx.Token)
  128. if ctx.ExpectedCode != 0 {
  129. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  130. return
  131. }
  132. resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
  133. var repository api.Repository
  134. DecodeJSON(t, resp, &repository)
  135. if len(callback) > 0 {
  136. callback[0](t, repository)
  137. }
  138. }
  139. }
  140. func doAPIDeleteRepository(ctx APITestContext) func(*testing.T) {
  141. return func(t *testing.T) {
  142. req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/%s", ctx.Username, ctx.Reponame)).
  143. AddTokenAuth(ctx.Token)
  144. if ctx.ExpectedCode != 0 {
  145. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  146. return
  147. }
  148. ctx.Session.MakeRequest(t, req, http.StatusNoContent)
  149. }
  150. }
  151. func doAPICreateUserKey(ctx APITestContext, keyname, keyFile string, callback ...func(*testing.T, api.PublicKey)) func(*testing.T) {
  152. return func(t *testing.T) {
  153. dataPubKey, err := os.ReadFile(keyFile + ".pub")
  154. assert.NoError(t, err)
  155. req := NewRequestWithJSON(t, "POST", "/api/v1/user/keys", &api.CreateKeyOption{
  156. Title: keyname,
  157. Key: string(dataPubKey),
  158. }).AddTokenAuth(ctx.Token)
  159. if ctx.ExpectedCode != 0 {
  160. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  161. return
  162. }
  163. resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
  164. var publicKey api.PublicKey
  165. DecodeJSON(t, resp, &publicKey)
  166. if len(callback) > 0 {
  167. callback[0](t, publicKey)
  168. }
  169. }
  170. }
  171. func doAPIDeleteUserKey(ctx APITestContext, keyID int64) func(*testing.T) {
  172. return func(t *testing.T) {
  173. req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/keys/%d", keyID)).
  174. AddTokenAuth(ctx.Token)
  175. if ctx.ExpectedCode != 0 {
  176. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  177. return
  178. }
  179. ctx.Session.MakeRequest(t, req, http.StatusNoContent)
  180. }
  181. }
  182. func doAPICreateDeployKey(ctx APITestContext, keyname, keyFile string, readOnly bool) func(*testing.T) {
  183. return func(t *testing.T) {
  184. dataPubKey, err := os.ReadFile(keyFile + ".pub")
  185. assert.NoError(t, err)
  186. req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/keys", ctx.Username, ctx.Reponame), api.CreateKeyOption{
  187. Title: keyname,
  188. Key: string(dataPubKey),
  189. ReadOnly: readOnly,
  190. }).AddTokenAuth(ctx.Token)
  191. if ctx.ExpectedCode != 0 {
  192. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  193. return
  194. }
  195. ctx.Session.MakeRequest(t, req, http.StatusCreated)
  196. }
  197. }
  198. func doAPICreatePullRequest(ctx APITestContext, owner, repo, baseBranch, headBranch string) func(*testing.T) (api.PullRequest, error) {
  199. return func(t *testing.T) (api.PullRequest, error) {
  200. req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner, repo), &api.CreatePullRequestOption{
  201. Head: headBranch,
  202. Base: baseBranch,
  203. Title: fmt.Sprintf("create a pr from %s to %s", headBranch, baseBranch),
  204. }).AddTokenAuth(ctx.Token)
  205. expected := http.StatusCreated
  206. if ctx.ExpectedCode != 0 {
  207. expected = ctx.ExpectedCode
  208. }
  209. resp := ctx.Session.MakeRequest(t, req, expected)
  210. decoder := json.NewDecoder(resp.Body)
  211. pr := api.PullRequest{}
  212. err := decoder.Decode(&pr)
  213. return pr, err
  214. }
  215. }
  216. func doAPIGetPullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) (api.PullRequest, error) {
  217. return func(t *testing.T) (api.PullRequest, error) {
  218. req := NewRequest(t, http.MethodGet, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner, repo, index)).
  219. AddTokenAuth(ctx.Token)
  220. expected := http.StatusOK
  221. if ctx.ExpectedCode != 0 {
  222. expected = ctx.ExpectedCode
  223. }
  224. resp := ctx.Session.MakeRequest(t, req, expected)
  225. decoder := json.NewDecoder(resp.Body)
  226. pr := api.PullRequest{}
  227. err := decoder.Decode(&pr)
  228. return pr, err
  229. }
  230. }
  231. func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) {
  232. return func(t *testing.T) {
  233. urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge", owner, repo, index)
  234. var req *RequestWrapper
  235. var resp *httptest.ResponseRecorder
  236. for range 6 {
  237. req = NewRequestWithJSON(t, http.MethodPost, urlStr, &forms.MergePullRequestForm{
  238. MergeMessageField: "doAPIMergePullRequest Merge",
  239. Do: string(repo_model.MergeStyleMerge),
  240. }).AddTokenAuth(ctx.Token)
  241. resp = ctx.Session.MakeRequest(t, req, NoExpectedStatus)
  242. if resp.Code != http.StatusMethodNotAllowed {
  243. break
  244. }
  245. err := api.APIError{}
  246. DecodeJSON(t, resp, &err)
  247. assert.Equal(t, "Please try again later", err.Message)
  248. queue.GetManager().FlushAll(t.Context(), 5*time.Second)
  249. <-time.After(1 * time.Second)
  250. }
  251. expected := ctx.ExpectedCode
  252. if expected == 0 {
  253. expected = http.StatusOK
  254. }
  255. if !assert.Equal(t, expected, resp.Code,
  256. "Request: %s %s", req.Method, req.URL.String()) {
  257. logUnexpectedResponse(t, resp)
  258. }
  259. }
  260. }
  261. func doAPIManuallyMergePullRequest(ctx APITestContext, owner, repo, commitID string, index int64) func(*testing.T) {
  262. return func(t *testing.T) {
  263. urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge", owner, repo, index)
  264. req := NewRequestWithJSON(t, http.MethodPost, urlStr, &forms.MergePullRequestForm{
  265. Do: string(repo_model.MergeStyleManuallyMerged),
  266. MergeCommitID: commitID,
  267. }).AddTokenAuth(ctx.Token)
  268. if ctx.ExpectedCode != 0 {
  269. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  270. return
  271. }
  272. ctx.Session.MakeRequest(t, req, http.StatusOK)
  273. }
  274. }
  275. func doAPIAutoMergePullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) {
  276. return func(t *testing.T) {
  277. urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge", owner, repo, index)
  278. req := NewRequestWithJSON(t, http.MethodPost, urlStr, &forms.MergePullRequestForm{
  279. MergeMessageField: "doAPIMergePullRequest Merge",
  280. Do: string(repo_model.MergeStyleMerge),
  281. MergeWhenChecksSucceed: true,
  282. }).AddTokenAuth(ctx.Token)
  283. if ctx.ExpectedCode != 0 {
  284. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  285. return
  286. }
  287. ctx.Session.MakeRequest(t, req, http.StatusOK)
  288. }
  289. }
  290. func doAPICancelAutoMergePullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) {
  291. return func(t *testing.T) {
  292. req := NewRequest(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge", owner, repo, index)).
  293. AddTokenAuth(ctx.Token)
  294. if ctx.ExpectedCode != 0 {
  295. ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
  296. return
  297. }
  298. ctx.Session.MakeRequest(t, req, http.StatusNoContent)
  299. }
  300. }
  301. func doAPIGetBranch(ctx APITestContext, branch string, callback ...func(*testing.T, api.Branch)) func(*testing.T) {
  302. return func(t *testing.T) {
  303. req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/branches/%s", ctx.Username, ctx.Reponame, branch).
  304. AddTokenAuth(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. req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", ctx.Username, ctx.Reponame, treepath), &options).
  320. AddTokenAuth(ctx.Token)
  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. req := NewRequestWithJSON(t, "POST", "/api/v1/orgs", &options).
  336. AddTokenAuth(ctx.Token)
  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. req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/repos", orgName), &options).
  352. AddTokenAuth(ctx.Token)
  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. req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/teams", orgName), &options).
  368. AddTokenAuth(ctx.Token)
  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. req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/teams/%d/members/%s", teamID, username)).
  384. AddTokenAuth(ctx.Token)
  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. req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/teams/%d/repos/%s/%s", teamID, orgName, repoName)).
  395. AddTokenAuth(ctx.Token)
  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. }