diff --git a/src/protean/fields/base.py b/src/protean/fields/base.py index eb752927..ddedb181 100644 --- a/src/protean/fields/base.py +++ b/src/protean/fields/base.py @@ -101,7 +101,7 @@ def __init__( messages.update(error_messages or {}) self.error_messages = messages - def _generic_param_values_for_repr(self): + def _generic_param_values_for_repr(self) -> list[str]: """Return the generic parameter values for the Field's repr""" values = [] if self.description: @@ -117,7 +117,10 @@ def _generic_param_values_for_repr(self): if callable(self.default): values.append(f"default={self.default.__name__}") else: - values.append(f"default='{self.default}'") + if isinstance(self.default, str): + values.append(f"default='{self.default}'") + else: + values.append(f"default={self.default}") return values def __repr__(self): diff --git a/src/protean/fields/basic.py b/src/protean/fields/basic.py index e3907844..de2615fb 100644 --- a/src/protean/fields/basic.py +++ b/src/protean/fields/basic.py @@ -130,8 +130,8 @@ def __repr__(self): # Generate repr values specific to this field values = self._generic_param_values_for_repr() if self.max_value: - values.append(f"max_value={self.max_length}") - if self.min_value: + values.append(f"max_value={self.max_value}") + if self.min_value or self.min_value == 0: values.append(f"min_value={self.min_value}") return f"{self.__class__.__name__}(" + ", ".join(values) + ")" @@ -173,8 +173,8 @@ def __repr__(self): # Generate repr values specific to this field values = self._generic_param_values_for_repr() if self.max_value: - values.append(f"max_value={self.max_length}") - if self.min_value: + values.append(f"max_value={self.max_value}") + if self.min_value or self.min_value == 0.0: values.append(f"min_value={self.min_value}") return f"{self.__class__.__name__}(" + ", ".join(values) + ")" @@ -462,6 +462,16 @@ def as_dict(self, value): """Return JSON-compatible value of self""" return value + def __repr__(self): + # Generate repr values specific to this field + values = [] + if self.schema_name: + values.append(f"'{self.schema_name}'") + + values.extend(self._generic_param_values_for_repr()) + + return f"{self.__class__.__name__}(" + ", ".join(values) + ")" + class Date(Field): """Concrete field implementation for the Date type.""" diff --git a/tests/field/test_boolean.py b/tests/field/test_boolean.py new file mode 100644 index 00000000..e55032be --- /dev/null +++ b/tests/field/test_boolean.py @@ -0,0 +1,23 @@ +import pytest + +from protean.fields.basic import Boolean + + +@pytest.mark.parametrize( + "value,expected", + [ + (True, True), + (False, False), + (1, True), + (0, False), + ("t", True), + ("f", False), + ("True", True), + ("False", False), + (1.0, True), + (0.0, False), + ], +) +def test_boolean_cast_to_type(value, expected): + field = Boolean() + assert field._cast_to_type(value) == expected diff --git a/tests/field/test_identifier.py b/tests/field/test_identifier.py index e6fd6a3d..697396c2 100644 --- a/tests/field/test_identifier.py +++ b/tests/field/test_identifier.py @@ -136,3 +136,7 @@ def test_that_default_is_picked_from_domain_config(self): assert identifier._load(uuid_val) == uuid_val assert identifier.identity_type == IdentityType.UUID.value assert identifier.as_dict(uuid_val) == str(uuid_val) + + def test_invalid_identity_type(self): + with pytest.raises(ValidationError): + Identifier(identity_type="invalid") diff --git a/tests/field/test_method.py b/tests/field/test_method.py new file mode 100644 index 00000000..4b40f366 --- /dev/null +++ b/tests/field/test_method.py @@ -0,0 +1,21 @@ +from datetime import datetime, timezone + +from protean.fields import Method + + +def utc_now(): + return datetime.now(timezone.utc) + + +def test_method_repr_and_str(): + method_obj1 = Method("fake_method") + method_obj2 = Method("fake_method", required=True) + method_obj4 = Method("fake_method", required=True, default=utc_now) + + assert repr(method_obj1) == str(method_obj1) == "Method()" + assert repr(method_obj2) == str(method_obj2) == "Method(required=True)" + assert ( + repr(method_obj4) + == str(method_obj4) + == "Method(required=True, default=utc_now)" + ) diff --git a/tests/field/test_nested.py b/tests/field/test_nested.py new file mode 100644 index 00000000..e6271e0c --- /dev/null +++ b/tests/field/test_nested.py @@ -0,0 +1,21 @@ +from protean.fields import Nested + + +def test_nested_field_repr_and_str(): + nested_obj1 = Nested("schema1") + nested_obj2 = Nested("schema1", many=True, required=True) + nested_obj3 = Nested("schema1", default={"name": "John Doe"}) + nested_obj4 = Nested("schema1", required=True, default={"name": "John Doe"}) + + assert repr(nested_obj1) == str(nested_obj1) == "Nested('schema1')" + assert repr(nested_obj2) == str(nested_obj2) == "Nested('schema1', required=True)" + assert ( + repr(nested_obj3) + == str(nested_obj3) + == "Nested('schema1', default={'name': 'John Doe'})" + ) + assert ( + repr(nested_obj4) + == str(nested_obj4) + == "Nested('schema1', required=True, default={'name': 'John Doe'})" + ) diff --git a/tests/field/test_repr.py b/tests/field/test_repr.py index 75b8398c..4a4005a4 100644 --- a/tests/field/test_repr.py +++ b/tests/field/test_repr.py @@ -1,4 +1,4 @@ -from protean.fields import String, Text +from protean.fields import Auto, Float, Integer, String, Text def test_identifier_in_repr(): @@ -85,3 +85,53 @@ def test_text_repr_and_str(): == str(text_obj4) == "Text(required=True, default='John Doe', sanitize=False)" ) + + +def test_integer_repr_and_str(): + int_obj1 = Integer(required=True) + int_obj2 = Integer(required=True, default=100) + int_obj3 = Integer(required=True, min_value=0, max_value=100) + int_obj4 = Integer(required=True, default=100, min_value=0, max_value=100) + + assert repr(int_obj1) == str(int_obj1) == "Integer(required=True)" + assert repr(int_obj2) == str(int_obj2) == "Integer(required=True, default=100)" + assert ( + repr(int_obj3) + == str(int_obj3) + == "Integer(required=True, max_value=100, min_value=0)" + ) + assert ( + repr(int_obj4) + == str(int_obj4) + == "Integer(required=True, default=100, max_value=100, min_value=0)" + ) + + +def test_float_repr_and_str(): + float_obj1 = Float(required=True) + float_obj2 = Float(required=True, default=100.0) + float_obj3 = Float(required=True, min_value=0.0, max_value=100.0) + float_obj4 = Float(required=True, default=100.0, min_value=0.0, max_value=100.0) + + assert repr(float_obj1) == str(float_obj1) == "Float(required=True)" + assert repr(float_obj2) == str(float_obj2) == "Float(required=True, default=100.0)" + assert ( + repr(float_obj3) + == str(float_obj3) + == "Float(required=True, max_value=100.0, min_value=0.0)" + ) + assert ( + repr(float_obj4) + == str(float_obj4) + == "Float(required=True, default=100.0, max_value=100.0, min_value=0.0)" + ) + + +def test_auto_repr_and_str(): + auto_obj1 = Auto() + auto_obj2 = Auto(required=True) + auto_obj3 = Auto(required=True, increment=True) + + assert repr(auto_obj1) == str(auto_obj1) == "Auto()" + assert repr(auto_obj2) == str(auto_obj2) == "Auto(required=True)" + assert repr(auto_obj3) == str(auto_obj3) == "Auto(required=True, increment=True)"