Watch
1
0
Fork
You've already forked pkg-proxy
1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-11-09 07:39:19 -05:00
pkg-proxy/internal/config/hosted_test.go
simon chrzanowski 69313c26d9
Serve hosted-only npm scopes and Composer vendors from pushed packages alone
A package the upstream doesn't know is served from its pushed versions, but the upstream is still asked about it. Once someone publishes the same name there, their versions are listed next to the pushed ones and a version range can resolve to them.

hosted.npm_scopes and hosted.composer_vendors (PROXY_HOSTED_NPM_SCOPES, PROXY_HOSTED_COMPOSER_VENDORS) name namespaces whose packages are served from pushed versions alone. Their metadata is built from what was pushed, npm latest included, and anything not pushed answers 404. The upstream is never asked about them. Packages in other namespaces are proxied as before.

The npm metadata handler's decisions about pushed versions move into serveLocalMetadata.

Refs #133
Refs #134
2026-10-14 13:23:19 +02:00

97 lines
3.9 KiB
Go

package config
import (
"slices"
"testing"
)
func TestHostedConfigValidate(t *testing.T) {
t.Setenv("HOSTED_TEST_TOKEN", "from-env")
tests := []struct {
name string
hosted HostedConfig
wantErr bool
}{
{name: "disabled without token", hosted: HostedConfig{}},
{name: "enabled without token", hosted: HostedConfig{Enabled: true}, wantErr: true},
{name: "enabled with token", hosted: HostedConfig{Enabled: true, Token: "s3cret"}},
{name: "token from env reference", hosted: HostedConfig{Enabled: true, Token: "${HOSTED_TEST_TOKEN}"}},
{name: "token env reference unset", hosted: HostedConfig{Enabled: true, Token: "${HOSTED_TEST_UNSET}"}, wantErr: true},
{name: "invalid upload size", hosted: HostedConfig{Enabled: true, Token: "s3cret", MaxUploadSize: "lots"}, wantErr: true},
{name: "zero upload size", hosted: HostedConfig{Enabled: true, Token: "s3cret", MaxUploadSize: "0"}, wantErr: true},
{name: "npm scopes", hosted: HostedConfig{Enabled: true, Token: "s3cret", NPMScopes: []string{"@acme", "@acme-internal"}}},
{name: "npm scope without @", hosted: HostedConfig{Enabled: true, Token: "s3cret", NPMScopes: []string{"acme"}}, wantErr: true},
{name: "npm scope with package", hosted: HostedConfig{Enabled: true, Token: "s3cret", NPMScopes: []string{"@acme/private"}}, wantErr: true},
{name: "bare @", hosted: HostedConfig{Enabled: true, Token: "s3cret", NPMScopes: []string{"@"}}, wantErr: true},
{name: "composer vendors", hosted: HostedConfig{Enabled: true, Token: "s3cret", ComposerVendors: []string{"acme", "acme-internal"}}},
{name: "composer vendor with package", hosted: HostedConfig{Enabled: true, Token: "s3cret", ComposerVendors: []string{"acme/private"}}, wantErr: true},
{name: "empty composer vendor", hosted: HostedConfig{Enabled: true, Token: "s3cret", ComposerVendors: []string{""}}, wantErr: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.hosted.Validate()
if (err != nil) != tt.wantErr {
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestHostedConfigDefaults(t *testing.T) {
h := HostedConfig{Enabled: true, Token: "${HOSTED_TEST_TOKEN}"}
t.Setenv("HOSTED_TEST_TOKEN", "from-env")
if err := h.Validate(); err != nil {
t.Fatalf("Validate: %v", err)
}
if got := h.TokenExpanded(); got != "from-env" {
t.Errorf("TokenExpanded() = %q, want from-env", got)
}
if got := h.MaxUploadBytes(); got != 100<<20 {
t.Errorf("MaxUploadBytes() = %d, want %d", got, 100<<20)
}
h.MaxUploadSize = "2GB"
if err := h.Validate(); err != nil {
t.Fatalf("Validate: %v", err)
}
if got := h.MaxUploadBytes(); got != 2<<30 {
t.Errorf("MaxUploadBytes() = %d, want %d", got, int64(2)<<30)
}
}
func TestValidateRejectsHostedWithoutCachedArtifacts(t *testing.T) {
cfg := Default()
cfg.Hosted = HostedConfig{Enabled: true, Token: "s3cret"}
cfg.Storage.CacheArtifacts = false
if err := cfg.Validate(); err == nil {
t.Error("Validate() accepted hosted.enabled with storage.cache_artifacts: false")
}
}
func TestLoadFromEnvHosted(t *testing.T) {
cfg := Default()
t.Setenv("PROXY_HOSTED_ENABLED", "true")
t.Setenv("PROXY_HOSTED_TOKEN", "env-token")
t.Setenv("PROXY_HOSTED_MAX_UPLOAD_SIZE", "50MB")
t.Setenv("PROXY_HOSTED_NPM_SCOPES", "@acme, @acme-internal")
t.Setenv("PROXY_HOSTED_COMPOSER_VENDORS", "acme")
cfg.LoadFromEnv()
if !cfg.Hosted.Enabled {
t.Error("Hosted.Enabled not loaded from PROXY_HOSTED_ENABLED")
}
if cfg.Hosted.Token != "env-token" {
t.Errorf("Hosted.Token = %q, want env-token", cfg.Hosted.Token)
}
if cfg.Hosted.MaxUploadSize != "50MB" {
t.Errorf("Hosted.MaxUploadSize = %q, want 50MB", cfg.Hosted.MaxUploadSize)
}
if !slices.Equal(cfg.Hosted.NPMScopes, []string{"@acme", "@acme-internal"}) {
t.Errorf("Hosted.NPMScopes = %q", cfg.Hosted.NPMScopes)
}
if !slices.Equal(cfg.Hosted.ComposerVendors, []string{"acme"}) {
t.Errorf("Hosted.ComposerVendors = %q", cfg.Hosted.ComposerVendors)
}
}