diff --git a/config/config.go b/config/config.go index ab4f5fe..78626cb 100644 --- a/config/config.go +++ b/config/config.go @@ -22,9 +22,9 @@ type Config struct { AllowSkewness TypeDuration `json:"allow-skewness"` } `json:"time"` AntiReplay struct { - Enabled bool `json:"enabled"` - MaxSize TypeBytes `json:"max-size"` - ErrorRate TypeFloat `json:"error-rate"` + Enabled bool `json:"enabled"` + MaxSize TypeBytes `json:"max-size"` + ErrorRate TypeErrorRate `json:"error-rate"` } `json:"anti-replay"` } `json:"probes"` Network struct { diff --git a/config/type_float.go b/config/type_error_rate.go similarity index 50% rename from config/type_float.go rename to config/type_error_rate.go index f30ccb4..690d85e 100644 --- a/config/type_float.go +++ b/config/type_error_rate.go @@ -5,18 +5,18 @@ import ( "strconv" ) -type TypeFloat struct { +type TypeErrorRate struct { value float64 } -func (c *TypeFloat) UnmarshalJSON(data []byte) error { +func (c *TypeErrorRate) UnmarshalJSON(data []byte) error { value, err := strconv.ParseFloat(string(data), 64) if err != nil { return fmt.Errorf("incorrect float value: %w", err) } - if value < 0 { - return fmt.Errorf("%f should be positive", value) + if value <= 0 || value >= 100 { + return fmt.Errorf("%f should be 0 < x < 100", value) } c.value = value @@ -24,16 +24,16 @@ func (c *TypeFloat) UnmarshalJSON(data []byte) error { return nil } -func (c *TypeFloat) MarshalText() ([]byte, error) { +func (c *TypeErrorRate) MarshalText() ([]byte, error) { return []byte(c.String()), nil } -func (c TypeFloat) String() string { +func (c TypeErrorRate) String() string { return strconv.FormatFloat(c.value, 'f', -1, 64) } -func (c TypeFloat) Value(defaultValue float64) float64 { - if c.value < 0.00001 { +func (c TypeErrorRate) Value(defaultValue float64) float64 { + if c.value < 1e-8 { return defaultValue } diff --git a/config/type_error_rate_test.go b/config/type_error_rate_test.go new file mode 100644 index 0000000..971b787 --- /dev/null +++ b/config/type_error_rate_test.go @@ -0,0 +1,119 @@ +package config_test + +import ( + "encoding/json" + "strconv" + "testing" + + "github.com/9seconds/mtg/v2/config" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type typeErrorRateTestStruct struct { + Value config.TypeErrorRate `json:"value"` +} + +type TypeErrorRateTestSuite struct { + suite.Suite +} + +func (suite *TypeErrorRateTestSuite) TestUnmarshalFail() { + testData := []float64{ + 1000, + -100, + -0.0001, + } + + for _, v := range testData { + data, err := json.Marshal(map[string]float64{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(strconv.FormatFloat(v, 'f', -1, 64), func(t *testing.T) { + assert.Error(t, json.Unmarshal(data, &typeErrorRateTestStruct{})) + }) + } +} + +func (suite *TypeErrorRateTestSuite) TestUnmarshalOk() { + testData := []float64{ + 1, + 55.5, + 0.0001, + 1e-6, + } + + for _, v := range testData { + value := v + + data, err := json.Marshal(map[string]float64{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(strconv.FormatFloat(v, 'f', -1, 64), func(t *testing.T) { + testStruct := &typeErrorRateTestStruct{} + + assert.NoError(t, json.Unmarshal(data, testStruct)) + assert.InEpsilon(t, value, testStruct.Value.Value(0), 1e-10) + }) + } +} + +func (suite *TypeErrorRateTestSuite) TestMarshalOk() { + testData := []float64{ + 1, + 55.5, + 0.0001, + 1e-6, + } + + for _, v := range testData { + value := v + + data, err := json.Marshal(map[string]float64{ + "value": v, + }) + suite.NoError(err) + + suite.T().Run(strconv.FormatFloat(v, 'f', -1, 64), func(t *testing.T) { + testStruct := &typeErrorRateTestStruct{} + + assert.NoError(t, json.Unmarshal(data, testStruct)) + + parsed, err := strconv.ParseFloat(testStruct.Value.String(), 64) + assert.NoError(t, err) + assert.InEpsilon(t, value, parsed, 1e-10) + + marshalled, err := testStruct.Value.MarshalText() + assert.NoError(t, err) + + parsed, err = strconv.ParseFloat(string(marshalled), 64) + assert.NoError(t, err) + assert.InEpsilon(t, value, parsed, 1e-10) + }) + } +} + +func (suite *TypeErrorRateTestSuite) TestValue() { + testStruct := &typeErrorRateTestStruct{} + + suite.InEpsilon(1, testStruct.Value.Value(1), 1e-10) + suite.InEpsilon(2, testStruct.Value.Value(2), 1e-10) + + data, err := json.Marshal(map[string]float64{ + "value": 1, + }) + suite.NoError(err) + suite.NoError(json.Unmarshal(data, testStruct)) + + suite.InEpsilon(1, testStruct.Value.Value(2), 1e-10) + suite.InEpsilon(1, testStruct.Value.Value(3), 1e-10) +} + +func TestTypeErrorRate(t *testing.T) { + t.Parallel() + suite.Run(t, &TypeErrorRateTestSuite{}) +}