Skip to content
Snippets Groups Projects
Commit 636b0906 authored by Pavel Antonov's avatar Pavel Antonov :asterisk:
Browse files

Merge branch 'feature/PRXS-1604-BigIntInNumberField' into 'master'

Добавлена дополнительная проверка при выполнении декодирования из типа float64 к int64 для поля типа "Number"

See merge request perxis/perxis-go!90
parents 4d9f1028 b4f6aae0
No related branches found
No related tags found
No related merge requests found
...@@ -10,6 +10,10 @@ import ( ...@@ -10,6 +10,10 @@ import (
const ( const (
NumberFormatInt = "int" NumberFormatInt = "int"
NumberFormatFloat = "float" NumberFormatFloat = "float"
// The largest integer that can be represented in IEEE 754 double (64-bit) is a value from -2^53 to 2^53.
maxInt = 1<<53 - 1
minInt = -1<<53 + 1
) )
var numberType = &NumberType{} var numberType = &NumberType{}
...@@ -84,10 +88,19 @@ func (NumberType) decode(_ context.Context, field *Field, v interface{}) (interf ...@@ -84,10 +88,19 @@ func (NumberType) decode(_ context.Context, field *Field, v interface{}) (interf
case NumberFormatInt: case NumberFormatInt:
switch i := n.(type) { switch i := n.(type) {
case int64: case int64:
if i > maxInt || i < minInt {
return nil, errors.New("integer out of range")
}
return i, nil return i, nil
case uint64: case uint64:
if i > maxInt {
return nil, errors.New("integer out of range")
}
return i, nil return i, nil
case float64: case float64:
if i > maxInt || i < minInt {
return nil, errors.New("integer out of range")
}
return int64(math.Round(i)), nil return int64(math.Round(i)), nil
} }
case NumberFormatFloat: case NumberFormatFloat:
......
package field package field
import ( import (
"context"
"math"
"reflect" "reflect"
"testing" "testing"
) )
...@@ -19,12 +21,19 @@ func TestNumberField_Decode(t *testing.T) { ...@@ -19,12 +21,19 @@ func TestNumberField_Decode(t *testing.T) {
{"Correct", Number("int"), float32(2.2), int64(2), false}, // #3 {"Correct", Number("int"), float32(2.2), int64(2), false}, // #3
{"Correct", Number("int"), float64(2.6), int64(3), false}, // #4 {"Correct", Number("int"), float64(2.6), int64(3), false}, // #4
{"Correct", Number("int"), 2.6, int64(3), false}, // #5 {"Correct", Number("int"), 2.6, int64(3), false}, // #5
{"MaxInt64", Number(NumberFormatInt), int64(math.MaxInt64), nil, true}, // #6
{"MinInt64", Number(NumberFormatInt), int64(math.MinInt64), nil, true}, // #7
{"maxInt in float", Number(NumberFormatInt), float64(maxInt), int64(maxInt), false}, // #8
{"minInt in float", Number(NumberFormatInt), float64(minInt), int64(minInt), false}, // #9
{"Convert error", Number(NumberFormatInt), math.MaxFloat64, nil, true}, // #10
{"Convert error", Number(NumberFormatInt), -math.MaxFloat64, nil, true}, // #11
{"Convert error", Number(NumberFormatInt), float64(math.MaxInt64), nil, true}, // #13
{"Convert error", Number(NumberFormatInt), float64(math.MinInt64), nil, true}, // #14
{"Correct", Number("float"), int8(2), 2.0, false}, // #6 {"Correct", Number("float"), int8(2), 2.0, false}, // #15
{"Correct", Number("float"), 2.2, 2.2, false}, // #7 {"Correct", Number("float"), 2.2, 2.2, false}, // #16
{"Correct", Number("float"), 2, 2.0, false}, // #8 {"Correct", Number("float"), 2, 2.0, false}, // #17
{"Correct", Number("float"), float32(2.2), 2.200000047683716, false}, // #9 {"Correct", Number("float"), int64(2), 2.0, false}, // #18
{"Correct", Number("float"), int64(2), 2.0, false}, // #10
{"Wrong data", Number("int"), "", nil, true}, // #0 {"Wrong data", Number("int"), "", nil, true}, // #0
{"Wrong data", Number("int"), []byte(""), nil, true}, // #1 {"Wrong data", Number("int"), []byte(""), nil, true}, // #1
...@@ -34,7 +43,7 @@ func TestNumberField_Decode(t *testing.T) { ...@@ -34,7 +43,7 @@ func TestNumberField_Decode(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, err := Decode(nil, tt.field, tt.data) got, err := Decode(context.Background(), tt.field, tt.data)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("Decode() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("Decode() error = %v, wantErr %v", err, tt.wantErr)
return return
...@@ -64,8 +73,7 @@ func TestNumberField_Encode(t *testing.T) { ...@@ -64,8 +73,7 @@ func TestNumberField_Encode(t *testing.T) {
{"Correct", Number("float"), int8(2), 2.0, false}, // #6 {"Correct", Number("float"), int8(2), 2.0, false}, // #6
{"Correct", Number("float"), 2.2, 2.2, false}, // #7 {"Correct", Number("float"), 2.2, 2.2, false}, // #7
{"Correct", Number("float"), 2, 2.0, false}, // #8 {"Correct", Number("float"), 2, 2.0, false}, // #8
{"Correct", Number("float"), float32(2.2), 2.200000047683716, false}, // #9 {"Correct", Number("float"), int64(2), 2.0, false}, // #9
{"Correct", Number("float"), int64(2), 2.0, false}, // #10
{"Wrong data", Number("int"), "", nil, true}, // #0 {"Wrong data", Number("int"), "", nil, true}, // #0
{"Wrong data", Number("int"), []byte(""), nil, true}, // #1 {"Wrong data", Number("int"), []byte(""), nil, true}, // #1
...@@ -75,7 +83,7 @@ func TestNumberField_Encode(t *testing.T) { ...@@ -75,7 +83,7 @@ func TestNumberField_Encode(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, err := Encode(nil, tt.field, tt.data) got, err := Encode(context.Background(), tt.field, tt.data)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("Decode() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("Decode() error = %v, wantErr %v", err, tt.wantErr)
return return
......
...@@ -74,6 +74,80 @@ func TestNumberField_JSON(t *testing.T) { ...@@ -74,6 +74,80 @@ func TestNumberField_JSON(t *testing.T) {
assert.Equal(t, fld, res) assert.Equal(t, fld, res)
} }
func TestNumberField_JSON_With_MaxInt(t *testing.T) {
t.Run("Without overflow from int64", func(t *testing.T) {
fld := field.Object("num", field.Number(field.NumberFormatInt))
itemData := map[string]interface{}{"num": 1<<53 - 1}
b, err := json.Marshal(itemData)
require.NoError(t, err)
itemDataToDecode := make(map[string]interface{})
require.NoError(t, json.Unmarshal(b, &itemDataToDecode))
_, err = field.Decode(context.Background(), fld, itemDataToDecode)
require.NoError(t, err)
})
t.Run("Without overflow from float64", func(t *testing.T) {
fld := field.Object("num", field.Number(field.NumberFormatInt))
itemData := map[string]interface{}{"num": float64(1<<53 - 1)}
b, err := json.Marshal(itemData)
require.NoError(t, err)
itemDataToDecode := make(map[string]interface{})
require.NoError(t, json.Unmarshal(b, &itemDataToDecode))
_, err = field.Decode(context.Background(), fld, itemDataToDecode)
require.NoError(t, err)
})
t.Run("With overflow from int64", func(t *testing.T) {
fld := field.Object("num", field.Number(field.NumberFormatInt))
itemData := map[string]interface{}{"num": 1 << 53}
b, err := json.Marshal(itemData)
require.NoError(t, err)
itemDataToDecode := make(map[string]interface{})
require.NoError(t, json.Unmarshal(b, &itemDataToDecode))
_, err = field.Decode(context.Background(), fld, itemDataToDecode)
require.Error(t, err, "integer out of range")
})
t.Run("With overflow from uint64", func(t *testing.T) {
fld := field.Object("num", field.Number(field.NumberFormatInt))
itemData := map[string]interface{}{"num": uint64(1 << 53)}
b, err := json.Marshal(itemData)
require.NoError(t, err)
itemDataToDecode := make(map[string]interface{})
require.NoError(t, json.Unmarshal(b, &itemDataToDecode))
_, err = field.Decode(context.Background(), fld, itemDataToDecode)
require.Error(t, err, "integer out of range")
})
t.Run("With overflow from float64", func(t *testing.T) {
fld := field.Object("num", field.Number(field.NumberFormatInt))
itemData := map[string]interface{}{"num": float64(1 << 53)}
b, err := json.Marshal(itemData)
require.NoError(t, err)
itemDataToDecode := make(map[string]interface{})
require.NoError(t, json.Unmarshal(b, &itemDataToDecode))
_, err = field.Decode(context.Background(), fld, itemDataToDecode)
require.Error(t, err, "integer out of range")
})
}
func TestSchema_JSON(t *testing.T) { func TestSchema_JSON(t *testing.T) {
enumStr := field.String().AddOptions( enumStr := field.String().AddOptions(
validate.Enum( validate.Enum(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment