class InvalidEntityException(AbidriaException):
    pass class EntityDoesNotExistException(AbidriaException):

    def __init__(self):
        super().__init__(source='entity', code='not_found', message='Entity not found') class NoLoggedException(AbidriaException):

    def __init__(self):
        super().__init__(source='authentication', code='required', message='Authentication required') class NoPermissionException(AbidriaException):

    def __init__(self):
        super().__init__(source='permission', code='denied', message='Permission denied') class ConflictException(AbidriaException):
    pass class Experience:

    def __init__(self, title, description, author_id,
                 author_username=None, id=None, picture=None, is_mine=False, is_saved=False):
        self._id = id
        self._title = title
        self._description = description
        self._picture = picture
        self._author_id = author_id
        self._author_username = author_username
        self._is_mine = is_mine
        self._is_saved = is_saved

    @property
    def id(self):
        return self._id

    @property
    def title(self):
        return self._title

    @property
    def description(self):
        return self._description

    @property
    def picture(self):
        return self._picture

    @property
    def author_id(self):
        return self._author_id

    @property
    def author_username(self):
        return self._author_username

    @property
    def is_mine(self):
        return self._is_mine

    @property
    def is_saved(self):
        return self._is_saved

    def builder(self):
        return Experience.Builder(self)

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

    class Builder:

        def __init__(self, experience):
            self._id = experience.id
            self._title = experience.title
            self._description = experience.description
            self._picture = experience.picture
            self._author_id = experience.author_id
            self._author_username = experience.author_username
            self._is_mine = experience.is_mine
            self._is_saved = experience.is_saved

        def id(self, id):
            self._id = id
            return self

        def title(self, title):
            self._title = title
            return self

        def description(self, description):
            self._description = description
            return self

        def picture(self, picture):
            self._picture = picture
            return self

        def author_id(self, author_id):
            self._author_id = author_id
            return self

        def author_username(self, author_username):
            self._author_username = author_username
            return self

        def is_mine(self, is_mine):
            self._is_mine = is_mine
            return self

        def is_saved(self, is_saved):
            self._is_saved = is_saved
            return self

        def build(self):
            return Experience(id=self._id, title=self._title, description=self._description,
                              picture=self._picture, author_id=self._author_id,
                              author_username=self._author_username, is_mine=self._is_mine,
                              is_saved=self._is_saved) class GetAllExperiencesInteractor:

    def __init__(self, experience_repo, permissions_validator):
        self.experience_repo = experience_repo
        self.permissions_validator = permissions_validator

    def set_params(self, mine, saved, logged_person_id):
        self.mine = mine
        self.saved = saved
        self.logged_person_id = logged_person_id
        return self

    def execute(self):
        self.permissions_validator.validate_permissions(logged_person_id=self.logged_person_id)

        return self.experience_repo.get_all_experiences(mine=self.mine, saved=self.saved,
                                                        logged_person_id=self.logged_person_id) class CreateNewExperienceInteractor:

    def __init__(self, experience_repo, experience_validator, permissions_validator):
        self.experience_repo = experience_repo
        self.experience_validator = experience_validator
        self.permissions_validator = permissions_validator

    def set_params(self, title, description, logged_person_id):
        self.title = title
        self.description = description
        self.logged_person_id = logged_person_id
        return self

    def execute(self):
        self.permissions_validator.validate_permissions(logged_person_id=self.logged_person_id,
                                                        wants_to_create_content=True)
        experience = Experience(title=self.title, description=self.description, author_id=self.logged_person_id)
        self.experience_validator.validate_experience(experience)
        return self.experience_repo.create_experience(experience) class ModifyExperienceInteractor:

    def __init__(self, experience_repo, experience_validator, permissions_validator):
        self.experience_repo = experience_repo
        self.experience_validator = experience_validator
        self.permissions_validator = permissions_validator

    def set_params(self, id, title, description, logged_person_id):
        self.id = id
        self.title = title
        self.description = description
        self.logged_person_id = logged_person_id
        return self

    def execute(self):
        self.permissions_validator.validate_permissions(logged_person_id=self.logged_person_id,
                                                        has_permissions_to_modify_experience=self.id)
        experience = self.experience_repo.get_experience(id=self.id, logged_person_id=self.logged_person_id)

        new_title = self.title if self.title is not None else experience.title
        new_description = self.description if self.description is not None else experience.description
        updated_experience = experience.builder().title(new_title).description(new_description).build()

        self.experience_validator.validate_experience(updated_experience)

        return self.experience_repo.update_experience(updated_experience) class UploadExperiencePictureInteractor:

    def __init__(self, experience_repo, permissions_validator):
        self.experience_repo = experience_repo
        self.permissions_validator = permissions_validator

    def set_params(self, experience_id, picture, logged_person_id):
        self.experience_id = experience_id
        self.picture = picture
        self.logged_person_id = logged_person_id
        return self

    def execute(self):
        self.permissions_validator.validate_permissions(logged_person_id=self.logged_person_id,
                                                        has_permissions_to_modify_experience=self.experience_id)
        return self.experience_repo.attach_picture_to_experience(experience_id=self.experience_id, picture=self.picture) class SaveUnsaveExperienceInteractor:

    class Action(Enum):
        SAVE = 1
        UNSAVE = 2

    def __init__(self, experience_repo, permissions_validator):
        self.experience_repo = experience_repo
        self.permissions_validator = permissions_validator

    def set_params(self, action, experience_id, logged_person_id):
        self.action = action
        self.experience_id = experience_id
        self.logged_person_id = logged_person_id
        return self

    def execute(self):
        self.permissions_validator.validate_permissions(logged_person_id=self.logged_person_id)

        experience = self.experience_repo.get_experience(id=self.experience_id)
        if experience.author_id == self.logged_person_id:
            raise ConflictException(source='experience', code='self_save',
                                    message='You cannot save your own experiences')

        if self.action is SaveUnsaveExperienceInteractor.Action.SAVE:
            self.experience_repo.save_experience(person_id=self.logged_person_id, experience_id=self.experience_id)
        elif self.action is SaveUnsaveExperienceInteractor.Action.UNSAVE:
            self.experience_repo.unsave_experience(person_id=self.logged_person_id, experience_id=self.experience_id)

        return True
