Skip to content

Commit e36a924

Browse files
committed
Clean up review feedback: use path.Ext for extension checks, remove dead getStripPrefix, add openArchive tests
1 parent 941ed51 commit e36a924

4 files changed

Lines changed: 198 additions & 33 deletions

File tree

internal/handler/composer.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io"
77
"net/http"
8+
"path"
89
"strings"
910
"time"
1011

@@ -289,7 +290,7 @@ func (h *ComposerHandler) rewriteDistURL(vmap map[string]any, packageName, versi
289290

290291
// GitHub zipball URLs end with a bare commit hash (no extension).
291292
// Append .zip so the archives library can detect the format.
292-
if !strings.Contains(filename, ".") {
293+
if path.Ext(filename) == "" {
293294
if distType, _ := dist["type"].(string); distType == "zip" {
294295
filename += ".zip"
295296
}

internal/server/browse.go

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const contentTypePlainText = "text/plain; charset=utf-8"
2323
// that have no extension. This adds .zip when the original has no extension
2424
// and the content is likely a zip archive.
2525
func archiveFilename(filename string) string {
26-
if !strings.Contains(filename, ".") {
26+
if path.Ext(filename) == "" {
2727
return filename + ".zip"
2828
}
2929
return filename
@@ -40,7 +40,7 @@ func detectSingleRootDir(reader archives.Reader) string {
4040

4141
var root string
4242
for _, f := range files {
43-
parts := strings.SplitN(f.Path, "/", 2)
43+
parts := strings.SplitN(f.Path, "/", 2) //nolint:mnd // split into dir + rest
4444
if len(parts) == 0 {
4545
continue
4646
}
@@ -61,7 +61,7 @@ func detectSingleRootDir(reader archives.Reader) string {
6161
// openArchive opens a cached artifact as an archive reader, auto-detecting
6262
// and stripping a single top-level directory prefix (like GitHub zipballs).
6363
// For npm, the hardcoded "package/" prefix takes precedence.
64-
func openArchive(filename string, content io.Reader, ecosystem string) (archives.Reader, error) {
64+
func openArchive(filename string, content io.Reader, ecosystem string) (archives.Reader, error) { //nolint:ireturn // wraps multiple archive implementations
6565
fname := archiveFilename(filename)
6666

6767
// npm always uses package/ prefix
@@ -86,16 +86,6 @@ func openArchive(filename string, content io.Reader, ecosystem string) (archives
8686
return archives.OpenWithPrefix(fname, bytes.NewReader(data), prefix)
8787
}
8888

89-
// getStripPrefix returns the path prefix to strip for a given ecosystem.
90-
// npm packages wrap content in a "package/" directory.
91-
func getStripPrefix(ecosystem string) string {
92-
switch ecosystem {
93-
case "npm":
94-
return "package/"
95-
default:
96-
return ""
97-
}
98-
}
9989

10090
// BrowseListResponse contains the file listing for a directory in an archives.
10191
type BrowseListResponse struct {

internal/server/browse_test.go

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package server
22

33
import (
44
"archive/tar"
5+
"archive/zip"
56
"bytes"
67
"compress/gzip"
78
"database/sql"
@@ -590,3 +591,195 @@ func TestHandleComparePage(t *testing.T) {
590591
t.Errorf("expected status 400 for invalid separator, got %d", w.Code)
591592
}
592593
}
594+
595+
func TestArchiveFilename(t *testing.T) {
596+
tests := []struct {
597+
input string
598+
want string
599+
}{
600+
{"package.tar.gz", "package.tar.gz"},
601+
{"d2e2f014ccd6ec9fae8dbe6336a4164346a2a856", "d2e2f014ccd6ec9fae8dbe6336a4164346a2a856.zip"},
602+
{"file.zip", "file.zip"},
603+
{"archive.tgz", "archive.tgz"},
604+
{"noext", "noext.zip"},
605+
}
606+
607+
for _, tt := range tests {
608+
t.Run(tt.input, func(t *testing.T) {
609+
got := archiveFilename(tt.input)
610+
if got != tt.want {
611+
t.Errorf("archiveFilename(%q) = %q, want %q", tt.input, got, tt.want)
612+
}
613+
})
614+
}
615+
}
616+
617+
func TestOpenArchiveStripsSingleRootDir(t *testing.T) {
618+
data := createZipArchive(t, map[string]string{
619+
"repo-abc123/README.md": "hello",
620+
"repo-abc123/src/main.go": "package main",
621+
"repo-abc123/go.mod": "module test",
622+
})
623+
reader, err := openArchive("test.zip", bytes.NewReader(data), "composer")
624+
if err != nil {
625+
t.Fatalf("openArchive failed: %v", err)
626+
}
627+
defer func() { _ = reader.Close() }()
628+
629+
files, err := reader.List()
630+
if err != nil {
631+
t.Fatalf("List failed: %v", err)
632+
}
633+
for _, f := range files {
634+
if strings.HasPrefix(f.Path, "repo-abc123/") {
635+
t.Errorf("file %q still has root prefix after stripping", f.Path)
636+
}
637+
}
638+
}
639+
640+
func TestOpenArchiveMultipleRootDirs(t *testing.T) {
641+
data := createZipArchive(t, map[string]string{
642+
"src/main.go": "package main",
643+
"docs/README.md": "hello",
644+
})
645+
reader, err := openArchive("test.zip", bytes.NewReader(data), "composer")
646+
if err != nil {
647+
t.Fatalf("openArchive failed: %v", err)
648+
}
649+
defer func() { _ = reader.Close() }()
650+
651+
files, err := reader.List()
652+
if err != nil {
653+
t.Fatalf("List failed: %v", err)
654+
}
655+
paths := make(map[string]bool)
656+
for _, f := range files {
657+
paths[f.Path] = true
658+
}
659+
if !paths["src/main.go"] {
660+
t.Error("expected src/main.go to remain unchanged")
661+
}
662+
if !paths["docs/README.md"] {
663+
t.Error("expected docs/README.md to remain unchanged")
664+
}
665+
}
666+
667+
func TestOpenArchiveFlatNoSubdirs(t *testing.T) {
668+
data := createZipArchive(t, map[string]string{
669+
"README.md": "hello",
670+
"main.go": "package main",
671+
})
672+
reader, err := openArchive("test.zip", bytes.NewReader(data), "composer")
673+
if err != nil {
674+
t.Fatalf("openArchive failed: %v", err)
675+
}
676+
defer func() { _ = reader.Close() }()
677+
678+
files, err := reader.List()
679+
if err != nil {
680+
t.Fatalf("List failed: %v", err)
681+
}
682+
paths := make(map[string]bool)
683+
for _, f := range files {
684+
paths[f.Path] = true
685+
}
686+
if !paths["README.md"] {
687+
t.Error("expected README.md at root")
688+
}
689+
}
690+
691+
func TestOpenArchiveNpmUsesPackagePrefix(t *testing.T) {
692+
data := createTarGzArchive(t, map[string]string{
693+
"package/README.md": "hello",
694+
"package/index.js": "module.exports = {}",
695+
})
696+
reader, err := openArchive("pkg.tgz", bytes.NewReader(data), "npm")
697+
if err != nil {
698+
t.Fatalf("openArchive failed: %v", err)
699+
}
700+
defer func() { _ = reader.Close() }()
701+
702+
files, err := reader.List()
703+
if err != nil {
704+
t.Fatalf("List failed: %v", err)
705+
}
706+
for _, f := range files {
707+
if strings.HasPrefix(f.Path, "package/") {
708+
t.Errorf("file %q still has package/ prefix", f.Path)
709+
}
710+
}
711+
}
712+
713+
func TestOpenArchiveExtensionlessFilename(t *testing.T) {
714+
data := createZipArchive(t, map[string]string{
715+
"repo-hash/README.md": "hello",
716+
})
717+
reader, err := openArchive("d2e2f014ccd6ec9fae8dbe6336a4164346a2a856", bytes.NewReader(data), "composer")
718+
if err != nil {
719+
t.Fatalf("openArchive failed: %v", err)
720+
}
721+
defer func() { _ = reader.Close() }()
722+
723+
files, err := reader.List()
724+
if err != nil {
725+
t.Fatalf("List failed: %v", err)
726+
}
727+
if len(files) == 0 {
728+
t.Fatal("expected files in archive")
729+
}
730+
for _, f := range files {
731+
if strings.HasPrefix(f.Path, "repo-hash/") {
732+
t.Errorf("file %q still has root prefix", f.Path)
733+
}
734+
}
735+
}
736+
737+
func createZipArchive(t *testing.T, files map[string]string) []byte {
738+
t.Helper()
739+
buf := new(bytes.Buffer)
740+
w := zip.NewWriter(buf)
741+
742+
for name, content := range files {
743+
f, err := w.Create(name)
744+
if err != nil {
745+
t.Fatalf("failed to create zip entry: %v", err)
746+
}
747+
if _, err := f.Write([]byte(content)); err != nil {
748+
t.Fatalf("failed to write zip content: %v", err)
749+
}
750+
}
751+
752+
if err := w.Close(); err != nil {
753+
t.Fatalf("failed to close zip writer: %v", err)
754+
}
755+
return buf.Bytes()
756+
}
757+
758+
func createTarGzArchive(t *testing.T, files map[string]string) []byte {
759+
t.Helper()
760+
buf := new(bytes.Buffer)
761+
gw := gzip.NewWriter(buf)
762+
tw := tar.NewWriter(gw)
763+
764+
for name, content := range files {
765+
header := &tar.Header{
766+
Name: name,
767+
Size: int64(len(content)),
768+
Mode: 0644,
769+
}
770+
if err := tw.WriteHeader(header); err != nil {
771+
t.Fatalf("failed to write tar header: %v", err)
772+
}
773+
if _, err := tw.Write([]byte(content)); err != nil {
774+
t.Fatalf("failed to write tar content: %v", err)
775+
}
776+
}
777+
778+
if err := tw.Close(); err != nil {
779+
t.Fatalf("failed to close tar writer: %v", err)
780+
}
781+
if err := gw.Close(); err != nil {
782+
t.Fatalf("failed to close gzip writer: %v", err)
783+
}
784+
return buf.Bytes()
785+
}

internal/server/templates_test.go

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -335,25 +335,6 @@ func TestSearchPage_EcosystemFilter(t *testing.T) {
335335
}
336336
}
337337

338-
func TestGetStripPrefix(t *testing.T) {
339-
tests := []struct {
340-
ecosystem string
341-
want string
342-
}{
343-
{"npm", "package/"},
344-
{"cargo", ""},
345-
{"pypi", ""},
346-
{"gem", ""},
347-
{"", ""},
348-
}
349-
350-
for _, tt := range tests {
351-
got := getStripPrefix(tt.ecosystem)
352-
if got != tt.want {
353-
t.Errorf("getStripPrefix(%q) = %q, want %q", tt.ecosystem, got, tt.want)
354-
}
355-
}
356-
}
357338

358339
func TestEcosystemBadgeLabel(t *testing.T) {
359340
tests := []struct {

0 commit comments

Comments
 (0)