ddd-value-objects 0.2.1.1__tar.gz → 0.2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ddd_value_objects-0.2.1.1/src/ddd_value_objects.egg-info → ddd_value_objects-0.2.2.0}/PKG-INFO +2 -2
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/README.md +1 -1
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/pyproject.toml +2 -2
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/__init__.py +3 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/decimal_value_object.py +8 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/float_value_object.py +9 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/int_value_object.py +1 -1
- ddd_value_objects-0.2.1.1/src/ddd_value_objects/positive_decimal_value_object.py → ddd_value_objects-0.2.2.0/src/ddd_value_objects/non_negative_decimal_value_object.py +2 -2
- ddd_value_objects-0.2.1.1/src/ddd_value_objects/positive_float_value_object.py → ddd_value_objects-0.2.2.0/src/ddd_value_objects/non_negative_float_value_object.py +2 -2
- ddd_value_objects-0.2.2.0/src/ddd_value_objects/non_negative_int_value_object.py +12 -0
- ddd_value_objects-0.2.2.0/src/ddd_value_objects/positive_decimal_value_object.py +20 -0
- ddd_value_objects-0.2.2.0/src/ddd_value_objects/positive_float_value_object.py +19 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/positive_int_value_object.py +1 -1
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0/src/ddd_value_objects.egg-info}/PKG-INFO +2 -2
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects.egg-info/SOURCES.txt +5 -0
- ddd_value_objects-0.2.2.0/tests/test_float_value_object.py +34 -0
- ddd_value_objects-0.2.2.0/tests/test_non_negative_value_objects.py +78 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_positive_value_objects.py +9 -9
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_primitives.py +40 -3
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_string_length_validations.py +6 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/LICENSE +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/setup.cfg +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/bool_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/composite_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/country_code_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/currency_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/date_time_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/date_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/email_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/enum_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/invalid_argument_error.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/ip_address_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/money_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/phone_number_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/string_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/url_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/uuid_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects.egg-info/dependency_links.txt +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects.egg-info/top_level.txt +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_composite_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_country_code_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_currency_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_date_time_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_date_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_email_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_enum_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_integrated_validations.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_ip_address_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_money_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_phone_number_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_url_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_uuid_value_object.py +0 -0
- {ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_value_object.py +0 -0
{ddd_value_objects-0.2.1.1/src/ddd_value_objects.egg-info → ddd_value_objects-0.2.2.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ddd-value-objects
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2.0
|
|
4
4
|
Summary: A collection of base classes for Domain-Driven Design (DDD) value objects and entities in Python
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -177,7 +177,7 @@ print(product1 == product2) # True (Same identity/ID)
|
|
|
177
177
|
| Category | Classes |
|
|
178
178
|
| :--- | :--- |
|
|
179
179
|
| **Primitives** | `StringValueObject`, `IntValueObject`, `FloatValueObject`, `BoolValueObject` |
|
|
180
|
-
| **Numeric** | `PositiveIntValueObject`, `PositiveFloatValueObject` |
|
|
180
|
+
| **Numeric** | `PositiveIntValueObject`, `PositiveFloatValueObject`, `PositiveDecimalValueObject`, `NonNegativeIntValueObject`, `NonNegativeFloatValueObject`, `NonNegativeDecimalValueObject` |
|
|
181
181
|
| **Identity** | `UuidValueObject` |
|
|
182
182
|
| **Network** | `EmailValueObject`, `UrlValueObject`, `IpAddressValueObject` |
|
|
183
183
|
| **Communication** | `PhoneNumberValueObject`, `CountryCodeValueObject` (ISO 3166-1 alpha-2) |
|
|
@@ -167,7 +167,7 @@ print(product1 == product2) # True (Same identity/ID)
|
|
|
167
167
|
| Category | Classes |
|
|
168
168
|
| :--- | :--- |
|
|
169
169
|
| **Primitives** | `StringValueObject`, `IntValueObject`, `FloatValueObject`, `BoolValueObject` |
|
|
170
|
-
| **Numeric** | `PositiveIntValueObject`, `PositiveFloatValueObject` |
|
|
170
|
+
| **Numeric** | `PositiveIntValueObject`, `PositiveFloatValueObject`, `PositiveDecimalValueObject`, `NonNegativeIntValueObject`, `NonNegativeFloatValueObject`, `NonNegativeDecimalValueObject` |
|
|
171
171
|
| **Identity** | `UuidValueObject` |
|
|
172
172
|
| **Network** | `EmailValueObject`, `UrlValueObject`, `IpAddressValueObject` |
|
|
173
173
|
| **Communication** | `PhoneNumberValueObject`, `CountryCodeValueObject` (ISO 3166-1 alpha-2) |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ddd-value-objects"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2.0"
|
|
4
4
|
description = "A collection of base classes for Domain-Driven Design (DDD) value objects and entities in Python"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = {text = "MIT"}
|
|
@@ -24,4 +24,4 @@ dev = [
|
|
|
24
24
|
]
|
|
25
25
|
|
|
26
26
|
[tool.pytest.ini_options]
|
|
27
|
-
addopts = "--cov=src --cov-report=term-missing --cov-fail-under=
|
|
27
|
+
addopts = "--cov=src --cov-report=term-missing --cov-fail-under=100"
|
|
@@ -11,6 +11,9 @@ from .date_value_object import DateValueObject
|
|
|
11
11
|
from .positive_int_value_object import PositiveIntValueObject
|
|
12
12
|
from .positive_float_value_object import PositiveFloatValueObject
|
|
13
13
|
from .positive_decimal_value_object import PositiveDecimalValueObject
|
|
14
|
+
from .non_negative_int_value_object import NonNegativeIntValueObject
|
|
15
|
+
from .non_negative_float_value_object import NonNegativeFloatValueObject
|
|
16
|
+
from .non_negative_decimal_value_object import NonNegativeDecimalValueObject
|
|
14
17
|
from .email_value_object import EmailValueObject
|
|
15
18
|
from .phone_number_value_object import PhoneNumberValueObject
|
|
16
19
|
from .currency_value_object import CurrencyValueObject
|
|
@@ -11,6 +11,7 @@ class DecimalValueObject(ValueObject[Decimal]):
|
|
|
11
11
|
def __post_init__(self):
|
|
12
12
|
super().__post_init__()
|
|
13
13
|
self._ensure_value_is_decimal(self.value)
|
|
14
|
+
self._ensure_is_finite(self.value)
|
|
14
15
|
self._ensure_is_within_range(self.value)
|
|
15
16
|
|
|
16
17
|
def _ensure_value_is_decimal(self, value: Decimal) -> None:
|
|
@@ -19,6 +20,10 @@ class DecimalValueObject(ValueObject[Decimal]):
|
|
|
19
20
|
self.get_invalid_type_error_message(value)
|
|
20
21
|
)
|
|
21
22
|
|
|
23
|
+
def _ensure_is_finite(self, value: Decimal) -> None:
|
|
24
|
+
if not value.is_finite():
|
|
25
|
+
raise InvalidArgumentError(self.get_non_finite_error_message(value))
|
|
26
|
+
|
|
22
27
|
def _ensure_is_within_range(self, value: Decimal) -> None:
|
|
23
28
|
min_value = self.min_value()
|
|
24
29
|
max_value = self.max_value()
|
|
@@ -35,6 +40,9 @@ class DecimalValueObject(ValueObject[Decimal]):
|
|
|
35
40
|
def max_value(self) -> Decimal | None:
|
|
36
41
|
return None
|
|
37
42
|
|
|
43
|
+
def get_non_finite_error_message(self, value: Decimal) -> str:
|
|
44
|
+
return f"Value must be finite, got {value}"
|
|
45
|
+
|
|
38
46
|
def get_invalid_type_error_message(self, value: Any) -> str:
|
|
39
47
|
return f"Value must be a decimal, got {type(value)}"
|
|
40
48
|
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/float_value_object.py
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from typing import Any
|
|
2
|
+
import math
|
|
2
3
|
from dataclasses import dataclass
|
|
3
4
|
|
|
4
5
|
from .invalid_argument_error import InvalidArgumentError
|
|
@@ -10,6 +11,7 @@ class FloatValueObject(ValueObject[float]):
|
|
|
10
11
|
def __post_init__(self):
|
|
11
12
|
super().__post_init__()
|
|
12
13
|
self._ensure_value_is_float(self.value)
|
|
14
|
+
self._ensure_is_finite(self.value)
|
|
13
15
|
self._ensure_is_within_range(self.value)
|
|
14
16
|
|
|
15
17
|
def _ensure_value_is_float(self, value: float) -> None:
|
|
@@ -18,6 +20,10 @@ class FloatValueObject(ValueObject[float]):
|
|
|
18
20
|
self.get_invalid_type_error_message(value)
|
|
19
21
|
)
|
|
20
22
|
|
|
23
|
+
def _ensure_is_finite(self, value: float) -> None:
|
|
24
|
+
if not math.isfinite(value):
|
|
25
|
+
raise InvalidArgumentError(self.get_non_finite_error_message(value))
|
|
26
|
+
|
|
21
27
|
def _ensure_is_within_range(self, value: float) -> None:
|
|
22
28
|
min_value = self.min_value()
|
|
23
29
|
max_value = self.max_value()
|
|
@@ -34,6 +40,9 @@ class FloatValueObject(ValueObject[float]):
|
|
|
34
40
|
def max_value(self) -> float | None:
|
|
35
41
|
return None
|
|
36
42
|
|
|
43
|
+
def get_non_finite_error_message(self, value: float) -> str:
|
|
44
|
+
return f"Value must be finite, got {value}"
|
|
45
|
+
|
|
37
46
|
def get_invalid_type_error_message(self, value: Any) -> str:
|
|
38
47
|
return f"Value must be a float, got {type(value)}"
|
|
39
48
|
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/int_value_object.py
RENAMED
|
@@ -13,7 +13,7 @@ class IntValueObject(ValueObject[int]):
|
|
|
13
13
|
self._ensure_is_within_range(self.value)
|
|
14
14
|
|
|
15
15
|
def _ensure_value_is_integer(self, value: int) -> None:
|
|
16
|
-
if not isinstance(value, int):
|
|
16
|
+
if isinstance(value, bool) or not isinstance(value, int):
|
|
17
17
|
raise InvalidArgumentError(
|
|
18
18
|
self.get_invalid_type_error_message(value)
|
|
19
19
|
)
|
|
@@ -5,9 +5,9 @@ from .decimal_value_object import DecimalValueObject
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
@dataclass(frozen=True, slots=True)
|
|
8
|
-
class
|
|
8
|
+
class NonNegativeDecimalValueObject(DecimalValueObject):
|
|
9
9
|
def min_value(self) -> Decimal | None:
|
|
10
10
|
return Decimal("0")
|
|
11
11
|
|
|
12
12
|
def get_too_low_error_message(self, value: Decimal, min_value: Decimal) -> str:
|
|
13
|
-
return f"'{value}' is not a
|
|
13
|
+
return f"'{value}' is not a non-negative decimal"
|
|
@@ -4,9 +4,9 @@ from .float_value_object import FloatValueObject
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
@dataclass(frozen=True, slots=True)
|
|
7
|
-
class
|
|
7
|
+
class NonNegativeFloatValueObject(FloatValueObject):
|
|
8
8
|
def min_value(self) -> float | None:
|
|
9
9
|
return 0.0
|
|
10
10
|
|
|
11
11
|
def get_too_low_error_message(self, value: float, min_value: float) -> str:
|
|
12
|
-
return f"'{value}' is not a
|
|
12
|
+
return f"'{value}' is not a non-negative float"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from .int_value_object import IntValueObject
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True, slots=True)
|
|
7
|
+
class NonNegativeIntValueObject(IntValueObject):
|
|
8
|
+
def min_value(self) -> int | None:
|
|
9
|
+
return 0
|
|
10
|
+
|
|
11
|
+
def get_too_low_error_message(self, value: int, min_value: int) -> str:
|
|
12
|
+
return f"'{value}' is not a non-negative integer"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
|
|
4
|
+
from .decimal_value_object import DecimalValueObject
|
|
5
|
+
from .invalid_argument_error import InvalidArgumentError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True, slots=True)
|
|
9
|
+
class PositiveDecimalValueObject(DecimalValueObject):
|
|
10
|
+
def min_value(self) -> Decimal | None:
|
|
11
|
+
return Decimal("0")
|
|
12
|
+
|
|
13
|
+
def get_too_low_error_message(self, value: Decimal, min_value: Decimal) -> str:
|
|
14
|
+
return f"'{value}' is not a positive decimal"
|
|
15
|
+
|
|
16
|
+
def _ensure_is_within_range(self, value: Decimal) -> None:
|
|
17
|
+
min_value = self.min_value()
|
|
18
|
+
if min_value is not None and value <= min_value:
|
|
19
|
+
raise InvalidArgumentError(self.get_too_low_error_message(value, min_value))
|
|
20
|
+
super()._ensure_is_within_range(value)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from .float_value_object import FloatValueObject
|
|
4
|
+
from .invalid_argument_error import InvalidArgumentError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True, slots=True)
|
|
8
|
+
class PositiveFloatValueObject(FloatValueObject):
|
|
9
|
+
def min_value(self) -> float | None:
|
|
10
|
+
return 0.0
|
|
11
|
+
|
|
12
|
+
def get_too_low_error_message(self, value: float, min_value: float) -> str:
|
|
13
|
+
return f"'{value}' is not a positive float"
|
|
14
|
+
|
|
15
|
+
def _ensure_is_within_range(self, value: float) -> None:
|
|
16
|
+
min_value = self.min_value()
|
|
17
|
+
if min_value is not None and value <= min_value:
|
|
18
|
+
raise InvalidArgumentError(self.get_too_low_error_message(value, min_value))
|
|
19
|
+
super()._ensure_is_within_range(value)
|
|
@@ -6,7 +6,7 @@ from .int_value_object import IntValueObject
|
|
|
6
6
|
@dataclass(frozen=True, slots=True)
|
|
7
7
|
class PositiveIntValueObject(IntValueObject):
|
|
8
8
|
def min_value(self) -> int | None:
|
|
9
|
-
return
|
|
9
|
+
return 1
|
|
10
10
|
|
|
11
11
|
def get_too_low_error_message(self, value: int, min_value: int) -> str:
|
|
12
12
|
return f"'{value}' is not a positive integer"
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0/src/ddd_value_objects.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ddd-value-objects
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2.0
|
|
4
4
|
Summary: A collection of base classes for Domain-Driven Design (DDD) value objects and entities in Python
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -177,7 +177,7 @@ print(product1 == product2) # True (Same identity/ID)
|
|
|
177
177
|
| Category | Classes |
|
|
178
178
|
| :--- | :--- |
|
|
179
179
|
| **Primitives** | `StringValueObject`, `IntValueObject`, `FloatValueObject`, `BoolValueObject` |
|
|
180
|
-
| **Numeric** | `PositiveIntValueObject`, `PositiveFloatValueObject` |
|
|
180
|
+
| **Numeric** | `PositiveIntValueObject`, `PositiveFloatValueObject`, `PositiveDecimalValueObject`, `NonNegativeIntValueObject`, `NonNegativeFloatValueObject`, `NonNegativeDecimalValueObject` |
|
|
181
181
|
| **Identity** | `UuidValueObject` |
|
|
182
182
|
| **Network** | `EmailValueObject`, `UrlValueObject`, `IpAddressValueObject` |
|
|
183
183
|
| **Communication** | `PhoneNumberValueObject`, `CountryCodeValueObject` (ISO 3166-1 alpha-2) |
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects.egg-info/SOURCES.txt
RENAMED
|
@@ -16,6 +16,9 @@ src/ddd_value_objects/int_value_object.py
|
|
|
16
16
|
src/ddd_value_objects/invalid_argument_error.py
|
|
17
17
|
src/ddd_value_objects/ip_address_value_object.py
|
|
18
18
|
src/ddd_value_objects/money_value_object.py
|
|
19
|
+
src/ddd_value_objects/non_negative_decimal_value_object.py
|
|
20
|
+
src/ddd_value_objects/non_negative_float_value_object.py
|
|
21
|
+
src/ddd_value_objects/non_negative_int_value_object.py
|
|
19
22
|
src/ddd_value_objects/phone_number_value_object.py
|
|
20
23
|
src/ddd_value_objects/positive_decimal_value_object.py
|
|
21
24
|
src/ddd_value_objects/positive_float_value_object.py
|
|
@@ -35,9 +38,11 @@ tests/test_date_time_value_object.py
|
|
|
35
38
|
tests/test_date_value_object.py
|
|
36
39
|
tests/test_email_value_object.py
|
|
37
40
|
tests/test_enum_value_object.py
|
|
41
|
+
tests/test_float_value_object.py
|
|
38
42
|
tests/test_integrated_validations.py
|
|
39
43
|
tests/test_ip_address_value_object.py
|
|
40
44
|
tests/test_money_value_object.py
|
|
45
|
+
tests/test_non_negative_value_objects.py
|
|
41
46
|
tests/test_phone_number_value_object.py
|
|
42
47
|
tests/test_positive_value_objects.py
|
|
43
48
|
tests/test_primitives.py
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from src.ddd_value_objects import FloatValueObject, InvalidArgumentError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_float_value_object_rejects_nan():
|
|
7
|
+
with pytest.raises(InvalidArgumentError, match="Value must be finite"):
|
|
8
|
+
FloatValueObject(float("nan"))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_float_value_object_rejects_infinity():
|
|
12
|
+
with pytest.raises(InvalidArgumentError, match="Value must be finite"):
|
|
13
|
+
FloatValueObject(float("inf"))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_float_value_object_rejects_negative_infinity():
|
|
17
|
+
with pytest.raises(InvalidArgumentError, match="Value must be finite"):
|
|
18
|
+
FloatValueObject(float("-inf"))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_float_value_object_accepts_finite_values():
|
|
22
|
+
assert FloatValueObject(1.5).value == 1.5
|
|
23
|
+
assert FloatValueObject(0.0).value == 0.0
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_float_value_object_non_finite_error_message():
|
|
27
|
+
vo = FloatValueObject(1.0)
|
|
28
|
+
assert vo.get_non_finite_error_message(float("nan")) == "Value must be finite, got nan"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_float_value_object_error_message_templates():
|
|
32
|
+
vo = FloatValueObject(1.0)
|
|
33
|
+
assert vo.get_too_low_error_message_template() == "Value is less than minimum required {min_value}"
|
|
34
|
+
assert vo.get_too_high_error_message_template() == "Value is greater than maximum allowed {max_value}"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
|
|
4
|
+
from src.ddd_value_objects import (
|
|
5
|
+
NonNegativeIntValueObject,
|
|
6
|
+
NonNegativeFloatValueObject,
|
|
7
|
+
NonNegativeDecimalValueObject,
|
|
8
|
+
InvalidArgumentError,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_non_negative_int_value_object():
|
|
13
|
+
vo1 = NonNegativeIntValueObject(10)
|
|
14
|
+
vo2 = NonNegativeIntValueObject(10)
|
|
15
|
+
vo3 = NonNegativeIntValueObject(20)
|
|
16
|
+
|
|
17
|
+
assert vo1.value == 10
|
|
18
|
+
assert vo1.equals(vo2)
|
|
19
|
+
assert not vo1.equals(vo3)
|
|
20
|
+
assert repr(vo1) == "NonNegativeIntValueObject(value=10)"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_non_negative_int_value_object_zero():
|
|
24
|
+
vo = NonNegativeIntValueObject(0)
|
|
25
|
+
assert vo.value == 0
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_non_negative_int_value_object_invalid():
|
|
29
|
+
with pytest.raises(InvalidArgumentError, match="is not a non-negative integer"):
|
|
30
|
+
NonNegativeIntValueObject(-1)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_non_negative_float_value_object():
|
|
34
|
+
vo1 = NonNegativeFloatValueObject(10.5)
|
|
35
|
+
vo2 = NonNegativeFloatValueObject(10.5)
|
|
36
|
+
vo3 = NonNegativeFloatValueObject(20.5)
|
|
37
|
+
|
|
38
|
+
assert vo1.value == 10.5
|
|
39
|
+
assert vo1.equals(vo2)
|
|
40
|
+
assert not vo1.equals(vo3)
|
|
41
|
+
assert repr(vo1) == "NonNegativeFloatValueObject(value=10.5)"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_non_negative_float_value_object_zero():
|
|
45
|
+
vo = NonNegativeFloatValueObject(0.0)
|
|
46
|
+
assert vo.value == 0.0
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_non_negative_float_value_object_invalid():
|
|
50
|
+
with pytest.raises(InvalidArgumentError, match="is not a non-negative float"):
|
|
51
|
+
NonNegativeFloatValueObject(-1.5)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_non_negative_decimal_value_object():
|
|
55
|
+
vo1 = NonNegativeDecimalValueObject(Decimal("10.5"))
|
|
56
|
+
assert vo1.value == Decimal("10.5")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_non_negative_decimal_value_object_zero():
|
|
60
|
+
vo = NonNegativeDecimalValueObject(Decimal("0"))
|
|
61
|
+
assert vo.value == Decimal("0")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_non_negative_decimal_value_object_invalid():
|
|
65
|
+
with pytest.raises(InvalidArgumentError, match="is not a non-negative decimal"):
|
|
66
|
+
NonNegativeDecimalValueObject(Decimal("-1.5"))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_non_negative_int_with_extra_range():
|
|
70
|
+
class PortNumber(NonNegativeIntValueObject):
|
|
71
|
+
def max_value(self) -> int:
|
|
72
|
+
return 65535
|
|
73
|
+
|
|
74
|
+
assert PortNumber(8080).value == 8080
|
|
75
|
+
with pytest.raises(InvalidArgumentError, match="greater than maximum allowed 65535"):
|
|
76
|
+
PortNumber(70000)
|
|
77
|
+
with pytest.raises(InvalidArgumentError, match="is not a non-negative integer"):
|
|
78
|
+
PortNumber(-1)
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_positive_value_objects.py
RENAMED
|
@@ -21,9 +21,9 @@ def test_positive_int_value_object_invalid():
|
|
|
21
21
|
with pytest.raises(InvalidArgumentError, match="is not a positive integer"):
|
|
22
22
|
PositiveIntValueObject(-1)
|
|
23
23
|
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
def test_positive_int_value_object_zero_invalid():
|
|
25
|
+
with pytest.raises(InvalidArgumentError, match="is not a positive integer"):
|
|
26
|
+
PositiveIntValueObject(0)
|
|
27
27
|
|
|
28
28
|
def test_positive_float_value_object():
|
|
29
29
|
vo1 = PositiveFloatValueObject(10.5)
|
|
@@ -39,9 +39,9 @@ def test_positive_float_value_object_invalid():
|
|
|
39
39
|
with pytest.raises(InvalidArgumentError, match="is not a positive float"):
|
|
40
40
|
PositiveFloatValueObject(-1.5)
|
|
41
41
|
|
|
42
|
-
def
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
def test_positive_float_value_object_zero_invalid():
|
|
43
|
+
with pytest.raises(InvalidArgumentError, match="is not a positive float"):
|
|
44
|
+
PositiveFloatValueObject(0.0)
|
|
45
45
|
|
|
46
46
|
def test_positive_decimal_value_object():
|
|
47
47
|
vo1 = PositiveDecimalValueObject(Decimal("10.5"))
|
|
@@ -51,9 +51,9 @@ def test_positive_decimal_value_object_invalid():
|
|
|
51
51
|
with pytest.raises(InvalidArgumentError, match="is not a positive decimal"):
|
|
52
52
|
PositiveDecimalValueObject(Decimal("-1.5"))
|
|
53
53
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
def test_positive_decimal_value_object_zero_invalid():
|
|
55
|
+
with pytest.raises(InvalidArgumentError, match="is not a positive decimal"):
|
|
56
|
+
PositiveDecimalValueObject(Decimal("0"))
|
|
57
57
|
|
|
58
58
|
def test_positive_int_with_extra_range():
|
|
59
59
|
class RestrictedAge(PositiveIntValueObject):
|
|
@@ -139,10 +139,10 @@ def test_positive_decimal_value_object_valid():
|
|
|
139
139
|
assert vo.value == val
|
|
140
140
|
|
|
141
141
|
|
|
142
|
-
def
|
|
142
|
+
def test_positive_decimal_value_object_zero_invalid():
|
|
143
143
|
val = Decimal("0.00")
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
with pytest.raises(InvalidArgumentError, match="is not a positive decimal"):
|
|
145
|
+
PositiveDecimalValueObject(val)
|
|
146
146
|
|
|
147
147
|
|
|
148
148
|
def test_positive_decimal_value_object_invalid():
|
|
@@ -190,3 +190,40 @@ def test_decimal_value_object_custom_too_high_message():
|
|
|
190
190
|
|
|
191
191
|
with pytest.raises(InvalidArgumentError, match='Too high: 15.0 > 10.0'):
|
|
192
192
|
CustomMessageDecimal(Decimal('15.0'))
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def test_decimal_value_object_rejects_nan():
|
|
196
|
+
with pytest.raises(InvalidArgumentError, match="Value must be finite"):
|
|
197
|
+
DecimalValueObject(Decimal("NaN"))
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def test_decimal_value_object_rejects_infinity():
|
|
201
|
+
with pytest.raises(InvalidArgumentError, match="Value must be finite"):
|
|
202
|
+
DecimalValueObject(Decimal("Infinity"))
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def test_decimal_value_object_rejects_negative_infinity():
|
|
206
|
+
with pytest.raises(InvalidArgumentError, match="Value must be finite"):
|
|
207
|
+
DecimalValueObject(Decimal("-Infinity"))
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def test_decimal_value_object_accepts_finite_values():
|
|
211
|
+
assert DecimalValueObject(Decimal("1.5")).value == Decimal("1.5")
|
|
212
|
+
assert DecimalValueObject(Decimal("0")).value == Decimal("0")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def test_decimal_value_object_non_finite_error_message():
|
|
216
|
+
vo = DecimalValueObject(Decimal("1"))
|
|
217
|
+
assert vo.get_non_finite_error_message(Decimal("NaN")) == "Value must be finite, got NaN"
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def test_int_value_object_error_message_templates():
|
|
221
|
+
vo = IntValueObject(1)
|
|
222
|
+
assert vo.get_too_low_error_message_template() == "Value is less than minimum required {min_value}"
|
|
223
|
+
assert vo.get_too_high_error_message_template() == "Value is greater than maximum allowed {max_value}"
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_decimal_value_object_error_message_templates():
|
|
227
|
+
vo = DecimalValueObject(Decimal("1"))
|
|
228
|
+
assert vo.get_too_low_error_message_template() == "Value is less than minimum required {min_value}"
|
|
229
|
+
assert vo.get_too_high_error_message_template() == "Value is greater than maximum allowed {max_value}"
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_string_length_validations.py
RENAMED
|
@@ -56,3 +56,9 @@ def test_custom_length_error_messages():
|
|
|
56
56
|
|
|
57
57
|
with pytest.raises(InvalidArgumentError, match="Demasiado corto: 4 < 5"):
|
|
58
58
|
CustomMessageString("1234")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_string_value_object_error_message_templates():
|
|
62
|
+
vo = StringValueObject("abc")
|
|
63
|
+
assert vo.get_too_short_error_message_template() == "Value is too short. Minimum length is {min_length}"
|
|
64
|
+
assert vo.get_too_long_error_message_template() == "Value is too long. Maximum length is {max_length}"
|
|
File without changes
|
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/bool_value_object.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/date_value_object.py
RENAMED
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/email_value_object.py
RENAMED
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/enum_value_object.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/money_value_object.py
RENAMED
|
File without changes
|
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/string_value_object.py
RENAMED
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/url_value_object.py
RENAMED
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/uuid_value_object.py
RENAMED
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects/value_object.py
RENAMED
|
File without changes
|
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/src/ddd_value_objects.egg-info/top_level.txt
RENAMED
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_composite_value_object.py
RENAMED
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_country_code_value_object.py
RENAMED
|
File without changes
|
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_date_time_value_object.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_integrated_validations.py
RENAMED
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_ip_address_value_object.py
RENAMED
|
File without changes
|
|
File without changes
|
{ddd_value_objects-0.2.1.1 → ddd_value_objects-0.2.2.0}/tests/test_phone_number_value_object.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|