summaryrefslogtreecommitdiffstats
path: root/pkg/artifacts/server_test.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel@debian.org>2024-10-20 23:07:42 +0200
committerDaniel Baumann <daniel@debian.org>2024-11-09 15:38:42 +0100
commit714c83b2736d7e308bc33c49057952490eb98be2 (patch)
tree1d9ba7035798368569cd49056f4d596efc908cd8 /pkg/artifacts/server_test.go
parentInitial commit. (diff)
downloadforgejo-act-714c83b2736d7e308bc33c49057952490eb98be2.tar.xz
forgejo-act-714c83b2736d7e308bc33c49057952490eb98be2.zip
Adding upstream version 1.21.4.HEADupstream/1.21.4upstreamdebian
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to 'pkg/artifacts/server_test.go')
-rw-r--r--pkg/artifacts/server_test.go395
1 files changed, 395 insertions, 0 deletions
diff --git a/pkg/artifacts/server_test.go b/pkg/artifacts/server_test.go
new file mode 100644
index 0000000..aeeb059
--- /dev/null
+++ b/pkg/artifacts/server_test.go
@@ -0,0 +1,395 @@
+package artifacts
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+ "testing"
+ "testing/fstest"
+
+ "github.com/julienschmidt/httprouter"
+ log "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/assert"
+
+ "github.com/nektos/act/pkg/model"
+ "github.com/nektos/act/pkg/runner"
+)
+
+type writableMapFile struct {
+ fstest.MapFile
+}
+
+func (f *writableMapFile) Write(data []byte) (int, error) {
+ f.Data = data
+ return len(data), nil
+}
+
+func (f *writableMapFile) Close() error {
+ return nil
+}
+
+type writeMapFS struct {
+ fstest.MapFS
+}
+
+func (fsys writeMapFS) OpenWritable(name string) (WritableFile, error) {
+ var file = &writableMapFile{
+ MapFile: fstest.MapFile{
+ Data: []byte("content2"),
+ },
+ }
+ fsys.MapFS[name] = &file.MapFile
+
+ return file, nil
+}
+
+func (fsys writeMapFS) OpenAppendable(name string) (WritableFile, error) {
+ var file = &writableMapFile{
+ MapFile: fstest.MapFile{
+ Data: []byte("content2"),
+ },
+ }
+ fsys.MapFS[name] = &file.MapFile
+
+ return file, nil
+}
+
+func TestNewArtifactUploadPrepare(t *testing.T) {
+ assert := assert.New(t)
+
+ var memfs = fstest.MapFS(map[string]*fstest.MapFile{})
+
+ router := httprouter.New()
+ uploads(router, "artifact/server/path", writeMapFS{memfs})
+
+ req, _ := http.NewRequest("POST", "http://localhost/_apis/pipelines/workflows/1/artifacts", nil)
+ rr := httptest.NewRecorder()
+
+ router.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != http.StatusOK {
+ assert.Fail("Wrong status")
+ }
+
+ response := FileContainerResourceURL{}
+ err := json.Unmarshal(rr.Body.Bytes(), &response)
+ if err != nil {
+ panic(err)
+ }
+
+ assert.Equal("http://localhost/upload/1", response.FileContainerResourceURL)
+}
+
+func TestArtifactUploadBlob(t *testing.T) {
+ assert := assert.New(t)
+
+ var memfs = fstest.MapFS(map[string]*fstest.MapFile{})
+
+ router := httprouter.New()
+ uploads(router, "artifact/server/path", writeMapFS{memfs})
+
+ req, _ := http.NewRequest("PUT", "http://localhost/upload/1?itemPath=some/file", strings.NewReader("content"))
+ rr := httptest.NewRecorder()
+
+ router.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != http.StatusOK {
+ assert.Fail("Wrong status")
+ }
+
+ response := ResponseMessage{}
+ err := json.Unmarshal(rr.Body.Bytes(), &response)
+ if err != nil {
+ panic(err)
+ }
+
+ assert.Equal("success", response.Message)
+ assert.Equal("content", string(memfs["artifact/server/path/1/some/file"].Data))
+}
+
+func TestFinalizeArtifactUpload(t *testing.T) {
+ assert := assert.New(t)
+
+ var memfs = fstest.MapFS(map[string]*fstest.MapFile{})
+
+ router := httprouter.New()
+ uploads(router, "artifact/server/path", writeMapFS{memfs})
+
+ req, _ := http.NewRequest("PATCH", "http://localhost/_apis/pipelines/workflows/1/artifacts", nil)
+ rr := httptest.NewRecorder()
+
+ router.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != http.StatusOK {
+ assert.Fail("Wrong status")
+ }
+
+ response := ResponseMessage{}
+ err := json.Unmarshal(rr.Body.Bytes(), &response)
+ if err != nil {
+ panic(err)
+ }
+
+ assert.Equal("success", response.Message)
+}
+
+func TestListArtifacts(t *testing.T) {
+ assert := assert.New(t)
+
+ var memfs = fstest.MapFS(map[string]*fstest.MapFile{
+ "artifact/server/path/1/file.txt": {
+ Data: []byte(""),
+ },
+ })
+
+ router := httprouter.New()
+ downloads(router, "artifact/server/path", memfs)
+
+ req, _ := http.NewRequest("GET", "http://localhost/_apis/pipelines/workflows/1/artifacts", nil)
+ rr := httptest.NewRecorder()
+
+ router.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != http.StatusOK {
+ assert.FailNow(fmt.Sprintf("Wrong status: %d", status))
+ }
+
+ response := NamedFileContainerResourceURLResponse{}
+ err := json.Unmarshal(rr.Body.Bytes(), &response)
+ if err != nil {
+ panic(err)
+ }
+
+ assert.Equal(1, response.Count)
+ assert.Equal("file.txt", response.Value[0].Name)
+ assert.Equal("http://localhost/download/1", response.Value[0].FileContainerResourceURL)
+}
+
+func TestListArtifactContainer(t *testing.T) {
+ assert := assert.New(t)
+
+ var memfs = fstest.MapFS(map[string]*fstest.MapFile{
+ "artifact/server/path/1/some/file": {
+ Data: []byte(""),
+ },
+ })
+
+ router := httprouter.New()
+ downloads(router, "artifact/server/path", memfs)
+
+ req, _ := http.NewRequest("GET", "http://localhost/download/1?itemPath=some/file", nil)
+ rr := httptest.NewRecorder()
+
+ router.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != http.StatusOK {
+ assert.FailNow(fmt.Sprintf("Wrong status: %d", status))
+ }
+
+ response := ContainerItemResponse{}
+ err := json.Unmarshal(rr.Body.Bytes(), &response)
+ if err != nil {
+ panic(err)
+ }
+
+ assert.Equal(1, len(response.Value))
+ assert.Equal("some/file", response.Value[0].Path)
+ assert.Equal("file", response.Value[0].ItemType)
+ assert.Equal("http://localhost/artifact/1/some/file/.", response.Value[0].ContentLocation)
+}
+
+func TestDownloadArtifactFile(t *testing.T) {
+ assert := assert.New(t)
+
+ var memfs = fstest.MapFS(map[string]*fstest.MapFile{
+ "artifact/server/path/1/some/file": {
+ Data: []byte("content"),
+ },
+ })
+
+ router := httprouter.New()
+ downloads(router, "artifact/server/path", memfs)
+
+ req, _ := http.NewRequest("GET", "http://localhost/artifact/1/some/file", nil)
+ rr := httptest.NewRecorder()
+
+ router.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != http.StatusOK {
+ assert.FailNow(fmt.Sprintf("Wrong status: %d", status))
+ }
+
+ data := rr.Body.Bytes()
+
+ assert.Equal("content", string(data))
+}
+
+type TestJobFileInfo struct {
+ workdir string
+ workflowPath string
+ eventName string
+ errorMessage string
+ platforms map[string]string
+ containerArchitecture string
+}
+
+var (
+ artifactsPath = path.Join(os.TempDir(), "test-artifacts")
+ artifactsAddr = "127.0.0.1"
+ artifactsPort = "12345"
+)
+
+func TestArtifactFlow(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping integration test")
+ }
+
+ ctx := context.Background()
+
+ cancel := Serve(ctx, artifactsPath, artifactsAddr, artifactsPort)
+ defer cancel()
+
+ platforms := map[string]string{
+ "ubuntu-latest": "node:16-buster", // Don't use node:16-buster-slim because it doesn't have curl command, which is used in the tests
+ }
+
+ tables := []TestJobFileInfo{
+ {"testdata", "upload-and-download", "push", "", platforms, ""},
+ {"testdata", "GHSL-2023-004", "push", "", platforms, ""},
+ }
+ log.SetLevel(log.DebugLevel)
+
+ for _, table := range tables {
+ runTestJobFile(ctx, t, table)
+ }
+}
+
+func runTestJobFile(ctx context.Context, t *testing.T, tjfi TestJobFileInfo) {
+ t.Run(tjfi.workflowPath, func(t *testing.T) {
+ fmt.Printf("::group::%s\n", tjfi.workflowPath)
+
+ if err := os.RemoveAll(artifactsPath); err != nil {
+ panic(err)
+ }
+
+ workdir, err := filepath.Abs(tjfi.workdir)
+ assert.Nil(t, err, workdir)
+ fullWorkflowPath := filepath.Join(workdir, tjfi.workflowPath)
+ runnerConfig := &runner.Config{
+ Workdir: workdir,
+ BindWorkdir: false,
+ EventName: tjfi.eventName,
+ Platforms: tjfi.platforms,
+ ReuseContainers: false,
+ ContainerArchitecture: tjfi.containerArchitecture,
+ GitHubInstance: "github.com",
+ ArtifactServerPath: artifactsPath,
+ ArtifactServerAddr: artifactsAddr,
+ ArtifactServerPort: artifactsPort,
+ }
+
+ runner, err := runner.New(runnerConfig)
+ assert.Nil(t, err, tjfi.workflowPath)
+
+ planner, err := model.NewWorkflowPlanner(fullWorkflowPath, true)
+ assert.Nil(t, err, fullWorkflowPath)
+
+ plan, err := planner.PlanEvent(tjfi.eventName)
+ if err == nil {
+ err = runner.NewPlanExecutor(plan)(ctx)
+ if tjfi.errorMessage == "" {
+ assert.Nil(t, err, fullWorkflowPath)
+ } else {
+ assert.Error(t, err, tjfi.errorMessage)
+ }
+ } else {
+ assert.Nil(t, plan)
+ }
+
+ fmt.Println("::endgroup::")
+ })
+}
+
+func TestMkdirFsImplSafeResolve(t *testing.T) {
+ assert := assert.New(t)
+
+ baseDir := "/foo/bar"
+
+ tests := map[string]struct {
+ input string
+ want string
+ }{
+ "simple": {input: "baz", want: "/foo/bar/baz"},
+ "nested": {input: "baz/blue", want: "/foo/bar/baz/blue"},
+ "dots in middle": {input: "baz/../../blue", want: "/foo/bar/blue"},
+ "leading dots": {input: "../../parent", want: "/foo/bar/parent"},
+ "root path": {input: "/root", want: "/foo/bar/root"},
+ "root": {input: "/", want: "/foo/bar"},
+ "empty": {input: "", want: "/foo/bar"},
+ }
+
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ assert.Equal(tc.want, safeResolve(baseDir, tc.input))
+ })
+ }
+}
+
+func TestDownloadArtifactFileUnsafePath(t *testing.T) {
+ assert := assert.New(t)
+
+ var memfs = fstest.MapFS(map[string]*fstest.MapFile{
+ "artifact/server/path/some/file": {
+ Data: []byte("content"),
+ },
+ })
+
+ router := httprouter.New()
+ downloads(router, "artifact/server/path", memfs)
+
+ req, _ := http.NewRequest("GET", "http://localhost/artifact/2/../../some/file", nil)
+ rr := httptest.NewRecorder()
+
+ router.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != http.StatusOK {
+ assert.FailNow(fmt.Sprintf("Wrong status: %d", status))
+ }
+
+ data := rr.Body.Bytes()
+
+ assert.Equal("content", string(data))
+}
+
+func TestArtifactUploadBlobUnsafePath(t *testing.T) {
+ assert := assert.New(t)
+
+ var memfs = fstest.MapFS(map[string]*fstest.MapFile{})
+
+ router := httprouter.New()
+ uploads(router, "artifact/server/path", writeMapFS{memfs})
+
+ req, _ := http.NewRequest("PUT", "http://localhost/upload/1?itemPath=../../some/file", strings.NewReader("content"))
+ rr := httptest.NewRecorder()
+
+ router.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != http.StatusOK {
+ assert.Fail("Wrong status")
+ }
+
+ response := ResponseMessage{}
+ err := json.Unmarshal(rr.Body.Bytes(), &response)
+ if err != nil {
+ panic(err)
+ }
+
+ assert.Equal("success", response.Message)
+ assert.Equal("content", string(memfs["artifact/server/path/1/some/file"].Data))
+}