from mock import Mock
from abidria.exceptions import InvalidEntityException, EntityDoesNotExistException, NoLoggedException, \
        NoPermissionException, ConflictException
from experiences.entities import Experience
from experiences.interactors import GetAllExperiencesInteractor, CreateNewExperienceInteractor, \
        ModifyExperienceInteractor, UploadExperiencePictureInteractor, SaveUnsaveExperienceInteractor
        def given_an_experience_repo_that_returns_that_experience_on_get(self):
            self.experience_repo = Mock()
            self.experience_repo.get_experience.return_value = self.experience
            return self

        def given_an_experience_repo_that_raises_entity_does_not_exist(self):
            self.experience_repo = Mock()
            self.experience_repo.get_experience.side_effect = EntityDoesNotExistException()
            return self

        def given_an_experience_validator(self):
            self.experience_validator = Mock()
            return self

        def given_an_experience_validator_that_accepts(self):
            self.experience_validator = Mock()
            self.experience_validator.validate_experience.return_value = True
            return self

        def given_an_experience_validator_that_raises_invalid_entity_exception(self):
            self.experience_validator = Mock()
            self.experience_validator.validate_experience.side_effect = \
                InvalidEntityException(source='title', code='empty_attribute',
                                       message='Title must be between 1 and 20 chars')
            return self

        def when_interactor_is_executed(self):
            try:
                self.result = ModifyExperienceInteractor(self.experience_repo, self.experience_validator,
                                                         self.permissions_validator) \
                    .set_params(id=self.id, title=None, description=self.description,
                                logged_person_id=self.logged_person_id).execute()
            except Exception as e:
                print(e)
                self.error = e
            return self

        def then_result_should_be_second_experience(self):
            assert self.result == self.updated_experience
            return self

        def then_get_experience_should_be_called_with_id_and_logged_person_id(self):
            self.experience_repo.get_experience \
                    .assert_called_once_with(id=self.id, logged_person_id=self.logged_person_id)
            return self

        def then_experience_validation_should_be_called_with_updated_experience(self):
            self.experience_validator.validate_experience.assert_called_once_with(self.updated_experience)
            return self

        def then_update_experience_should_be_called_with_updated_experience(self):
            self.experience_validator.validate_experience.assert_called_once_with(self.updated_experience)
            return self

        def then_update_experience_should_be_not_called(self):
            self.experience_repo.updated_experience.assert_not_called()
            return self

        def then_should_raise_invalid_entity_exception(self):
            assert type(self.error) is InvalidEntityException
            assert self.error.source == 'title'
            assert self.error.code == 'empty_attribute'
            assert str(self.error) == 'Title must be between 1 and 20 chars'
            return self

        def then_should_raise_entity_does_not_exists(self):
            assert type(self.error) is EntityDoesNotExistException
            return self

        def then_should_validate_permissions(self):
            self.permissions_validator.validate_permissions \
                    .assert_called_once_with(logged_person_id=self.logged_person_id,
                                             has_permissions_to_modify_experience=self.id)
            return self

        def then_should_raise_no_permissions_exception(self):
            assert type(self.error) is NoPermissionException
            return self


class TestUploadExperiencePictureInteractor:

    def test_validates_permissions_and_attach_picture_to_experience(self):
        TestUploadExperiencePictureInteractor.ScenarioMaker() \
                .given_a_logged_person_id() \
                .given_a_permissions_validator_that_returns_true() \
                .given_an_experience() \
                .given_an_experience_repo_that_returns_that_experience_on_attach() \
                .given_an_experience_id() \
                .given_a_picture() \
                .when_interactor_is_executed() \
                .then_should_validate_permissions() \
                .then_should_call_repo_attach_picture_to_experience() \
                .then_should_return_experience()

    def test_invalid_permissions_doesnt_attach_picture(self):
        TestUploadExperiencePictureInteractor.ScenarioMaker() \
                .given_a_logged_person_id() \
                .given_a_permissions_validator_that_raises_no_permissions_exception() \
                .given_an_experience_repo() \
                .given_an_experience_id() \
                .given_a_picture() \
                .when_interactor_is_executed() \
                .then_should_validate_permissions() \
                .then_should_not_call_repo_attach_picture_to_experience() \
                .then_should_raise_no_permissions_exception()

    class ScenarioMaker:

        def given_a_logged_person_id(self):
            self.logged_person_id = '9'
            return self

        def given_a_permissions_validator_that_returns_true(self):
            self.permissions_validator = Mock()
            self.permissions_validator.validate_permissions.return_value = True
            return self

        def given_a_permissions_validator_that_raises_no_permissions_exception(self):
            self.permissions_validator = Mock()

            self.permissions_validator.validate_permissions.side_effect = NoPermissionException