gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "fmt"
  6. "net/url"
  7. "path"
  8. "strings"
  9. "testing"
  10. "time"
  11. repo_model "code.gitea.io/gitea/models/repo"
  12. "code.gitea.io/gitea/models/unittest"
  13. "code.gitea.io/gitea/modules/git"
  14. "code.gitea.io/gitea/modules/gitrepo"
  15. "code.gitea.io/gitea/modules/setting"
  16. api "code.gitea.io/gitea/modules/structs"
  17. "code.gitea.io/gitea/modules/util"
  18. "code.gitea.io/gitea/services/contexttest"
  19. files_service "code.gitea.io/gitea/services/repository/files"
  20. "github.com/stretchr/testify/assert"
  21. )
  22. func getCreateRepoFilesOptions(repo *repo_model.Repository) *files_service.ChangeRepoFilesOptions {
  23. return &files_service.ChangeRepoFilesOptions{
  24. Files: []*files_service.ChangeRepoFile{
  25. {
  26. Operation: "create",
  27. TreePath: "new/file.txt",
  28. ContentReader: strings.NewReader("This is a NEW file"),
  29. },
  30. },
  31. OldBranch: repo.DefaultBranch,
  32. NewBranch: repo.DefaultBranch,
  33. Message: "Creates new/file.txt",
  34. Author: nil,
  35. Committer: nil,
  36. }
  37. }
  38. func getUpdateRepoFilesOptions(repo *repo_model.Repository) *files_service.ChangeRepoFilesOptions {
  39. return &files_service.ChangeRepoFilesOptions{
  40. Files: []*files_service.ChangeRepoFile{
  41. {
  42. Operation: "update",
  43. TreePath: "README.md",
  44. SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
  45. ContentReader: strings.NewReader("This is UPDATED content for the README file"),
  46. },
  47. },
  48. OldBranch: repo.DefaultBranch,
  49. NewBranch: repo.DefaultBranch,
  50. Message: "Updates README.md",
  51. Author: nil,
  52. Committer: nil,
  53. }
  54. }
  55. func getUpdateRepoFilesRenameOptions(repo *repo_model.Repository) *files_service.ChangeRepoFilesOptions {
  56. return &files_service.ChangeRepoFilesOptions{
  57. Files: []*files_service.ChangeRepoFile{
  58. // move normally
  59. {
  60. Operation: "rename",
  61. FromTreePath: "README.md",
  62. TreePath: "README.txt",
  63. },
  64. // move from in lfs
  65. {
  66. Operation: "rename",
  67. FromTreePath: "crypt.bin",
  68. TreePath: "crypt1.bin",
  69. },
  70. // move from lfs to normal
  71. {
  72. Operation: "rename",
  73. FromTreePath: "jpeg.jpg",
  74. TreePath: "jpeg.jpeg",
  75. },
  76. // move from normal to lfs
  77. {
  78. Operation: "rename",
  79. FromTreePath: "CONTRIBUTING.md",
  80. TreePath: "CONTRIBUTING.md.bin",
  81. },
  82. },
  83. OldBranch: repo.DefaultBranch,
  84. NewBranch: repo.DefaultBranch,
  85. Message: "Rename files",
  86. }
  87. }
  88. func getDeleteRepoFilesOptions(repo *repo_model.Repository) *files_service.ChangeRepoFilesOptions {
  89. return &files_service.ChangeRepoFilesOptions{
  90. Files: []*files_service.ChangeRepoFile{
  91. {
  92. Operation: "delete",
  93. TreePath: "README.md",
  94. SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
  95. },
  96. },
  97. LastCommitID: "",
  98. OldBranch: repo.DefaultBranch,
  99. NewBranch: repo.DefaultBranch,
  100. Message: "Deletes README.md",
  101. Author: &files_service.IdentityOptions{
  102. GitUserName: "Bob Smith",
  103. GitUserEmail: "bob@smith.com",
  104. },
  105. Committer: nil,
  106. }
  107. }
  108. func getExpectedFileResponseForRepoFilesDelete() *api.FileResponse {
  109. // Just returns fields that don't change, i.e. fields with commit SHAs and dates can't be determined
  110. return &api.FileResponse{
  111. Content: nil,
  112. Commit: &api.FileCommitResponse{
  113. Author: &api.CommitUser{
  114. Identity: api.Identity{
  115. Name: "Bob Smith",
  116. Email: "bob@smith.com",
  117. },
  118. },
  119. Committer: &api.CommitUser{
  120. Identity: api.Identity{
  121. Name: "Bob Smith",
  122. Email: "bob@smith.com",
  123. },
  124. },
  125. Message: "Deletes README.md\n",
  126. },
  127. Verification: &api.PayloadCommitVerification{
  128. Verified: false,
  129. Reason: "gpg.error.not_signed_commit",
  130. Signature: "",
  131. Payload: "",
  132. },
  133. }
  134. }
  135. func getExpectedFileResponseForRepoFilesCreate(commitID string, lastCommit *git.Commit) *api.FileResponse {
  136. treePath := "new/file.txt"
  137. encoding := "base64"
  138. content := "VGhpcyBpcyBhIE5FVyBmaWxl"
  139. selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master"
  140. htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + treePath
  141. gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/103ff9234cefeee5ec5361d22b49fbb04d385885"
  142. downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
  143. return &api.FileResponse{
  144. Content: &api.ContentsResponse{
  145. Name: path.Base(treePath),
  146. Path: treePath,
  147. SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
  148. LastCommitSHA: util.ToPointer(lastCommit.ID.String()),
  149. LastCommitterDate: util.ToPointer(lastCommit.Committer.When),
  150. LastAuthorDate: util.ToPointer(lastCommit.Author.When),
  151. Type: "file",
  152. Size: 18,
  153. Encoding: &encoding,
  154. Content: &content,
  155. URL: &selfURL,
  156. HTMLURL: &htmlURL,
  157. GitURL: &gitURL,
  158. DownloadURL: &downloadURL,
  159. Links: &api.FileLinksResponse{
  160. Self: &selfURL,
  161. GitURL: &gitURL,
  162. HTMLURL: &htmlURL,
  163. },
  164. },
  165. Commit: &api.FileCommitResponse{
  166. CommitMeta: api.CommitMeta{
  167. URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + commitID,
  168. SHA: commitID,
  169. },
  170. HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
  171. Author: &api.CommitUser{
  172. Identity: api.Identity{
  173. Name: "User Two",
  174. Email: "user2@noreply.example.org",
  175. },
  176. Date: time.Now().UTC().Format(time.RFC3339),
  177. },
  178. Committer: &api.CommitUser{
  179. Identity: api.Identity{
  180. Name: "User Two",
  181. Email: "user2@noreply.example.org",
  182. },
  183. Date: time.Now().UTC().Format(time.RFC3339),
  184. },
  185. Parents: []*api.CommitMeta{
  186. {
  187. URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
  188. SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
  189. },
  190. },
  191. Message: "Creates new/file.txt\n",
  192. Tree: &api.CommitMeta{
  193. URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
  194. SHA: "f93e3a1a1525fb5b91020git dda86e44810c87a2d7bc",
  195. },
  196. },
  197. Verification: &api.PayloadCommitVerification{
  198. Verified: false,
  199. Reason: "gpg.error.not_signed_commit",
  200. Signature: "",
  201. Payload: "",
  202. },
  203. }
  204. }
  205. func getExpectedFileResponseForRepoFilesUpdate(commitID, filename, lastCommitSHA string, lastCommitterWhen, lastAuthorWhen time.Time) *api.FileResponse {
  206. encoding := "base64"
  207. content := "VGhpcyBpcyBVUERBVEVEIGNvbnRlbnQgZm9yIHRoZSBSRUFETUUgZmlsZQ=="
  208. selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + filename + "?ref=master"
  209. htmlURL := setting.AppURL + "user2/repo1/src/branch/master/" + filename
  210. gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/dbf8d00e022e05b7e5cf7e535de857de57925647"
  211. downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + filename
  212. return &api.FileResponse{
  213. Content: &api.ContentsResponse{
  214. Name: filename,
  215. Path: filename,
  216. SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647",
  217. LastCommitSHA: util.ToPointer(lastCommitSHA),
  218. LastCommitterDate: util.ToPointer(lastCommitterWhen),
  219. LastAuthorDate: util.ToPointer(lastAuthorWhen),
  220. Type: "file",
  221. Size: 43,
  222. Encoding: &encoding,
  223. Content: &content,
  224. URL: &selfURL,
  225. HTMLURL: &htmlURL,
  226. GitURL: &gitURL,
  227. DownloadURL: &downloadURL,
  228. Links: &api.FileLinksResponse{
  229. Self: &selfURL,
  230. GitURL: &gitURL,
  231. HTMLURL: &htmlURL,
  232. },
  233. },
  234. Commit: &api.FileCommitResponse{
  235. CommitMeta: api.CommitMeta{
  236. URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/" + commitID,
  237. SHA: commitID,
  238. },
  239. HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
  240. Author: &api.CommitUser{
  241. Identity: api.Identity{
  242. Name: "User Two",
  243. Email: "user2@noreply.example.org",
  244. },
  245. Date: time.Now().UTC().Format(time.RFC3339),
  246. },
  247. Committer: &api.CommitUser{
  248. Identity: api.Identity{
  249. Name: "User Two",
  250. Email: "user2@noreply.example.org",
  251. },
  252. Date: time.Now().UTC().Format(time.RFC3339),
  253. },
  254. Parents: []*api.CommitMeta{
  255. {
  256. URL: setting.AppURL + "api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d",
  257. SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
  258. },
  259. },
  260. Message: "Updates README.md\n",
  261. Tree: &api.CommitMeta{
  262. URL: setting.AppURL + "api/v1/repos/user2/repo1/git/trees/f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
  263. SHA: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc",
  264. },
  265. },
  266. Verification: &api.PayloadCommitVerification{
  267. Verified: false,
  268. Reason: "gpg.error.not_signed_commit",
  269. Signature: "",
  270. Payload: "",
  271. },
  272. }
  273. }
  274. func getExpectedFileResponseForRepoFilesUpdateRename(commitID, lastCommitSHA string) *api.FilesResponse {
  275. details := []struct {
  276. filename, sha, content string
  277. size int64
  278. lfsOid *string
  279. lfsSize *int64
  280. }{
  281. {
  282. filename: "README.txt",
  283. sha: "8276d2a29779af982c0afa976bdb793b52d442a8",
  284. size: 22,
  285. content: "IyBBbiBMRlMtZW5hYmxlZCByZXBvCg==",
  286. },
  287. {
  288. filename: "crypt1.bin",
  289. sha: "d4a41a0d4db4949e129bd22f871171ea988103ef",
  290. size: 129,
  291. content: "dmVyc2lvbiBodHRwczovL2dpdC1sZnMuZ2l0aHViLmNvbS9zcGVjL3YxCm9pZCBzaGEyNTY6MmVjY2RiNDM4MjVkMmE0OWQ5OWQ1NDJkYWEyMDA3NWNmZjFkOTdkOWQyMzQ5YTg5NzdlZmU5YzAzNjYxNzM3YwpzaXplIDIwNDgK",
  292. lfsOid: util.ToPointer("2eccdb43825d2a49d99d542daa20075cff1d97d9d2349a8977efe9c03661737c"),
  293. lfsSize: util.ToPointer(int64(2048)),
  294. },
  295. {
  296. filename: "jpeg.jpeg",
  297. sha: "71911bf48766c7181518c1070911019fbb00b1fc",
  298. size: 107,
  299. content: "/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=",
  300. },
  301. {
  302. filename: "CONTRIBUTING.md.bin",
  303. sha: "2b6c6c4eaefa24b22f2092c3d54b263ff26feb58",
  304. size: 127,
  305. content: "dmVyc2lvbiBodHRwczovL2dpdC1sZnMuZ2l0aHViLmNvbS9zcGVjL3YxCm9pZCBzaGEyNTY6N2I2YjJjODhkYmE5Zjc2MGExYTU4NDY5YjY3ZmVlMmI2OThlZjdlOTM5OWM0Y2E0ZjM0YTE0Y2NiZTM5ZjYyMwpzaXplIDI3Cg==",
  306. lfsOid: util.ToPointer("7b6b2c88dba9f760a1a58469b67fee2b698ef7e9399c4ca4f34a14ccbe39f623"),
  307. lfsSize: util.ToPointer(int64(27)),
  308. },
  309. }
  310. var responses []*api.ContentsResponse
  311. for _, detail := range details {
  312. selfURL := setting.AppURL + "api/v1/repos/user2/lfs/contents/" + detail.filename + "?ref=master"
  313. htmlURL := setting.AppURL + "user2/lfs/src/branch/master/" + detail.filename
  314. gitURL := setting.AppURL + "api/v1/repos/user2/lfs/git/blobs/" + detail.sha
  315. downloadURL := setting.AppURL + "user2/lfs/raw/branch/master/" + detail.filename
  316. // don't set time related fields because there might be different time in one operation
  317. responses = append(responses, &api.ContentsResponse{
  318. Name: detail.filename,
  319. Path: detail.filename,
  320. SHA: detail.sha,
  321. LastCommitSHA: util.ToPointer(lastCommitSHA),
  322. Type: "file",
  323. Size: detail.size,
  324. Encoding: util.ToPointer("base64"),
  325. Content: &detail.content,
  326. URL: &selfURL,
  327. HTMLURL: &htmlURL,
  328. GitURL: &gitURL,
  329. DownloadURL: &downloadURL,
  330. Links: &api.FileLinksResponse{
  331. Self: &selfURL,
  332. GitURL: &gitURL,
  333. HTMLURL: &htmlURL,
  334. },
  335. LfsOid: detail.lfsOid,
  336. LfsSize: detail.lfsSize,
  337. })
  338. }
  339. return &api.FilesResponse{
  340. Files: responses,
  341. Commit: &api.FileCommitResponse{
  342. CommitMeta: api.CommitMeta{
  343. URL: setting.AppURL + "api/v1/repos/user2/lfs/git/commits/" + commitID,
  344. SHA: commitID,
  345. },
  346. HTMLURL: setting.AppURL + "user2/lfs/commit/" + commitID,
  347. Author: &api.CommitUser{
  348. Identity: api.Identity{
  349. Name: "User Two",
  350. Email: "user2@noreply.example.org",
  351. },
  352. },
  353. Committer: &api.CommitUser{
  354. Identity: api.Identity{
  355. Name: "User Two",
  356. Email: "user2@noreply.example.org",
  357. },
  358. },
  359. Parents: []*api.CommitMeta{
  360. {
  361. URL: setting.AppURL + "api/v1/repos/user2/lfs/git/commits/73cf03db6ece34e12bf91e8853dc58f678f2f82d",
  362. SHA: "73cf03db6ece34e12bf91e8853dc58f678f2f82d",
  363. },
  364. },
  365. Message: "Rename files\n",
  366. Tree: &api.CommitMeta{
  367. URL: setting.AppURL + "api/v1/repos/user2/lfs/git/trees/5307376dc3a5557dc1c403c29a8984668ca9ecb5",
  368. SHA: "5307376dc3a5557dc1c403c29a8984668ca9ecb5",
  369. },
  370. },
  371. Verification: &api.PayloadCommitVerification{
  372. Verified: false,
  373. Reason: "gpg.error.not_signed_commit",
  374. Signature: "",
  375. Payload: "",
  376. },
  377. }
  378. }
  379. func TestChangeRepoFilesForCreate(t *testing.T) {
  380. // setup
  381. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  382. ctx, _ := contexttest.MockContext(t, "user2/repo1")
  383. ctx.SetPathParam("id", "1")
  384. contexttest.LoadRepo(t, ctx, 1)
  385. contexttest.LoadRepoCommit(t, ctx)
  386. contexttest.LoadUser(t, ctx, 2)
  387. contexttest.LoadGitRepo(t, ctx)
  388. defer ctx.Repo.GitRepo.Close()
  389. repo := ctx.Repo.Repository
  390. doer := ctx.Doer
  391. opts := getCreateRepoFilesOptions(repo)
  392. // test
  393. filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
  394. // asserts
  395. assert.NoError(t, err)
  396. gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo)
  397. defer gitRepo.Close()
  398. commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
  399. lastCommit, _ := gitRepo.GetCommitByPath("new/file.txt")
  400. expectedFileResponse := getExpectedFileResponseForRepoFilesCreate(commitID, lastCommit)
  401. assert.NotNil(t, expectedFileResponse)
  402. if expectedFileResponse != nil {
  403. assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0])
  404. assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA)
  405. assert.Equal(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
  406. assert.Equal(t, expectedFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email)
  407. assert.Equal(t, expectedFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name)
  408. }
  409. })
  410. }
  411. func TestChangeRepoFilesForUpdate(t *testing.T) {
  412. // setup
  413. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  414. ctx, _ := contexttest.MockContext(t, "user2/repo1")
  415. ctx.SetPathParam("id", "1")
  416. contexttest.LoadRepo(t, ctx, 1)
  417. contexttest.LoadRepoCommit(t, ctx)
  418. contexttest.LoadUser(t, ctx, 2)
  419. contexttest.LoadGitRepo(t, ctx)
  420. defer ctx.Repo.GitRepo.Close()
  421. repo := ctx.Repo.Repository
  422. doer := ctx.Doer
  423. opts := getUpdateRepoFilesOptions(repo)
  424. // test
  425. filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
  426. // asserts
  427. assert.NoError(t, err)
  428. gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo)
  429. defer gitRepo.Close()
  430. commit, _ := gitRepo.GetBranchCommit(opts.NewBranch)
  431. lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath)
  432. expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When)
  433. assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0])
  434. assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA)
  435. assert.Equal(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
  436. assert.Equal(t, expectedFileResponse.Commit.Author.Email, filesResponse.Commit.Author.Email)
  437. assert.Equal(t, expectedFileResponse.Commit.Author.Name, filesResponse.Commit.Author.Name)
  438. })
  439. }
  440. func TestChangeRepoFilesForUpdateWithFileMove(t *testing.T) {
  441. // setup
  442. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  443. ctx, _ := contexttest.MockContext(t, "user2/repo1")
  444. ctx.SetPathParam("id", "1")
  445. contexttest.LoadRepo(t, ctx, 1)
  446. contexttest.LoadRepoCommit(t, ctx)
  447. contexttest.LoadUser(t, ctx, 2)
  448. contexttest.LoadGitRepo(t, ctx)
  449. defer ctx.Repo.GitRepo.Close()
  450. repo := ctx.Repo.Repository
  451. doer := ctx.Doer
  452. opts := getUpdateRepoFilesOptions(repo)
  453. opts.Files[0].FromTreePath = "README.md"
  454. opts.Files[0].TreePath = "README_new.md" // new file name, README_new.md
  455. // test
  456. filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
  457. // asserts
  458. assert.NoError(t, err)
  459. gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo)
  460. defer gitRepo.Close()
  461. commit, _ := gitRepo.GetBranchCommit(opts.NewBranch)
  462. lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath)
  463. expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When)
  464. // assert that the old file no longer exists in the last commit of the branch
  465. fromEntry, err := commit.GetTreeEntryByPath(opts.Files[0].FromTreePath)
  466. switch err.(type) {
  467. case git.ErrNotExist:
  468. // correct, continue
  469. default:
  470. t.Fatalf("expected git.ErrNotExist, got:%v", err)
  471. }
  472. toEntry, err := commit.GetTreeEntryByPath(opts.Files[0].TreePath)
  473. assert.NoError(t, err)
  474. assert.Nil(t, fromEntry) // Should no longer exist here
  475. assert.NotNil(t, toEntry) // Should exist here
  476. // assert SHA has remained the same but paths use the new file name
  477. assert.Equal(t, expectedFileResponse.Content.SHA, filesResponse.Files[0].SHA)
  478. assert.Equal(t, expectedFileResponse.Content.Name, filesResponse.Files[0].Name)
  479. assert.Equal(t, expectedFileResponse.Content.Path, filesResponse.Files[0].Path)
  480. assert.Equal(t, expectedFileResponse.Content.URL, filesResponse.Files[0].URL)
  481. assert.Equal(t, expectedFileResponse.Commit.SHA, filesResponse.Commit.SHA)
  482. assert.Equal(t, expectedFileResponse.Commit.HTMLURL, filesResponse.Commit.HTMLURL)
  483. })
  484. }
  485. func TestChangeRepoFilesForUpdateWithFileRename(t *testing.T) {
  486. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  487. ctx, _ := contexttest.MockContext(t, "user2/lfs")
  488. ctx.SetPathParam("id", "54")
  489. contexttest.LoadRepo(t, ctx, 54)
  490. contexttest.LoadRepoCommit(t, ctx)
  491. contexttest.LoadUser(t, ctx, 2)
  492. contexttest.LoadGitRepo(t, ctx)
  493. defer ctx.Repo.GitRepo.Close()
  494. repo := ctx.Repo.Repository
  495. opts := getUpdateRepoFilesRenameOptions(repo)
  496. // test
  497. filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, ctx.Doer, opts)
  498. // asserts
  499. assert.NoError(t, err)
  500. gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo)
  501. defer gitRepo.Close()
  502. commit, _ := gitRepo.GetBranchCommit(repo.DefaultBranch)
  503. lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath)
  504. expectedFileResponse := getExpectedFileResponseForRepoFilesUpdateRename(commit.ID.String(), lastCommit.ID.String())
  505. for _, file := range filesResponse.Files {
  506. file.LastCommitterDate, file.LastAuthorDate = nil, nil // there might be different time in one operation, so we ignore them
  507. }
  508. assert.Len(t, filesResponse.Files, 4)
  509. assert.Equal(t, expectedFileResponse.Files, filesResponse.Files)
  510. })
  511. }
  512. // Test opts with branch names removed, should get same results as above test
  513. func TestChangeRepoFilesWithoutBranchNames(t *testing.T) {
  514. // setup
  515. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  516. ctx, _ := contexttest.MockContext(t, "user2/repo1")
  517. ctx.SetPathParam("id", "1")
  518. contexttest.LoadRepo(t, ctx, 1)
  519. contexttest.LoadRepoCommit(t, ctx)
  520. contexttest.LoadUser(t, ctx, 2)
  521. contexttest.LoadGitRepo(t, ctx)
  522. defer ctx.Repo.GitRepo.Close()
  523. repo := ctx.Repo.Repository
  524. doer := ctx.Doer
  525. opts := getUpdateRepoFilesOptions(repo)
  526. opts.OldBranch = ""
  527. opts.NewBranch = ""
  528. // test
  529. filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
  530. // asserts
  531. assert.NoError(t, err)
  532. gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo)
  533. defer gitRepo.Close()
  534. commit, _ := gitRepo.GetBranchCommit(repo.DefaultBranch)
  535. lastCommit, _ := commit.GetCommitByPath(opts.Files[0].TreePath)
  536. expectedFileResponse := getExpectedFileResponseForRepoFilesUpdate(commit.ID.String(), opts.Files[0].TreePath, lastCommit.ID.String(), lastCommit.Committer.When, lastCommit.Author.When)
  537. assert.Equal(t, expectedFileResponse.Content, filesResponse.Files[0])
  538. })
  539. }
  540. func TestChangeRepoFilesForDelete(t *testing.T) {
  541. onGiteaRun(t, testDeleteRepoFiles)
  542. }
  543. func testDeleteRepoFiles(t *testing.T, u *url.URL) {
  544. // setup
  545. unittest.PrepareTestEnv(t)
  546. ctx, _ := contexttest.MockContext(t, "user2/repo1")
  547. ctx.SetPathParam("id", "1")
  548. contexttest.LoadRepo(t, ctx, 1)
  549. contexttest.LoadRepoCommit(t, ctx)
  550. contexttest.LoadUser(t, ctx, 2)
  551. contexttest.LoadGitRepo(t, ctx)
  552. defer ctx.Repo.GitRepo.Close()
  553. repo := ctx.Repo.Repository
  554. doer := ctx.Doer
  555. opts := getDeleteRepoFilesOptions(repo)
  556. t.Run("Delete README.md file", func(t *testing.T) {
  557. filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
  558. assert.NoError(t, err)
  559. expectedFileResponse := getExpectedFileResponseForRepoFilesDelete()
  560. assert.NotNil(t, filesResponse)
  561. assert.Nil(t, filesResponse.Files[0])
  562. assert.Equal(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)
  563. assert.Equal(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity)
  564. assert.Equal(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity)
  565. assert.Equal(t, expectedFileResponse.Verification, filesResponse.Verification)
  566. })
  567. t.Run("Verify README.md has been deleted", func(t *testing.T) {
  568. filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
  569. assert.Nil(t, filesResponse)
  570. expectedError := "repository file does not exist [path: " + opts.Files[0].TreePath + "]"
  571. assert.EqualError(t, err, expectedError)
  572. })
  573. }
  574. // Test opts with branch names removed, same results
  575. func TestChangeRepoFilesForDeleteWithoutBranchNames(t *testing.T) {
  576. onGiteaRun(t, testDeleteRepoFilesWithoutBranchNames)
  577. }
  578. func testDeleteRepoFilesWithoutBranchNames(t *testing.T, u *url.URL) {
  579. // setup
  580. unittest.PrepareTestEnv(t)
  581. ctx, _ := contexttest.MockContext(t, "user2/repo1")
  582. ctx.SetPathParam("id", "1")
  583. contexttest.LoadRepo(t, ctx, 1)
  584. contexttest.LoadRepoCommit(t, ctx)
  585. contexttest.LoadUser(t, ctx, 2)
  586. contexttest.LoadGitRepo(t, ctx)
  587. defer ctx.Repo.GitRepo.Close()
  588. repo := ctx.Repo.Repository
  589. doer := ctx.Doer
  590. opts := getDeleteRepoFilesOptions(repo)
  591. opts.OldBranch = ""
  592. opts.NewBranch = ""
  593. t.Run("Delete README.md without Branch Name", func(t *testing.T) {
  594. filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
  595. assert.NoError(t, err)
  596. expectedFileResponse := getExpectedFileResponseForRepoFilesDelete()
  597. assert.NotNil(t, filesResponse)
  598. assert.Nil(t, filesResponse.Files[0])
  599. assert.Equal(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)
  600. assert.Equal(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity)
  601. assert.Equal(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity)
  602. assert.Equal(t, expectedFileResponse.Verification, filesResponse.Verification)
  603. })
  604. }
  605. func TestChangeRepoFilesErrors(t *testing.T) {
  606. // setup
  607. onGiteaRun(t, func(t *testing.T, u *url.URL) {
  608. ctx, _ := contexttest.MockContext(t, "user2/repo1")
  609. ctx.SetPathParam("id", "1")
  610. contexttest.LoadRepo(t, ctx, 1)
  611. contexttest.LoadRepoCommit(t, ctx)
  612. contexttest.LoadUser(t, ctx, 2)
  613. contexttest.LoadGitRepo(t, ctx)
  614. defer ctx.Repo.GitRepo.Close()
  615. repo := ctx.Repo.Repository
  616. doer := ctx.Doer
  617. t.Run("bad branch", func(t *testing.T) {
  618. opts := getUpdateRepoFilesOptions(repo)
  619. opts.OldBranch = "bad_branch"
  620. filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
  621. assert.Error(t, err)
  622. assert.Nil(t, filesResponse)
  623. expectedError := fmt.Sprintf("branch does not exist [repo_id: %d name: %s]", repo.ID, opts.OldBranch)
  624. assert.EqualError(t, err, expectedError)
  625. })
  626. t.Run("bad SHA", func(t *testing.T) {
  627. opts := getUpdateRepoFilesOptions(repo)
  628. origSHA := opts.Files[0].SHA
  629. opts.Files[0].SHA = "bad_sha"
  630. filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
  631. assert.Nil(t, filesResponse)
  632. assert.Error(t, err)
  633. expectedError := "sha does not match [given: " + opts.Files[0].SHA + ", expected: " + origSHA + "]"
  634. assert.EqualError(t, err, expectedError)
  635. })
  636. t.Run("new branch already exists", func(t *testing.T) {
  637. opts := getUpdateRepoFilesOptions(repo)
  638. opts.NewBranch = "develop"
  639. filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
  640. assert.Nil(t, filesResponse)
  641. assert.Error(t, err)
  642. expectedError := "branch already exists [name: " + opts.NewBranch + "]"
  643. assert.EqualError(t, err, expectedError)
  644. })
  645. t.Run("treePath is empty:", func(t *testing.T) {
  646. opts := getUpdateRepoFilesOptions(repo)
  647. opts.Files[0].TreePath = ""
  648. filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
  649. assert.Nil(t, filesResponse)
  650. assert.Error(t, err)
  651. expectedError := "path contains a malformed path component [path: ]"
  652. assert.EqualError(t, err, expectedError)
  653. })
  654. t.Run("treePath is a git directory:", func(t *testing.T) {
  655. opts := getUpdateRepoFilesOptions(repo)
  656. opts.Files[0].TreePath = ".git"
  657. filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
  658. assert.Nil(t, filesResponse)
  659. assert.Error(t, err)
  660. expectedError := "path contains a malformed path component [path: " + opts.Files[0].TreePath + "]"
  661. assert.EqualError(t, err, expectedError)
  662. })
  663. t.Run("create file that already exists", func(t *testing.T) {
  664. opts := getCreateRepoFilesOptions(repo)
  665. opts.Files[0].TreePath = "README.md" // already exists
  666. fileResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
  667. assert.Nil(t, fileResponse)
  668. assert.Error(t, err)
  669. expectedError := "repository file already exists [path: " + opts.Files[0].TreePath + "]"
  670. assert.EqualError(t, err, expectedError)
  671. })
  672. })
  673. }