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

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