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.

repofiles_update_test.go 13KB

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