snapshot of https://github.com/centrifugal/centrifugo.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
test / Lint (push) Failing after 3s
test / Test with Go 1.26.8 (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-16 01:17:21 +05:00
commit b352a9a30d
472 changed files with 108375 additions and 0 deletions

View File

@@ -0,0 +1,707 @@
package config
import (
"testing"
"time"
"github.com/centrifugal/centrifugo/v6/internal/configtypes"
"github.com/stretchr/testify/require"
)
func TestValidatePublicationDataFormat(t *testing.T) {
tests := []struct {
name string
format string
wantErr bool
}{
{
name: "empty string is valid",
format: "",
wantErr: false,
},
{
name: "json is valid",
format: configtypes.PublicationDataFormatJSON,
wantErr: false,
},
{
name: "json_object is valid",
format: configtypes.PublicationDataFormatJSONObject,
wantErr: false,
},
{
name: "binary is valid",
format: configtypes.PublicationDataFormatBinary,
wantErr: false,
},
{
name: "unknown format xml is invalid",
format: "xml",
wantErr: true,
},
{
name: "unknown format text is invalid",
format: "text",
wantErr: true,
},
{
name: "random string is invalid",
format: "foobar",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := DefaultConfig()
cfg.Channel.WithoutNamespace.PublicationDataFormat = tt.format
err := cfg.Validate()
if tt.wantErr {
require.Error(t, err)
require.Contains(t, err.Error(), "unknown publication_data_format")
} else {
require.NoError(t, err)
}
})
}
}
func TestValidatePublicationDataFormatInNamespace(t *testing.T) {
tests := []struct {
name string
format string
wantErr bool
}{
{
name: "json in namespace",
format: "json",
wantErr: false,
},
{
name: "json_object in namespace",
format: "json_object",
wantErr: false,
},
{
name: "binary in namespace",
format: "binary",
wantErr: false,
},
{
name: "invalid format in namespace",
format: "invalid",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := DefaultConfig()
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{
{
Name: "test",
ChannelOptions: configtypes.ChannelOptions{
PublicationDataFormat: tt.format,
},
},
}
err := cfg.Validate()
if tt.wantErr {
require.Error(t, err)
require.Contains(t, err.Error(), "unknown publication_data_format")
} else {
require.NoError(t, err)
}
})
}
}
func TestValidateGlobalPublicationDataFormat(t *testing.T) {
tests := []struct {
name string
format string
wantErr bool
}{
{
name: "empty global format is valid",
format: "",
wantErr: false,
},
{
name: "json global format is valid",
format: "json",
wantErr: false,
},
{
name: "binary global format is valid",
format: "binary",
wantErr: false,
},
{
name: "invalid global format",
format: "xml",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := DefaultConfig()
cfg.Channel.PublicationDataFormat = tt.format
err := cfg.Validate()
if tt.wantErr {
require.Error(t, err)
require.Contains(t, err.Error(), "unknown channel.publication_data_format")
} else {
require.NoError(t, err)
}
})
}
}
// mapDefaultConfig returns a Config suitable as a base for map namespace validation tests.
func mapDefaultConfig() Config {
cfg := DefaultConfig()
return cfg
}
func mapNamespace(name, mode string) configtypes.ChannelNamespace {
return configtypes.ChannelNamespace{
Name: name,
ChannelOptions: configtypes.ChannelOptions{
SubscriptionType: "map",
Map: configtypes.MapConfig{Mode: mode},
},
}
}
func TestValidateMapNamespace_Ephemeral(t *testing.T) {
t.Run("valid_minimal", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ee", "ephemeral")
ns.Map.KeyTTL = configtypes.Duration(60 * time.Second)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
require.NoError(t, cfg.Validate())
})
t.Run("missing_key_ttl", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ee", "ephemeral")
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "map.key_ttl is required")
})
t.Run("stream_size_rejected", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ee", "ephemeral")
ns.Map.KeyTTL = configtypes.Duration(60 * time.Second)
ns.Map.StreamSize = 100
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "map.stream_size must be 0")
})
t.Run("stream_ttl_rejected", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ee", "ephemeral")
ns.Map.KeyTTL = configtypes.Duration(60 * time.Second)
ns.Map.StreamTTL = configtypes.Duration(time.Minute)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "map.stream_ttl must be 0")
})
t.Run("meta_ttl_rejected", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ee", "ephemeral")
ns.Map.KeyTTL = configtypes.Duration(60 * time.Second)
ns.Map.MetaTTL = configtypes.Duration(10 * time.Minute)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "map.meta_ttl must be 0")
})
}
func TestValidateAutoCacheRecover(t *testing.T) {
t.Run("valid_with_cache_recovery", func(t *testing.T) {
cfg := DefaultConfig()
opts := &cfg.Channel.WithoutNamespace
opts.HistorySize = 1
opts.HistoryTTL = configtypes.Duration(time.Hour)
opts.ForceRecovery = true
opts.ForceRecoveryMode = "cache"
opts.AutoCacheRecover = true
require.NoError(t, cfg.Validate())
})
t.Run("requires_force_recovery", func(t *testing.T) {
cfg := DefaultConfig()
opts := &cfg.Channel.WithoutNamespace
opts.HistorySize = 1
opts.HistoryTTL = configtypes.Duration(time.Hour)
opts.ForceRecoveryMode = "cache"
opts.AutoCacheRecover = true
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "auto_cache_recover requires force_recovery")
})
t.Run("requires_cache_mode", func(t *testing.T) {
cfg := DefaultConfig()
opts := &cfg.Channel.WithoutNamespace
opts.HistorySize = 1
opts.HistoryTTL = configtypes.Duration(time.Hour)
opts.ForceRecovery = true
opts.AutoCacheRecover = true
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "force_recovery_mode set to cache")
})
}
func TestValidateMapNamespace_Recoverable(t *testing.T) {
t.Run("valid_minimal_defaults_zero", func(t *testing.T) {
// Recoverable with all stream options at zero is valid
// because centrifuge auto-derives defaults at runtime.
cfg := mapDefaultConfig()
ns := mapNamespace("ce", "recoverable")
ns.Map.KeyTTL = configtypes.Duration(30 * time.Second)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
require.NoError(t, cfg.Validate())
})
t.Run("valid_explicit_stream_options", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ce", "recoverable")
ns.Map.KeyTTL = configtypes.Duration(30 * time.Second)
ns.Map.StreamSize = 200
ns.Map.StreamTTL = configtypes.Duration(2 * time.Minute)
ns.Map.MetaTTL = configtypes.Duration(20 * time.Minute)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
require.NoError(t, cfg.Validate())
})
t.Run("missing_key_ttl", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ce", "recoverable")
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "map.key_ttl is required")
})
t.Run("meta_ttl_less_than_stream_ttl", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ce", "recoverable")
ns.Map.KeyTTL = configtypes.Duration(30 * time.Second)
ns.Map.StreamTTL = configtypes.Duration(10 * time.Minute)
ns.Map.MetaTTL = configtypes.Duration(5 * time.Minute) // less than stream_ttl
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "metadata must outlive stream")
})
t.Run("meta_ttl_equals_stream_ttl_ok", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ce", "recoverable")
ns.Map.KeyTTL = configtypes.Duration(30 * time.Second)
ns.Map.StreamTTL = configtypes.Duration(5 * time.Minute)
ns.Map.MetaTTL = configtypes.Duration(5 * time.Minute) // equals stream_ttl
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
require.NoError(t, cfg.Validate())
})
t.Run("only_stream_ttl_set_meta_ttl_zero_ok", func(t *testing.T) {
// When only stream_ttl is set, meta_ttl=0 means auto-derived by centrifuge.
cfg := mapDefaultConfig()
ns := mapNamespace("ce", "recoverable")
ns.Map.KeyTTL = configtypes.Duration(30 * time.Second)
ns.Map.StreamTTL = configtypes.Duration(5 * time.Minute)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
require.NoError(t, cfg.Validate())
})
t.Run("only_meta_ttl_set_stream_ttl_zero_ok", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ce", "recoverable")
ns.Map.KeyTTL = configtypes.Duration(30 * time.Second)
ns.Map.MetaTTL = configtypes.Duration(10 * time.Minute)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
require.NoError(t, cfg.Validate())
})
t.Run("meta_ttl_less_than_auto_derived_stream_ttl", func(t *testing.T) {
// StreamTTL=0 will be auto-derived to 1min by centrifuge.
// MetaTTL=30s is less than that — must be caught at config time.
cfg := mapDefaultConfig()
ns := mapNamespace("ce", "recoverable")
ns.Map.KeyTTL = configtypes.Duration(30 * time.Second)
ns.Map.MetaTTL = configtypes.Duration(30 * time.Second) // < auto-derived 1min
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "metadata must outlive stream")
})
t.Run("meta_ttl_equals_auto_derived_stream_ttl_ok", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ce", "recoverable")
ns.Map.KeyTTL = configtypes.Duration(30 * time.Second)
ns.Map.MetaTTL = configtypes.Duration(1 * time.Minute) // == auto-derived 1min
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
require.NoError(t, cfg.Validate())
})
}
func TestValidateMapNamespace_Persistent(t *testing.T) {
t.Run("valid_minimal", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("cp", "persistent")
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
require.NoError(t, cfg.Validate())
})
t.Run("valid_with_stream_options", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("cp", "persistent")
ns.Map.StreamSize = 500
ns.Map.StreamTTL = configtypes.Duration(5 * time.Minute)
ns.Map.MetaTTL = configtypes.Duration(50 * time.Minute)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
require.NoError(t, cfg.Validate())
})
t.Run("key_ttl_rejected", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("cp", "persistent")
ns.Map.KeyTTL = configtypes.Duration(60 * time.Second)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "map.key_ttl must not be set")
})
t.Run("meta_ttl_less_than_stream_ttl", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("cp", "persistent")
ns.Map.StreamTTL = configtypes.Duration(10 * time.Minute)
ns.Map.MetaTTL = configtypes.Duration(1 * time.Minute)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "metadata must outlive stream")
})
}
func TestValidateMapNamespace_RequiredFields(t *testing.T) {
t.Run("missing_mode", func(t *testing.T) {
cfg := mapDefaultConfig()
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{{
Name: "ns",
ChannelOptions: configtypes.ChannelOptions{
SubscriptionType: "map",
Map: configtypes.MapConfig{},
},
}}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "map.mode is required")
})
t.Run("invalid_mode", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ns", "unknown_mode")
ns.Map.KeyTTL = configtypes.Duration(30 * time.Second)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "unknown map.mode")
})
t.Run("invalid_subscription_type", func(t *testing.T) {
cfg := mapDefaultConfig()
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{{
Name: "ns",
ChannelOptions: configtypes.ChannelOptions{
SubscriptionType: "invalid_type",
},
}}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "unknown subscription_type")
})
}
func TestValidateMapNamespace_SubscriptionType(t *testing.T) {
t.Run("map_clients", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ns", "ephemeral")
ns.SubscriptionType = "map_clients"
ns.Map.KeyTTL = configtypes.Duration(60 * time.Second)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
require.NoError(t, cfg.Validate())
})
t.Run("map_users", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ns", "ephemeral")
ns.SubscriptionType = "map_users"
ns.Map.KeyTTL = configtypes.Duration(60 * time.Second)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
require.NoError(t, cfg.Validate())
})
t.Run("stream_only_no_map_config_needed", func(t *testing.T) {
cfg := DefaultConfig()
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{{
Name: "ns",
ChannelOptions: configtypes.ChannelOptions{
SubscriptionType: "stream",
},
}}
require.NoError(t, cfg.Validate())
})
t.Run("map_clients_persistent_rejected", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ns", "persistent")
ns.SubscriptionType = "map_clients"
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), `map.mode "persistent" is not allowed with subscription_type "map_clients"`)
})
t.Run("map_users_persistent_rejected", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ns", "persistent")
ns.SubscriptionType = "map_users"
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), `map.mode "persistent" is not allowed with subscription_type "map_users"`)
})
t.Run("client_key_with_publish_proxy_rejected", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ns", "ephemeral")
ns.Map.KeyTTL = configtypes.Duration(60 * time.Second)
ns.Map.ClientKey = "client_id"
ns.Map.PublishProxyEnabled = true
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "map.client_key is not compatible with map.publish_proxy_enabled")
})
t.Run("client_key_with_remove_proxy_rejected", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ns", "ephemeral")
ns.Map.KeyTTL = configtypes.Duration(60 * time.Second)
ns.Map.ClientKey = "user_id"
ns.Map.RemoveProxyEnabled = true
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "map.client_key is not compatible with")
})
}
func TestValidateMapNamespace_PresenceAndRemoveOptions(t *testing.T) {
t.Run("remove_on_unsubscribe_valid", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("ns", "ephemeral")
ns.Map.KeyTTL = configtypes.Duration(60 * time.Second)
ns.Map.RemoveClientOnUnsubscribe = true
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
require.NoError(t, cfg.Validate())
})
t.Run("remove_on_unsubscribe_requires_map_type", func(t *testing.T) {
cfg := mapDefaultConfig()
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{{
Name: "ns",
ChannelOptions: configtypes.ChannelOptions{
SubscriptionType: "stream",
Map: configtypes.MapConfig{RemoveClientOnUnsubscribe: true},
},
}}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "map.remove_client_on_unsubscribe requires subscription_type to be a map type")
})
t.Run("map_clients_presence_channel_prefix_valid", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("games", "ephemeral")
ns.Map.KeyTTL = configtypes.Duration(60 * time.Second)
ns.MapClientsPresenceChannelPrefix = "clients:"
clients := mapNamespace("clients", "ephemeral")
clients.SubscriptionType = "map_clients"
clients.Map.KeyTTL = configtypes.Duration(60 * time.Second)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns, clients}
require.NoError(t, cfg.Validate())
})
t.Run("map_clients_presence_channel_prefix_not_found", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("games", "ephemeral")
ns.Map.KeyTTL = configtypes.Duration(60 * time.Second)
ns.MapClientsPresenceChannelPrefix = "nonexistent:"
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "map_clients_presence_channel_prefix")
require.Contains(t, err.Error(), "does not exist")
})
t.Run("map_users_presence_channel_prefix_not_found", func(t *testing.T) {
cfg := mapDefaultConfig()
ns := mapNamespace("games", "ephemeral")
ns.Map.KeyTTL = configtypes.Duration(60 * time.Second)
ns.MapUsersPresenceChannelPrefix = "nonexistent:"
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{ns}
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "map_users_presence_channel_prefix")
require.Contains(t, err.Error(), "does not exist")
})
t.Run("presence_channel_prefix_works_with_stream_type", func(t *testing.T) {
cfg := mapDefaultConfig()
clients := mapNamespace("clients", "ephemeral")
clients.SubscriptionType = "map_clients"
clients.Map.KeyTTL = configtypes.Duration(60 * time.Second)
cfg.Channel.Namespaces = []configtypes.ChannelNamespace{{
Name: "ns",
ChannelOptions: configtypes.ChannelOptions{
SubscriptionType: "stream",
MapClientsPresenceChannelPrefix: "clients:",
},
}, clients}
require.NoError(t, cfg.Validate())
})
}
func TestValidateMapNamespace_MapBrokerType(t *testing.T) {
t.Run("unknown_type", func(t *testing.T) {
cfg := DefaultConfig()
cfg.MapBroker.Type = "unknown"
err := cfg.Validate()
require.Error(t, err)
require.Contains(t, err.Error(), "unknown map broker type")
})
for _, brokerType := range []string{"memory", "redis", "postgres"} {
t.Run("valid_"+brokerType, func(t *testing.T) {
cfg := DefaultConfig()
cfg.MapBroker.Type = brokerType
require.NoError(t, cfg.Validate())
})
}
}
func TestValidateConnectCodeToHTTPResponseTransforms(t *testing.T) {
validTransform := configtypes.ConnectCodeToHTTPResponseTransform{
Code: 3500,
To: configtypes.TransformedConnectErrorHttpResponse{
StatusCode: 403,
Body: "forbidden",
},
}
noCode := validTransform
noCode.Code = 0
noStatusCode := validTransform
noStatusCode.To.StatusCode = 0
tests := []struct {
name string
transforms configtypes.ConnectCodeToHTTPResponseTransforms
wantErr string
}{
{
name: "valid transform",
transforms: configtypes.ConnectCodeToHTTPResponseTransforms{validTransform},
},
{
name: "missing code",
transforms: configtypes.ConnectCodeToHTTPResponseTransforms{noCode},
wantErr: "code should be set in connect_code_to_http_response.transforms[0]",
},
{
name: "missing status code",
transforms: configtypes.ConnectCodeToHTTPResponseTransforms{validTransform, noStatusCode},
wantErr: "status_code should be set in connect_code_to_http_response.transforms[1].to",
},
}
// The same transforms are validated for both unidirectional HTTP transports,
// each reporting under its own config path.
transports := []struct {
name string
prefix string
set func(cfg *Config, transforms configtypes.ConnectCodeToHTTPResponseTransforms)
}{
{
name: "uni_sse",
prefix: "in uni_sse.connect_code_to_http_response.transforms: ",
set: func(cfg *Config, transforms configtypes.ConnectCodeToHTTPResponseTransforms) {
cfg.UniSSE.ConnectCodeToHTTPResponse.Transforms = transforms
},
},
{
name: "uni_http_stream",
prefix: "in uni_http_stream.connect_code_to_http_response.transforms: ",
set: func(cfg *Config, transforms configtypes.ConnectCodeToHTTPResponseTransforms) {
cfg.UniHTTPStream.ConnectCodeToHTTPResponse.Transforms = transforms
},
},
}
for _, transport := range transports {
for _, tt := range tests {
t.Run(transport.name+"/"+tt.name, func(t *testing.T) {
cfg := DefaultConfig()
transport.set(&cfg, tt.transforms)
err := cfg.Validate()
if tt.wantErr == "" {
require.NoError(t, err)
return
}
require.EqualError(t, err, transport.prefix+tt.wantErr)
})
}
}
}
func TestValidateDuplicateProxyName(t *testing.T) {
proxy := configtypes.Proxy{
Endpoint: "http://localhost:3000/proxy",
Timeout: configtypes.Duration(time.Second),
}
t.Run("unique names are valid", func(t *testing.T) {
cfg := DefaultConfig()
cfg.Proxies = configtypes.NamedProxies{
{Name: "first", Proxy: proxy},
{Name: "second", Proxy: proxy},
}
require.NoError(t, cfg.Validate())
})
t.Run("duplicate name is invalid", func(t *testing.T) {
cfg := DefaultConfig()
cfg.Proxies = configtypes.NamedProxies{
{Name: "first", Proxy: proxy},
{Name: "first", Proxy: proxy},
}
require.EqualError(t, cfg.Validate(), "duplicate proxy name in proxies: first")
})
}