Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

api_actions_artifact_test.go 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "net/http"
  6. "strings"
  7. "testing"
  8. "code.gitea.io/gitea/tests"
  9. "github.com/stretchr/testify/assert"
  10. )
  11. type uploadArtifactResponse struct {
  12. FileContainerResourceURL string `json:"fileContainerResourceUrl"`
  13. }
  14. type getUploadArtifactRequest struct {
  15. Type string
  16. Name string
  17. RetentionDays int64
  18. }
  19. func TestActionsArtifactUploadSingleFile(t *testing.T) {
  20. defer tests.PrepareTestEnv(t)()
  21. // acquire artifact upload url
  22. req := NewRequestWithJSON(t, "POST", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts", getUploadArtifactRequest{
  23. Type: "actions_storage",
  24. Name: "artifact",
  25. }).AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  26. resp := MakeRequest(t, req, http.StatusOK)
  27. var uploadResp uploadArtifactResponse
  28. DecodeJSON(t, resp, &uploadResp)
  29. assert.Contains(t, uploadResp.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
  30. // get upload url
  31. idx := strings.Index(uploadResp.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
  32. url := uploadResp.FileContainerResourceURL[idx:] + "?itemPath=artifact/abc.txt"
  33. // upload artifact chunk
  34. body := strings.Repeat("A", 1024)
  35. req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body)).
  36. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a").
  37. SetHeader("Content-Range", "bytes 0-1023/1024").
  38. SetHeader("x-tfs-filelength", "1024").
  39. SetHeader("x-actions-results-md5", "1HsSe8LeLWh93ILaw1TEFQ==") // base64(md5(body))
  40. MakeRequest(t, req, http.StatusOK)
  41. t.Logf("Create artifact confirm")
  42. // confirm artifact upload
  43. req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName=artifact").
  44. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  45. MakeRequest(t, req, http.StatusOK)
  46. }
  47. func TestActionsArtifactUploadInvalidHash(t *testing.T) {
  48. defer tests.PrepareTestEnv(t)()
  49. // artifact id 54321 not exist
  50. url := "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts/8e5b948a454515dbabfc7eb718ddddddd/upload?itemPath=artifact/abc.txt"
  51. body := strings.Repeat("A", 1024)
  52. req := NewRequestWithBody(t, "PUT", url, strings.NewReader(body)).
  53. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a").
  54. SetHeader("Content-Range", "bytes 0-1023/1024").
  55. SetHeader("x-tfs-filelength", "1024").
  56. SetHeader("x-actions-results-md5", "1HsSe8LeLWh93ILaw1TEFQ==") // base64(md5(body))
  57. resp := MakeRequest(t, req, http.StatusBadRequest)
  58. assert.Contains(t, resp.Body.String(), "Invalid artifact hash")
  59. }
  60. func TestActionsArtifactConfirmUploadWithoutName(t *testing.T) {
  61. defer tests.PrepareTestEnv(t)()
  62. req := NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts").
  63. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  64. resp := MakeRequest(t, req, http.StatusBadRequest)
  65. assert.Contains(t, resp.Body.String(), "artifact name is empty")
  66. }
  67. func TestActionsArtifactUploadWithoutToken(t *testing.T) {
  68. defer tests.PrepareTestEnv(t)()
  69. req := NewRequestWithJSON(t, "POST", "/api/actions_pipeline/_apis/pipelines/workflows/1/artifacts", nil)
  70. MakeRequest(t, req, http.StatusUnauthorized)
  71. }
  72. type (
  73. listArtifactsResponseItem struct {
  74. Name string `json:"name"`
  75. FileContainerResourceURL string `json:"fileContainerResourceUrl"`
  76. }
  77. listArtifactsResponse struct {
  78. Count int64 `json:"count"`
  79. Value []listArtifactsResponseItem `json:"value"`
  80. }
  81. downloadArtifactResponseItem struct {
  82. Path string `json:"path"`
  83. ItemType string `json:"itemType"`
  84. ContentLocation string `json:"contentLocation"`
  85. }
  86. downloadArtifactResponse struct {
  87. Value []downloadArtifactResponseItem `json:"value"`
  88. }
  89. )
  90. func TestActionsArtifactDownload(t *testing.T) {
  91. defer tests.PrepareTestEnv(t)()
  92. req := NewRequest(t, "GET", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts").
  93. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  94. resp := MakeRequest(t, req, http.StatusOK)
  95. var listResp listArtifactsResponse
  96. DecodeJSON(t, resp, &listResp)
  97. assert.Equal(t, int64(1), listResp.Count)
  98. assert.Equal(t, "artifact", listResp.Value[0].Name)
  99. assert.Contains(t, listResp.Value[0].FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
  100. idx := strings.Index(listResp.Value[0].FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
  101. url := listResp.Value[0].FileContainerResourceURL[idx+1:] + "?itemPath=artifact"
  102. req = NewRequest(t, "GET", url).
  103. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  104. resp = MakeRequest(t, req, http.StatusOK)
  105. var downloadResp downloadArtifactResponse
  106. DecodeJSON(t, resp, &downloadResp)
  107. assert.Len(t, downloadResp.Value, 1)
  108. assert.Equal(t, "artifact/abc.txt", downloadResp.Value[0].Path)
  109. assert.Equal(t, "file", downloadResp.Value[0].ItemType)
  110. assert.Contains(t, downloadResp.Value[0].ContentLocation, "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
  111. idx = strings.Index(downloadResp.Value[0].ContentLocation, "/api/actions_pipeline/_apis/pipelines/")
  112. url = downloadResp.Value[0].ContentLocation[idx:]
  113. req = NewRequest(t, "GET", url).
  114. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  115. resp = MakeRequest(t, req, http.StatusOK)
  116. body := strings.Repeat("A", 1024)
  117. assert.Equal(t, resp.Body.String(), body)
  118. }
  119. func TestActionsArtifactUploadMultipleFile(t *testing.T) {
  120. defer tests.PrepareTestEnv(t)()
  121. const testArtifactName = "multi-files"
  122. // acquire artifact upload url
  123. req := NewRequestWithJSON(t, "POST", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts", getUploadArtifactRequest{
  124. Type: "actions_storage",
  125. Name: testArtifactName,
  126. }).AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  127. resp := MakeRequest(t, req, http.StatusOK)
  128. var uploadResp uploadArtifactResponse
  129. DecodeJSON(t, resp, &uploadResp)
  130. assert.Contains(t, uploadResp.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
  131. type uploadingFile struct {
  132. Path string
  133. Content string
  134. MD5 string
  135. }
  136. files := []uploadingFile{
  137. {
  138. Path: "abc.txt",
  139. Content: strings.Repeat("A", 1024),
  140. MD5: "1HsSe8LeLWh93ILaw1TEFQ==",
  141. },
  142. {
  143. Path: "xyz/def.txt",
  144. Content: strings.Repeat("B", 1024),
  145. MD5: "6fgADK/7zjadf+6cB9Q1CQ==",
  146. },
  147. }
  148. for _, f := range files {
  149. // get upload url
  150. idx := strings.Index(uploadResp.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
  151. url := uploadResp.FileContainerResourceURL[idx:] + "?itemPath=" + testArtifactName + "/" + f.Path
  152. // upload artifact chunk
  153. req = NewRequestWithBody(t, "PUT", url, strings.NewReader(f.Content)).
  154. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a").
  155. SetHeader("Content-Range", "bytes 0-1023/1024").
  156. SetHeader("x-tfs-filelength", "1024").
  157. SetHeader("x-actions-results-md5", f.MD5) // base64(md5(body))
  158. MakeRequest(t, req, http.StatusOK)
  159. }
  160. t.Logf("Create artifact confirm")
  161. // confirm artifact upload
  162. req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName="+testArtifactName).
  163. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  164. MakeRequest(t, req, http.StatusOK)
  165. }
  166. func TestActionsArtifactDownloadMultiFiles(t *testing.T) {
  167. defer tests.PrepareTestEnv(t)()
  168. const testArtifactName = "multi-files"
  169. req := NewRequest(t, "GET", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts").
  170. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  171. resp := MakeRequest(t, req, http.StatusOK)
  172. var listResp listArtifactsResponse
  173. DecodeJSON(t, resp, &listResp)
  174. assert.Equal(t, int64(2), listResp.Count)
  175. var fileContainerResourceURL string
  176. for _, v := range listResp.Value {
  177. if v.Name == testArtifactName {
  178. fileContainerResourceURL = v.FileContainerResourceURL
  179. break
  180. }
  181. }
  182. assert.Contains(t, fileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
  183. idx := strings.Index(fileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
  184. url := fileContainerResourceURL[idx+1:] + "?itemPath=" + testArtifactName
  185. req = NewRequest(t, "GET", url).
  186. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  187. resp = MakeRequest(t, req, http.StatusOK)
  188. var downloadResp downloadArtifactResponse
  189. DecodeJSON(t, resp, &downloadResp)
  190. assert.Len(t, downloadResp.Value, 2)
  191. downloads := [][]string{{"multi-files/abc.txt", "A"}, {"multi-files/xyz/def.txt", "B"}}
  192. for _, v := range downloadResp.Value {
  193. var bodyChar string
  194. var path string
  195. for _, d := range downloads {
  196. if v.Path == d[0] {
  197. path = d[0]
  198. bodyChar = d[1]
  199. break
  200. }
  201. }
  202. value := v
  203. assert.Equal(t, path, value.Path)
  204. assert.Equal(t, "file", value.ItemType)
  205. assert.Contains(t, value.ContentLocation, "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
  206. idx = strings.Index(value.ContentLocation, "/api/actions_pipeline/_apis/pipelines/")
  207. url = value.ContentLocation[idx:]
  208. req = NewRequest(t, "GET", url).
  209. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  210. resp = MakeRequest(t, req, http.StatusOK)
  211. body := strings.Repeat(bodyChar, 1024)
  212. assert.Equal(t, resp.Body.String(), body)
  213. }
  214. }
  215. func TestActionsArtifactUploadWithRetentionDays(t *testing.T) {
  216. defer tests.PrepareTestEnv(t)()
  217. // acquire artifact upload url
  218. req := NewRequestWithJSON(t, "POST", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts", getUploadArtifactRequest{
  219. Type: "actions_storage",
  220. Name: "artifact-retention-days",
  221. RetentionDays: 9,
  222. }).AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  223. resp := MakeRequest(t, req, http.StatusOK)
  224. var uploadResp uploadArtifactResponse
  225. DecodeJSON(t, resp, &uploadResp)
  226. assert.Contains(t, uploadResp.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
  227. assert.Contains(t, uploadResp.FileContainerResourceURL, "?retentionDays=9")
  228. // get upload url
  229. idx := strings.Index(uploadResp.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
  230. url := uploadResp.FileContainerResourceURL[idx:] + "&itemPath=artifact-retention-days/abc.txt"
  231. // upload artifact chunk
  232. body := strings.Repeat("A", 1024)
  233. req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body)).
  234. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a").
  235. SetHeader("Content-Range", "bytes 0-1023/1024").
  236. SetHeader("x-tfs-filelength", "1024").
  237. SetHeader("x-actions-results-md5", "1HsSe8LeLWh93ILaw1TEFQ==") // base64(md5(body))
  238. MakeRequest(t, req, http.StatusOK)
  239. t.Logf("Create artifact confirm")
  240. // confirm artifact upload
  241. req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName=artifact-retention-days").
  242. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  243. MakeRequest(t, req, http.StatusOK)
  244. }
  245. func TestActionsArtifactOverwrite(t *testing.T) {
  246. defer tests.PrepareTestEnv(t)()
  247. {
  248. // download old artifact uploaded by tests above, it should 1024 A
  249. req := NewRequest(t, "GET", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts").
  250. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  251. resp := MakeRequest(t, req, http.StatusOK)
  252. var listResp listArtifactsResponse
  253. DecodeJSON(t, resp, &listResp)
  254. idx := strings.Index(listResp.Value[0].FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
  255. url := listResp.Value[0].FileContainerResourceURL[idx+1:] + "?itemPath=artifact"
  256. req = NewRequest(t, "GET", url).
  257. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  258. resp = MakeRequest(t, req, http.StatusOK)
  259. var downloadResp downloadArtifactResponse
  260. DecodeJSON(t, resp, &downloadResp)
  261. idx = strings.Index(downloadResp.Value[0].ContentLocation, "/api/actions_pipeline/_apis/pipelines/")
  262. url = downloadResp.Value[0].ContentLocation[idx:]
  263. req = NewRequest(t, "GET", url).
  264. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  265. resp = MakeRequest(t, req, http.StatusOK)
  266. body := strings.Repeat("A", 1024)
  267. assert.Equal(t, resp.Body.String(), body)
  268. }
  269. {
  270. // upload same artifact, it uses 4096 B
  271. req := NewRequestWithJSON(t, "POST", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts", getUploadArtifactRequest{
  272. Type: "actions_storage",
  273. Name: "artifact",
  274. }).AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  275. resp := MakeRequest(t, req, http.StatusOK)
  276. var uploadResp uploadArtifactResponse
  277. DecodeJSON(t, resp, &uploadResp)
  278. idx := strings.Index(uploadResp.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
  279. url := uploadResp.FileContainerResourceURL[idx:] + "?itemPath=artifact/abc.txt"
  280. body := strings.Repeat("B", 4096)
  281. req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body)).
  282. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a").
  283. SetHeader("Content-Range", "bytes 0-4095/4096").
  284. SetHeader("x-tfs-filelength", "4096").
  285. SetHeader("x-actions-results-md5", "wUypcJFeZCK5T6r4lfqzqg==") // base64(md5(body))
  286. MakeRequest(t, req, http.StatusOK)
  287. // confirm artifact upload
  288. req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName=artifact").
  289. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  290. MakeRequest(t, req, http.StatusOK)
  291. }
  292. {
  293. // download artifact again, it should 4096 B
  294. req := NewRequest(t, "GET", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts").
  295. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  296. resp := MakeRequest(t, req, http.StatusOK)
  297. var listResp listArtifactsResponse
  298. DecodeJSON(t, resp, &listResp)
  299. var uploadedItem listArtifactsResponseItem
  300. for _, item := range listResp.Value {
  301. if item.Name == "artifact" {
  302. uploadedItem = item
  303. break
  304. }
  305. }
  306. assert.Equal(t, uploadedItem.Name, "artifact")
  307. idx := strings.Index(uploadedItem.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
  308. url := uploadedItem.FileContainerResourceURL[idx+1:] + "?itemPath=artifact"
  309. req = NewRequest(t, "GET", url).
  310. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  311. resp = MakeRequest(t, req, http.StatusOK)
  312. var downloadResp downloadArtifactResponse
  313. DecodeJSON(t, resp, &downloadResp)
  314. idx = strings.Index(downloadResp.Value[0].ContentLocation, "/api/actions_pipeline/_apis/pipelines/")
  315. url = downloadResp.Value[0].ContentLocation[idx:]
  316. req = NewRequest(t, "GET", url).
  317. AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
  318. resp = MakeRequest(t, req, http.StatusOK)
  319. body := strings.Repeat("B", 4096)
  320. assert.Equal(t, resp.Body.String(), body)
  321. }
  322. }