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.

357 lines
12 KiB

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