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_repo_test.go 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "fmt"
  6. "net/http"
  7. "net/url"
  8. "testing"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/models/db"
  11. access_model "code.gitea.io/gitea/models/perm/access"
  12. repo_model "code.gitea.io/gitea/models/repo"
  13. "code.gitea.io/gitea/models/unittest"
  14. user_model "code.gitea.io/gitea/models/user"
  15. "code.gitea.io/gitea/modules/setting"
  16. api "code.gitea.io/gitea/modules/structs"
  17. "code.gitea.io/gitea/tests"
  18. "github.com/stretchr/testify/assert"
  19. )
  20. func TestAPIUserReposNotLogin(t *testing.T) {
  21. defer tests.PrepareTestEnv(t)()
  22. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
  23. req := NewRequestf(t, "GET", "/api/v1/users/%s/repos", user.Name)
  24. resp := MakeRequest(t, req, http.StatusOK)
  25. var apiRepos []api.Repository
  26. DecodeJSON(t, resp, &apiRepos)
  27. expectedLen := unittest.GetCount(t, repo_model.Repository{OwnerID: user.ID},
  28. unittest.Cond("is_private = ?", false))
  29. assert.Len(t, apiRepos, expectedLen)
  30. for _, repo := range apiRepos {
  31. assert.EqualValues(t, user.ID, repo.Owner.ID)
  32. assert.False(t, repo.Private)
  33. }
  34. }
  35. func TestAPISearchRepo(t *testing.T) {
  36. defer tests.PrepareTestEnv(t)()
  37. const keyword = "test"
  38. req := NewRequestf(t, "GET", "/api/v1/repos/search?q=%s", keyword)
  39. resp := MakeRequest(t, req, http.StatusOK)
  40. var body api.SearchResults
  41. DecodeJSON(t, resp, &body)
  42. assert.NotEmpty(t, body.Data)
  43. for _, repo := range body.Data {
  44. assert.Contains(t, repo.Name, keyword)
  45. assert.False(t, repo.Private)
  46. }
  47. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15})
  48. user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 16})
  49. user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 18})
  50. user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20})
  51. orgUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 17})
  52. oldAPIDefaultNum := setting.API.DefaultPagingNum
  53. defer func() {
  54. setting.API.DefaultPagingNum = oldAPIDefaultNum
  55. }()
  56. setting.API.DefaultPagingNum = 10
  57. // Map of expected results, where key is user for login
  58. type expectedResults map[*user_model.User]struct {
  59. count int
  60. repoOwnerID int64
  61. repoName string
  62. includesPrivate bool
  63. }
  64. testCases := []struct {
  65. name, requestURL string
  66. expectedResults
  67. }{
  68. {
  69. name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50&private=false", expectedResults: expectedResults{
  70. nil: {count: 30},
  71. user: {count: 30},
  72. user2: {count: 30},
  73. },
  74. },
  75. {
  76. name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10&private=false", expectedResults: expectedResults{
  77. nil: {count: 10},
  78. user: {count: 10},
  79. user2: {count: 10},
  80. },
  81. },
  82. {
  83. name: "RepositoriesDefault", requestURL: "/api/v1/repos/search?default&private=false", expectedResults: expectedResults{
  84. nil: {count: 10},
  85. user: {count: 10},
  86. user2: {count: 10},
  87. },
  88. },
  89. {
  90. name: "RepositoriesByName", requestURL: fmt.Sprintf("/api/v1/repos/search?q=%s&private=false", "big_test_"), expectedResults: expectedResults{
  91. nil: {count: 7, repoName: "big_test_"},
  92. user: {count: 7, repoName: "big_test_"},
  93. user2: {count: 7, repoName: "big_test_"},
  94. },
  95. },
  96. {
  97. name: "RepositoriesByName", requestURL: fmt.Sprintf("/api/v1/repos/search?q=%s&private=false", "user2/big_test_"), expectedResults: expectedResults{
  98. user2: {count: 2, repoName: "big_test_"},
  99. },
  100. },
  101. {
  102. name: "RepositoriesAccessibleAndRelatedToUser", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user.ID), expectedResults: expectedResults{
  103. nil: {count: 5},
  104. user: {count: 9, includesPrivate: true},
  105. user2: {count: 6, includesPrivate: true},
  106. },
  107. },
  108. {
  109. name: "RepositoriesAccessibleAndRelatedToUser2", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user2.ID), expectedResults: expectedResults{
  110. nil: {count: 1},
  111. user: {count: 2, includesPrivate: true},
  112. user2: {count: 2, includesPrivate: true},
  113. user4: {count: 1},
  114. },
  115. },
  116. {
  117. name: "RepositoriesAccessibleAndRelatedToUser3", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user3.ID), expectedResults: expectedResults{
  118. nil: {count: 1},
  119. user: {count: 4, includesPrivate: true},
  120. user2: {count: 3, includesPrivate: true},
  121. user3: {count: 4, includesPrivate: true},
  122. },
  123. },
  124. {
  125. name: "RepositoriesOwnedByOrganization", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", orgUser.ID), expectedResults: expectedResults{
  126. nil: {count: 1, repoOwnerID: orgUser.ID},
  127. user: {count: 2, repoOwnerID: orgUser.ID, includesPrivate: true},
  128. user2: {count: 1, repoOwnerID: orgUser.ID},
  129. },
  130. },
  131. {name: "RepositoriesAccessibleAndRelatedToUser4", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user4.ID), expectedResults: expectedResults{
  132. nil: {count: 3},
  133. user: {count: 4, includesPrivate: true},
  134. user4: {count: 7, includesPrivate: true},
  135. }},
  136. {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeSource", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "source"), expectedResults: expectedResults{
  137. nil: {count: 0},
  138. user: {count: 1, includesPrivate: true},
  139. user4: {count: 1, includesPrivate: true},
  140. }},
  141. {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "fork"), expectedResults: expectedResults{
  142. nil: {count: 1},
  143. user: {count: 1},
  144. user4: {count: 2, includesPrivate: true},
  145. }},
  146. {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork/Exclusive", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s&exclusive=1", user4.ID, "fork"), expectedResults: expectedResults{
  147. nil: {count: 1},
  148. user: {count: 1},
  149. user4: {count: 2, includesPrivate: true},
  150. }},
  151. {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeMirror", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "mirror"), expectedResults: expectedResults{
  152. nil: {count: 2},
  153. user: {count: 2},
  154. user4: {count: 4, includesPrivate: true},
  155. }},
  156. {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeMirror/Exclusive", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s&exclusive=1", user4.ID, "mirror"), expectedResults: expectedResults{
  157. nil: {count: 1},
  158. user: {count: 1},
  159. user4: {count: 2, includesPrivate: true},
  160. }},
  161. {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeCollaborative", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "collaborative"), expectedResults: expectedResults{
  162. nil: {count: 0},
  163. user: {count: 1, includesPrivate: true},
  164. user4: {count: 1, includesPrivate: true},
  165. }},
  166. }
  167. for _, testCase := range testCases {
  168. t.Run(testCase.name, func(t *testing.T) {
  169. for userToLogin, expected := range testCase.expectedResults {
  170. var session *TestSession
  171. var testName string
  172. var userID int64
  173. var token string
  174. if userToLogin != nil && userToLogin.ID > 0 {
  175. testName = fmt.Sprintf("LoggedUser%d", userToLogin.ID)
  176. session = loginUser(t, userToLogin.Name)
  177. token = getTokenForLoggedInUser(t, session)
  178. userID = userToLogin.ID
  179. } else {
  180. testName = "AnonymousUser"
  181. session = emptyTestSession(t)
  182. }
  183. t.Run(testName, func(t *testing.T) {
  184. request := NewRequest(t, "GET", testCase.requestURL+"&token="+token)
  185. response := session.MakeRequest(t, request, http.StatusOK)
  186. var body api.SearchResults
  187. DecodeJSON(t, response, &body)
  188. repoNames := make([]string, 0, len(body.Data))
  189. for _, repo := range body.Data {
  190. repoNames = append(repoNames, fmt.Sprintf("%d:%s:%t", repo.ID, repo.FullName, repo.Private))
  191. }
  192. assert.Len(t, repoNames, expected.count)
  193. for _, repo := range body.Data {
  194. r := getRepo(t, repo.ID)
  195. hasAccess, err := access_model.HasAccess(db.DefaultContext, userID, r)
  196. assert.NoError(t, err, "Error when checking if User: %d has access to %s: %v", userID, repo.FullName, err)
  197. assert.True(t, hasAccess, "User: %d does not have access to %s", userID, repo.FullName)
  198. assert.NotEmpty(t, repo.Name)
  199. assert.Equal(t, repo.Name, r.Name)
  200. if len(expected.repoName) > 0 {
  201. assert.Contains(t, repo.Name, expected.repoName)
  202. }
  203. if expected.repoOwnerID > 0 {
  204. assert.Equal(t, expected.repoOwnerID, repo.Owner.ID)
  205. }
  206. if !expected.includesPrivate {
  207. assert.False(t, repo.Private, "User: %d not expecting private repository: %s", userID, repo.FullName)
  208. }
  209. }
  210. })
  211. }
  212. })
  213. }
  214. }
  215. var repoCache = make(map[int64]*repo_model.Repository)
  216. func getRepo(t *testing.T, repoID int64) *repo_model.Repository {
  217. if _, ok := repoCache[repoID]; !ok {
  218. repoCache[repoID] = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
  219. }
  220. return repoCache[repoID]
  221. }
  222. func TestAPIViewRepo(t *testing.T) {
  223. defer tests.PrepareTestEnv(t)()
  224. var repo api.Repository
  225. req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1")
  226. resp := MakeRequest(t, req, http.StatusOK)
  227. DecodeJSON(t, resp, &repo)
  228. assert.EqualValues(t, 1, repo.ID)
  229. assert.EqualValues(t, "repo1", repo.Name)
  230. assert.EqualValues(t, 2, repo.Releases)
  231. assert.EqualValues(t, 1, repo.OpenIssues)
  232. assert.EqualValues(t, 3, repo.OpenPulls)
  233. req = NewRequest(t, "GET", "/api/v1/repos/user12/repo10")
  234. resp = MakeRequest(t, req, http.StatusOK)
  235. DecodeJSON(t, resp, &repo)
  236. assert.EqualValues(t, 10, repo.ID)
  237. assert.EqualValues(t, "repo10", repo.Name)
  238. assert.EqualValues(t, 1, repo.OpenPulls)
  239. assert.EqualValues(t, 1, repo.Forks)
  240. req = NewRequest(t, "GET", "/api/v1/repos/user5/repo4")
  241. resp = MakeRequest(t, req, http.StatusOK)
  242. DecodeJSON(t, resp, &repo)
  243. assert.EqualValues(t, 4, repo.ID)
  244. assert.EqualValues(t, "repo4", repo.Name)
  245. assert.EqualValues(t, 1, repo.Stars)
  246. }
  247. func TestAPIOrgRepos(t *testing.T) {
  248. defer tests.PrepareTestEnv(t)()
  249. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
  250. user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
  251. user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
  252. // User3 is an Org. Check their repos.
  253. sourceOrg := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
  254. expectedResults := map[*user_model.User]struct {
  255. count int
  256. includesPrivate bool
  257. }{
  258. nil: {count: 1},
  259. user: {count: 3, includesPrivate: true},
  260. user2: {count: 3, includesPrivate: true},
  261. user3: {count: 1},
  262. }
  263. for userToLogin, expected := range expectedResults {
  264. var session *TestSession
  265. var testName string
  266. var token string
  267. if userToLogin != nil && userToLogin.ID > 0 {
  268. testName = fmt.Sprintf("LoggedUser%d", userToLogin.ID)
  269. session = loginUser(t, userToLogin.Name)
  270. token = getTokenForLoggedInUser(t, session)
  271. } else {
  272. testName = "AnonymousUser"
  273. session = emptyTestSession(t)
  274. }
  275. t.Run(testName, func(t *testing.T) {
  276. req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token="+token, sourceOrg.Name)
  277. resp := session.MakeRequest(t, req, http.StatusOK)
  278. var apiRepos []*api.Repository
  279. DecodeJSON(t, resp, &apiRepos)
  280. assert.Len(t, apiRepos, expected.count)
  281. for _, repo := range apiRepos {
  282. if !expected.includesPrivate {
  283. assert.False(t, repo.Private)
  284. }
  285. }
  286. })
  287. }
  288. }
  289. func TestAPIGetRepoByIDUnauthorized(t *testing.T) {
  290. defer tests.PrepareTestEnv(t)()
  291. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
  292. session := loginUser(t, user.Name)
  293. token := getTokenForLoggedInUser(t, session)
  294. req := NewRequestf(t, "GET", "/api/v1/repositories/2?token="+token)
  295. session.MakeRequest(t, req, http.StatusNotFound)
  296. }
  297. func TestAPIRepoMigrate(t *testing.T) {
  298. testCases := []struct {
  299. ctxUserID, userID int64
  300. cloneURL, repoName string
  301. expectedStatus int
  302. }{
  303. {ctxUserID: 1, userID: 2, cloneURL: "https://github.com/go-gitea/test_repo.git", repoName: "git-admin", expectedStatus: http.StatusCreated},
  304. {ctxUserID: 2, userID: 2, cloneURL: "https://github.com/go-gitea/test_repo.git", repoName: "git-own", expectedStatus: http.StatusCreated},
  305. {ctxUserID: 2, userID: 1, cloneURL: "https://github.com/go-gitea/test_repo.git", repoName: "git-bad", expectedStatus: http.StatusForbidden},
  306. {ctxUserID: 2, userID: 3, cloneURL: "https://github.com/go-gitea/test_repo.git", repoName: "git-org", expectedStatus: http.StatusCreated},
  307. {ctxUserID: 2, userID: 6, cloneURL: "https://github.com/go-gitea/test_repo.git", repoName: "git-bad-org", expectedStatus: http.StatusForbidden},
  308. {ctxUserID: 2, userID: 3, cloneURL: "https://localhost:3000/user/test_repo.git", repoName: "private-ip", expectedStatus: http.StatusUnprocessableEntity},
  309. {ctxUserID: 2, userID: 3, cloneURL: "https://10.0.0.1/user/test_repo.git", repoName: "private-ip", expectedStatus: http.StatusUnprocessableEntity},
  310. }
  311. defer tests.PrepareTestEnv(t)()
  312. for _, testCase := range testCases {
  313. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID})
  314. session := loginUser(t, user.Name)
  315. token := getTokenForLoggedInUser(t, session)
  316. req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{
  317. CloneAddr: testCase.cloneURL,
  318. RepoOwnerID: testCase.userID,
  319. RepoName: testCase.repoName,
  320. })
  321. resp := MakeRequest(t, req, NoExpectedStatus)
  322. if resp.Code == http.StatusUnprocessableEntity {
  323. respJSON := map[string]string{}
  324. DecodeJSON(t, resp, &respJSON)
  325. switch respJSON["message"] {
  326. case "Remote visit addressed rate limitation.":
  327. t.Log("test hit github rate limitation")
  328. case "You can not import from disallowed hosts.":
  329. assert.EqualValues(t, "private-ip", testCase.repoName)
  330. default:
  331. assert.Failf(t, "unexpected error '%v' on url '%s'", respJSON["message"], testCase.cloneURL)
  332. }
  333. } else {
  334. assert.EqualValues(t, testCase.expectedStatus, resp.Code)
  335. }
  336. }
  337. }
  338. func TestAPIRepoMigrateConflict(t *testing.T) {
  339. onGiteaRun(t, testAPIRepoMigrateConflict)
  340. }
  341. func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) {
  342. username := "user2"
  343. baseAPITestContext := NewAPITestContext(t, username, "repo1")
  344. u.Path = baseAPITestContext.GitPath()
  345. t.Run("Existing", func(t *testing.T) {
  346. httpContext := baseAPITestContext
  347. httpContext.Reponame = "repo-tmp-17"
  348. t.Run("CreateRepo", doAPICreateRepository(httpContext, false))
  349. user, err := user_model.GetUserByName(db.DefaultContext, httpContext.Username)
  350. assert.NoError(t, err)
  351. userID := user.ID
  352. cloneURL := "https://github.com/go-gitea/test_repo.git"
  353. req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+httpContext.Token,
  354. &api.MigrateRepoOptions{
  355. CloneAddr: cloneURL,
  356. RepoOwnerID: userID,
  357. RepoName: httpContext.Reponame,
  358. })
  359. resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict)
  360. respJSON := map[string]string{}
  361. DecodeJSON(t, resp, &respJSON)
  362. assert.Equal(t, "The repository with the same name already exists.", respJSON["message"])
  363. })
  364. }
  365. // mirror-sync must fail with "400 (Bad Request)" when an attempt is made to
  366. // sync a non-mirror repository.
  367. func TestAPIMirrorSyncNonMirrorRepo(t *testing.T) {
  368. defer tests.PrepareTestEnv(t)()
  369. session := loginUser(t, "user2")
  370. token := getTokenForLoggedInUser(t, session)
  371. var repo api.Repository
  372. req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1")
  373. resp := MakeRequest(t, req, http.StatusOK)
  374. DecodeJSON(t, resp, &repo)
  375. assert.EqualValues(t, false, repo.Mirror)
  376. req = NewRequestf(t, "POST", "/api/v1/repos/user2/repo1/mirror-sync?token=%s", token)
  377. resp = session.MakeRequest(t, req, http.StatusBadRequest)
  378. errRespJSON := map[string]string{}
  379. DecodeJSON(t, resp, &errRespJSON)
  380. assert.Equal(t, "Repository is not a mirror", errRespJSON["message"])
  381. }
  382. func TestAPIOrgRepoCreate(t *testing.T) {
  383. testCases := []struct {
  384. ctxUserID int64
  385. orgName, repoName string
  386. expectedStatus int
  387. }{
  388. {ctxUserID: 1, orgName: "user3", repoName: "repo-admin", expectedStatus: http.StatusCreated},
  389. {ctxUserID: 2, orgName: "user3", repoName: "repo-own", expectedStatus: http.StatusCreated},
  390. {ctxUserID: 2, orgName: "user6", repoName: "repo-bad-org", expectedStatus: http.StatusForbidden},
  391. {ctxUserID: 28, orgName: "user3", repoName: "repo-creator", expectedStatus: http.StatusCreated},
  392. {ctxUserID: 28, orgName: "user6", repoName: "repo-not-creator", expectedStatus: http.StatusForbidden},
  393. }
  394. defer tests.PrepareTestEnv(t)()
  395. for _, testCase := range testCases {
  396. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID})
  397. session := loginUser(t, user.Name)
  398. token := getTokenForLoggedInUser(t, session)
  399. req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos?token="+token, testCase.orgName), &api.CreateRepoOption{
  400. Name: testCase.repoName,
  401. })
  402. session.MakeRequest(t, req, testCase.expectedStatus)
  403. }
  404. }
  405. func TestAPIRepoCreateConflict(t *testing.T) {
  406. onGiteaRun(t, testAPIRepoCreateConflict)
  407. }
  408. func testAPIRepoCreateConflict(t *testing.T, u *url.URL) {
  409. username := "user2"
  410. baseAPITestContext := NewAPITestContext(t, username, "repo1")
  411. u.Path = baseAPITestContext.GitPath()
  412. t.Run("Existing", func(t *testing.T) {
  413. httpContext := baseAPITestContext
  414. httpContext.Reponame = "repo-tmp-17"
  415. t.Run("CreateRepo", doAPICreateRepository(httpContext, false))
  416. req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos?token="+httpContext.Token,
  417. &api.CreateRepoOption{
  418. Name: httpContext.Reponame,
  419. })
  420. resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict)
  421. respJSON := map[string]string{}
  422. DecodeJSON(t, resp, &respJSON)
  423. assert.Equal(t, respJSON["message"], "The repository with the same name already exists.")
  424. })
  425. }
  426. func TestAPIRepoTransfer(t *testing.T) {
  427. testCases := []struct {
  428. ctxUserID int64
  429. newOwner string
  430. teams *[]int64
  431. expectedStatus int
  432. }{
  433. // Disclaimer for test story: "user1" is an admin, "user2" is normal user and part of in owner team of org "user3"
  434. // Transfer to a user with teams in another org should fail
  435. {ctxUserID: 1, newOwner: "user3", teams: &[]int64{5}, expectedStatus: http.StatusForbidden},
  436. // Transfer to a user with non-existent team IDs should fail
  437. {ctxUserID: 1, newOwner: "user2", teams: &[]int64{2}, expectedStatus: http.StatusUnprocessableEntity},
  438. // Transfer should go through
  439. {ctxUserID: 1, newOwner: "user3", teams: &[]int64{2}, expectedStatus: http.StatusAccepted},
  440. // Let user transfer it back to himself
  441. {ctxUserID: 2, newOwner: "user2", expectedStatus: http.StatusAccepted},
  442. // And revert transfer
  443. {ctxUserID: 2, newOwner: "user3", teams: &[]int64{2}, expectedStatus: http.StatusAccepted},
  444. // Cannot start transfer to an existing repo
  445. {ctxUserID: 2, newOwner: "user3", teams: nil, expectedStatus: http.StatusUnprocessableEntity},
  446. // Start transfer, repo is now in pending transfer mode
  447. {ctxUserID: 2, newOwner: "user6", teams: nil, expectedStatus: http.StatusCreated},
  448. }
  449. defer tests.PrepareTestEnv(t)()
  450. // create repo to move
  451. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
  452. session := loginUser(t, user.Name)
  453. token := getTokenForLoggedInUser(t, session)
  454. repoName := "moveME"
  455. apiRepo := new(api.Repository)
  456. req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{
  457. Name: repoName,
  458. Description: "repo move around",
  459. Private: false,
  460. Readme: "Default",
  461. AutoInit: true,
  462. })
  463. resp := session.MakeRequest(t, req, http.StatusCreated)
  464. DecodeJSON(t, resp, apiRepo)
  465. // start testing
  466. for _, testCase := range testCases {
  467. user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID})
  468. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID})
  469. session = loginUser(t, user.Name)
  470. token = getTokenForLoggedInUser(t, session)
  471. req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer?token=%s", repo.OwnerName, repo.Name, token), &api.TransferRepoOption{
  472. NewOwner: testCase.newOwner,
  473. TeamIDs: testCase.teams,
  474. })
  475. session.MakeRequest(t, req, testCase.expectedStatus)
  476. }
  477. // cleanup
  478. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID})
  479. _ = models.DeleteRepository(user, repo.OwnerID, repo.ID)
  480. }
  481. func transfer(t *testing.T) *repo_model.Repository {
  482. // create repo to move
  483. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
  484. session := loginUser(t, user.Name)
  485. token := getTokenForLoggedInUser(t, session)
  486. repoName := "moveME"
  487. apiRepo := new(api.Repository)
  488. req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{
  489. Name: repoName,
  490. Description: "repo move around",
  491. Private: false,
  492. Readme: "Default",
  493. AutoInit: true,
  494. })
  495. resp := session.MakeRequest(t, req, http.StatusCreated)
  496. DecodeJSON(t, resp, apiRepo)
  497. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID})
  498. req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer?token=%s", repo.OwnerName, repo.Name, token), &api.TransferRepoOption{
  499. NewOwner: "user4",
  500. })
  501. session.MakeRequest(t, req, http.StatusCreated)
  502. return repo
  503. }
  504. func TestAPIAcceptTransfer(t *testing.T) {
  505. defer tests.PrepareTestEnv(t)()
  506. repo := transfer(t)
  507. // try to accept with not authorized user
  508. session := loginUser(t, "user2")
  509. token := getTokenForLoggedInUser(t, session)
  510. req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token))
  511. session.MakeRequest(t, req, http.StatusForbidden)
  512. // try to accept repo that's not marked as transferred
  513. req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/accept?token=%s", "user2", "repo1", token))
  514. session.MakeRequest(t, req, http.StatusNotFound)
  515. // accept transfer
  516. session = loginUser(t, "user4")
  517. token = getTokenForLoggedInUser(t, session)
  518. req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/accept?token=%s", repo.OwnerName, repo.Name, token))
  519. resp := session.MakeRequest(t, req, http.StatusAccepted)
  520. apiRepo := new(api.Repository)
  521. DecodeJSON(t, resp, apiRepo)
  522. assert.Equal(t, "user4", apiRepo.Owner.UserName)
  523. }
  524. func TestAPIRejectTransfer(t *testing.T) {
  525. defer tests.PrepareTestEnv(t)()
  526. repo := transfer(t)
  527. // try to reject with not authorized user
  528. session := loginUser(t, "user2")
  529. token := getTokenForLoggedInUser(t, session)
  530. req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token))
  531. session.MakeRequest(t, req, http.StatusForbidden)
  532. // try to reject repo that's not marked as transferred
  533. req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", "user2", "repo1", token))
  534. session.MakeRequest(t, req, http.StatusNotFound)
  535. // reject transfer
  536. session = loginUser(t, "user4")
  537. token = getTokenForLoggedInUser(t, session)
  538. req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token))
  539. resp := session.MakeRequest(t, req, http.StatusOK)
  540. apiRepo := new(api.Repository)
  541. DecodeJSON(t, resp, apiRepo)
  542. assert.Equal(t, "user2", apiRepo.Owner.UserName)
  543. }
  544. func TestAPIGenerateRepo(t *testing.T) {
  545. defer tests.PrepareTestEnv(t)()
  546. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
  547. session := loginUser(t, user.Name)
  548. token := getTokenForLoggedInUser(t, session)
  549. templateRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 44})
  550. // user
  551. repo := new(api.Repository)
  552. req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/generate?token=%s", templateRepo.OwnerName, templateRepo.Name, token), &api.GenerateRepoOption{
  553. Owner: user.Name,
  554. Name: "new-repo",
  555. Description: "test generate repo",
  556. Private: false,
  557. GitContent: true,
  558. })
  559. resp := session.MakeRequest(t, req, http.StatusCreated)
  560. DecodeJSON(t, resp, repo)
  561. assert.Equal(t, "new-repo", repo.Name)
  562. // org
  563. req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/generate?token=%s", templateRepo.OwnerName, templateRepo.Name, token), &api.GenerateRepoOption{
  564. Owner: "user3",
  565. Name: "new-repo",
  566. Description: "test generate repo",
  567. Private: false,
  568. GitContent: true,
  569. })
  570. resp = session.MakeRequest(t, req, http.StatusCreated)
  571. DecodeJSON(t, resp, repo)
  572. assert.Equal(t, "new-repo", repo.Name)
  573. }
  574. func TestAPIRepoGetReviewers(t *testing.T) {
  575. defer tests.PrepareTestEnv(t)()
  576. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
  577. session := loginUser(t, user.Name)
  578. token := getTokenForLoggedInUser(t, session)
  579. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
  580. req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/reviewers?token=%s", user.Name, repo.Name, token)
  581. resp := session.MakeRequest(t, req, http.StatusOK)
  582. var reviewers []*api.User
  583. DecodeJSON(t, resp, &reviewers)
  584. assert.Len(t, reviewers, 4)
  585. }
  586. func TestAPIRepoGetAssignees(t *testing.T) {
  587. defer tests.PrepareTestEnv(t)()
  588. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
  589. session := loginUser(t, user.Name)
  590. token := getTokenForLoggedInUser(t, session)
  591. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
  592. req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/assignees?token=%s", user.Name, repo.Name, token)
  593. resp := session.MakeRequest(t, req, http.StatusOK)
  594. var assignees []*api.User
  595. DecodeJSON(t, resp, &assignees)
  596. assert.Len(t, assignees, 1)
  597. }