feat: add generic Value[T] base class for value objects
- Create Value[T] (frozen dataclass + ABC + Generic) in domain/framework/ - Migrate ClassifiedAdTitle and ClassifiedAdText to inherit Value[str] - Standardize all value access via .value instead of type-specific fields
This commit is contained in:
@@ -1,18 +1,45 @@
|
|||||||
# Plan: Add FastAPI to the Project
|
# Plan: Value Object Base Class
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
Add FastAPI as the web framework with a minimal health check endpoint.
|
Create a generic `Value[T]` base class in the framework layer. All value objects standardize on `.value` access via inheritance.
|
||||||
|
|
||||||
## Changes
|
## Design
|
||||||
|
|
||||||
### 1. Dependencies
|
### `Value[T]` base class (`domain/framework/value.py`)
|
||||||
- Add `fastapi` and `uvicorn[standard]` to `pyproject.toml` under `[project.dependencies]`
|
```python
|
||||||
- Install dependencies into the virtual environment
|
@dataclass(frozen=True)
|
||||||
|
class Value(ABC, Generic[T]):
|
||||||
|
value: T
|
||||||
|
```
|
||||||
|
|
||||||
### 2. Application Entry Point
|
- Frozen dataclass → immutability + structural equality (core Value Object semantics)
|
||||||
- Create `main.py` with a FastAPI app instance
|
- `Generic[T]` → type-safe `.value` access in subclasses (e.g., `Value[str]`)
|
||||||
- Add a health check endpoint (`GET /`) that returns `{"status": "ok"}`
|
- `ABC` → mirrors the `Entity` pattern
|
||||||
|
|
||||||
|
### Subclasses
|
||||||
|
- `ClassifiedAdTitle(Value[str])` — drops `title: str`, inherits `value: T` bound to `str`
|
||||||
|
- `ClassifiedAdText(Value[str])` — drops `text: str`, inherits `value: T` bound to `str`
|
||||||
|
|
||||||
|
### Ripple effects
|
||||||
|
Every `.title` / `.text` reference on these objects becomes `.value`:
|
||||||
|
- `classified_ad.py` — `self.title.title` → `self.title.value`, construction calls, method bodies
|
||||||
|
- Tests — assertions, constructions, equality checks
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
| File | Change |
|
||||||
|
|------|--------|
|
||||||
|
| `domain/framework/value.py` | New — `Value[T]` base class |
|
||||||
|
| `domain/framework/__init__.py` | Add `Value` re-export |
|
||||||
|
| `domain/marketplace/classified_ad_title.py` | Inherit `Value[str]`, rename field |
|
||||||
|
| `domain/marketplace/classified_ad_text.py` | Inherit `Value[str]`, rename field |
|
||||||
|
| `domain/marketplace/classified_ad.py` | Update `.title`/`.text` refs → `.value` |
|
||||||
|
| `tests/test_classified_ad_title.py` | Update refs |
|
||||||
|
| `tests/test_classified_ad_text.py` | Update refs |
|
||||||
|
| `tests/test_classified_ad.py` | Update refs |
|
||||||
|
|
||||||
|
## Not in scope
|
||||||
|
`ClassifiedAdId`, `UserId`, `Money`, `Price` — stay as-is.
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
- `pytest tests/` — all tests pass
|
- `pytest tests/` — all 63 tests pass
|
||||||
- `uvicorn main:app --reload` — server starts and health endpoint responds
|
- `ruff check .` && `ruff format --check .` — clean
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
from .value import Value as Value
|
||||||
|
|||||||
7
domain/framework/value.py
Normal file
7
domain/framework/value.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from abc import ABC
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Value[T](ABC):
|
||||||
|
value: T
|
||||||
@@ -28,23 +28,27 @@ from domain.marketplace.user_id import UserId
|
|||||||
class ClassifiedAd(Entity):
|
class ClassifiedAd(Entity):
|
||||||
id: ClassifiedAdId
|
id: ClassifiedAdId
|
||||||
_ownerId: UserId
|
_ownerId: UserId
|
||||||
title: ClassifiedAdTitle = field(default_factory=lambda: ClassifiedAdTitle(title=""))
|
title: ClassifiedAdTitle = field(
|
||||||
text: ClassifiedAdText = field(default_factory=lambda: ClassifiedAdText(text=""))
|
default_factory=lambda: ClassifiedAdTitle(value="")
|
||||||
|
)
|
||||||
|
text: ClassifiedAdText = field(default_factory=lambda: ClassifiedAdText(value=""))
|
||||||
price: Price = field(default_factory=lambda: Price(amount=Decimal("0.00")))
|
price: Price = field(default_factory=lambda: Price(amount=Decimal("0.00")))
|
||||||
state: ClassifiedAdState = ClassifiedAdState.Inactive
|
state: ClassifiedAdState = ClassifiedAdState.Inactive
|
||||||
_changes: tuple[DomainEvent, ...] = ()
|
_changes: tuple[DomainEvent, ...] = ()
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if not self._changes:
|
if not self._changes:
|
||||||
object.__setattr__(self, '_changes', (
|
object.__setattr__(
|
||||||
ClassifiedAdCreated(id=self.id.value, owner_id=self._ownerId.value),
|
self,
|
||||||
))
|
"_changes",
|
||||||
|
(ClassifiedAdCreated(id=self.id.value, owner_id=self._ownerId.value),),
|
||||||
|
)
|
||||||
|
|
||||||
def _ensure_valid_state(self):
|
def _ensure_valid_state(self):
|
||||||
errors = []
|
errors = []
|
||||||
if not self.title.title:
|
if not self.title.value:
|
||||||
errors.append("title")
|
errors.append("title")
|
||||||
if not self.text.text:
|
if not self.text.value:
|
||||||
errors.append("text")
|
errors.append("text")
|
||||||
if self.price.amount <= 0:
|
if self.price.amount <= 0:
|
||||||
errors.append("price")
|
errors.append("price")
|
||||||
@@ -57,9 +61,9 @@ class ClassifiedAd(Entity):
|
|||||||
def when(self, event: DomainEvent) -> Self:
|
def when(self, event: DomainEvent) -> Self:
|
||||||
match event:
|
match event:
|
||||||
case ClassifiedAdTitleChanged():
|
case ClassifiedAdTitleChanged():
|
||||||
return replace(self, title=ClassifiedAdTitle(title=event.title))
|
return replace(self, title=ClassifiedAdTitle(value=event.title))
|
||||||
case ClassifiedAdTextUpdated():
|
case ClassifiedAdTextUpdated():
|
||||||
return replace(self, text=ClassifiedAdText(text=event.ad_text))
|
return replace(self, text=ClassifiedAdText(value=event.ad_text))
|
||||||
case ClassifiedAdPriceUpdated():
|
case ClassifiedAdPriceUpdated():
|
||||||
return replace(self, price=Price(amount=event.price))
|
return replace(self, price=Price(amount=event.price))
|
||||||
case ClassifiedAdSentForReview():
|
case ClassifiedAdSentForReview():
|
||||||
@@ -70,10 +74,12 @@ class ClassifiedAd(Entity):
|
|||||||
return self.apply(ClassifiedAdSentForReview(id=self.id.value))
|
return self.apply(ClassifiedAdSentForReview(id=self.id.value))
|
||||||
|
|
||||||
def set_title(self, title: ClassifiedAdTitle) -> ClassifiedAd:
|
def set_title(self, title: ClassifiedAdTitle) -> ClassifiedAd:
|
||||||
return self.apply(ClassifiedAdTitleChanged(id=self.id.value, title=title.title))
|
return self.apply(ClassifiedAdTitleChanged(id=self.id.value, title=title.value))
|
||||||
|
|
||||||
def update_text(self, text: ClassifiedAdText) -> ClassifiedAd:
|
def update_text(self, text: ClassifiedAdText) -> ClassifiedAd:
|
||||||
return self.apply(ClassifiedAdTextUpdated(id=self.id.value, ad_text=text.text))
|
return self.apply(ClassifiedAdTextUpdated(id=self.id.value, ad_text=text.value))
|
||||||
|
|
||||||
def update_price(self, price: Price) -> ClassifiedAd:
|
def update_price(self, price: Price) -> ClassifiedAd:
|
||||||
return self.apply(ClassifiedAdPriceUpdated(id=self.id.value, price=price.amount))
|
return self.apply(
|
||||||
|
ClassifiedAdPriceUpdated(id=self.id.value, price=price.amount)
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from domain.framework.value import Value
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ClassifiedAdText:
|
class ClassifiedAdText(Value[str]):
|
||||||
text: str
|
pass
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from domain.framework.value import Value
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ClassifiedAdTitle:
|
class ClassifiedAdTitle(Value[str]):
|
||||||
title: str
|
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if len(self.title) > 100:
|
if len(self.value) > 100:
|
||||||
raise ValueError(f"Title cannot be longer than 100 characters: {self.title}")
|
raise ValueError(
|
||||||
|
f"Title cannot be longer than 100 characters: {self.value}"
|
||||||
|
)
|
||||||
|
|||||||
@@ -30,15 +30,15 @@ def test_classified_ad_creation():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ad_id,
|
id=ad_id,
|
||||||
_ownerId=owner_id,
|
_ownerId=owner_id,
|
||||||
title=ClassifiedAdTitle(title="Test Title"),
|
title=ClassifiedAdTitle(value="Test Title"),
|
||||||
text=ClassifiedAdText(text="Test text content"),
|
text=ClassifiedAdText(value="Test text content"),
|
||||||
price=Price(amount=Decimal("99.99")),
|
price=Price(amount=Decimal("99.99")),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert ad.id == ad_id
|
assert ad.id == ad_id
|
||||||
assert ad._ownerId == owner_id
|
assert ad._ownerId == owner_id
|
||||||
assert ad.title == ClassifiedAdTitle(title="Test Title")
|
assert ad.title == ClassifiedAdTitle(value="Test Title")
|
||||||
assert ad.text == ClassifiedAdText(text="Test text content")
|
assert ad.text == ClassifiedAdText(value="Test text content")
|
||||||
assert ad.price == Price(amount=Decimal("99.99"))
|
assert ad.price == Price(amount=Decimal("99.99"))
|
||||||
|
|
||||||
|
|
||||||
@@ -46,8 +46,8 @@ def test_classified_ad_can_not_be_created_without_id():
|
|||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
ClassifiedAd(
|
ClassifiedAd(
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -56,8 +56,8 @@ def test_classified_ad_can_not_be_created_without_owner_id():
|
|||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
ClassifiedAd(
|
ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -69,14 +69,14 @@ def test_classified_ad_can_be_created_without_price():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ad_id,
|
id=ad_id,
|
||||||
_ownerId=owner_id,
|
_ownerId=owner_id,
|
||||||
title=ClassifiedAdTitle(title="Test Title"),
|
title=ClassifiedAdTitle(value="Test Title"),
|
||||||
text=ClassifiedAdText(text="Test text content"),
|
text=ClassifiedAdText(value="Test text content"),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert ad.id == ad_id
|
assert ad.id == ad_id
|
||||||
assert ad._ownerId == owner_id
|
assert ad._ownerId == owner_id
|
||||||
assert ad.title == ClassifiedAdTitle(title="Test Title")
|
assert ad.title == ClassifiedAdTitle(value="Test Title")
|
||||||
assert ad.text == ClassifiedAdText(text="Test text content")
|
assert ad.text == ClassifiedAdText(value="Test text content")
|
||||||
assert ad.price == Price(amount=Decimal("0.00"))
|
assert ad.price == Price(amount=Decimal("0.00"))
|
||||||
|
|
||||||
|
|
||||||
@@ -85,8 +85,8 @@ def test_classified_ad_can_not_have_negative_price():
|
|||||||
ClassifiedAd(
|
ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("-10.00")),
|
price=Price(amount=Decimal("-10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -95,8 +95,8 @@ def test_classified_ad_is_immutable():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -114,15 +114,15 @@ def test_classified_ad_equality():
|
|||||||
ad1 = ClassifiedAd(
|
ad1 = ClassifiedAd(
|
||||||
id=ad_id,
|
id=ad_id,
|
||||||
_ownerId=owner_id,
|
_ownerId=owner_id,
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
ad2 = ClassifiedAd(
|
ad2 = ClassifiedAd(
|
||||||
id=ad_id,
|
id=ad_id,
|
||||||
_ownerId=owner_id,
|
_ownerId=owner_id,
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -135,8 +135,8 @@ def test_classified_ad_title_validation():
|
|||||||
ClassifiedAd(
|
ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title=long_title),
|
title=ClassifiedAdTitle(value=long_title),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -145,8 +145,8 @@ def test_request_to_publish_sets_state_to_pending_review():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -165,8 +165,8 @@ def test_request_to_publish_fails_when_title_empty():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title=""),
|
title=ClassifiedAdTitle(value=""),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -180,8 +180,8 @@ def test_request_to_publish_fails_when_text_empty():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text=""),
|
text=ClassifiedAdText(value=""),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -195,8 +195,8 @@ def test_request_to_publish_fails_when_price_is_zero():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(InvalidEntityStateException) as exc_info:
|
with pytest.raises(InvalidEntityStateException) as exc_info:
|
||||||
@@ -209,8 +209,8 @@ def test_request_to_publish_reports_all_errors():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title=""),
|
title=ClassifiedAdTitle(value=""),
|
||||||
text=ClassifiedAdText(text=""),
|
text=ClassifiedAdText(value=""),
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(InvalidEntityStateException) as exc_info:
|
with pytest.raises(InvalidEntityStateException) as exc_info:
|
||||||
@@ -225,8 +225,8 @@ def test_request_to_publish_does_not_mutate_original():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -240,8 +240,8 @@ def test_classified_ad_is_instance_of_entity():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -252,8 +252,8 @@ def test_classified_ad_can_use_entity_apply():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
event = ClassifiedAdSentForReview(id=ad.id.value)
|
event = ClassifiedAdSentForReview(id=ad.id.value)
|
||||||
@@ -269,32 +269,32 @@ def test_set_title_updates_title():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Old Title"),
|
title=ClassifiedAdTitle(value="Old Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
new_title = ClassifiedAdTitle(title="New Title")
|
new_title = ClassifiedAdTitle(value="New Title")
|
||||||
|
|
||||||
updated = ad.set_title(new_title)
|
updated = ad.set_title(new_title)
|
||||||
|
|
||||||
assert updated.title == new_title
|
assert updated.title == new_title
|
||||||
assert ad.title == ClassifiedAdTitle(title="Old Title")
|
assert ad.title == ClassifiedAdTitle(value="Old Title")
|
||||||
|
|
||||||
|
|
||||||
def test_set_title_preserves_other_fields():
|
def test_set_title_preserves_other_fields():
|
||||||
ad_id = ClassifiedAdId(value=uuid4())
|
ad_id = ClassifiedAdId(value=uuid4())
|
||||||
owner_id = UserId(value=uuid4())
|
owner_id = UserId(value=uuid4())
|
||||||
text = ClassifiedAdText(text="Text")
|
text = ClassifiedAdText(value="Text")
|
||||||
price = Price(amount=Decimal("10.00"))
|
price = Price(amount=Decimal("10.00"))
|
||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ad_id,
|
id=ad_id,
|
||||||
_ownerId=owner_id,
|
_ownerId=owner_id,
|
||||||
title=ClassifiedAdTitle(title="Old Title"),
|
title=ClassifiedAdTitle(value="Old Title"),
|
||||||
text=text,
|
text=text,
|
||||||
price=price,
|
price=price,
|
||||||
)
|
)
|
||||||
|
|
||||||
updated = ad.set_title(ClassifiedAdTitle(title="New Title"))
|
updated = ad.set_title(ClassifiedAdTitle(value="New Title"))
|
||||||
|
|
||||||
assert updated.id == ad_id
|
assert updated.id == ad_id
|
||||||
assert updated._ownerId == owner_id
|
assert updated._ownerId == owner_id
|
||||||
@@ -306,13 +306,13 @@ def test_set_title_fails_when_new_title_empty():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(InvalidEntityStateException) as exc_info:
|
with pytest.raises(InvalidEntityStateException) as exc_info:
|
||||||
ad.set_title(ClassifiedAdTitle(title=""))
|
ad.set_title(ClassifiedAdTitle(value=""))
|
||||||
|
|
||||||
assert "title" in str(exc_info.value)
|
assert "title" in str(exc_info.value)
|
||||||
|
|
||||||
@@ -321,12 +321,12 @@ def test_set_title_raises_classified_ad_title_changed_event():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Old Title"),
|
title=ClassifiedAdTitle(value="Old Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
updated = ad.set_title(ClassifiedAdTitle(title="New Title"))
|
updated = ad.set_title(ClassifiedAdTitle(value="New Title"))
|
||||||
|
|
||||||
changes = updated.get_changes()
|
changes = updated.get_changes()
|
||||||
assert len(changes) == 2
|
assert len(changes) == 2
|
||||||
@@ -340,29 +340,29 @@ def test_update_text_updates_text():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Old text"),
|
text=ClassifiedAdText(value="Old text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
new_text = ClassifiedAdText(text="New text")
|
new_text = ClassifiedAdText(value="New text")
|
||||||
|
|
||||||
updated = ad.update_text(new_text)
|
updated = ad.update_text(new_text)
|
||||||
|
|
||||||
assert updated.text == new_text
|
assert updated.text == new_text
|
||||||
assert ad.text == ClassifiedAdText(text="Old text")
|
assert ad.text == ClassifiedAdText(value="Old text")
|
||||||
|
|
||||||
|
|
||||||
def test_update_text_fails_when_new_text_empty():
|
def test_update_text_fails_when_new_text_empty():
|
||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(InvalidEntityStateException) as exc_info:
|
with pytest.raises(InvalidEntityStateException) as exc_info:
|
||||||
ad.update_text(ClassifiedAdText(text=""))
|
ad.update_text(ClassifiedAdText(value=""))
|
||||||
|
|
||||||
assert "text" in str(exc_info.value)
|
assert "text" in str(exc_info.value)
|
||||||
|
|
||||||
@@ -371,12 +371,12 @@ def test_update_text_raises_classified_ad_text_updated_event():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Old text"),
|
text=ClassifiedAdText(value="Old text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
updated = ad.update_text(ClassifiedAdText(text="New text"))
|
updated = ad.update_text(ClassifiedAdText(value="New text"))
|
||||||
|
|
||||||
changes = updated.get_changes()
|
changes = updated.get_changes()
|
||||||
assert len(changes) == 2
|
assert len(changes) == 2
|
||||||
@@ -390,8 +390,8 @@ def test_update_price_updates_price():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
new_price = Price(amount=Decimal("25.00"))
|
new_price = Price(amount=Decimal("25.00"))
|
||||||
@@ -406,8 +406,8 @@ def test_update_price_fails_when_price_zero():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -421,8 +421,8 @@ def test_update_price_raises_classified_ad_price_updated_event():
|
|||||||
ad = ClassifiedAd(
|
ad = ClassifiedAd(
|
||||||
id=ClassifiedAdId(value=uuid4()),
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
_ownerId=UserId(value=uuid4()),
|
_ownerId=UserId(value=uuid4()),
|
||||||
title=ClassifiedAdTitle(title="Title"),
|
title=ClassifiedAdTitle(value="Title"),
|
||||||
text=ClassifiedAdText(text="Text"),
|
text=ClassifiedAdText(value="Text"),
|
||||||
price=Price(amount=Decimal("10.00")),
|
price=Price(amount=Decimal("10.00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ from domain.marketplace.classified_ad_text import ClassifiedAdText
|
|||||||
|
|
||||||
|
|
||||||
def test_classified_ad_text_creation():
|
def test_classified_ad_text_creation():
|
||||||
text = ClassifiedAdText(text="This is a classified ad description.")
|
text = ClassifiedAdText(value="This is a classified ad description.")
|
||||||
|
|
||||||
assert text.text == "This is a classified ad description."
|
assert text.value == "This is a classified ad description."
|
||||||
|
|||||||
@@ -4,10 +4,13 @@ from domain.marketplace.classified_ad_title import ClassifiedAdTitle
|
|||||||
|
|
||||||
|
|
||||||
def test_classified_ad_title_creation():
|
def test_classified_ad_title_creation():
|
||||||
title = ClassifiedAdTitle(title="Test Ad")
|
title = ClassifiedAdTitle(value="Test Ad")
|
||||||
|
|
||||||
|
assert title.value == "Test Ad"
|
||||||
|
|
||||||
assert title.title == "Test Ad"
|
|
||||||
|
|
||||||
def test_classified_ad_title_can_not_be_longer_then_100_chars():
|
def test_classified_ad_title_can_not_be_longer_then_100_chars():
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
ClassifiedAdTitle(title="efbqzzqmsjdlhkovmnmyykrjvlftrzzaefbqzzqmsjdlhkovmnmyykrjvlftrzzaefbqzzqmsjdlhkovmnmyykrjvlftrzzaefbqzzqmsjdlhkovmnmyykrjvlftrzzaefbqzzqmsjdlhkovmnmyykrjvlftrzza")
|
ClassifiedAdTitle(
|
||||||
|
value="efbqzzqmsjdlhkovmnmyykrjvlftrzzaefbqzzqmsjdlhkovmnmyykrjvlftrzzaefbqzzqmsjdlhkovmnmyykrjvlftrzzaefbqzzqmsjdlhkovmnmyykrjvlftrzzaefbqzzqmsjdlhkovmnmyykrjvlftrzza"
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user