Skip to content

Commit 7b57217

Browse files
committed
fix tests
1 parent bd82468 commit 7b57217

File tree

8 files changed

+127
-29
lines changed

8 files changed

+127
-29
lines changed

src/input/input_json.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ impl<'a> Input<'a> for JsonInput {
116116

117117
fn strict_int(&'a self) -> ValResult<EitherInt<'a>> {
118118
match self {
119-
JsonInput::Int(i) => Ok(EitherInt::Rust(*i)),
119+
JsonInput::Int(i) => Ok(EitherInt::I64(*i)),
120+
JsonInput::Uint(u) => Ok(EitherInt::U64(*u)),
120121
_ => Err(ValError::new(ErrorType::IntType, self)),
121122
}
122123
}
@@ -127,11 +128,12 @@ impl<'a> Input<'a> for JsonInput {
127128
false => Ok(0),
128129
},
129130
JsonInput::Int(i) => Ok(*i),
131+
JsonInput::Uint(u) => return Ok(EitherInt::U64(*u)),
130132
JsonInput::Float(f) => float_as_int(self, *f),
131133
JsonInput::String(str) => str_as_int(self, str),
132134
_ => Err(ValError::new(ErrorType::IntType, self)),
133135
};
134-
int_result.map(EitherInt::Rust)
136+
int_result.map(EitherInt::I64)
135137
}
136138

