Watch
1
0
Fork
You've already forked pkg-proxy
1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-11-09 15:49:16 -05:00
pkg-proxy/internal/server/mirror_pypi_test.go

141 lines
4.7 KiB
Go
Raw Permalink Normal View History

package server
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
"github.com/git-pkgs/proxy/internal/config"
"github.com/git-pkgs/proxy/internal/handler"
)
const (
mirrorWheelPath = "/packages/ab/cd/example_pkg-1.0-py3-none-any.whl"
mirrorLinuxWheelPath = "/packages/23/45/example_pkg-1.0-cp312-cp312-manylinux_2_17_x86_64.whl"
mirrorSdistPath = "/packages/ef/01/example.pkg-1.0.tar.gz"
)
// pypiUpstream serves the JSON API for example-pkg 1.0 with two wheels, which
// are cached under the same name and version, and an sdist, whose filename
// spells the name differently. It counts file downloads by path.
type pypiUpstream struct {
*httptest.Server
mu sync.Mutex
files map[string]string
hits map[string]int
}
func newPyPIUpstream(t *testing.T, digests map[string]string) *pypiUpstream {
t.Helper()
u := &pypiUpstream{
files: map[string]string{mirrorWheelPath: "wheel", mirrorLinuxWheelPath: "linux wheel", mirrorSdistPath: "sdist"},
hits: map[string]int{},
}
u.Server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/pypi/example-pkg/json" {
var files []map[string]any
for _, path := range []string{mirrorWheelPath, mirrorLinuxWheelPath, mirrorSdistPath} {
sum := sha256.Sum256([]byte(u.files[path]))
digest := hex.EncodeToString(sum[:])
if d, ok := digests[path]; ok {
digest = d
}
files = append(files, map[string]any{
"url": u.URL + path,
"digests": map[string]string{"sha256": digest},
"size": len(u.files[path]),
})
}
_ = json.NewEncoder(w).Encode(map[string]any{
"info": map[string]any{"name": "example-pkg"},
"releases": map[string]any{"1.0": files},
})
return
}
body, ok := u.files[r.URL.Path]
if !ok {
http.NotFound(w, r)
return
}
u.mu.Lock()
u.hits[r.URL.Path]++
u.mu.Unlock()
_, _ = io.WriteString(w, body)
}))
t.Cleanup(u.Close)
return u
}
func (u *pypiUpstream) hitsFor(path string) int {
u.mu.Lock()
defer u.mu.Unlock()
return u.hits[path]
}
func pypiMirrorConfig(upstream *pypiUpstream) *config.Config {
cfg := config.Default()
cfg.Upstream.PyPI = upstream.URL
cfg.Upstream.PyPIDownload = upstream.URL
return cfg
}
func TestMirrorPyPIFetchesEveryFile(t *testing.T) {
upstream := newPyPIUpstream(t, nil)
cfg := pypiMirrorConfig(upstream)
progress, proxy := mirrorWithConfig(t, cfg, "pkg:pypi/example-pkg@1.0")
if progress.Completed != 1 || progress.Failed != 0 {
t.Fatalf("completed = %d, failed = %d, errors = %v", progress.Completed, progress.Failed, progress.Errors)
}
for path := range upstream.files {
if got := upstream.hitsFor(path); got != 1 {
t.Errorf("%s fetched %d times, want 1", path, got)
}
}
// pip gets every file from the cache once the upstream is down.
upstream.Close()
pypi := handler.NewPyPIHandlerWithUpstreams(proxy, "http://proxy.test", cfg.Upstream.PyPI, cfg.Upstream.PyPIDownload)
for path, want := range upstream.files {
rec := httptest.NewRecorder()
pypi.Routes().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, path, nil))
if rec.Code != http.StatusOK || rec.Body.String() != want {
t.Errorf("%s from cache: status = %d, body = %q, want 200 and %q", path, rec.Code, rec.Body, want)
}
}
}
func TestMirrorPyPIFileNameQualifier(t *testing.T) {
upstream := newPyPIUpstream(t, nil)
progress, _ := mirrorWithConfig(t, pypiMirrorConfig(upstream), "pkg:pypi/example-pkg@1.0?file_name=example.pkg-1.0.tar.gz")
if progress.Completed != 1 || upstream.hitsFor(mirrorSdistPath) != 1 {
t.Errorf("completed = %d, sdist hits = %d, errors = %v; want the sdist", progress.Completed, upstream.hitsFor(mirrorSdistPath), progress.Errors)
}
if upstream.hitsFor(mirrorWheelPath) != 0 {
t.Error("the wheel was mirrored although file_name names the sdist")
}
progress, _ = mirrorWithConfig(t, pypiMirrorConfig(upstream), "pkg:pypi/example-pkg@1.0?file_name=missing-1.0.tar.gz")
if progress.Failed != 1 || len(progress.Errors) != 1 || !strings.Contains(progress.Errors[0].Error, "missing-1.0.tar.gz") {
t.Errorf("failed = %d, errors = %v; want a failure naming the missing file", progress.Failed, progress.Errors)
}
}
func TestMirrorPyPIRefusesDigestMismatch(t *testing.T) {
upstream := newPyPIUpstream(t, map[string]string{mirrorWheelPath: strings.Repeat("0", 64)})
progress, _ := mirrorWithConfig(t, pypiMirrorConfig(upstream), "pkg:pypi/example-pkg@1.0?file_name=example_pkg-1.0-py3-none-any.whl")
if progress.Failed != 1 || progress.Completed != 0 {
t.Errorf("completed = %d, failed = %d, errors = %v; want the mismatching wheel refused", progress.Completed, progress.Failed, progress.Errors)
}
}