package pull import ( "archive/zip" "os" "path/filepath" "reflect" "testing" "code.geekeey.de/actions/sdk" ) func TestParseConfig(t *testing.T) { t.Parallel() cases := []struct { name string env map[string]string want *config wantErr bool }{ { name: "default path", env: map[string]string{"name": "foo"}, want: &config{Name: "foo", Path: "."}, }, { name: "explicit path", env: map[string]string{"name": "foo", "path": "out"}, want: &config{Name: "foo", Path: "out"}, }, { name: "other repository", env: map[string]string{ "name": "foo", "repository": "owner/repo", "run-id": "42", "github-token": "tok", }, want: &config{Name: "foo", Path: ".", Repository: "owner/repo", RunID: "42", Token: "tok"}, }, { name: "missing name", env: map[string]string{}, wantErr: true, }, } for _, tc := range cases { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() got, err := parseConfig(func(k string) string { return tc.env[k] }) if tc.wantErr { if err == nil { t.Fatalf("expected error, got %+v", got) } return } if err != nil { t.Fatal(err) } if !reflect.DeepEqual(got, tc.want) { t.Errorf("expected %+v, got %+v", tc.want, got) } }) } } func TestUseRepositoryAPI(t *testing.T) { t.Parallel() gh := &sdk.GitHubContext{Repository: "owner/repo", RunID: "100"} cases := []struct { name string cfg *config want bool }{ {"current", &config{Repository: "owner/repo", RunID: "100"}, false}, {"empty means current", &config{}, false}, {"other repository", &config{Repository: "other/repo", RunID: "100"}, true}, {"other run", &config{Repository: "owner/repo", RunID: "200"}, true}, {"repository only matches", &config{Repository: "owner/repo"}, false}, {"repository only differs", &config{Repository: "other/repo"}, true}, } for _, tc := range cases { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() if got := useRepositoryAPI(tc.cfg, gh); got != tc.want { t.Errorf("expected %v, got %v", tc.want, got) } }) } } func TestExtractArchive(t *testing.T) { t.Parallel() zipPath := filepath.Join(t.TempDir(), "artifact.zip") writeZip(t, zipPath, map[string]string{ "a.txt": "a", "sub/b.txt": "b", }) dest := t.TempDir() if err := extractArchive(zipPath, dest); err != nil { t.Fatal(err) } for name, want := range map[string]string{"a.txt": "a", "sub/b.txt": "b"} { got, err := os.ReadFile(filepath.Join(dest, name)) if err != nil { t.Fatal(err) } if string(got) != want { t.Errorf("expected %q to contain %q, got %q", name, want, got) } } } func TestExtractArchive_PathTraversal(t *testing.T) { t.Parallel() zipPath := filepath.Join(t.TempDir(), "artifact.zip") writeZip(t, zipPath, map[string]string{"../evil.txt": "evil"}) if err := extractArchive(zipPath, t.TempDir()); err == nil { t.Fatal("expected error for path traversal entry") } } func TestDownloadPath(t *testing.T) { t.Parallel() abs := t.TempDir() if got := downloadPath(abs); got != abs { t.Errorf("expected %q, got %q", abs, got) } wd, err := os.Getwd() if err != nil { t.Fatal(err) } if got, want := downloadPath("out"), filepath.Join(wd, "out"); got != want { t.Errorf("expected %q, got %q", want, got) } } func writeZip(t *testing.T, path string, files map[string]string) { t.Helper() f, err := os.Create(path) if err != nil { t.Fatal(err) } defer f.Close() zw := zip.NewWriter(f) for name, content := range files { w, err := zw.Create(name) if err != nil { t.Fatal(err) } if _, err := w.Write([]byte(content)); err != nil { t.Fatal(err) } } if err := zw.Close(); err != nil { t.Fatal(err) } }