137139
fn ultra_strict_float(&self) -> ValResult<f64> {
@@ -144,6 +146,7 @@ impl<'a> Input<'a> for JsonInput {
144146
match self {
145147
JsonInput::Float(f) => Ok(*f),
146148
JsonInput::Int(i) => Ok(*i as f64),
149+
JsonInput::Uint(u) => Ok(*u as f64),
147150
_ => Err(ValError::new(ErrorType::FloatType, self)),
148151
}
149152
}
@@ -155,6 +158,7 @@ impl<'a> Input<'a> for JsonInput {
155158
},
156159
JsonInput::Float(f) => Ok(*f),
157160
JsonInput::Int(i) => Ok(*i as f64),
161+
JsonInput::Uint(u) => Ok(*u as f64),
158162
JsonInput::String(str) => match str.parse::<f64>() {
159163
Ok(i) => Ok(i),
160164
Err(_) => Err(ValError::new(ErrorType::FloatParsing, self)),
@@ -363,7 +367,7 @@ impl<'a> Input<'a> for String {
363367
}
364368
fn lax_int(&'a self) -> ValResult<EitherInt<'a>> {
365369
match self.parse() {
366-
Ok(i) => Ok(EitherInt::Rust(i)),
370+
Ok(i) => Ok(EitherInt::I64(i)),
367371
Err(_) => Err(ValError::new(ErrorType::IntParsing, self)),
368372
}
369373
}

src/input/input_python.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,9 +288,9 @@ impl<'a> Input<'a> for PyAny {
288288
Ok(EitherInt::Py(self))
289289
} else if let Some(cow_str) = maybe_as_string(self, ErrorType::IntParsing)? {
290290
let int = str_as_int(self, &cow_str)?;
291-
Ok(EitherInt::Rust(int))
291+
Ok(EitherInt::I64(int))
292292
} else if let Ok(float) = self.extract::<f64>() {
293-
Ok(EitherInt::Rust(float_as_int(self, float)?))
293+
Ok(EitherInt::I64(float_as_int(self, float)?))
294294
} else {
295295
Err(ValError::new(ErrorType::IntType, self))
296296
}

src/input/parse_json.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub enum JsonInput {
1212
Null,
1313
Bool(bool),
1414
Int(i64),
15+
Uint(u64),
1516
Float(f64),
1617
String(String),
1718
Array(JsonArray),
@@ -26,6 +27,7 @@ impl ToPyObject for JsonInput {
2627
Self::Null => py.None(),
2728
Self::Bool(b) => b.into_py(py),
2829
Self::Int(i) => i.into_py(py),
30+
Self::Uint(i) => i.into_py(py),
2931
Self::Float(f) => f.into_py(py),
3032
Self::String(s) => s.into_py(py),
3133
Self::Array(v) => PyList::new(py, v.iter().map(|v| v.to_object(py))).into_py(py),
@@ -64,7 +66,10 @@ impl<'de> Deserialize<'de> for JsonInput {
6466
}
6567

6668
fn visit_u64<E>(self, value: u64) -> Result<JsonInput, E> {
67-
Ok(JsonInput::Int(value as i64))
69+
match i64::try_from(value) {
70+
Ok(i) => Ok(JsonInput::Int(i)),
71+
Err(_) => Ok(JsonInput::Uint(value)),
72+
}
6873
}
6974

7075
fn visit_f64<E>(self, value: f64) -> Result<JsonInput, E> {

src/input/return_enums.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -820,16 +820,19 @@ impl<'a> IntoPy<PyObject> for EitherBytes<'a> {
820820

821821
#[cfg_attr(debug_assertions, derive(Debug))]
822822
pub enum EitherInt<'a> {
823-
Rust(i64),
823+
I64(i64),
824+
U64(u64),
824825
Py(&'a PyAny),
825826
}
826827

827-
impl<'a> TryInto<i64> for EitherInt<'a> {
828-
type Error = ValError<'a>;
829-
830-
fn try_into(self) -> ValResult<'a, i64> {
828+
impl<'a> EitherInt<'a> {
829+
pub fn into_i64(self, py: Python<'a>) -> ValResult<'a, i64> {
831830
match self {
832-
EitherInt::Rust(i) => Ok(i),
831+
EitherInt::I64(i) => Ok(i),
832+
EitherInt::U64(u) => match i64::try_from(u) {
833+
Ok(u) => Ok(u),
834+
Err(_) => Err(ValError::new(ErrorType::IntOverflow, u.into_py(py).into_ref(py))),
835+
},
833836
EitherInt::Py(i) => i.extract().map_err(|_| ValError::new(ErrorType::IntOverflow, i)),
834837
}
835838
}
@@ -838,7 +841,8 @@ impl<'a> TryInto<i64> for EitherInt<'a> {
838841
impl<'a> IntoPy<PyObject> for EitherInt<'a> {
839842
fn into_py(self, py: Python<'_>) -> PyObject {
840843
match self {
841-
Self::Rust(int) => int.into_py(py),
844+
Self::I64(int) => int.into_py(py),
845+
Self::U64(int) => int.into_py(py),
842846
Self::Py(int) => int.into_py(py),
843847
}
844848
}

src/input/shared.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ pub fn float_as_int<'a>(input: &'a impl Input<'a>, float: f64) -> ValResult<'a,
5858
Err(ValError::new(ErrorType::FiniteNumber, input))
5959
} else if float % 1.0 != 0.0 {
6060
Err(ValError::new(ErrorType::IntFromFloat, input))
61+
} else if float > i64::MAX as f64 || float < i64::MIN as f64 {
62+
Err(ValError::new(ErrorType::IntOverflow, input))
6163
} else {
6264
Ok(float as i64)
6365
}

src/validators/literal.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl BuildValidator for LiteralValidator {
5050
repr_args.push(item.repr()?.extract()?);
5151
if let Ok(either_int) = item.strict_int() {
5252
let int = either_int
53-
.try_into()
53+
.into_i64(py)
5454
.map_err(|_| py_schema_error_type!("error extracting int {:?}", item))?;
5555
expected_int.insert(int);
5656
} else if let Ok(either_str) = item.strict_str() {
@@ -84,7 +84,7 @@ impl Validator for LiteralValidator {
8484
) -> ValResult<'data, PyObject> {
8585
if let Some(expected_ints) = &self.expected_int {
8686
if let Ok(either_int) = input.strict_int() {
87-
let int = either_int.try_into()?;
87+
let int = either_int.into_i64(py)?;
8888
if expected_ints.contains(&int) {
8989
return Ok(input.to_object(py));
9090
}

src/validators/union.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ impl Validator for TaggedUnionValidator {
382382
match lookup_key.$get_method($( $dict ),+)? {
383383
Some((_, value)) => {
384384
if let Ok(either_int) = value.validate_int(self.strict) {
385-
let int = either_int.try_into()?;
385+
let int = either_int.into_i64(py)?;
386386
Ok(ChoiceKey::Int(int))
387387
} else {
388388
Ok(ChoiceKey::Str(value.validate_str(self.strict)?.as_cow()?.as_ref().to_string()))

tests/validators/test_int.py

Lines changed: 96 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import re
23
from decimal import Decimal
34
from typing import Any, Dict
@@ -59,8 +60,19 @@ def test_int_py_and_json(py_and_json: PyAndJson, input_value, expected):
5960
(Decimal('1'), 1),
6061
(Decimal('1.0'), 1),
6162
(i64_max, i64_max),
63+
(str(i64_max), i64_max),
64+
# (str(i64_max + 1), i64_max + 1),
65+
(
66+
str(i64_max * 2),
67+
Err(
68+
"Input integer too large to convert to 64-bit integer "
69+
"[type=int_overflow, input_value='18446744073709551614', input_type=str]"
70+
),
71+
),
6272
(i64_max + 1, i64_max + 1),
73+
(-i64_max + 1, -i64_max + 1),
6374
(i64_max * 2, i64_max * 2),
75+
(-i64_max * 2, -i64_max * 2),
6476
pytest.param(
6577
Decimal('1.001'),
6678
Err(
@@ -93,21 +105,14 @@ def test_int(input_value, expected):
93105
(Decimal('1'), 1),
94106
(Decimal('1.0'), 1),
95107
(i64_max, i64_max),
108+
(i64_max + 1, i64_max + 1),
96109
(
97-
i64_max + 1,
98-
Err(
99-
'Input integer too large to convert to 64-bit integer '
100-
'[type=int_overflow, input_value=9223372036854775808, input_type=int]'
101-
),
102-
),
103-
(
104-
i64_max * 2,
105-
Err(
106-
'Input integer too large to convert to 64-bit integer '
107-
'[type=int_overflow, input_value=18446744073709551614, input_type=int]'
108-
),
110+
-i64_max + 1,
111+
Err('Input should be greater than 0 [type=greater_than, input_value=-9223372036854775806, input_type=int]'),
109112
),
110-
# (0, Err('Input should be greater than 0 [type=greater_than, input_value=-1, input_type=int]')),
113+
(i64_max * 2, i64_max * 2),
114+
(int('9' * 30), int('9' * 30)),
115+
(0, Err('Input should be greater than 0 [type=greater_than, input_value=0, input_type=int]')),
111116
(-1, Err('Input should be greater than 0 [type=greater_than, input_value=-1, input_type=int]')),
112117
pytest.param(
113118
Decimal('1.001'),
@@ -135,6 +140,84 @@ def test_positive_int(input_value, expected):
135140
assert isinstance(output, int)
136141

137142

143+
@pytest.mark.parametrize(
144+
'input_value,expected',
145+
[
146+
(-1, -1),
147+
(0, Err('Input should be less than 0 [type=less_than, input_value=0, input_type=int]')),
148+
(-i64_max, -i64_max),
149+
(-i64_max - 1, -i64_max - 1),
150+
(-int('9' * 30), -int('9' * 30)),
151+
],
152+
)
153+
def test_negative_int(input_value, expected):
154+
v = SchemaValidator({'type': 'int', 'lt': 0})
155+
if isinstance(expected, Err):
156+
with pytest.raises(ValidationError, match=re.escape(expected.message)):
157+
v.validate_python(input_value)
158+
else:
159+
output = v.validate_python(input_value)
160+
assert output == expected
161+
assert isinstance(output, int)
162+
163+
164+
@pytest.mark.parametrize(
165+
'input_value,expected',
166+
[
167+
(1, 1),
168+
(i64_max, i64_max),
169+
(i64_max + 1, i64_max + 1),
170+
(i64_max * 2, i64_max * 2),
171+
(
172+
int(1e30),
173+
Err(
174+
'Input integer too large to convert to 64-bit integer '
175+
'[type=int_overflow, input_value=1e+30, input_type=float]'
176+
),
177+
),
178+
(0, Err('Input should be greater than 0 [type=greater_than, input_value=0, input_type=int]')),
179+
(-1, Err('Input should be greater than 0 [type=greater_than, input_value=-1, input_type=int]')),
180+
pytest.param(
181+
[1, 2],
182+
Err('Input should be a valid integer [type=int_type, input_value=[1, 2], input_type=list]'),
183+
id='list',
184+
),
185+
],
186+
)
187+
def test_positive_json(input_value, expected):
188+
v = SchemaValidator({'type': 'int', 'gt': 0})
189+
json_input = json.dumps(input_value)
190+
if isinstance(expected, Err):
191+
with pytest.raises(ValidationError, match=re.escape(expected.message)):
192+
v.validate_json(json_input)
193+
else:
194+
output = v.validate_json(json_input)
195+
assert output == expected
196+
assert isinstance(output, int)
197+
198+
199+
@pytest.mark.parametrize(
200+
'input_value,expected',
201+
[
202+
(-1, -1),
203+
(0, Err('Input should be less than 0 [type=less_than, input_value=0, input_type=int]')),
204+
(-i64_max, -i64_max),
205+
(-i64_max - 1, -i64_max - 1),
206+
(-i64_max * 2, Err('Input integer too large to convert to 64-bit integer [type=int_overflow, input_value=')),
207+
],
208+
)
209+
def test_negative_json(input_value, expected):
210+
v = SchemaValidator({'type': 'int', 'lt': 0})
211+
json_input = json.dumps(input_value)
212+
if isinstance(expected, Err):
213+
with pytest.raises(ValidationError, match=re.escape(expected.message)):
214+
v.validate_json(json_input)
215+
else:
216+
output = v.validate_json(json_input)
217+
assert output == expected
218+
assert isinstance(output, int)
219+
220+
138221
@pytest.mark.parametrize(
139222
'input_value,expected',
140223
[

0 commit comments

Comments
 (0)