pkg-proxy/internal/handler/conda_test.go

305 lines
7.5 KiB
Go
Raw Permalink Normal View History

2026-01-20 21:52:44 +00:00
package handler
import (
"encoding/json"
2026-01-20 21:52:44 +00:00
"log/slog"
"net/http"
"net/http/httptest"
2026-01-20 21:52:44 +00:00
"testing"
"time"
"github.com/git-pkgs/proxy/internal/cooldown"
2026-01-20 21:52:44 +00:00
)
func TestCondaParseFilename(t *testing.T) {
h := &CondaHandler{proxy: &Proxy{Logger: slog.Default()}}
tests := []struct {
filename string
wantName string
wantVersion string
}{
{"numpy-1.24.0-py311h64a7726_0.conda", "numpy", "1.24.0"},
{"scipy-1.11.4-py310h64a7726_0.tar.bz2", "scipy", "1.11.4"},
{"python-dateutil-2.8.2-pyhd8ed1ab_0.conda", "python-dateutil", "2.8.2"},
{"ca-certificates-2023.11.17-hbcca054_0.conda", "ca-certificates", "2023.11.17"},
{"invalid", "", ""},
}
for _, tt := range tests {
name, version := h.parseFilename(tt.filename)
if name != tt.wantName || version != tt.wantVersion {
t.Errorf("parseFilename(%q) = (%q, %q), want (%q, %q)",
tt.filename, name, version, tt.wantName, tt.wantVersion)
}
}
}
func TestCondaIsPackageFile(t *testing.T) {
h := &CondaHandler{}
tests := []struct {
filename string
want bool
}{
{"numpy-1.24.0-py311h64a7726_0.conda", true},
{"scipy-1.11.4-py310h64a7726_0.tar.bz2", true},
{"repodata.json", false},
{"repodata.json.bz2", false},
}
for _, tt := range tests {
got := h.isPackageFile(tt.filename)
if got != tt.want {
t.Errorf("isPackageFile(%q) = %v, want %v", tt.filename, got, tt.want)
}
}
}
func TestCondaCooldownFiltering(t *testing.T) {
now := time.Now()
oldTimestamp := float64(now.Add(-7 * 24 * time.Hour).UnixMilli())
recentTimestamp := float64(now.Add(-1 * time.Hour).UnixMilli())
repodata := map[string]any{
"info": map[string]any{},
"packages": map[string]any{
"numpy-1.24.0-old.tar.bz2": map[string]any{
"name": "numpy",
"version": "1.24.0",
"timestamp": oldTimestamp,
},
"numpy-1.25.0-new.tar.bz2": map[string]any{
"name": "numpy",
"version": "1.25.0",
"timestamp": recentTimestamp,
},
},
"packages.conda": map[string]any{
"scipy-1.11.0-old.conda": map[string]any{
"name": "scipy",
"version": "1.11.0",
"timestamp": oldTimestamp,
},
"scipy-1.12.0-new.conda": map[string]any{
"name": "scipy",
"version": "1.12.0",
"timestamp": recentTimestamp,
},
},
}
body, err := json.Marshal(repodata)
if err != nil {
t.Fatal(err)
}
proxy := testProxy()
proxy.Cooldown = &cooldown.Config{
Default: "3d",
}
h := &CondaHandler{
proxy: proxy,
proxyURL: "http://localhost:8080",
}
filtered, err := h.applyCooldownFiltering(body)
if err != nil {
t.Fatal(err)
}
var result map[string]any
if err := json.Unmarshal(filtered, &result); err != nil {
t.Fatal(err)
}
packages := result["packages"].(map[string]any)
if len(packages) != 1 {
t.Fatalf("expected 1 package in packages, got %d", len(packages))
}
if _, ok := packages["numpy-1.24.0-old.tar.bz2"]; !ok {
t.Error("expected old numpy to survive filtering")
}
condaPkgs := result["packages.conda"].(map[string]any)
if len(condaPkgs) != 1 {
t.Fatalf("expected 1 package in packages.conda, got %d", len(condaPkgs))
}
if _, ok := condaPkgs["scipy-1.11.0-old.conda"]; !ok {
t.Error("expected old scipy to survive filtering")
}
}
func TestCondaCooldownFilteringWithPackageOverride(t *testing.T) {
now := time.Now()
recentTimestamp := float64(now.Add(-2 * time.Hour).UnixMilli())
repodata := map[string]any{
"info": map[string]any{},
"packages": map[string]any{
"special-1.0.0-build.tar.bz2": map[string]any{
"name": "special",
"version": "1.0.0",
"timestamp": recentTimestamp,
},
},
"packages.conda": map[string]any{},
}
body, err := json.Marshal(repodata)
if err != nil {
t.Fatal(err)
}
proxy := testProxy()
proxy.Cooldown = &cooldown.Config{
Default: "3d",
Packages: map[string]string{"pkg:conda/special": "1h"},
}
h := &CondaHandler{
proxy: proxy,
proxyURL: "http://localhost:8080",
}
filtered, err := h.applyCooldownFiltering(body)
if err != nil {
t.Fatal(err)
}
var result map[string]any
if err := json.Unmarshal(filtered, &result); err != nil {
t.Fatal(err)
}
packages := result["packages"].(map[string]any)
if len(packages) != 1 {
t.Fatalf("expected 1 package (override allows it), got %d", len(packages))
}
}
func TestCondaCooldownFilteringNoTimestamp(t *testing.T) {
repodata := map[string]any{
"info": map[string]any{},
"packages": map[string]any{
"old-pkg-1.0.0-build.tar.bz2": map[string]any{
"name": "old-pkg",
"version": "1.0.0",
// no timestamp field
},
},
"packages.conda": map[string]any{},
}
body, err := json.Marshal(repodata)
if err != nil {
t.Fatal(err)
}
proxy := testProxy()
proxy.Cooldown = &cooldown.Config{
Default: "3d",
}
h := &CondaHandler{
proxy: proxy,
proxyURL: "http://localhost:8080",
}
filtered, err := h.applyCooldownFiltering(body)
if err != nil {
t.Fatal(err)
}
var result map[string]any
if err := json.Unmarshal(filtered, &result); err != nil {
t.Fatal(err)
}
packages := result["packages"].(map[string]any)
if len(packages) != 1 {
t.Fatalf("entries without timestamp should pass through, got %d", len(packages))
}
}
func TestCondaHandleRepodataWithCooldown(t *testing.T) {
now := time.Now()
oldTimestamp := float64(now.Add(-7 * 24 * time.Hour).UnixMilli())
recentTimestamp := float64(now.Add(-1 * time.Hour).UnixMilli())
repodataJSON, _ := json.Marshal(map[string]any{
"info": map[string]any{},
"packages": map[string]any{
"old-1.0.0-build.tar.bz2": map[string]any{
"name": "testpkg", "version": "1.0.0", "timestamp": oldTimestamp,
},
"new-2.0.0-build.tar.bz2": map[string]any{
"name": "testpkg", "version": "2.0.0", "timestamp": recentTimestamp,
},
},
"packages.conda": map[string]any{},
})
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(repodataJSON)
}))
defer upstream.Close()
proxy := testProxy()
proxy.Cooldown = &cooldown.Config{
Default: "3d",
}
h := &CondaHandler{
proxy: proxy,
upstreamURL: upstream.URL,
proxyURL: "http://proxy.local",
}
req := httptest.NewRequest(http.MethodGet, "/conda-forge/noarch/repodata.json", nil)
req.SetPathValue("channel", "conda-forge")
req.SetPathValue("arch", "noarch")
w := httptest.NewRecorder()
h.handleRepodata(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status = %d, want %d", w.Code, http.StatusOK)
}
var result map[string]any
if err := json.Unmarshal(w.Body.Bytes(), &result); err != nil {
t.Fatal(err)
}
packages := result["packages"].(map[string]any)
if len(packages) != 1 {
t.Fatalf("expected 1 package after filtering, got %d", len(packages))
}
}
func TestCondaHandleRepodataWithoutCooldown(t *testing.T) {
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"info":{},"packages":{},"packages.conda":{}}`))
}))
defer upstream.Close()
h := &CondaHandler{
proxy: &Proxy{Logger: slog.Default(), HTTPClient: http.DefaultClient},
upstreamURL: upstream.URL,
proxyURL: "http://proxy.local",
}
req := httptest.NewRequest(http.MethodGet, "/conda-forge/noarch/repodata.json", nil)
req.SetPathValue("channel", "conda-forge")
req.SetPathValue("arch", "noarch")
w := httptest.NewRecorder()
h.handleRepodata(w, req)
// Without cooldown, should proxy directly (response comes from upstream)
if w.Code != http.StatusOK {
t.Fatalf("status = %d, want %d", w.Code, http.StatusOK)
}
}