from decimal import Decimal from uuid import uuid4 import pytest from domain.framework.entity import Entity from domain.marketplace.classified_ad import ClassifiedAd from domain.marketplace.classified_ad_id import ClassifiedAdId from domain.marketplace.classified_ad_state import ClassifiedAdState from domain.marketplace.classified_ad_text import ClassifiedAdText from domain.marketplace.classified_ad_title import ClassifiedAdTitle from domain.marketplace.domain_event import ( ClassifiedAdCreated, ClassifiedAdPriceUpdated, ClassifiedAdSentForReview, ClassifiedAdTextUpdated, ClassifiedAdTitleChanged, ) from domain.marketplace.invalid_entity_state_exception import ( InvalidEntityStateException, ) from domain.marketplace.price import Price from domain.marketplace.user_id import UserId def test_classified_ad_creation(): ad_id = ClassifiedAdId(value=uuid4()) owner_id = UserId(value=uuid4()) ad = ClassifiedAd( id=ad_id, _ownerId=owner_id, title=ClassifiedAdTitle(title="Test Title"), text=ClassifiedAdText(text="Test text content"), price=Price(amount=Decimal("99.99")), ) assert ad.id == ad_id assert ad._ownerId == owner_id assert ad.title == ClassifiedAdTitle(title="Test Title") assert ad.text == ClassifiedAdText(text="Test text content") assert ad.price == Price(amount=Decimal("99.99")) def test_classified_ad_can_not_be_created_without_id(): with pytest.raises(TypeError): ClassifiedAd( _ownerId=UserId(value=uuid4()), title=ClassifiedAdTitle(title="Title"), text=ClassifiedAdText(text="Text"), price=Price(amount=Decimal("10.00")), ) def test_classified_ad_can_not_be_created_without_owner_id(): with pytest.raises(TypeError): ClassifiedAd( id=ClassifiedAdId(value=uuid4()), title=ClassifiedAdTitle(title="Title"), text=ClassifiedAdText(text="Text"), price=Price(amount=Decimal("10.00")), ) def test_classified_ad_can_be_created_without_price(): ad_id = ClassifiedAdId(value=uuid4()) owner_id = UserId(value=uuid4()) ad = ClassifiedAd( id=ad_id, _ownerId=owner_id, title=ClassifiedAdTitle(title="Test Title"), text=ClassifiedAdText(text="Test text content"), ) assert ad.id == ad_id assert ad._ownerId == owner_id assert ad.title == ClassifiedAdTitle(title="Test Title") assert ad.text == ClassifiedAdText(text="Test text content") assert ad.price == Price(amount=Decimal("0.00")) def test_classified_ad_can_not_have_negative_price(): with pytest.raises(ValueError): ClassifiedAd( id=ClassifiedAdId(value=uuid4()), _ownerId=UserId(value=uuid4()), title=ClassifiedAdTitle(title="Title"), text=ClassifiedAdText(text="Text"), price=Price(amount=Decimal("-10.00")), ) def test_classified_ad_is_immutable(): ad = ClassifiedAd( id=ClassifiedAdId(value=uuid4()), _ownerId=UserId(value=uuid4()), title=ClassifiedAdTitle(title="Title"), text=ClassifiedAdText(text="Text"), price=Price(amount=Decimal("10.00")), ) try: ad.title = "New Title" # type: ignore assert False, "Should have raised FrozenInstanceError" except AttributeError: pass def test_classified_ad_equality(): ad_id = ClassifiedAdId(value=uuid4()) owner_id = UserId(value=uuid4()) ad1 = ClassifiedAd( id=ad_id, _ownerId=owner_id, title=ClassifiedAdTitle(title="Title"), text=ClassifiedAdText(text="Text"), price=Price(amount=Decimal("10.00")), ) ad2 = ClassifiedAd( id=ad_id, _ownerId=owner_id, title=ClassifiedAdTitle(title="Title"), text=ClassifiedAdText(text="Text"), price=Price(amount=Decimal("10.00")), ) assert ad1 == ad2 def test_classified_ad_title_validation(): long_title = "a" * 101 with pytest.raises(ValueError): ClassifiedAd( id=ClassifiedAdId(value=uuid4()), _ownerId=UserId(value=uuid4()), title=ClassifiedAdTitle(title=long_title), text=ClassifiedAdText(text="Text"), price=Price(amount=Decimal("10.00")), ) def test_request_to_publish_sets_state_to_pending_review(): ad = ClassifiedAd( id=ClassifiedAdId(value=uuid4()), _ownerId=UserId(value=uuid4()), title=ClassifiedAdTitle(title="Title"), text=ClassifiedAdText(text="Text"), price=Price(amount=Decimal("10.00")), ) published = ad.request_to_publish() assert published.state == ClassifiedAdState.PendingReview changes = published.get_changes() assert len(changes) == 2 assert isinstance(changes[0], ClassifiedAdCreated) assert isinstance(changes[1], ClassifiedAdSentForReview) assert changes[1].id == ad.id.value def test_request_to_publish_fails_when_title_empty(): ad = ClassifiedAd( id=ClassifiedAdId(value=uuid4()), _ownerId=UserId(value=uuid4()), title=ClassifiedAdTitle(title=""), text=ClassifiedAdText(text="Text"), price=Price(amount=Decimal("10.00")), ) with pytest.raises(InvalidEntityStateException) as exc_info: ad.request_to_publish() assert "title" in str(exc_info.value) def test_request_to_publish_fails_when_text_empty(): ad = ClassifiedAd( id=ClassifiedAdId(value=uuid4()), _ownerId=UserId(value=uuid4()), title=ClassifiedAdTitle(title="Title"), text=ClassifiedAdText(text=""), price=Price(amount=Decimal("10.00")), ) with pytest.raises(InvalidEntityStateException) as exc_info: ad.request_to_publish() assert "text" in str(exc_info.value) def test_request_to_publish_fails_when_price_is_zero(): ad = ClassifiedAd( id=ClassifiedAdId(value=uuid4()), _ownerId=UserId(value=uuid4()), title=ClassifiedAdTitle(title="Title"), text=ClassifiedAdText(text="Text"), ) with pytest.raises(InvalidEntityStateException) as exc_info: ad.request_to_publish() assert "price" in str(exc_info.value) def test_request_to_publish_reports_all_errors(): ad = ClassifiedAd( id=ClassifiedAdId(value=uuid4()), _ownerId=UserId(value=uuid4()), title=ClassifiedAdTitle(title=""), text=ClassifiedAdText(text=""), ) with pytest.raises(InvalidEntityStateException) as exc_info: ad.request_to_publish() assert "title" in str(exc_info.value) assert "text" in str(exc_info.value) assert "price" in str(exc_info.value) def test_request_to_publish_does_not_mutate_original(): ad = ClassifiedAd( id=ClassifiedAdId(value=uuid4()), _ownerId=UserId(value=uuid4()), title=ClassifiedAdTitle(title="Title"), text=ClassifiedAdText(text="Text"), price=Price(amount=Decimal("10.00")), ) ad.request_to_publish() assert ad.state == ClassifiedAdState.Inactive assert ad is not ad.request_to_publish() 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_apply_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.apply_event(event) assert updated_ad.get_changes() == (ad.get_changes()[0], event) assert len(ad.get_changes()) == 1 assert isinstance(ad.get_changes()[0], ClassifiedAdCreated) 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) == 2 assert isinstance(changes[0], ClassifiedAdCreated) assert isinstance(changes[1], ClassifiedAdTitleChanged) assert changes[1].id == ad.id.value assert changes[1].title == "New Title" def test_update_text_updates_text(): ad = ClassifiedAd( id=ClassifiedAdId(value=uuid4()), _ownerId=UserId(value=uuid4()), title=ClassifiedAdTitle(title="Title"), text=ClassifiedAdText(text="Old text"), price=Price(amount=Decimal("10.00")), ) new_text = ClassifiedAdText(text="New text") updated = ad.update_text(new_text) assert updated.text == new_text assert ad.text == ClassifiedAdText(text="Old text") def test_update_text_fails_when_new_text_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.update_text(ClassifiedAdText(text="")) assert "text" in str(exc_info.value) def test_update_text_raises_classified_ad_text_updated_event(): ad = ClassifiedAd( id=ClassifiedAdId(value=uuid4()), _ownerId=UserId(value=uuid4()), title=ClassifiedAdTitle(title="Title"), text=ClassifiedAdText(text="Old text"), price=Price(amount=Decimal("10.00")), ) updated = ad.update_text(ClassifiedAdText(text="New text")) changes = updated.get_changes() assert len(changes) == 2 assert isinstance(changes[0], ClassifiedAdCreated) assert isinstance(changes[1], ClassifiedAdTextUpdated) assert changes[1].id == ad.id.value assert changes[1].ad_text == "New text" def test_update_price_updates_price(): ad = ClassifiedAd( id=ClassifiedAdId(value=uuid4()), _ownerId=UserId(value=uuid4()), title=ClassifiedAdTitle(title="Title"), text=ClassifiedAdText(text="Text"), price=Price(amount=Decimal("10.00")), ) new_price = Price(amount=Decimal("25.00")) updated = ad.update_price(new_price) assert updated.price == new_price assert ad.price == Price(amount=Decimal("10.00")) def test_update_price_fails_when_price_zero(): 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.update_price(Price(amount=Decimal("0.00"))) assert "price" in str(exc_info.value) def test_update_price_raises_classified_ad_price_updated_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")), ) updated = ad.update_price(Price(amount=Decimal("25.00"))) changes = updated.get_changes() assert len(changes) == 2 assert isinstance(changes[0], ClassifiedAdCreated) assert isinstance(changes[1], ClassifiedAdPriceUpdated) assert changes[1].id == ad.id.value assert changes[1].price == Decimal("25.00")