package config_test
import (
"lunar/engine/config"
sharedConfig "lunar/shared-model/config"
"testing"
"github.com/stretchr/testify/assert"
)
func initValidations() {
sharedConfig.Validate.RegisterStructValidation(
config.ValidateStructLevel,
sharedConfig.Remedy{},
sharedConfig.Diagnosis{},
sharedConfig.PoliciesConfig{},
)
_ = sharedConfig.Validate.RegisterValidation(
"validateInt",
config.ValidateInt,
)
}
func buildStrategyBasedThrottling(
name string,
windowSize int,
) sharedConfig.Remedy {
return sharedConfig.Remedy{
Enabled: true, Name: name,
Config: sharedConfig.RemedyConfig{
StrategyBasedThrottling: &sharedConfig.StrategyBasedThrottlingConfig{
WindowSizeInSeconds: windowSize,
},
},
}
}
func buildPoliciesConfigForDuplicateNameTesting(
nameA string,
nameB string,
) sharedConfig.PoliciesConfig {
return sharedConfig.PoliciesConfig{
Global: sharedConfig.Global{Remedies: []sharedConfig.Remedy{
{
Enabled: true, Name: nameA,
Config: sharedConfig.RemedyConfig{
StrategyBasedThrottling: &sharedConfig.StrategyBasedThrottlingConfig{
WindowSizeInSeconds: 10,
},
},
},
}},
Endpoints: []sharedConfig.EndpointConfig{
{
URL: "api.com", Method: "GET",
Diagnosis: []sharedConfig.Diagnosis{
{
Enabled: true, Name: nameB,
Config: sharedConfig.DiagnosisConfig{
Void: &sharedConfig.VoidConfig{},
},
Export: "prometheus",
},
},
},
},
}
}
func buildPoliciesConfigWithExporter(
exporter string,
) sharedConfig.PoliciesConfig {
return sharedConfig.PoliciesConfig{
Endpoints: []sharedConfig.EndpointConfig{
{
URL: "api.com", Method: "GET",
Diagnosis: []sharedConfig.Diagnosis{
{
Enabled: true, Name: "foo",
Config: sharedConfig.DiagnosisConfig{
Void: &sharedConfig.VoidConfig{},
},
Export: exporter,
},
},
},
},
}
}
func TestValidateFailsIfChainedSBTRemediesWindowsHaveNoCommonDenominator(
t *testing.T,
) {
initValidations()
policiesConfig := sharedConfig.PoliciesConfig{
Global: sharedConfig.Global{Remedies: []sharedConfig.Remedy{
buildStrategyBasedThrottling("foo", 5),
buildStrategyBasedThrottling("bar", 6),
}},
}
err := config.Validate(&policiesConfig)
assert.Error(t, err)
}
func TestValidateSucceedsIfChainedSBTRemediesWindowsHaveCommonDenominator(
t *testing.T,
) {
initValidations()
policiesConfig := sharedConfig.PoliciesConfig{
Global: sharedConfig.Global{Remedies: []sharedConfig.Remedy{
buildStrategyBasedThrottling("foo", 5),
buildStrategyBasedThrottling("bar", 1200),
}},
}
err := config.Validate(&policiesConfig)
assert.Nil(t, err)
}
func TestValidateSucceedsIfUnchainedSBTRemediesWindowsHaveNoCommonDenominator(
t *testing.T,
) {
initValidations()
policiesConfig := sharedConfig.PoliciesConfig{
Endpoints: []sharedConfig.EndpointConfig{
{
URL: "api.com", Method: "GET",
Remedies: []sharedConfig.Remedy{
buildStrategyBasedThrottling("foo", 6),
},
},
{
URL: "api.com", Method: "POST",
Remedies: []sharedConfig.Remedy{
buildStrategyBasedThrottling("bar", 5),
},
},
},
}
err := config.Validate(&policiesConfig)
assert.Nil(t, err)
}
func TestValidateFailsIfNoTwoPoliciesShareTheSameName(
t *testing.T,
) {
initValidations()
policiesConfig := buildPoliciesConfigForDuplicateNameTesting(
"some name",
"some other name",
)
err := config.Validate(&policiesConfig)
assert.Nil(t, err)
}
func TestValidateFailsIfTwoPoliciesShareTheSameName(
t *testing.T,
) {
initValidations()
policiesConfig := buildPoliciesConfigForDuplicateNameTesting(
"some name",
"some name",
)
err := config.Validate(&policiesConfig)
assert.Error(t, err)
}
func TestValidateFailsIfCachePolicyMissingPathParam(
t *testing.T,
) {
initValidations()
policiesConfig := sharedConfig.PoliciesConfig{
Endpoints: []sharedConfig.EndpointConfig{
{
URL: "random-word.ryanrk.com/api/{language}/word/random",
Method: "GET",
Remedies: []sharedConfig.Remedy{
{
Enabled: true,
Name: "testing cache validation",
Config: buildRemedyConfigForCachePluginTesting(
[]string{"bla"},
),
},
},
},
},
}
err := config.Validate(&policiesConfig)
assert.Error(t, err)
}
func TestCachePolicy(
t *testing.T,
) {
initValidations()
policiesConfig := sharedConfig.PoliciesConfig{
Endpoints: []sharedConfig.EndpointConfig{
{
URL: "random-word.ryanrk.com/api/{language}/word/random",
Method: "GET",
Remedies: []sharedConfig.Remedy{
{
Enabled: true,
Name: "testing cache validation",
Config: buildRemedyConfigForCachePluginTesting(
[]string{"language"},
),
},
},
},
},
}
err := config.Validate(&policiesConfig)
assert.Nil(t, err)
}
func TestPriorityFailsOnActualFloat(
t *testing.T,
) {
initValidations()
policiesConfig := sharedConfig.PoliciesConfig{
Endpoints: []sharedConfig.EndpointConfig{
{
URL: "random-word.ryanrk.com/api/{language}/word/random",
Method: "GET",
Remedies: []sharedConfig.Remedy{
{
Enabled: true,
Name: "testing priority validation",
Config: buildStrategyBasedQueueRemedy(1.5),
},
},
},
},
}
err := config.Validate(&policiesConfig)
assert.NotNil(t, err)
}
func TestPrioritySucceedsOnWholeNumberFloat(
t *testing.T,
) {
initValidations()
policiesConfig := sharedConfig.PoliciesConfig{
Endpoints: []sharedConfig.EndpointConfig{
{
URL: "random-word.ryanrk.com/api/{language}/word/random",
Method: "GET",
Remedies: []sharedConfig.Remedy{
{
Enabled: true,
Name: "testing priority validation",
Config: buildStrategyBasedQueueRemedy(1.0),
},
},
},
},
}
err := config.Validate(&policiesConfig)
assert.Nil(t, err)
}
func buildRemedyConfigForCachePluginTesting(
pathParams []string,
) sharedConfig.RemedyConfig {
var requestPayloadPaths []sharedConfig.PayloadPath
for _, path := range pathParams {
requestPayloadPaths = append(
requestPayloadPaths,
sharedConfig.PayloadPath{
PayloadType: sharedConfig.PayloadRequestPathParams.String(),
Path: path,
},
)
}
cachingConfig := sharedConfig.CachingConfig{
RequestPayloadPaths: requestPayloadPaths,
TTLSeconds: 60.0,
MaxRecordSizeBytes: 1024,
MaxCacheSizeMegabytes: 100.0,
}
return sharedConfig.RemedyConfig{
Caching: &cachingConfig,
}
}
func TestValidateFailsIfExporterIsMissing(t *testing.T) {
initValidations()
policiesConfig := buildPoliciesConfigWithExporter("file")
err := config.Validate(&policiesConfig)
assert.Error(t, err)
}
func TestValidateReturnErrDependentOnLogLevelIfExporterIsMissing(t *testing.T) {
initValidations()
policiesConfig := buildPoliciesConfigWithExporter("file")
err := config.ValidateWithDebugLevel(&policiesConfig, false)
assert.Error(t, err)
assert.ErrorContains(t, err, "Policies configuration")
err = config.ValidateWithDebugLevel(&policiesConfig, true)
assert.ErrorContains(t, err, "PoliciesConfig.Endpoints[0].Diagnosis[0]")
}
func buildStrategyBasedQueueRemedy(
priority float64,
) sharedConfig.RemedyConfig {
cachingConfig := sharedConfig.StrategyBasedQueueConfig{
AllowedRequestCount: 1,
WindowSizeInSeconds: 1,
ResponseStatusCode: 429,
TTLSeconds: 10,
QueueSize: 10,
Prioritization: &sharedConfig.GroupPrioritization{
GroupBy: sharedConfig.GroupBy{HeaderName: "bla"},
Groups: map[string]sharedConfig.Prioritization{
"foo": {Priority: priority},
},
},
}
return sharedConfig.RemedyConfig{
StrategyBasedQueue: &cachingConfig,
}
}