]> source.dussan.org Git - gitea.git/commitdiff
Add artifacts test fixture (#30300)
authorKyle D. <kdumontnu@gmail.com>
Fri, 1 Nov 2024 02:29:54 +0000 (22:29 -0400)
committerGitHub <noreply@github.com>
Fri, 1 Nov 2024 02:29:54 +0000 (10:29 +0800)
Closes https://github.com/go-gitea/gitea/issues/30296

- Adds a DB fixture for actions artifacts
- Adds artifacts test files
- Clears artifacts test files between each run
- Note: I initially initialized the artifacts only for artifacts tests,
but because the files are small it only takes ~8ms, so I changed it to
always run in test setup for simplicity
- Fix some otherwise flaky tests by making them not depend on previous
tests

models/fixtures/action_artifact.yml [new file with mode: 0644]
modules/storage/storage.go
tests/integration/api_actions_artifact_test.go
tests/integration/api_actions_artifact_v4_test.go
tests/test_utils.go
tests/testdata/data/artifacts/26/1/1712166500347189545.chunk [new file with mode: 0644]
tests/testdata/data/artifacts/26/19/1712348022422036662.chunk [new file with mode: 0644]
tests/testdata/data/artifacts/26/20/1712348022423431524.chunk [new file with mode: 0644]
tests/testdata/data/artifacts/27/5/1730330775594233150.chunk [new file with mode: 0644]

diff --git a/models/fixtures/action_artifact.yml b/models/fixtures/action_artifact.yml
new file mode 100644 (file)
index 0000000..2c51c11
--- /dev/null
@@ -0,0 +1,71 @@
+-
+  id: 1
+  run_id: 791
+  runner_id: 1
+  repo_id: 4
+  owner_id: 1
+  commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+  storage_path: "26/1/1712166500347189545.chunk"
+  file_size: 1024
+  file_compressed_size: 1024
+  content_encoding: ""
+  artifact_path: "abc.txt"
+  artifact_name: "artifact-download"
+  status: 1
+  created_unix: 1712338649
+  updated_unix: 1712338649
+  expired_unix: 1720114649
+
+-
+  id: 19
+  run_id: 791
+  runner_id: 1
+  repo_id: 4
+  owner_id: 1
+  commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+  storage_path: "26/19/1712348022422036662.chunk"
+  file_size: 1024
+  file_compressed_size: 1024
+  content_encoding: ""
+  artifact_path: "abc.txt"
+  artifact_name: "multi-file-download"
+  status: 2
+  created_unix: 1712348022
+  updated_unix: 1712348022
+  expired_unix: 1720124022
+
+-
+  id: 20
+  run_id: 791
+  runner_id: 1
+  repo_id: 4
+  owner_id: 1
+  commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+  storage_path: "26/20/1712348022423431524.chunk"
+  file_size: 1024
+  file_compressed_size: 1024
+  content_encoding: ""
+  artifact_path: "xyz/def.txt"
+  artifact_name: "multi-file-download"
+  status: 2
+  created_unix: 1712348022
+  updated_unix: 1712348022
+  expired_unix: 1720124022
+
+-
+  id: 22
+  run_id: 792
+  runner_id: 1
+  repo_id: 4
+  owner_id: 1
+  commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
+  storage_path: "27/5/1730330775594233150.chunk"
+  file_size: 1024
+  file_compressed_size: 1024
+  content_encoding: "application/zip"
+  artifact_path: "artifact-v4-download.zip"
+  artifact_name: "artifact-v4-download"
+  status: 2
+  created_unix: 1730330775
+  updated_unix: 1730330775
+  expired_unix: 1738106775
index 52a250080cd4a50484e55e472e54a11698e0c21d..750ecdfe0db70fe5a33464c034ec628efc87af8d 100644 (file)
@@ -131,7 +131,7 @@ var (
        ActionsArtifacts ObjectStorage = uninitializedStorage
 )
 
-// Init init the stoarge
+// Init init the storage
 func Init() error {
        for _, f := range []func() error{
                initAttachments,
index ce2d14cc0c9c1979a2d8bb17acf767f088286a15..aa2e2f2adfe06e020ac31b58adf9b8fecc1e4618 100644 (file)
@@ -38,21 +38,21 @@ func TestActionsArtifactUploadSingleFile(t *testing.T) {
 
        // get upload url
        idx := strings.Index(uploadResp.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
-       url := uploadResp.FileContainerResourceURL[idx:] + "?itemPath=artifact/abc.txt"
+       url := uploadResp.FileContainerResourceURL[idx:] + "?itemPath=artifact/abc-2.txt"
 
        // upload artifact chunk
-       body := strings.Repeat("A", 1024)
+       body := strings.Repeat("C", 1024)
        req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body)).
                AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a").
                SetHeader("Content-Range", "bytes 0-1023/1024").
                SetHeader("x-tfs-filelength", "1024").
-               SetHeader("x-actions-results-md5", "1HsSe8LeLWh93ILaw1TEFQ==") // base64(md5(body))
+               SetHeader("x-actions-results-md5", "XVlf820rMInUi64wmMi6EA==") // base64(md5(body))
        MakeRequest(t, req, http.StatusOK)
 
        t.Logf("Create artifact confirm")
 
        // confirm artifact upload
-       req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName=artifact").
+       req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName=artifact-single").
                AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
        MakeRequest(t, req, http.StatusOK)
 }
@@ -115,29 +115,40 @@ func TestActionsArtifactDownload(t *testing.T) {
        resp := MakeRequest(t, req, http.StatusOK)
        var listResp listArtifactsResponse
        DecodeJSON(t, resp, &listResp)
-       assert.Equal(t, int64(1), listResp.Count)
-       assert.Equal(t, "artifact", listResp.Value[0].Name)
-       assert.Contains(t, listResp.Value[0].FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
+       assert.Equal(t, int64(2), listResp.Count)
+
+       // Return list might be in any order. Get one file.
+       var artifactIdx int
+       for i, artifact := range listResp.Value {
+               if artifact.Name == "artifact-download" {
+                       artifactIdx = i
+                       break
+               }
+       }
+       assert.NotNil(t, artifactIdx)
+       assert.Equal(t, listResp.Value[artifactIdx].Name, "artifact-download")
+       assert.Contains(t, listResp.Value[artifactIdx].FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
 
-       idx := strings.Index(listResp.Value[0].FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
-       url := listResp.Value[0].FileContainerResourceURL[idx+1:] + "?itemPath=artifact"
+       idx := strings.Index(listResp.Value[artifactIdx].FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
+       url := listResp.Value[artifactIdx].FileContainerResourceURL[idx+1:] + "?itemPath=artifact-download"
        req = NewRequest(t, "GET", url).
                AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
        resp = MakeRequest(t, req, http.StatusOK)
        var downloadResp downloadArtifactResponse
        DecodeJSON(t, resp, &downloadResp)
        assert.Len(t, downloadResp.Value, 1)
-       assert.Equal(t, "artifact/abc.txt", downloadResp.Value[0].Path)
-       assert.Equal(t, "file", downloadResp.Value[0].ItemType)
-       assert.Contains(t, downloadResp.Value[0].ContentLocation, "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
+       assert.Equal(t, "artifact-download/abc.txt", downloadResp.Value[artifactIdx].Path)
+       assert.Equal(t, "file", downloadResp.Value[artifactIdx].ItemType)
+       assert.Contains(t, downloadResp.Value[artifactIdx].ContentLocation, "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts")
 
-       idx = strings.Index(downloadResp.Value[0].ContentLocation, "/api/actions_pipeline/_apis/pipelines/")
-       url = downloadResp.Value[0].ContentLocation[idx:]
+       idx = strings.Index(downloadResp.Value[artifactIdx].ContentLocation, "/api/actions_pipeline/_apis/pipelines/")
+       url = downloadResp.Value[artifactIdx].ContentLocation[idx:]
        req = NewRequest(t, "GET", url).
                AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
        resp = MakeRequest(t, req, http.StatusOK)
+
        body := strings.Repeat("A", 1024)
-       assert.Equal(t, resp.Body.String(), body)
+       assert.Equal(t, body, resp.Body.String())
 }
 
 func TestActionsArtifactUploadMultipleFile(t *testing.T) {
@@ -163,14 +174,14 @@ func TestActionsArtifactUploadMultipleFile(t *testing.T) {
 
        files := []uploadingFile{
                {
-                       Path:    "abc.txt",
-                       Content: strings.Repeat("A", 1024),
-                       MD5:     "1HsSe8LeLWh93ILaw1TEFQ==",
+                       Path:    "abc-3.txt",
+                       Content: strings.Repeat("D", 1024),
+                       MD5:     "9nqj7E8HZmfQtPifCJ5Zww==",
                },
                {
-                       Path:    "xyz/def.txt",
-                       Content: strings.Repeat("B", 1024),
-                       MD5:     "6fgADK/7zjadf+6cB9Q1CQ==",
+                       Path:    "xyz/def-2.txt",
+                       Content: strings.Repeat("E", 1024),
+                       MD5:     "/s1kKvxeHlUX85vaTaVxuA==",
                },
        }
 
@@ -199,7 +210,7 @@ func TestActionsArtifactUploadMultipleFile(t *testing.T) {
 func TestActionsArtifactDownloadMultiFiles(t *testing.T) {
        defer tests.PrepareTestEnv(t)()
 
-       const testArtifactName = "multi-files"
+       const testArtifactName = "multi-file-download"
 
        req := NewRequest(t, "GET", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts").
                AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
@@ -226,7 +237,7 @@ func TestActionsArtifactDownloadMultiFiles(t *testing.T) {
        DecodeJSON(t, resp, &downloadResp)
        assert.Len(t, downloadResp.Value, 2)
 
-       downloads := [][]string{{"multi-files/abc.txt", "A"}, {"multi-files/xyz/def.txt", "B"}}
+       downloads := [][]string{{"multi-file-download/abc.txt", "B"}, {"multi-file-download/xyz/def.txt", "C"}}
        for _, v := range downloadResp.Value {
                var bodyChar string
                var path string
@@ -247,8 +258,7 @@ func TestActionsArtifactDownloadMultiFiles(t *testing.T) {
                req = NewRequest(t, "GET", url).
                        AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
                resp = MakeRequest(t, req, http.StatusOK)
-               body := strings.Repeat(bodyChar, 1024)
-               assert.Equal(t, resp.Body.String(), body)
+               assert.Equal(t, strings.Repeat(bodyChar, 1024), resp.Body.String())
        }
 }
 
@@ -300,7 +310,7 @@ func TestActionsArtifactOverwrite(t *testing.T) {
                DecodeJSON(t, resp, &listResp)
 
                idx := strings.Index(listResp.Value[0].FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
-               url := listResp.Value[0].FileContainerResourceURL[idx+1:] + "?itemPath=artifact"
+               url := listResp.Value[0].FileContainerResourceURL[idx+1:] + "?itemPath=artifact-download"
                req = NewRequest(t, "GET", url).
                        AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
                resp = MakeRequest(t, req, http.StatusOK)
@@ -320,14 +330,14 @@ func TestActionsArtifactOverwrite(t *testing.T) {
                // upload same artifact, it uses 4096 B
                req := NewRequestWithJSON(t, "POST", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts", getUploadArtifactRequest{
                        Type: "actions_storage",
-                       Name: "artifact",
+                       Name: "artifact-download",
                }).AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
                resp := MakeRequest(t, req, http.StatusOK)
                var uploadResp uploadArtifactResponse
                DecodeJSON(t, resp, &uploadResp)
 
                idx := strings.Index(uploadResp.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
-               url := uploadResp.FileContainerResourceURL[idx:] + "?itemPath=artifact/abc.txt"
+               url := uploadResp.FileContainerResourceURL[idx:] + "?itemPath=artifact-download/abc.txt"
                body := strings.Repeat("B", 4096)
                req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body)).
                        AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a").
@@ -337,7 +347,7 @@ func TestActionsArtifactOverwrite(t *testing.T) {
                MakeRequest(t, req, http.StatusOK)
 
                // confirm artifact upload
-               req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName=artifact").
+               req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName=artifact-download").
                        AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
                MakeRequest(t, req, http.StatusOK)
        }
@@ -352,15 +362,15 @@ func TestActionsArtifactOverwrite(t *testing.T) {
 
                var uploadedItem listArtifactsResponseItem
                for _, item := range listResp.Value {
-                       if item.Name == "artifact" {
+                       if item.Name == "artifact-download" {
                                uploadedItem = item
                                break
                        }
                }
-               assert.Equal(t, uploadedItem.Name, "artifact")
+               assert.Equal(t, uploadedItem.Name, "artifact-download")
 
                idx := strings.Index(uploadedItem.FileContainerResourceURL, "/api/actions_pipeline/_apis/pipelines/")
-               url := uploadedItem.FileContainerResourceURL[idx+1:] + "?itemPath=artifact"
+               url := uploadedItem.FileContainerResourceURL[idx+1:] + "?itemPath=artifact-download"
                req = NewRequest(t, "GET", url).
                        AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
                resp = MakeRequest(t, req, http.StatusOK)
index ec0fbbfa60a1dccb3b14c876efbaa0b6b3097c15..e731542037f91b50f05ff62f97b920505ad33b15 100644 (file)
@@ -308,7 +308,7 @@ func TestActionsArtifactV4DownloadSingle(t *testing.T) {
 
        // acquire artifact upload url
        req := NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/ListArtifacts", toProtoJSON(&actions.ListArtifactsRequest{
-               NameFilter:              wrapperspb.String("artifact"),
+               NameFilter:              wrapperspb.String("artifact-v4-download"),
                WorkflowRunBackendId:    "792",
                WorkflowJobRunBackendId: "193",
        })).AddTokenAuth(token)
@@ -319,7 +319,7 @@ func TestActionsArtifactV4DownloadSingle(t *testing.T) {
 
        // confirm artifact upload
        req = NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/GetSignedArtifactURL", toProtoJSON(&actions.GetSignedArtifactURLRequest{
-               Name:                    "artifact",
+               Name:                    "artifact-v4-download",
                WorkflowRunBackendId:    "792",
                WorkflowJobRunBackendId: "193",
        })).
@@ -331,8 +331,8 @@ func TestActionsArtifactV4DownloadSingle(t *testing.T) {
 
        req = NewRequest(t, "GET", finalizeResp.SignedUrl)
        resp = MakeRequest(t, req, http.StatusOK)
-       body := strings.Repeat("A", 1024)
-       assert.Equal(t, resp.Body.String(), body)
+       body := strings.Repeat("D", 1024)
+       assert.Equal(t, body, resp.Body.String())
 }
 
 func TestActionsArtifactV4Delete(t *testing.T) {
@@ -343,7 +343,7 @@ func TestActionsArtifactV4Delete(t *testing.T) {
 
        // delete artifact by name
        req := NewRequestWithBody(t, "POST", "/twirp/github.actions.results.api.v1.ArtifactService/DeleteArtifact", toProtoJSON(&actions.DeleteArtifactRequest{
-               Name:                    "artifact",
+               Name:                    "artifact-v4-download",
                WorkflowRunBackendId:    "792",
                WorkflowJobRunBackendId: "193",
        })).AddTokenAuth(token)
index 6f9592b204112a38f98b6d49aad00529f1829f2a..b31491caf278115d82d0c7d75cfc527f8fdba963 100644 (file)
@@ -192,6 +192,20 @@ func PrepareAttachmentsStorage(t testing.TB) {
        }))
 }
 
+func PrepareArtifactsStorage(t testing.TB) {
+       // prepare actions artifacts directory and files
+       assert.NoError(t, storage.Clean(storage.ActionsArtifacts))
+
+       s, err := storage.NewStorage(setting.LocalStorageType, &setting.Storage{
+               Path: filepath.Join(filepath.Dir(setting.AppPath), "tests", "testdata", "data", "artifacts"),
+       })
+       assert.NoError(t, err)
+       assert.NoError(t, s.IterateObjects("", func(p string, obj storage.Object) error {
+               _, err = storage.Copy(storage.ActionsArtifacts, p, s, p)
+               return err
+       }))
+}
+
 func PrepareTestEnv(t testing.TB, skip ...int) func() {
        t.Helper()
        ourSkip := 1
@@ -206,6 +220,7 @@ func PrepareTestEnv(t testing.TB, skip ...int) func() {
        // load git repo fixtures
        assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
        assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
+
        ownerDirs, err := os.ReadDir(setting.RepoRootPath)
        if err != nil {
                assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
@@ -227,6 +242,9 @@ func PrepareTestEnv(t testing.TB, skip ...int) func() {
                }
        }
 
+       // Initialize actions artifact data
+       PrepareArtifactsStorage(t)
+
        // load LFS object fixtures
        // (LFS storage can be on any of several backends, including remote servers, so we init it with the storage API)
        lfsFixtures, err := storage.NewStorage(setting.LocalStorageType, &setting.Storage{
diff --git a/tests/testdata/data/artifacts/26/1/1712166500347189545.chunk b/tests/testdata/data/artifacts/26/1/1712166500347189545.chunk
new file mode 100644 (file)
index 0000000..bc7c569
--- /dev/null
@@ -0,0 +1 @@
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
\ No newline at end of file
diff --git a/tests/testdata/data/artifacts/26/19/1712348022422036662.chunk b/tests/testdata/data/artifacts/26/19/1712348022422036662.chunk
new file mode 100644 (file)
index 0000000..b4fb0b0
--- /dev/null
@@ -0,0 +1 @@
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
\ No newline at end of file
diff --git a/tests/testdata/data/artifacts/26/20/1712348022423431524.chunk b/tests/testdata/data/artifacts/26/20/1712348022423431524.chunk
new file mode 100644 (file)
index 0000000..45b2320
--- /dev/null
@@ -0,0 +1 @@
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
\ No newline at end of file
diff --git a/tests/testdata/data/artifacts/27/5/1730330775594233150.chunk b/tests/testdata/data/artifacts/27/5/1730330775594233150.chunk
new file mode 100644 (file)
index 0000000..b1d6b8e
--- /dev/null
@@ -0,0 +1 @@
+DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
\ No newline at end of file