proxy-pool/internal/config/config_test.go

146 lines
3.6 KiB
Go

package config
import (
"os"
"path/filepath"
"strings"
"testing"
)
const validConfig = `
version: 1
security:
requireProtectionOnPublicListen: true
gateway:
enabled: true
listen: 127.0.0.1:8080
auth:
mode: none
distribution:
enabled: true
listen: 127.0.0.1:8081
auth:
mode: none
extraction:
fulfillment: partial
maxCountPerRequest: 20
minRemainingTTL: 30s
maxHealthCheckAge: 15s
reserveForGateway: 5
routing:
- name: extract
enabled: true
purpose: extract
upstreams: [provider-a]
strategy:
type: sequential
switchAfterEmptyFetch: 5
onUnavailable:
action: reject
upstreams:
provider-a:
enabled: true
exposure: [gateway, extract]
provider:
billingMode: fetch
protocols: [http]
api:
url: https://provider.example/proxies
method: GET
template: '{{.}}'
auth:
type: none
proxyAuth:
type: response
pool:
maxSize: 100
capacity:
maxConcurrencyPerProxy: 10
lifecycle:
ttl: 120s
allocationSafetyMargin: 10s
fetch:
requestInterval: 1s
timeout: 3s
maxAttempts: 5
maxInFlight: 1
maxTotal: 1000
check:
interval: 30s
jitter: 20
maxInFlight: 100
timeout: 2s
maxAttempts: 2
maxConsecutiveFailures: 3
urls: [http://connect.rom.miui.com/generate_204]
`
func TestLoadStrictValidConfiguration(t *testing.T) {
cfg, err := Load(strings.NewReader(validConfig))
if err != nil {
t.Fatalf("Load(): %v", err)
}
if cfg.Version != 1 || cfg.Upstreams["provider-a"].Pool.MaxSize != 100 {
t.Fatalf("unexpected config: %+v", cfg)
}
}
func TestLoadRejectsUnknownFields(t *testing.T) {
_, err := Load(strings.NewReader(validConfig + "unknownField: true\n"))
if err == nil || !strings.Contains(err.Error(), "unknownField") {
t.Fatalf("Load() error = %v, want unknown field error", err)
}
}
func TestValidateRejectsUnprotectedPublicListener(t *testing.T) {
cfg, err := Load(strings.NewReader(strings.Replace(validConfig,
"listen: 127.0.0.1:8080", "listen: 0.0.0.0:8080", 1)))
if err == nil || !strings.Contains(err.Error(), "gateway") || !strings.Contains(err.Error(), "public") {
t.Fatalf("Load() error = %v, want unprotected public listener error", err)
}
if cfg != nil {
t.Fatal("invalid config must not be returned")
}
}
func TestValidateRejectsMissingUpstreamReference(t *testing.T) {
broken := strings.Replace(validConfig, "upstreams: [provider-a]", "upstreams: [missing]", 1)
_, err := Load(strings.NewReader(broken))
if err == nil || !strings.Contains(err.Error(), "missing") {
t.Fatalf("Load() error = %v, want missing upstream error", err)
}
}
func TestValidateSeparatesPoolAndFetchLimits(t *testing.T) {
broken := strings.Replace(validConfig, "maxTotal: 1000", "maxTotal: 50", 1)
_, err := Load(strings.NewReader(broken))
if err == nil || !strings.Contains(err.Error(), "maxTotal") {
t.Fatalf("Load() error = %v, want maxTotal validation error", err)
}
}
func TestShippedConfigurationsAreValid(t *testing.T) {
paths, err := filepath.Glob(filepath.Join("..", "..", "examples", "config", "*.yaml"))
if err != nil {
t.Fatalf("Glob(): %v", err)
}
paths = append(paths, filepath.Join("..", "..", "configs", "proxy-pool.yaml"))
if len(paths) != 21 {
t.Fatalf("configuration count = %d, want 21", len(paths))
}
for _, path := range paths {
path := path
t.Run(filepath.Base(path), func(t *testing.T) {
file, err := os.Open(path)
if err != nil {
t.Fatalf("Open(): %v", err)
}
defer file.Close()
if _, err := Load(file); err != nil {
t.Fatalf("Load(): %v", err)
}
})
}
}