2026-10-13 13:23:29 +02:00
|
|
|
package handler
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/json"
|
2026-10-14 13:21:57 +02:00
|
|
|
"io"
|
2026-10-13 13:23:29 +02:00
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
"time"
|
2026-10-13 13:34:52 +02:00
|
|
|
|
|
|
|
|
"github.com/git-pkgs/cooldown"
|
2026-10-14 13:21:57 +02:00
|
|
|
"github.com/git-pkgs/registries/fetch"
|
2026-10-13 13:23:29 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const requestPackument = `{
|
|
|
|
|
"name": "request",
|
|
|
|
|
"dist-tags": {"latest": "2.88.2"},
|
|
|
|
|
"versions": {
|
|
|
|
|
"2.88.2": {"name": "request", "version": "2.88.2", "dependencies": {"qs": "~6.5.1"},
|
|
|
|
|
"dist": {"tarball": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-upstream2"}},
|
|
|
|
|
"2.88.3": {"name": "request", "version": "2.88.3", "dependencies": {"old": "1.0.0"},
|
|
|
|
|
"dist": {"tarball": "https://registry.npmjs.org/request/-/request-2.88.3.tgz", "integrity": "sha512-upstream3"}}
|
|
|
|
|
},
|
|
|
|
|
"time": {"modified": "2020-02-11T00:00:00Z", "2.88.2": "2020-02-11T00:00:00Z", "2.88.3": "2020-02-12T00:00:00Z"}
|
|
|
|
|
}`
|
|
|
|
|
|
|
|
|
|
type npmHostedFixture struct {
|
2026-10-13 13:42:34 +02:00
|
|
|
proxy *Proxy
|
|
|
|
|
handler http.Handler
|
|
|
|
|
upstreamRequests *int
|
2026-10-14 13:21:57 +02:00
|
|
|
fetcher *mockFetcher
|
2026-10-13 13:23:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// newNPMHostedFixture serves packuments from upstream, keyed by request path,
|
|
|
|
|
// and answers 404 for anything else.
|
|
|
|
|
func newNPMHostedFixture(t *testing.T, upstream map[string]string) npmHostedFixture {
|
|
|
|
|
t.Helper()
|
2026-10-13 13:42:34 +02:00
|
|
|
upstreamRequests := 0
|
2026-10-13 13:23:29 +02:00
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2026-10-13 13:42:34 +02:00
|
|
|
upstreamRequests++
|
2026-10-13 13:23:29 +02:00
|
|
|
body, ok := upstream[r.URL.EscapedPath()]
|
|
|
|
|
if !ok {
|
|
|
|
|
http.NotFound(w, r)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
_, _ = w.Write([]byte(body))
|
|
|
|
|
}))
|
|
|
|
|
t.Cleanup(srv.Close)
|
|
|
|
|
|
2026-10-14 13:21:57 +02:00
|
|
|
proxy, _, _, fetcher := setupTestProxy(t)
|
2026-10-13 13:23:29 +02:00
|
|
|
proxy.HostedEnabled = true
|
|
|
|
|
proxy.HTTPClient = srv.Client()
|
2026-10-14 13:21:57 +02:00
|
|
|
return npmHostedFixture{proxy: proxy, handler: NewNPMHandler(proxy, "http://proxy.local", srv.URL).Routes(),
|
|
|
|
|
upstreamRequests: &upstreamRequests, fetcher: fetcher}
|
2026-10-13 13:23:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f npmHostedFixture) push(t *testing.T, name, version, manifest string) HostedArtifact {
|
|
|
|
|
t.Helper()
|
|
|
|
|
target, err := HostedTargetFor("npm", name, version)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("HostedTargetFor: %v", err)
|
|
|
|
|
}
|
|
|
|
|
pushed, err := f.proxy.PushLocalArtifact(context.Background(), "npm", target, bytes.NewReader(npmTarball(manifest)), "")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("PushLocalArtifact: %v", err)
|
|
|
|
|
}
|
|
|
|
|
return pushed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f npmHostedFixture) get(t *testing.T, path string) *httptest.ResponseRecorder {
|
|
|
|
|
t.Helper()
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
f.handler.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, path, nil))
|
|
|
|
|
return rec
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type npmTestPackument struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
DistTags map[string]string `json:"dist-tags"`
|
|
|
|
|
Versions map[string]npmTestVersion `json:"versions"`
|
|
|
|
|
Time map[string]string `json:"time"`
|
|
|
|
|
Extra map[string]json.RawMessage `json:"-"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type npmTestVersion struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
Version string `json:"version"`
|
|
|
|
|
ID string `json:"_id"`
|
|
|
|
|
Dependencies map[string]string `json:"dependencies"`
|
|
|
|
|
Dist struct {
|
|
|
|
|
Tarball string `json:"tarball"`
|
|
|
|
|
Integrity string `json:"integrity"`
|
|
|
|
|
} `json:"dist"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func decodePackument(t *testing.T, rec *httptest.ResponseRecorder) npmTestPackument {
|
|
|
|
|
t.Helper()
|
|
|
|
|
if rec.Code != http.StatusOK {
|
|
|
|
|
t.Fatalf("status = %d, body %s", rec.Code, rec.Body)
|
|
|
|
|
}
|
|
|
|
|
var doc npmTestPackument
|
|
|
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &doc); err != nil {
|
|
|
|
|
t.Fatalf("decoding packument: %v\n%s", err, rec.Body)
|
|
|
|
|
}
|
|
|
|
|
return doc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNPMMetadataListsPushedVersion(t *testing.T) {
|
|
|
|
|
f := newNPMHostedFixture(t, map[string]string{"/request": requestPackument})
|
|
|
|
|
pushed := f.push(t, "request", "2.88.4", `{"name":"request","version":"2.88.4","dependencies":{"qs":"~6.5.2"}}`)
|
|
|
|
|
|
|
|
|
|
doc := decodePackument(t, f.get(t, "/request"))
|
|
|
|
|
|
|
|
|
|
local, ok := doc.Versions["2.88.4"]
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Fatalf("pushed version missing from versions: %v", doc.Versions)
|
|
|
|
|
}
|
|
|
|
|
if local.Name != "request" || local.Version != "2.88.4" || local.ID != "request@2.88.4" || local.Dependencies["qs"] != "~6.5.2" {
|
|
|
|
|
t.Errorf("pushed version entry = %+v", local)
|
|
|
|
|
}
|
|
|
|
|
if local.Dist.Tarball != "http://proxy.local/npm/request/-/request-2.88.4.tgz" {
|
|
|
|
|
t.Errorf("tarball = %q", local.Dist.Tarball)
|
|
|
|
|
}
|
|
|
|
|
if !strings.HasPrefix(local.Dist.Integrity, "sha512-") {
|
|
|
|
|
t.Errorf("integrity = %q, want the pushed archive's sha512", local.Dist.Integrity)
|
|
|
|
|
}
|
|
|
|
|
if doc.Time["2.88.4"] != pushed.PushedAt.Format(time.RFC3339) {
|
|
|
|
|
t.Errorf("time[2.88.4] = %q, want %q", doc.Time["2.88.4"], pushed.PushedAt.Format(time.RFC3339))
|
|
|
|
|
}
|
|
|
|
|
if doc.Versions["2.88.2"].Dist.Tarball != "http://proxy.local/npm/request/-/request-2.88.2.tgz" {
|
|
|
|
|
t.Errorf("upstream version not rewritten: %+v", doc.Versions["2.88.2"])
|
|
|
|
|
}
|
|
|
|
|
if doc.DistTags["latest"] != "2.88.2" {
|
|
|
|
|
t.Errorf("dist-tags.latest = %q, want upstream's 2.88.2", doc.DistTags["latest"])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNPMMetadataPushedVersionReplacesUpstream(t *testing.T) {
|
|
|
|
|
f := newNPMHostedFixture(t, map[string]string{"/request": requestPackument})
|
|
|
|
|
f.push(t, "request", "2.88.3", `{"name":"request","version":"2.88.3","dependencies":{"qs":"~6.5.2"}}`)
|
|
|
|
|
|
|
|
|
|
rec := f.get(t, "/request")
|
|
|
|
|
if body := rec.Body.String(); strings.Contains(body, "sha512-upstream3") || strings.Count(body, `"2.88.3":{`) != 1 {
|
|
|
|
|
t.Fatalf("upstream entry for the pushed version still listed:\n%s", body)
|
|
|
|
|
}
|
|
|
|
|
local := decodePackument(t, rec).Versions["2.88.3"]
|
|
|
|
|
if local.Dependencies["qs"] != "~6.5.2" || local.Dependencies["old"] != "" {
|
|
|
|
|
t.Errorf("dependencies = %v, want the pushed package.json's", local.Dependencies)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNPMMetadataForPackageOnlyPushed(t *testing.T) {
|
|
|
|
|
f := newNPMHostedFixture(t, nil)
|
|
|
|
|
f.push(t, "@acme/private", "1.10.0", `{"name":"@acme/private","version":"1.10.0"}`)
|
|
|
|
|
f.push(t, "@acme/private", "1.9.0", `{"name":"@acme/private","version":"1.9.0"}`)
|
|
|
|
|
|
|
|
|
|
doc := decodePackument(t, f.get(t, "/@acme/private"))
|
|
|
|
|
if doc.Name != "@acme/private" || len(doc.Versions) != 2 {
|
|
|
|
|
t.Errorf("packument = %+v", doc)
|
|
|
|
|
}
|
|
|
|
|
if doc.DistTags["latest"] != "1.10.0" {
|
|
|
|
|
t.Errorf("dist-tags.latest = %q, want the highest pushed version 1.10.0", doc.DistTags["latest"])
|
|
|
|
|
}
|
|
|
|
|
if doc.Versions["1.9.0"].Dist.Tarball != "http://proxy.local/npm/@acme%2Fprivate/-/private-1.9.0.tgz" {
|
|
|
|
|
t.Errorf("tarball = %q", doc.Versions["1.9.0"].Dist.Tarball)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if rec := f.get(t, "/@acme/other"); rec.Code != http.StatusNotFound {
|
|
|
|
|
t.Errorf("unknown package status = %d, want 404", rec.Code)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNPMVersionMetadataForPushedVersion(t *testing.T) {
|
|
|
|
|
f := newNPMHostedFixture(t, map[string]string{"/request": requestPackument})
|
|
|
|
|
f.push(t, "request", "2.88.4", `{"name":"request","version":"2.88.4","dependencies":{"qs":"~6.5.2"}}`)
|
|
|
|
|
|
|
|
|
|
rec := f.get(t, "/request/2.88.4")
|
|
|
|
|
if rec.Code != http.StatusOK {
|
|
|
|
|
t.Fatalf("status = %d, body %s", rec.Code, rec.Body)
|
|
|
|
|
}
|
|
|
|
|
var version npmTestVersion
|
|
|
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &version); err != nil {
|
|
|
|
|
t.Fatalf("decoding version: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if version.Version != "2.88.4" || version.Dist.Tarball != "http://proxy.local/npm/request/-/request-2.88.4.tgz" {
|
|
|
|
|
t.Errorf("version document = %+v", version)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNPMMetadataFollowsPushAndDelete(t *testing.T) {
|
|
|
|
|
f := newNPMHostedFixture(t, map[string]string{"/request": requestPackument})
|
|
|
|
|
f.proxy.CacheMetadata = true
|
|
|
|
|
f.proxy.MetadataTTL = time.Hour
|
|
|
|
|
f.proxy.SetMetadataRewriteCacheSize(1 << 20)
|
|
|
|
|
|
|
|
|
|
before := f.get(t, "/request")
|
|
|
|
|
if before.Code != http.StatusOK {
|
|
|
|
|
t.Fatalf("status = %d", before.Code)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f.push(t, "request", "2.88.4", `{"name":"request","version":"2.88.4"}`)
|
|
|
|
|
pushed := f.get(t, "/request")
|
|
|
|
|
if _, ok := decodePackument(t, pushed).Versions["2.88.4"]; !ok {
|
|
|
|
|
t.Fatal("pushed version not listed while the upstream document is cached")
|
|
|
|
|
}
|
|
|
|
|
if pushed.Header().Get("ETag") == before.Header().Get("ETag") {
|
|
|
|
|
t.Error("ETag unchanged after a push")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := f.proxy.DeleteLocalVersion("npm", "request", "2.88.4"); err != nil {
|
|
|
|
|
t.Fatalf("DeleteLocalVersion: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if _, ok := decodePackument(t, f.get(t, "/request")).Versions["2.88.4"]; ok {
|
|
|
|
|
t.Error("deleted version still listed")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNPMMetadataIgnoresPushesWhenHostedDisabled(t *testing.T) {
|
|
|
|
|
f := newNPMHostedFixture(t, map[string]string{"/request": requestPackument})
|
|
|
|
|
f.push(t, "request", "2.88.4", `{"name":"request","version":"2.88.4"}`)
|
|
|
|
|
f.proxy.HostedEnabled = false
|
|
|
|
|
|
|
|
|
|
if _, ok := decodePackument(t, f.get(t, "/request")).Versions["2.88.4"]; ok {
|
|
|
|
|
t.Error("pushed version listed with hosted packages disabled")
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-13 13:34:52 +02:00
|
|
|
|
|
|
|
|
func TestNPMDownloadOfPushedPackageWithCooldown(t *testing.T) {
|
|
|
|
|
f := newNPMHostedFixture(t, nil)
|
|
|
|
|
f.proxy.Cooldown = &cooldown.Config{Default: "7d"}
|
|
|
|
|
f.push(t, "@acme/private", "1.0.0", `{"name":"@acme/private","version":"1.0.0"}`)
|
|
|
|
|
|
|
|
|
|
rec := f.get(t, "/@acme/private/-/private-1.0.0.tgz")
|
|
|
|
|
if rec.Code != http.StatusOK {
|
|
|
|
|
t.Fatalf("download status = %d, body %s, want the pushed tarball", rec.Code, rec.Body)
|
|
|
|
|
}
|
|
|
|
|
if _, ok := decodePackument(t, f.get(t, "/@acme/private")).Versions["1.0.0"]; !ok {
|
|
|
|
|
t.Error("pushed version missing from the packument with cooldown enabled")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNPMDownloadOfPushedOverrideOfVersionInCooldown(t *testing.T) {
|
|
|
|
|
recent := time.Now().Add(-time.Hour).Format(time.RFC3339)
|
|
|
|
|
packument := `{"name":"request","dist-tags":{"latest":"2.88.2"},
|
|
|
|
|
"versions":{
|
|
|
|
|
"2.88.2":{"name":"request","version":"2.88.2","dist":{"tarball":"https://registry.npmjs.org/request/-/request-2.88.2.tgz"}},
|
|
|
|
|
"2.88.3":{"name":"request","version":"2.88.3","dist":{"tarball":"https://registry.npmjs.org/request/-/request-2.88.3.tgz"}}},
|
|
|
|
|
"time":{"2.88.2":"2020-02-11T00:00:00Z","2.88.3":"` + recent + `"}}`
|
|
|
|
|
f := newNPMHostedFixture(t, map[string]string{"/request": packument})
|
|
|
|
|
f.proxy.Cooldown = &cooldown.Config{Default: "7d"}
|
|
|
|
|
f.push(t, "request", "2.88.3", `{"name":"request","version":"2.88.3"}`)
|
|
|
|
|
|
|
|
|
|
if _, ok := decodePackument(t, f.get(t, "/request")).Versions["2.88.3"]; !ok {
|
|
|
|
|
t.Fatal("pushed version missing from the packument")
|
|
|
|
|
}
|
|
|
|
|
rec := f.get(t, "/request/-/request-2.88.3.tgz")
|
|
|
|
|
if rec.Code != http.StatusOK || rec.Body.String() != string(npmTarball(`{"name":"request","version":"2.88.3"}`)) {
|
|
|
|
|
t.Errorf("download status = %d, want 200 and the pushed tarball: the packument lists it, so the download must not be withheld", rec.Code)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-13 13:42:34 +02:00
|
|
|
|
|
|
|
|
func TestNPMHostedOnlyScopeNeverAsksUpstream(t *testing.T) {
|
|
|
|
|
squatter := `{"name":"@acme/private","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.1":{"name":"@acme/private","version":"1.0.1",
|
|
|
|
|
"dist":{"tarball":"https://registry.npmjs.org/@acme/private/-/private-1.0.1.tgz"}}}}`
|
|
|
|
|
f := newNPMHostedFixture(t, map[string]string{"/@acme%2fprivate": squatter, "/@acme%2Fprivate": squatter, "/request": requestPackument})
|
|
|
|
|
f.proxy.HostedNPMScopes = []string{"@acme"}
|
|
|
|
|
f.push(t, "@acme/private", "1.0.0", `{"name":"@acme/private","version":"1.0.0"}`)
|
|
|
|
|
|
|
|
|
|
doc := decodePackument(t, f.get(t, "/@acme/private"))
|
|
|
|
|
if len(doc.Versions) != 1 || doc.DistTags["latest"] != "1.0.0" {
|
|
|
|
|
t.Errorf("packument = %+v, want only the pushed 1.0.0", doc)
|
|
|
|
|
}
|
|
|
|
|
if rec := f.get(t, "/@acme/private/latest"); rec.Code != http.StatusOK || !strings.Contains(rec.Body.String(), `"version":"1.0.0"`) {
|
|
|
|
|
t.Errorf("latest status = %d, body %s", rec.Code, rec.Body)
|
|
|
|
|
}
|
|
|
|
|
if rec := f.get(t, "/@acme/private/1.0.1"); rec.Code != http.StatusNotFound {
|
|
|
|
|
t.Errorf("unpushed version status = %d, want 404", rec.Code)
|
|
|
|
|
}
|
|
|
|
|
if rec := f.get(t, "/@acme/private/-/private-1.0.1.tgz"); rec.Code != http.StatusNotFound {
|
|
|
|
|
t.Errorf("unpushed tarball status = %d, want 404", rec.Code)
|
|
|
|
|
}
|
|
|
|
|
if rec := f.get(t, "/@acme/other"); rec.Code != http.StatusNotFound {
|
|
|
|
|
t.Errorf("unpushed package status = %d, want 404", rec.Code)
|
|
|
|
|
}
|
|
|
|
|
if rec := f.get(t, "/@acme/private/-/private-1.0.0.tgz"); rec.Code != http.StatusOK {
|
|
|
|
|
t.Errorf("pushed tarball status = %d, want 200", rec.Code)
|
|
|
|
|
}
|
|
|
|
|
if *f.upstreamRequests != 0 {
|
|
|
|
|
t.Errorf("hosted-only scope sent %d requests upstream", *f.upstreamRequests)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if rec := f.get(t, "/request"); rec.Code != http.StatusOK || *f.upstreamRequests == 0 {
|
|
|
|
|
t.Errorf("package outside the scope status = %d after %d upstream requests, want it fetched upstream", rec.Code, *f.upstreamRequests)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-13 15:16:55 +02:00
|
|
|
|
|
|
|
|
func TestNPMDownloadOfUnreadablePushedTarball(t *testing.T) {
|
|
|
|
|
f := newNPMHostedFixture(t, map[string]string{"/request": requestPackument})
|
|
|
|
|
f.push(t, "request", "2.88.3", `{"name":"request","version":"2.88.3"}`)
|
|
|
|
|
removeStored(t, f.proxy, "pkg:npm/request@2.88.3", "request-2.88.3.tgz")
|
|
|
|
|
|
|
|
|
|
if rec := f.get(t, "/request/-/request-2.88.3.tgz"); rec.Code != http.StatusServiceUnavailable {
|
|
|
|
|
t.Errorf("download status = %d, body %s, want 503 rather than the upstream tarball", rec.Code, rec.Body)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-13 15:18:49 +02:00
|
|
|
|
|
|
|
|
func TestNPMMetadataLeavesOutDeniedPushedVersions(t *testing.T) {
|
|
|
|
|
f := newNPMHostedFixture(t, nil)
|
|
|
|
|
f.push(t, "@acme/private", "1.0.0", `{"name":"@acme/private","version":"1.0.0"}`)
|
|
|
|
|
f.push(t, "@acme/private", "1.1.0", `{"name":"@acme/private","version":"1.1.0"}`)
|
|
|
|
|
setTestDenylist(t, f.proxy, "pkg:npm/@acme/private@1.1.0")
|
|
|
|
|
|
|
|
|
|
doc := decodePackument(t, f.get(t, "/@acme/private"))
|
|
|
|
|
if _, ok := doc.Versions["1.1.0"]; ok || len(doc.Versions) != 1 {
|
|
|
|
|
t.Errorf("versions = %v, want the denied 1.1.0 left out", doc.Versions)
|
|
|
|
|
}
|
|
|
|
|
if doc.DistTags["latest"] != "1.0.0" {
|
|
|
|
|
t.Errorf("dist-tags.latest = %q, want 1.0.0", doc.DistTags["latest"])
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-14 13:21:57 +02:00
|
|
|
|
|
|
|
|
func TestNPMDownloadOfPushedTarballWhileStreamingArtifacts(t *testing.T) {
|
|
|
|
|
const manifest = `{"name":"request","version":"2.88.3"}`
|
|
|
|
|
f := newNPMHostedFixture(t, map[string]string{"/request": requestPackument})
|
|
|
|
|
f.fetcher.artifact = &fetch.Artifact{Body: io.NopCloser(strings.NewReader("upstream tarball")), ContentType: "application/gzip"}
|
|
|
|
|
f.push(t, "request", "2.88.3", manifest)
|
|
|
|
|
f.proxy.StreamArtifacts = true
|
|
|
|
|
|
|
|
|
|
rec := f.get(t, "/request/-/request-2.88.3.tgz")
|
|
|
|
|
if rec.Code != http.StatusOK || rec.Body.String() != string(npmTarball(manifest)) {
|
|
|
|
|
t.Errorf("download status = %d, body %q, want 200 and the pushed tarball", rec.Code, rec.Body)
|
|
|
|
|
}
|
|
|
|
|
if f.fetcher.fetchCalled {
|
|
|
|
|
t.Error("a pushed tarball was fetched upstream while streaming artifacts")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNPMDownloadOfUnreadablePushedTarballWhileStreamingArtifacts(t *testing.T) {
|
|
|
|
|
f := newNPMHostedFixture(t, map[string]string{"/request": requestPackument})
|
|
|
|
|
f.push(t, "request", "2.88.3", `{"name":"request","version":"2.88.3"}`)
|
|
|
|
|
removeStored(t, f.proxy, "pkg:npm/request@2.88.3", "request-2.88.3.tgz")
|
|
|
|
|
f.proxy.StreamArtifacts = true
|
|
|
|
|
|
|
|
|
|
if rec := f.get(t, "/request/-/request-2.88.3.tgz"); rec.Code != http.StatusServiceUnavailable {
|
|
|
|
|
t.Errorf("download status = %d, body %s, want 503 rather than the upstream tarball", rec.Code, rec.Body)
|
|
|
|
|
}
|
|
|
|
|
}
|