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_composer_test.go

126 lines
4.5 KiB
Go
Raw Permalink Normal View History

package server
import (
"crypto/sha1" //nolint:gosec // Packagist lists SHA-1 shasums
"encoding/hex"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"strings"
"sync/atomic"
"testing"
"github.com/git-pkgs/cooldown"
"github.com/git-pkgs/proxy/internal/config"
"github.com/git-pkgs/proxy/internal/handler"
)
const composerCommit = "0123456789abcdef0123456789abcdef01234567"
// composerUpstream serves the Packagist API for acme/widget with release
// 1.0.0, published in 2020, and branch dev-main, and the 1.0.0 archive.
type composerUpstream struct {
*httptest.Server
archive string
shasum string
apiHits atomic.Int32
distHits atomic.Int32
}
func newComposerUpstream(t *testing.T) *composerUpstream {
t.Helper()
u := &composerUpstream{archive: composerZip(`{"name":"acme/widget"}`)}
sum := sha1.Sum([]byte(u.archive)) //nolint:gosec // see import
u.shasum = hex.EncodeToString(sum[:])
u.Server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/packages/acme/widget.json":
u.apiHits.Add(1)
dist := func(shasum string) map[string]string {
return map[string]string{"type": "zip", "url": u.URL + "/zipball/" + composerCommit, "shasum": shasum}
}
_ = json.NewEncoder(w).Encode(map[string]any{"package": map[string]any{
"name": "acme/widget",
"versions": map[string]any{
"1.0.0": map[string]any{"version": "1.0.0", "time": "2020-01-02T03:04:05+00:00", "dist": dist(u.shasum)},
"dev-main": map[string]any{"version": "dev-main", "time": "2026-10-01T00:00:00+00:00", "dist": dist("")},
},
}})
case "/zipball/" + composerCommit:
u.distHits.Add(1)
_, _ = io.WriteString(w, u.archive)
default:
http.NotFound(w, r)
}
}))
t.Cleanup(u.Close)
return u
}
func composerMirrorConfig(upstream *composerUpstream) *config.Config {
cfg := config.Default()
cfg.Upstream.Composer = upstream.URL
cfg.Upstream.ComposerRepository = upstream.URL
return cfg
}
func TestMirrorComposerReleaseIsServedFromCache(t *testing.T) {
upstream := newComposerUpstream(t)
cfg := composerMirrorConfig(upstream)
progress, proxy := mirrorWithConfig(t, cfg, "pkg:composer/Acme/Widget@1.0.0")
if progress.Completed != 1 || upstream.distHits.Load() != 1 {
t.Fatalf("completed = %d, dist hits = %d, errors = %v", progress.Completed, upstream.distHits.Load(), progress.Errors)
}
// Composer gets the archive from the cache once the upstream is down,
// with cooldown on as well: the mirror recorded the publish time.
upstream.Close()
proxy.Cooldown = &cooldown.Config{Default: "3d"}
composer := handler.NewComposerHandlerWithUpstreams(proxy, "http://proxy.test", cfg.Upstream.Composer, cfg.Upstream.ComposerRepository)
rec := httptest.NewRecorder()
composer.Routes().ServeHTTP(rec, httptest.NewRequest(http.MethodGet,
"/files/acme/widget/1.0.0/"+composerCommit+".zip", nil))
if rec.Code != http.StatusOK || rec.Body.String() != upstream.archive {
t.Errorf("archive from cache: status = %d, body %q; want 200 and the mirrored archive", rec.Code, strings.TrimSpace(rec.Body.String()))
}
}
func TestMirrorComposerRefusesShasumMismatch(t *testing.T) {
upstream := newComposerUpstream(t)
upstream.shasum = strings.Repeat("0", 40)
progress, _ := mirrorWithConfig(t, composerMirrorConfig(upstream), "pkg:composer/acme/widget@1.0.0")
if progress.Failed != 1 || progress.Completed != 0 {
t.Errorf("completed = %d, failed = %d, errors = %v; want the archive refused", progress.Completed, progress.Failed, progress.Errors)
}
}
func TestMirrorComposerRefusesBranches(t *testing.T) {
upstream := newComposerUpstream(t)
progress, _ := mirrorWithConfig(t, composerMirrorConfig(upstream), "pkg:composer/acme/widget@dev-main")
if progress.Failed != 1 || len(progress.Errors) != 1 || !strings.Contains(progress.Errors[0].Error, "branch") {
t.Errorf("failed = %d, errors = %v; want the branch refused", progress.Failed, progress.Errors)
}
if upstream.distHits.Load() != 0 {
t.Error("the branch archive was fetched")
}
}
func TestMirrorComposerRefusesRoutedPackages(t *testing.T) {
upstream := newComposerUpstream(t)
cfg := composerMirrorConfig(upstream)
cfg.Upstream.ComposerRoutes = map[string]string{"acme/*": "https://composer.example.com"}
progress, _ := mirrorWithConfig(t, cfg, "pkg:composer/acme/widget@1.0.0")
if progress.Failed != 1 || upstream.apiHits.Load() != 0 || upstream.distHits.Load() != 0 {
t.Errorf("failed = %d, api hits = %d, dist hits = %d; want a routed package refused without asking Packagist",
progress.Failed, upstream.apiHits.Load(), upstream.distHits.Load())
}
}