diff --git a/types/conversion.go b/types/conversion.go index 1e3d873..b8ed889 100644 --- a/types/conversion.go +++ b/types/conversion.go @@ -14,15 +14,14 @@ package types -import ( - "errors" -) - // Uint64ToUint32 safely converts a uint64 into a uint32 without overflow func Uint64ToUint32(v uint64) (uint32, error) { // Check for overflow without casting if v > uint64(^uint32(0)) { // ^uint32(0) is the maximum uint32 value (4,294,967,295) - return 0, errors.New("value exceeds uint32 range") + return 0, &OutOfRangeError{ + Value: v, + Type: "uint32", + } } return uint32(v), nil // #nosec G103: safe after bounds check diff --git a/types/conversion_test.go b/types/conversion_test.go index ec9efce..06c1622 100644 --- a/types/conversion_test.go +++ b/types/conversion_test.go @@ -22,24 +22,33 @@ func TestUint64ToUint32(t *testing.T) { name string input uint64 want uint32 - wantErr bool + errType error }{ - {"Zero", 0, 0, false}, - {"Max uint32", 4294967295, 4294967295, false}, - {"Overflow", 4294967296, 0, true}, - {"Large overflow", 18446744073709551615, 0, true}, - {"Mid-range value", 2147483648, 2147483648, false}, + {"Zero", 0, 0, nil}, + {"Max uint32", 4294967295, 4294967295, nil}, + {"Overflow", 4294967296, 0, &OutOfRangeError{}}, + {"Large overflow", 18446744073709551615, 0, &OutOfRangeError{}}, + {"Mid-range value", 2147483648, 2147483648, nil}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := Uint64ToUint32(tt.input) - if (err != nil) != tt.wantErr { - t.Errorf("Uint64ToUint32() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("Uint64ToUint32() = %v, want %v", got, tt.want) + if tt.errType != nil { + // Check if the error is of the expected type + if err == nil { + t.Errorf("Expected error type %T, got no error", tt.errType) + } else if _, ok := err.(*OutOfRangeError); !ok { + t.Errorf("Expected error type *OutOfRangeError, got %T", err) + } + } else { + if err != nil { + t.Errorf("Uint64ToUint32() error = %v, want no error", err) + return + } + if got != tt.want { + t.Errorf("Uint64ToUint32() = %v, want %v", got, tt.want) + } } }) } diff --git a/types/errors.go b/types/errors.go index 791f15e..6d48b0a 100644 --- a/types/errors.go +++ b/types/errors.go @@ -106,6 +106,17 @@ func (e *ValidationError) Error() string { ) } +// OutOfRangeError indicates that a value is outside the expected range. +type OutOfRangeError struct { + Value uint64 + Type string +} + +// Error returns a formatted error string for the OutOfRangeError. +func (e *OutOfRangeError) Error() string { + return fmt.Sprintf("value %d out of range for %s", e.Value, e.Type) +} + // HandleServerError handles separate server errors and sends appropriate responses func HandleServerError(writer http.ResponseWriter, err error) { var level = log.Warn() // set the default log level for most HTTP responses @@ -113,7 +124,7 @@ func HandleServerError(writer http.ResponseWriter, err error) { var respErr error switch err := err.(type) { - case *RouterMissingParamError, *RouterParsingError, *json.SyntaxError, *NoBodyError, *ValidationError: + case *RouterMissingParamError, *RouterParsingError, *json.SyntaxError, *NoBodyError, *ValidationError, *OutOfRangeError: respErr = responses.SendBadRequest(writer, err.Error()) case *json.UnmarshalTypeError: respErr = responses.SendBadRequest(writer, "bad type in json data")