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/handler/swift_cache_test.go
Simon Chrzanowski 8ea75a30a3 Check cached Swift documents against their digest and resolve source PURLs
Release lists and manifests record their sha256 digest and are read back
through storedMetadataEntry and openMetadataEntry, so a corrupted copy is
fetched again. pkg:swift PURLs naming a source repository are looked up
through the registry's /identifiers.
2026-10-26 16:43:42 +01:00

124 lines
4.5 KiB
Go

package handler
import (
"context"
"io"
"net/http"
"net/http/httptest"
"strings"
"sync/atomic"
"testing"
"time"
)
func TestSwiftReleaseListAndManifestServedFromCacheWhenUpstreamFails(t *testing.T) {
var down atomic.Bool
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if down.Load() {
http.Error(w, "down", http.StatusServiceUnavailable)
return
}
w.Header().Set("Link", `</registry/apple/example?page=2>; rel="next"`)
if strings.HasSuffix(r.URL.Path, "/Package.swift") {
w.Header().Set("Content-Type", "text/x-swift")
_, _ = io.WriteString(w, "// swift-tools-version:5.9")
return
}
w.Header().Set("Content-Type", "application/json")
_, _ = io.WriteString(w, `{"releases":{"1.0.0":{}}}`)
}))
defer upstream.Close()
for _, cacheMetadata := range []bool{true, false} {
proxy, _, _, _ := setupTestProxy(t)
proxy.CacheMetadata = cacheMetadata
routes := NewSwiftHandler(proxy, "https://proxy.example", upstream.URL+"/registry").Routes()
get := func(path string) *httptest.ResponseRecorder {
w := httptest.NewRecorder()
routes.ServeHTTP(w, httptest.NewRequest(http.MethodGet, path, nil))
return w
}
down.Store(false)
for _, path := range []string{"/apple/example", "/apple/example/1.0.0/Package.swift"} {
if w := get(path); w.Code != http.StatusOK {
t.Fatalf("%s: status = %d with the upstream up", path, w.Code)
}
}
down.Store(true)
for path, body := range map[string]string{"/apple/example": `"1.0.0"`, "/apple/example/1.0.0/Package.swift": "swift-tools-version:5.9"} {
w := get(path)
if !cacheMetadata {
if w.Code == http.StatusOK {
t.Errorf("%s: served without cache_metadata while the upstream is down", path)
}
continue
}
if w.Code != http.StatusOK || !strings.Contains(w.Body.String(), body) {
t.Errorf("%s: status = %d, body %q; want the cached copy", path, w.Code, w.Body.String())
}
if link := w.Header().Get("Link"); link != `<https://proxy.example/swift/apple/example?page=2>; rel="next"` {
t.Errorf("%s: Link = %q, want the cached Link rewritten", path, link)
}
}
}
}
// TestSwiftCorruptManifestRefetched checks that a cached manifest whose
// stored bytes no longer match the recorded digest is fetched again, even
// within metadata_ttl.
func TestSwiftCorruptManifestRefetched(t *testing.T) {
var hits atomic.Int32
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hits.Add(1)
w.Header().Set("Content-Type", "text/x-swift")
_, _ = io.WriteString(w, "// swift-tools-version:5.9")
}))
defer upstream.Close()
proxy, db, store, _ := setupTestProxy(t)
proxy.CacheMetadata = true
proxy.MetadataTTL = time.Hour
routes := NewSwiftHandler(proxy, "https://proxy.example", upstream.URL+"/registry").Routes()
get := func() *httptest.ResponseRecorder {
w := httptest.NewRecorder()
routes.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/apple/example/1.0.0/Package.swift", nil))
return w
}
get()
entry, err := db.GetMetadataCache("swift", swiftManifestCacheKey("apple", "example", "1.0.0", ""))
if err != nil || entry == nil {
t.Fatalf("manifest not cached: %v", err)
}
if !strings.HasPrefix(entry.ContentDigest.String, "sha256:") {
t.Fatalf("ContentDigest = %q, want a sha256 digest", entry.ContentDigest.String)
}
if w := get(); hits.Load() != 1 || w.Code != http.StatusOK {
t.Fatalf("fresh cached manifest: status %d, %d upstream fetches; want it served from the cache", w.Code, hits.Load())
}
if _, _, err := store.Store(context.Background(), entry.StoragePath, strings.NewReader("// tampered")); err != nil {
t.Fatal(err)
}
w := get()
if hits.Load() != 2 {
t.Errorf("upstream fetches = %d, want the corrupt manifest fetched again", hits.Load())
}
if w.Code != http.StatusOK || !strings.Contains(w.Body.String(), "swift-tools-version:5.9") {
t.Errorf("status = %d, body %q; want the upstream manifest", w.Code, w.Body.String())
}
}
func TestSwiftManifestCacheKeyIgnoresToolsVersionZeros(t *testing.T) {
key := swiftManifestCacheKey("apple", "example", "1.0.0", "swift-version=5.8")
if got := swiftManifestCacheKey("apple", "example", "1.0.0", "swift-version=5.8.0"); got != key {
t.Error("5.8.0 and 5.8 have different keys")
}
if got := swiftManifestCacheKey("apple", "example", "1.0.0", "swift-version=5.9"); got == key {
t.Error("5.9 and 5.8 share a key")
}
if got := swiftManifestCacheKey("apple", "example", "1.0.0", ""); got == key {
t.Error("Package.swift and the 5.8 manifest share a key")
}
}