feat: add SetTitle to ClassifiedAd with domain events, extract EnsureValidState
This commit is contained in:
@@ -1,10 +1,17 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from domain.framework.entity import Entity
|
||||||
from domain.marketplace.classified_ad_id import ClassifiedAdId
|
from domain.marketplace.classified_ad_id import ClassifiedAdId
|
||||||
from domain.marketplace.classified_ad_state import ClassifiedAdState
|
from domain.marketplace.classified_ad_state import ClassifiedAdState
|
||||||
from domain.marketplace.classified_ad_text import ClassifiedAdText
|
from domain.marketplace.classified_ad_text import ClassifiedAdText
|
||||||
from domain.marketplace.classified_ad_title import ClassifiedAdTitle
|
from domain.marketplace.classified_ad_title import ClassifiedAdTitle
|
||||||
|
from domain.marketplace.domain_event import (
|
||||||
|
ClassifiedAdTitleChanged,
|
||||||
|
DomainEvent,
|
||||||
|
)
|
||||||
from domain.marketplace.invalid_entity_state_exception import (
|
from domain.marketplace.invalid_entity_state_exception import (
|
||||||
InvalidEntityStateException,
|
InvalidEntityStateException,
|
||||||
)
|
)
|
||||||
@@ -13,15 +20,16 @@ from domain.marketplace.user_id import UserId
|
|||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ClassifiedAd:
|
class ClassifiedAd(Entity):
|
||||||
id: ClassifiedAdId
|
id: ClassifiedAdId
|
||||||
_ownerId: UserId
|
_ownerId: UserId
|
||||||
title: ClassifiedAdTitle
|
title: ClassifiedAdTitle
|
||||||
text: ClassifiedAdText
|
text: ClassifiedAdText
|
||||||
price: Price = field(default_factory=lambda: Price(amount=Decimal("0.00")))
|
price: Price = field(default_factory=lambda: Price(amount=Decimal("0.00")))
|
||||||
state: ClassifiedAdState | None = None
|
state: ClassifiedAdState | None = None
|
||||||
|
_events: tuple[DomainEvent, ...] = ()
|
||||||
|
|
||||||
def request_to_publish(self):
|
def _ensure_valid_state(self):
|
||||||
errors = []
|
errors = []
|
||||||
if not self.title.title:
|
if not self.title.title:
|
||||||
errors.append("title")
|
errors.append("title")
|
||||||
@@ -35,6 +43,9 @@ class ClassifiedAd:
|
|||||||
f"Cannot publish classified ad with missing or invalid: {', '.join(errors)}"
|
f"Cannot publish classified ad with missing or invalid: {', '.join(errors)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def request_to_publish(self):
|
||||||
|
self._ensure_valid_state()
|
||||||
|
|
||||||
return ClassifiedAd(
|
return ClassifiedAd(
|
||||||
id=self.id,
|
id=self.id,
|
||||||
_ownerId=self._ownerId,
|
_ownerId=self._ownerId,
|
||||||
@@ -42,4 +53,20 @@ class ClassifiedAd:
|
|||||||
text=self.text,
|
text=self.text,
|
||||||
price=self.price,
|
price=self.price,
|
||||||
state=ClassifiedAdState.PendingReview,
|
state=ClassifiedAdState.PendingReview,
|
||||||
|
_events=self._events,
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_title(self, title: ClassifiedAdTitle) -> ClassifiedAd:
|
||||||
|
updated = ClassifiedAd(
|
||||||
|
id=self.id,
|
||||||
|
_ownerId=self._ownerId,
|
||||||
|
title=title,
|
||||||
|
text=self.text,
|
||||||
|
price=self.price,
|
||||||
|
state=self.state,
|
||||||
|
_events=self._events,
|
||||||
|
)
|
||||||
|
updated._ensure_valid_state()
|
||||||
|
return updated.raise_event(
|
||||||
|
ClassifiedAdTitleChanged(id=self.id.value, title=title.title)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,11 +3,16 @@ from uuid import uuid4
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from domain.framework.entity import Entity
|
||||||
from domain.marketplace.classified_ad import ClassifiedAd
|
from domain.marketplace.classified_ad import ClassifiedAd
|
||||||
from domain.marketplace.classified_ad_id import ClassifiedAdId
|
from domain.marketplace.classified_ad_id import ClassifiedAdId
|
||||||
from domain.marketplace.classified_ad_state import ClassifiedAdState
|
from domain.marketplace.classified_ad_state import ClassifiedAdState
|
||||||
from domain.marketplace.classified_ad_text import ClassifiedAdText
|
from domain.marketplace.classified_ad_text import ClassifiedAdText
|
||||||
from domain.marketplace.classified_ad_title import ClassifiedAdTitle
|
from domain.marketplace.classified_ad_title import ClassifiedAdTitle
|
||||||
|
from domain.marketplace.domain_event import (
|
||||||
|
ClassifiedAdSentForReview,
|
||||||
|
ClassifiedAdTitleChanged,
|
||||||
|
)
|
||||||
from domain.marketplace.invalid_entity_state_exception import (
|
from domain.marketplace.invalid_entity_state_exception import (
|
||||||
InvalidEntityStateException,
|
InvalidEntityStateException,
|
||||||
)
|
)
|
||||||
@@ -219,3 +224,102 @@ def test_request_to_publish_does_not_mutate_original():
|
|||||||
ad.request_to_publish()
|
ad.request_to_publish()
|
||||||
|
|
||||||
assert ad.state is None
|
assert ad.state is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_classified_ad_is_instance_of_entity():
|
||||||
|
ad = ClassifiedAd(
|
||||||
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
|
_ownerId=UserId(value=uuid4()),
|
||||||
|
title=ClassifiedAdTitle(title="Title"),
|
||||||
|
text=ClassifiedAdText(text="Text"),
|
||||||
|
price=Price(amount=Decimal("10.00")),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert isinstance(ad, Entity)
|
||||||
|
|
||||||
|
|
||||||
|
def test_classified_ad_can_use_entity_raise_event():
|
||||||
|
ad = ClassifiedAd(
|
||||||
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
|
_ownerId=UserId(value=uuid4()),
|
||||||
|
title=ClassifiedAdTitle(title="Title"),
|
||||||
|
text=ClassifiedAdText(text="Text"),
|
||||||
|
price=Price(amount=Decimal("10.00")),
|
||||||
|
)
|
||||||
|
event = ClassifiedAdSentForReview(id=ad.id.value)
|
||||||
|
|
||||||
|
updated_ad = ad.raise_event(event)
|
||||||
|
|
||||||
|
assert updated_ad.get_changes() == (event,)
|
||||||
|
assert ad.get_changes() == ()
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_title_updates_title():
|
||||||
|
ad = ClassifiedAd(
|
||||||
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
|
_ownerId=UserId(value=uuid4()),
|
||||||
|
title=ClassifiedAdTitle(title="Old Title"),
|
||||||
|
text=ClassifiedAdText(text="Text"),
|
||||||
|
price=Price(amount=Decimal("10.00")),
|
||||||
|
)
|
||||||
|
new_title = ClassifiedAdTitle(title="New Title")
|
||||||
|
|
||||||
|
updated = ad.set_title(new_title)
|
||||||
|
|
||||||
|
assert updated.title == new_title
|
||||||
|
assert ad.title == ClassifiedAdTitle(title="Old Title")
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_title_preserves_other_fields():
|
||||||
|
ad_id = ClassifiedAdId(value=uuid4())
|
||||||
|
owner_id = UserId(value=uuid4())
|
||||||
|
text = ClassifiedAdText(text="Text")
|
||||||
|
price = Price(amount=Decimal("10.00"))
|
||||||
|
ad = ClassifiedAd(
|
||||||
|
id=ad_id,
|
||||||
|
_ownerId=owner_id,
|
||||||
|
title=ClassifiedAdTitle(title="Old Title"),
|
||||||
|
text=text,
|
||||||
|
price=price,
|
||||||
|
)
|
||||||
|
|
||||||
|
updated = ad.set_title(ClassifiedAdTitle(title="New Title"))
|
||||||
|
|
||||||
|
assert updated.id == ad_id
|
||||||
|
assert updated._ownerId == owner_id
|
||||||
|
assert updated.text == text
|
||||||
|
assert updated.price == price
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_title_fails_when_new_title_empty():
|
||||||
|
ad = ClassifiedAd(
|
||||||
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
|
_ownerId=UserId(value=uuid4()),
|
||||||
|
title=ClassifiedAdTitle(title="Title"),
|
||||||
|
text=ClassifiedAdText(text="Text"),
|
||||||
|
price=Price(amount=Decimal("10.00")),
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(InvalidEntityStateException) as exc_info:
|
||||||
|
ad.set_title(ClassifiedAdTitle(title=""))
|
||||||
|
|
||||||
|
assert "title" in str(exc_info.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_title_raises_classified_ad_title_changed_event():
|
||||||
|
ad = ClassifiedAd(
|
||||||
|
id=ClassifiedAdId(value=uuid4()),
|
||||||
|
_ownerId=UserId(value=uuid4()),
|
||||||
|
title=ClassifiedAdTitle(title="Old Title"),
|
||||||
|
text=ClassifiedAdText(text="Text"),
|
||||||
|
price=Price(amount=Decimal("10.00")),
|
||||||
|
)
|
||||||
|
|
||||||
|
updated = ad.set_title(ClassifiedAdTitle(title="New Title"))
|
||||||
|
|
||||||
|
changes = updated.get_changes()
|
||||||
|
assert len(changes) == 1
|
||||||
|
event = changes[0]
|
||||||
|
assert isinstance(event, ClassifiedAdTitleChanged)
|
||||||
|
assert event.id == ad.id.value
|
||||||
|
assert event.title == "New Title"
|
||||||
|
|||||||
Reference in New Issue
Block a user