THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

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.

408 lines
14KB

  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. "net/url"
  7. "path/filepath"
  8. "testing"
  9. "time"
  10. "code.gitea.io/gitea/models"
  11. "code.gitea.io/gitea/modules/git"
  12. "code.gitea.io/gitea/modules/repofiles"
  13. "code.gitea.io/gitea/modules/setting"
  14. api "code.gitea.io/gitea/modules/structs"
  15. "code.gitea.io/gitea/modules/test"
  16. "github.com/stretchr/testify/assert"
  17. )
  18. func getCreateRepoFileOptions(repo *models.Repository) *repofiles.UpdateRepoFileOptions {
  19. return &repofiles.UpdateRepoFileOptions{
  20. OldBranch: repo.DefaultBranch,
  21. NewBranch: repo.DefaultBranch,
  22. TreePath: "new/file.txt",
  23. Message: "Creates new/file.txt",
  24. Content: "This is a NEW file",
  25. IsNewFile: true,
  26. Author: nil,
  27. Committer: nil,
  28. }
  29. }
  30. func getUpdateRepoFileOptions(repo *models.Repository) *repofiles.UpdateRepoFileOptions {
  31. return &repofiles.UpdateRepoFileOptions{
  32. OldBranch: repo.DefaultBranch,
  33. NewBranch: repo.DefaultBranch,
  34. TreePath: "README.md",
  35. Message: "Updates README.md",
  36. SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
  37. Content: "This is UPDATED content for the README file",
  38. IsNewFile: false,
  39. Author: nil,
  40. Committer: nil,
  41. }
  42. }
  43. func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileResponse {
  44. treePath := "new/file.txt"
  45. encoding := "base64"
  46. content := "VGhpcyBpcyBhIE5FVyBmaWxl"
  47. selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master"
  48. htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + treePath
  49. gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885"
  50. downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
  51. return &api.FileResponse{
  52. Content: &api.ContentsResponse{
  53. Name: filepath.Base(treePath),
  54. Path: treePath,
  55. SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
  56. Type: "file",
  57. Size: 18,
  58. Encoding: &encoding,
  59. Content: &content,
  60. URL: &selfURL,
  61. HTMLURL: &htmlURL,
  62. GitURL: &gitURL,
  63. DownloadURL: &downloadURL,
  64. Links: &api.FileLinksResponse{
  65. Self: &selfURL,
  66. GitURL: &gitURL,
  67. HTMLURL: &htmlURL,
  68. },
  69. },
  70. Commit: &api.FileCommitResponse{
  71. CommitMeta: api.CommitMeta{
  72. URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + commitID,
  73. SHA: commitID,
  74. },
  75. HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
  76. Author: &api.CommitUser{
  77. Identity: api.Identity{
  78. Name: "User Two",
  79. Email: "user2@noreply.example.org",
  80. },
  81. Date: time.Now().UTC().Format(time.RFC3339),
  82. },
  83. Committer: &api.CommitUser{
  84. Identity: api.Identity{
  85. Name: "User Two",
  86. Email: "user2@noreply.example.org",
  87. },
  88. Date: time.Now().UTC().Format(time.RFC3339),
  89. },
  90. Parents: []*api.CommitMeta{
  91. {
  92. URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
  93. SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
  94. },
  95. },
  96. Message: "Updates README.md\n",
  97. Tree: &api.CommitMeta{
  98. URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
  99. SHA: "f93e3a1a1525fb5b91020git dda86e44810c87a2d7bc",
  100. },
  101. },
  102. Verification: &api.PayloadCommitVerification{
  103. Verified: false,
  104. Reason: "gpg.error.not_signed_commit",
  105. Signature: "",
  106. Payload: "",
  107. },
  108. }
  109. }
  110. func getExpectedFileResponseForRepofilesUpdate(commitID, filename string) *api.FileResponse {
  111. encoding := "base64"
  112. content := "VGhpcyBpcyBVUERBVEVEIGNvbnRlbnQgZm9yIHRoZSBSRUFETUUgZmlsZQ=="
  113. selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + filename + "?ref=master"
  114. htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + filename
  115. gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647"
  116. downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + filename
  117. return &api.FileResponse{
  118. Content: &api.ContentsResponse{
  119. Name: filename,
  120. Path: filename,
  121. SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647",
  122. Type: "file",
  123. Size: 43,
  124. Encoding: &encoding,
  125. Content: &content,
  126. URL: &selfURL,
  127. HTMLURL: &htmlURL,
  128. GitURL: &gitURL,
  129. DownloadURL: &downloadURL,
  130. Links: &api.FileLinksResponse{
  131. Self: &selfURL,
  132. GitURL: &gitURL,
  133. HTMLURL: &htmlURL,
  134. },
  135. },
  136. Commit: &api.FileCommitResponse{
  137. CommitMeta: api.CommitMeta{
  138. URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + commitID,
  139. SHA: commitID,
  140. },
  141. HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
  142. Author: &api.CommitUser{
  143. Identity: api.Identity{
  144. Name: "User Two",
  145. Email: "user2@noreply.example.org",
  146. },
  147. Date: time.Now().UTC().Format(time.RFC3339),
  148. },
  149. Committer: &api.CommitUser{
  150. Identity: api.Identity{
  151. Name: "User Two",
  152. Email: "user2@noreply.example.org",
  153. },
  154. Date: time.Now().UTC().Format(time.RFC3339),
  155. },
  156. Parents: []*api.CommitMeta{
  157. {
  158. URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
  159. SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
  160. },
  161. },
  162. Message: "Updates README.md\n",
  163. Tree: &api.CommitMeta{
  164. URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
  165. SHA: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
  166. },
  167. },
  168. Verification: &api.PayloadCommitVerification{
  169. Verified: false,
  170. Reason: "gpg.error.not_signed_commit",
  171. Signature: "",
  172. Payload: "",
  173. },
  174. }
  175. }
  176. func TestCreateOrUpdateRepoFileForCreate(t *testing.T) {
  177. // setup
  178. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  179. ctx := test.MockContext(t, "user2/repo1")
  180. ctx.SetParams(":id", "1")
  181. test.LoadRepo(t, ctx, 1)
  182. test.LoadRepoCommit(t, ctx)
  183. test.LoadUser(t, ctx, 2)
  184. test.LoadGitRepo(t, ctx)
  185. defer ctx.Repo.GitRepo.Close()
  186. repo := ctx.Repo.Repository
  187. doer := ctx.User
  188. opts := getCreateRepoFileOptions(repo)
  189. // test
  190. fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
  191. // asserts
  192. assert.Nil(t, err)
  193. gitRepo, _ := git.OpenRepository(repo.RepoPath())
  194. defer gitRepo.Close()
  195. commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
  196. expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID)
  197. assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
  198. assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
  199. assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
  200. assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
  201. assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
  202. })
  203. }
  204. func TestCreateOrUpdateRepoFileForUpdate(t *testing.T) {
  205. // setup
  206. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  207. ctx := test.MockContext(t, "user2/repo1")
  208. ctx.SetParams(":id", "1")
  209. test.LoadRepo(t, ctx, 1)
  210. test.LoadRepoCommit(t, ctx)
  211. test.LoadUser(t, ctx, 2)
  212. test.LoadGitRepo(t, ctx)
  213. defer ctx.Repo.GitRepo.Close()
  214. repo := ctx.Repo.Repository
  215. doer := ctx.User
  216. opts := getUpdateRepoFileOptions(repo)
  217. // test
  218. fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
  219. // asserts
  220. assert.Nil(t, err)
  221. gitRepo, _ := git.OpenRepository(repo.RepoPath())
  222. defer gitRepo.Close()
  223. commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
  224. expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID, opts.TreePath)
  225. assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
  226. assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
  227. assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
  228. assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
  229. assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
  230. })
  231. }
  232. func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) {
  233. // setup
  234. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  235. ctx := test.MockContext(t, "user2/repo1")
  236. ctx.SetParams(":id", "1")
  237. test.LoadRepo(t, ctx, 1)
  238. test.LoadRepoCommit(t, ctx)
  239. test.LoadUser(t, ctx, 2)
  240. test.LoadGitRepo(t, ctx)
  241. defer ctx.Repo.GitRepo.Close()
  242. repo := ctx.Repo.Repository
  243. doer := ctx.User
  244. opts := getUpdateRepoFileOptions(repo)
  245. opts.FromTreePath = "README.md"
  246. opts.TreePath = "README_new.md" // new file name, README_new.md
  247. // test
  248. fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
  249. // asserts
  250. assert.Nil(t, err)
  251. gitRepo, _ := git.OpenRepository(repo.RepoPath())
  252. defer gitRepo.Close()
  253. commit, _ := gitRepo.GetBranchCommit(opts.NewBranch)
  254. expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.TreePath)
  255. // assert that the old file no longer exists in the last commit of the branch
  256. fromEntry, err := commit.GetTreeEntryByPath(opts.FromTreePath)
  257. switch err.(type) {
  258. case git.ErrNotExist:
  259. // correct, continue
  260. default:
  261. t.Fatalf("expected git.ErrNotExist, got:%v", err)
  262. }
  263. toEntry, err := commit.GetTreeEntryByPath(opts.TreePath)
  264. assert.Nil(t, err)
  265. assert.Nil(t, fromEntry) // Should no longer exist here
  266. assert.NotNil(t, toEntry) // Should exist here
  267. // assert SHA has remained the same but paths use the new file name
  268. assert.EqualValues(t, expectedFileResponse.Content.SHA, fileResponse.Content.SHA)
  269. assert.EqualValues(t, expectedFileResponse.Content.Name, fileResponse.Content.Name)
  270. assert.EqualValues(t, expectedFileResponse.Content.Path, fileResponse.Content.Path)
  271. assert.EqualValues(t, expectedFileResponse.Content.URL, fileResponse.Content.URL)
  272. assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
  273. assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
  274. })
  275. }
  276. // Test opts with branch names removed, should get same results as above test
  277. func TestCreateOrUpdateRepoFileWithoutBranchNames(t *testing.T) {
  278. // setup
  279. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  280. ctx := test.MockContext(t, "user2/repo1")
  281. ctx.SetParams(":id", "1")
  282. test.LoadRepo(t, ctx, 1)
  283. test.LoadRepoCommit(t, ctx)
  284. test.LoadUser(t, ctx, 2)
  285. test.LoadGitRepo(t, ctx)
  286. defer ctx.Repo.GitRepo.Close()
  287. repo := ctx.Repo.Repository
  288. doer := ctx.User
  289. opts := getUpdateRepoFileOptions(repo)
  290. opts.OldBranch = ""
  291. opts.NewBranch = ""
  292. // test
  293. fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
  294. // asserts
  295. assert.Nil(t, err)
  296. gitRepo, _ := git.OpenRepository(repo.RepoPath())
  297. defer gitRepo.Close()
  298. commitID, _ := gitRepo.GetBranchCommitID(repo.DefaultBranch)
  299. expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID, opts.TreePath)
  300. assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
  301. })
  302. }
  303. func TestCreateOrUpdateRepoFileErrors(t *testing.T) {
  304. // setup
  305. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  306. ctx := test.MockContext(t, "user2/repo1")
  307. ctx.SetParams(":id", "1")
  308. test.LoadRepo(t, ctx, 1)
  309. test.LoadRepoCommit(t, ctx)
  310. test.LoadUser(t, ctx, 2)
  311. test.LoadGitRepo(t, ctx)
  312. defer ctx.Repo.GitRepo.Close()
  313. repo := ctx.Repo.Repository
  314. doer := ctx.User
  315. t.Run("bad branch", func(t *testing.T) {
  316. opts := getUpdateRepoFileOptions(repo)
  317. opts.OldBranch = "bad_branch"
  318. fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
  319. assert.Error(t, err)
  320. assert.Nil(t, fileResponse)
  321. expectedError := "branch does not exist [name: " + opts.OldBranch + "]"
  322. assert.EqualError(t, err, expectedError)
  323. })
  324. t.Run("bad SHA", func(t *testing.T) {
  325. opts := getUpdateRepoFileOptions(repo)
  326. origSHA := opts.SHA
  327. opts.SHA = "bad_sha"
  328. fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
  329. assert.Nil(t, fileResponse)
  330. assert.Error(t, err)
  331. expectedError := "sha does not match [given: " + opts.SHA + ", expected: " + origSHA + "]"
  332. assert.EqualError(t, err, expectedError)
  333. })
  334. t.Run("new branch already exists", func(t *testing.T) {
  335. opts := getUpdateRepoFileOptions(repo)
  336. opts.NewBranch = "develop"
  337. fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
  338. assert.Nil(t, fileResponse)
  339. assert.Error(t, err)
  340. expectedError := "branch already exists [name: " + opts.NewBranch + "]"
  341. assert.EqualError(t, err, expectedError)
  342. })
  343. t.Run("treePath is empty:", func(t *testing.T) {
  344. opts := getUpdateRepoFileOptions(repo)
  345. opts.TreePath = ""
  346. fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
  347. assert.Nil(t, fileResponse)
  348. assert.Error(t, err)
  349. expectedError := "path contains a malformed path component [path: ]"
  350. assert.EqualError(t, err, expectedError)
  351. })
  352. t.Run("treePath is a git directory:", func(t *testing.T) {
  353. opts := getUpdateRepoFileOptions(repo)
  354. opts.TreePath = ".git"
  355. fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
  356. assert.Nil(t, fileResponse)
  357. assert.Error(t, err)
  358. expectedError := "path contains a malformed path component [path: " + opts.TreePath + "]"
  359. assert.EqualError(t, err, expectedError)
  360. })
  361. t.Run("create file that already exists", func(t *testing.T) {
  362. opts := getCreateRepoFileOptions(repo)
  363. opts.TreePath = "README.md" //already exists
  364. fileResponse, err := repofiles.CreateOrUpdateRepoFile(repo, doer, opts)
  365. assert.Nil(t, fileResponse)
  366. assert.Error(t, err)
  367. expectedError := "repository file already exists [path: " + opts.TreePath + "]"
  368. assert.EqualError(t, err, expectedError)
  369. })
  370. })
  371. }