Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

api_repo_test.go 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. // Copyright 2017 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. "io/ioutil"
  8. "net/http"
  9. "net/url"
  10. "os"
  11. "testing"
  12. "code.gitea.io/gitea/models"
  13. "code.gitea.io/gitea/modules/setting"
  14. api "code.gitea.io/gitea/modules/structs"
  15. "github.com/stretchr/testify/assert"
  16. )
  17. func TestAPIUserReposNotLogin(t *testing.T) {
  18. defer prepareTestEnv(t)()
  19. user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
  20. req := NewRequestf(t, "GET", "/api/v1/users/%s/repos", user.Name)
  21. resp := MakeRequest(t, req, http.StatusOK)
  22. var apiRepos []api.Repository
  23. DecodeJSON(t, resp, &apiRepos)
  24. expectedLen := models.GetCount(t, models.Repository{OwnerID: user.ID},
  25. models.Cond("is_private = ?", false))
  26. assert.Len(t, apiRepos, expectedLen)
  27. for _, repo := range apiRepos {
  28. assert.EqualValues(t, user.ID, repo.Owner.ID)
  29. assert.False(t, repo.Private)
  30. }
  31. }
  32. func TestAPISearchRepo(t *testing.T) {
  33. defer prepareTestEnv(t)()
  34. const keyword = "test"
  35. req := NewRequestf(t, "GET", "/api/v1/repos/search?q=%s", keyword)
  36. resp := MakeRequest(t, req, http.StatusOK)
  37. var body api.SearchResults
  38. DecodeJSON(t, resp, &body)
  39. assert.NotEmpty(t, body.Data)
  40. for _, repo := range body.Data {
  41. assert.Contains(t, repo.Name, keyword)
  42. assert.False(t, repo.Private)
  43. }
  44. user := models.AssertExistsAndLoadBean(t, &models.User{ID: 15}).(*models.User)
  45. user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 16}).(*models.User)
  46. user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 18}).(*models.User)
  47. user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 20}).(*models.User)
  48. orgUser := models.AssertExistsAndLoadBean(t, &models.User{ID: 17}).(*models.User)
  49. oldAPIDefaultNum := setting.API.DefaultPagingNum
  50. defer func() {
  51. setting.API.DefaultPagingNum = oldAPIDefaultNum
  52. }()
  53. setting.API.DefaultPagingNum = 10
  54. // Map of expected results, where key is user for login
  55. type expectedResults map[*models.User]struct {
  56. count int
  57. repoOwnerID int64
  58. repoName string
  59. includesPrivate bool
  60. }
  61. testCases := []struct {
  62. name, requestURL string
  63. expectedResults
  64. }{
  65. {name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50&private=false", expectedResults: expectedResults{
  66. nil: {count: 27},
  67. user: {count: 27},
  68. user2: {count: 27}},
  69. },
  70. {name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10&private=false", expectedResults: expectedResults{
  71. nil: {count: 10},
  72. user: {count: 10},
  73. user2: {count: 10}},
  74. },
  75. {name: "RepositoriesDefault", requestURL: "/api/v1/repos/search?default&private=false", expectedResults: expectedResults{
  76. nil: {count: 10},
  77. user: {count: 10},
  78. user2: {count: 10}},
  79. },
  80. {name: "RepositoriesByName", requestURL: fmt.Sprintf("/api/v1/repos/search?q=%s&private=false", "big_test_"), expectedResults: expectedResults{
  81. nil: {count: 7, repoName: "big_test_"},
  82. user: {count: 7, repoName: "big_test_"},
  83. user2: {count: 7, repoName: "big_test_"}},
  84. },
  85. {name: "RepositoriesAccessibleAndRelatedToUser", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user.ID), expectedResults: expectedResults{
  86. nil: {count: 5},
  87. user: {count: 9, includesPrivate: true},
  88. user2: {count: 6, includesPrivate: true}},
  89. },
  90. {name: "RepositoriesAccessibleAndRelatedToUser2", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user2.ID), expectedResults: expectedResults{
  91. nil: {count: 1},
  92. user: {count: 2, includesPrivate: true},
  93. user2: {count: 2, includesPrivate: true},
  94. user4: {count: 1}},
  95. },
  96. {name: "RepositoriesAccessibleAndRelatedToUser3", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user3.ID), expectedResults: expectedResults{
  97. nil: {count: 1},
  98. user: {count: 4, includesPrivate: true},
  99. user2: {count: 3, includesPrivate: true},
  100. user3: {count: 4, includesPrivate: true}},
  101. },
  102. {name: "RepositoriesOwnedByOrganization", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", orgUser.ID), expectedResults: expectedResults{
  103. nil: {count: 1, repoOwnerID: orgUser.ID},
  104. user: {count: 2, repoOwnerID: orgUser.ID, includesPrivate: true},
  105. user2: {count: 1, repoOwnerID: orgUser.ID}},
  106. },
  107. {name: "RepositoriesAccessibleAndRelatedToUser4", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user4.ID), expectedResults: expectedResults{
  108. nil: {count: 3},
  109. user: {count: 4, includesPrivate: true},
  110. user4: {count: 7, includesPrivate: true}}},
  111. {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeSource", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "source"), expectedResults: expectedResults{
  112. nil: {count: 0},
  113. user: {count: 1, includesPrivate: true},
  114. user4: {count: 1, includesPrivate: true}}},
  115. {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "fork"), expectedResults: expectedResults{
  116. nil: {count: 1},
  117. user: {count: 1},
  118. user4: {count: 2, includesPrivate: true}}},
  119. {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork/Exclusive", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s&exclusive=1", user4.ID, "fork"), expectedResults: expectedResults{
  120. nil: {count: 1},
  121. user: {count: 1},
  122. user4: {count: 2, includesPrivate: true}}},
  123. {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeMirror", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "mirror"), expectedResults: expectedResults{
  124. nil: {count: 2},
  125. user: {count: 2},
  126. user4: {count: 4, includesPrivate: true}}},
  127. {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeMirror/Exclusive", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s&exclusive=1", user4.ID, "mirror"), expectedResults: expectedResults{
  128. nil: {count: 1},
  129. user: {count: 1},
  130. user4: {count: 2, includesPrivate: true}}},
  131. {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeCollaborative", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "collaborative"), expectedResults: expectedResults{
  132. nil: {count: 0},
  133. user: {count: 1, includesPrivate: true},
  134. user4: {count: 1, includesPrivate: true}}},
  135. }
  136. for _, testCase := range testCases {
  137. t.Run(testCase.name, func(t *testing.T) {
  138. for userToLogin, expected := range testCase.expectedResults {
  139. var session *TestSession
  140. var testName string
  141. var userID int64
  142. var token string
  143. if userToLogin != nil && userToLogin.ID > 0 {
  144. testName = fmt.Sprintf("LoggedUser%d", userToLogin.ID)
  145. session = loginUser(t, userToLogin.Name)
  146. token = getTokenForLoggedInUser(t, session)
  147. userID = userToLogin.ID
  148. } else {
  149. testName = "AnonymousUser"
  150. session = emptyTestSession(t)
  151. }
  152. t.Run(testName, func(t *testing.T) {
  153. request := NewRequest(t, "GET", testCase.requestURL+"&token="+token)
  154. response := session.MakeRequest(t, request, http.StatusOK)
  155. var body api.SearchResults
  156. DecodeJSON(t, response, &body)
  157. repoNames := make([]string, 0, len(body.Data))
  158. for _, repo := range body.Data {
  159. repoNames = append(repoNames, fmt.Sprintf("%d:%s:%t", repo.ID, repo.FullName, repo.Private))
  160. }
  161. assert.Len(t, repoNames, expected.count)
  162. for _, repo := range body.Data {
  163. r := getRepo(t, repo.ID)
  164. hasAccess, err := models.HasAccess(userID, r)
  165. assert.NoError(t, err, "Error when checking if User: %d has access to %s: %v", userID, repo.FullName, err)
  166. assert.True(t, hasAccess, "User: %d does not have access to %s", userID, repo.FullName)
  167. assert.NotEmpty(t, repo.Name)
  168. assert.Equal(t, repo.Name, r.Name)
  169. if len(expected.repoName) > 0 {
  170. assert.Contains(t, repo.Name, expected.repoName)
  171. }
  172. if expected.repoOwnerID > 0 {
  173. assert.Equal(t, expected.repoOwnerID, repo.Owner.ID)
  174. }
  175. if !expected.includesPrivate {
  176. assert.False(t, repo.Private, "User: %d not expecting private repository: %s", userID, repo.FullName)
  177. }
  178. }
  179. })
  180. }
  181. })
  182. }
  183. }
  184. var repoCache = make(map[int64]*models.Repository)
  185. func getRepo(t *testing.T, repoID int64) *models.Repository {
  186. if _, ok := repoCache[repoID]; !ok {
  187. repoCache[repoID] = models.AssertExistsAndLoadBean(t, &models.Repository{ID: repoID}).(*models.Repository)
  188. }
  189. return repoCache[repoID]
  190. }
  191. func TestAPIViewRepo(t *testing.T) {
  192. defer prepareTestEnv(t)()
  193. var repo api.Repository
  194. req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1")
  195. resp := MakeRequest(t, req, http.StatusOK)
  196. DecodeJSON(t, resp, &repo)
  197. assert.EqualValues(t, 1, repo.ID)
  198. assert.EqualValues(t, "repo1", repo.Name)
  199. assert.EqualValues(t, 1, repo.Releases)
  200. assert.EqualValues(t, 1, repo.OpenIssues)
  201. assert.EqualValues(t, 3, repo.OpenPulls)
  202. req = NewRequest(t, "GET", "/api/v1/repos/user12/repo10")
  203. resp = MakeRequest(t, req, http.StatusOK)
  204. DecodeJSON(t, resp, &repo)
  205. assert.EqualValues(t, 10, repo.ID)
  206. assert.EqualValues(t, "repo10", repo.Name)
  207. assert.EqualValues(t, 1, repo.OpenPulls)
  208. assert.EqualValues(t, 1, repo.Forks)
  209. req = NewRequest(t, "GET", "/api/v1/repos/user5/repo4")
  210. resp = MakeRequest(t, req, http.StatusOK)
  211. DecodeJSON(t, resp, &repo)
  212. assert.EqualValues(t, 4, repo.ID)
  213. assert.EqualValues(t, "repo4", repo.Name)
  214. assert.EqualValues(t, 1, repo.Stars)
  215. }
  216. func TestAPIOrgRepos(t *testing.T) {
  217. defer prepareTestEnv(t)()
  218. user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
  219. user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
  220. user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 5}).(*models.User)
  221. // User3 is an Org. Check their repos.
  222. sourceOrg := models.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)
  223. expectedResults := map[*models.User]struct {
  224. count int
  225. includesPrivate bool
  226. }{
  227. nil: {count: 1},
  228. user: {count: 3, includesPrivate: true},
  229. user2: {count: 3, includesPrivate: true},
  230. user3: {count: 1},
  231. }
  232. for userToLogin, expected := range expectedResults {
  233. var session *TestSession
  234. var testName string
  235. var token string
  236. if userToLogin != nil && userToLogin.ID > 0 {
  237. testName = fmt.Sprintf("LoggedUser%d", userToLogin.ID)
  238. session = loginUser(t, userToLogin.Name)
  239. token = getTokenForLoggedInUser(t, session)
  240. } else {
  241. testName = "AnonymousUser"
  242. session = emptyTestSession(t)
  243. }
  244. t.Run(testName, func(t *testing.T) {
  245. req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token="+token, sourceOrg.Name)
  246. resp := session.MakeRequest(t, req, http.StatusOK)
  247. var apiRepos []*api.Repository
  248. DecodeJSON(t, resp, &apiRepos)
  249. assert.Len(t, apiRepos, expected.count)
  250. for _, repo := range apiRepos {
  251. if !expected.includesPrivate {
  252. assert.False(t, repo.Private)
  253. }
  254. }
  255. })
  256. }
  257. }
  258. func TestAPIGetRepoByIDUnauthorized(t *testing.T) {
  259. defer prepareTestEnv(t)()
  260. user := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User)
  261. session := loginUser(t, user.Name)
  262. token := getTokenForLoggedInUser(t, session)
  263. req := NewRequestf(t, "GET", "/api/v1/repositories/2?token="+token)
  264. session.MakeRequest(t, req, http.StatusNotFound)
  265. }
  266. func TestAPIRepoMigrate(t *testing.T) {
  267. testCases := []struct {
  268. ctxUserID, userID int64
  269. cloneURL, repoName string
  270. expectedStatus int
  271. }{
  272. {ctxUserID: 1, userID: 2, cloneURL: "https://github.com/go-gitea/git.git", repoName: "git-admin", expectedStatus: http.StatusCreated},
  273. {ctxUserID: 2, userID: 2, cloneURL: "https://github.com/go-gitea/git.git", repoName: "git-own", expectedStatus: http.StatusCreated},
  274. {ctxUserID: 2, userID: 1, cloneURL: "https://github.com/go-gitea/git.git", repoName: "git-bad", expectedStatus: http.StatusForbidden},
  275. {ctxUserID: 2, userID: 3, cloneURL: "https://github.com/go-gitea/git.git", repoName: "git-org", expectedStatus: http.StatusCreated},
  276. {ctxUserID: 2, userID: 6, cloneURL: "https://github.com/go-gitea/git.git", repoName: "git-bad-org", expectedStatus: http.StatusForbidden},
  277. }
  278. defer prepareTestEnv(t)()
  279. for _, testCase := range testCases {
  280. user := models.AssertExistsAndLoadBean(t, &models.User{ID: testCase.ctxUserID}).(*models.User)
  281. session := loginUser(t, user.Name)
  282. token := getTokenForLoggedInUser(t, session)
  283. req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOption{
  284. CloneAddr: testCase.cloneURL,
  285. UID: int(testCase.userID),
  286. RepoName: testCase.repoName,
  287. })
  288. session.MakeRequest(t, req, testCase.expectedStatus)
  289. }
  290. }
  291. func TestAPIRepoMigrateConflict(t *testing.T) {
  292. onGiteaRun(t, testAPIRepoMigrateConflict)
  293. }
  294. func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) {
  295. username := "user2"
  296. baseAPITestContext := NewAPITestContext(t, username, "repo1")
  297. u.Path = baseAPITestContext.GitPath()
  298. t.Run("Existing", func(t *testing.T) {
  299. httpContext := baseAPITestContext
  300. httpContext.Reponame = "repo-tmp-17"
  301. dstPath, err := ioutil.TempDir("", httpContext.Reponame)
  302. assert.NoError(t, err)
  303. defer os.RemoveAll(dstPath)
  304. t.Run("CreateRepo", doAPICreateRepository(httpContext, false))
  305. user, err := models.GetUserByName(httpContext.Username)
  306. assert.NoError(t, err)
  307. userID := user.ID
  308. cloneURL := "https://github.com/go-gitea/git.git"
  309. req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+httpContext.Token,
  310. &api.MigrateRepoOption{
  311. CloneAddr: cloneURL,
  312. UID: int(userID),
  313. RepoName: httpContext.Reponame,
  314. })
  315. resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict)
  316. respJSON := map[string]string{}
  317. DecodeJSON(t, resp, &respJSON)
  318. assert.Equal(t, "The repository with the same name already exists.", respJSON["message"])
  319. })
  320. }
  321. func TestAPIOrgRepoCreate(t *testing.T) {
  322. testCases := []struct {
  323. ctxUserID int64
  324. orgName, repoName string
  325. expectedStatus int
  326. }{
  327. {ctxUserID: 1, orgName: "user3", repoName: "repo-admin", expectedStatus: http.StatusCreated},
  328. {ctxUserID: 2, orgName: "user3", repoName: "repo-own", expectedStatus: http.StatusCreated},
  329. {ctxUserID: 2, orgName: "user6", repoName: "repo-bad-org", expectedStatus: http.StatusForbidden},
  330. {ctxUserID: 28, orgName: "user3", repoName: "repo-creator", expectedStatus: http.StatusCreated},
  331. {ctxUserID: 28, orgName: "user6", repoName: "repo-not-creator", expectedStatus: http.StatusForbidden},
  332. }
  333. defer prepareTestEnv(t)()
  334. for _, testCase := range testCases {
  335. user := models.AssertExistsAndLoadBean(t, &models.User{ID: testCase.ctxUserID}).(*models.User)
  336. session := loginUser(t, user.Name)
  337. token := getTokenForLoggedInUser(t, session)
  338. req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos?token="+token, testCase.orgName), &api.CreateRepoOption{
  339. Name: testCase.repoName,
  340. })
  341. session.MakeRequest(t, req, testCase.expectedStatus)
  342. }
  343. }
  344. func TestAPIRepoCreateConflict(t *testing.T) {
  345. onGiteaRun(t, testAPIRepoCreateConflict)
  346. }
  347. func testAPIRepoCreateConflict(t *testing.T, u *url.URL) {
  348. username := "user2"
  349. baseAPITestContext := NewAPITestContext(t, username, "repo1")
  350. u.Path = baseAPITestContext.GitPath()
  351. t.Run("Existing", func(t *testing.T) {
  352. httpContext := baseAPITestContext
  353. httpContext.Reponame = "repo-tmp-17"
  354. dstPath, err := ioutil.TempDir("", httpContext.Reponame)
  355. assert.NoError(t, err)
  356. defer os.RemoveAll(dstPath)
  357. t.Run("CreateRepo", doAPICreateRepository(httpContext, false))
  358. req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos?token="+httpContext.Token,
  359. &api.CreateRepoOption{
  360. Name: httpContext.Reponame,
  361. })
  362. resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict)
  363. respJSON := map[string]string{}
  364. DecodeJSON(t, resp, &respJSON)
  365. assert.Equal(t, respJSON["message"], "The repository with the same name already exists.")
  366. })
  367. }
  368. func TestAPIRepoTransfer(t *testing.T) {
  369. testCases := []struct {
  370. ctxUserID int64
  371. newOwner string
  372. teams *[]int64
  373. expectedStatus int
  374. }{
  375. {ctxUserID: 1, newOwner: "user2", teams: nil, expectedStatus: http.StatusAccepted},
  376. {ctxUserID: 2, newOwner: "user1", teams: nil, expectedStatus: http.StatusAccepted},
  377. {ctxUserID: 2, newOwner: "user6", teams: nil, expectedStatus: http.StatusForbidden},
  378. {ctxUserID: 1, newOwner: "user2", teams: &[]int64{2}, expectedStatus: http.StatusUnprocessableEntity},
  379. {ctxUserID: 1, newOwner: "user3", teams: &[]int64{5}, expectedStatus: http.StatusForbidden},
  380. {ctxUserID: 1, newOwner: "user3", teams: &[]int64{2}, expectedStatus: http.StatusAccepted},
  381. }
  382. defer prepareTestEnv(t)()
  383. //create repo to move
  384. user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
  385. session := loginUser(t, user.Name)
  386. token := getTokenForLoggedInUser(t, session)
  387. repoName := "moveME"
  388. repo := new(models.Repository)
  389. req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{
  390. Name: repoName,
  391. Description: "repo move around",
  392. Private: false,
  393. Readme: "Default",
  394. AutoInit: true,
  395. })
  396. resp := session.MakeRequest(t, req, http.StatusCreated)
  397. DecodeJSON(t, resp, repo)
  398. //start testing
  399. for _, testCase := range testCases {
  400. user = models.AssertExistsAndLoadBean(t, &models.User{ID: testCase.ctxUserID}).(*models.User)
  401. repo = models.AssertExistsAndLoadBean(t, &models.Repository{ID: repo.ID}).(*models.Repository)
  402. session = loginUser(t, user.Name)
  403. token = getTokenForLoggedInUser(t, session)
  404. req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer?token=%s", repo.OwnerName, repo.Name, token), &api.TransferRepoOption{
  405. NewOwner: testCase.newOwner,
  406. TeamIDs: testCase.teams,
  407. })
  408. session.MakeRequest(t, req, testCase.expectedStatus)
  409. }
  410. //cleanup
  411. repo = models.AssertExistsAndLoadBean(t, &models.Repository{ID: repo.ID}).(*models.Repository)
  412. _ = models.DeleteRepository(user, repo.OwnerID, repo.ID)
  413. }