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

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

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

    def __init__(self, id=None, is_registered=False, username=None, email=None, is_email_confirmed=None):
        self._id = id
        self._is_registered = is_registered
        self._username = username
        self._email = email
        self._is_email_confirmed = is_email_confirmed

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

    @property
    def is_registered(self):
        return self._is_registered

    @property
    def username(self):
        return self._username

    @property
    def email(self):
        return self._email

    @property
    def is_email_confirmed(self):
        return self._is_email_confirmed

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

    def __ne__(self, other):
        return not self == other class AuthToken:

    def __init__(self, person_id, access_token, refresh_token):
        self._person_id = person_id
        self._access_token = access_token
        self._refresh_token = refresh_token

    @property
    def person_id(self):
        return self._person_id

    @property
    def access_token(self):
        return self._access_token

    @property
    def refresh_token(self):
        return self._refresh_token

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

    def __ne__(self, other):
        return not self == other class CreateGuestPersonAndReturnAuthTokenInteractor:

    def __init__(self, client_secret_key_validator, person_repo, auth_token_repo):
        self.client_secret_key_validator = client_secret_key_validator
        self.person_repo = person_repo
        self.auth_token_repo = auth_token_repo

    def set_params(self, client_secret_key):
        self.client_secret_key = client_secret_key
        return self

    def execute(self):
        self.client_secret_key_validator.validate(client_secret_key=self.client_secret_key)

        created_guest_person = self.person_repo.create_guest_person()

        return self.auth_token_repo.create_auth_token(person_id=created_guest_person.id) class AuthenticateInteractor:

    def __init__(self, auth_token_repo):
        self.auth_token_repo = auth_token_repo

    def set_params(self, access_token):
        self.access_token = access_token
        return self

    def execute(self):
        try:
            auth_token = self.auth_token_repo.get_auth_token(access_token=self.access_token)
            return auth_token.person_id
        except EntityDoesNotExistException:
            return None class RegisterUsernameAndEmailInteractor:

    def __init__(self, person_validator, person_repo, confirmation_token_repo, mailer_service):
        self.person_validator = person_validator
        self.person_repo = person_repo
        self.confirmation_token_repo = confirmation_token_repo
        self.mailer_service = mailer_service

    def set_params(self, logged_person_id, username, email):
        self.logged_person_id = logged_person_id
        self.username = username
        self.email = email
        return self

    def execute(self):
        if self.logged_person_id is None:
            raise NoLoggedException()

        person = self.person_repo.get_person(id=self.logged_person_id)
        if person.is_email_confirmed:
            raise ConflictException(source='person', code='already_registered', message='Person already registered')

        updated_person = Person(id=person.id, is_registered=True,
                                username=self.username, email=self.email, is_email_confirmed=False)
        self.person_validator.validate(updated_person)
        updated_person = self.person_repo.update_person(updated_person)

        self.confirmation_token_repo.delete_confirmation_tokens(person_id=updated_person.id)
        confirmation_token = self.confirmation_token_repo.create_confirmation_token(person_id=updated_person.id)
        self.mailer_service.send_ask_confirmation_mail(confirmation_token=confirmation_token,
                                                       username=updated_person.username, email=updated_person.email)
        return updated_person class ConfirmEmailInteractor:

    def __init__(self, person_repo, confirmation_token_repo):
        self.person_repo = person_repo
        self.confirmation_token_repo = confirmation_token_repo

    def set_params(self, logged_person_id, confirmation_token):
        self.logged_person_id = logged_person_id
        self.confirmation_token = confirmation_token
        return self

    def execute(self):
        if self.logged_person_id is None:
            raise NoLoggedException()

        try:
            person_id = self.confirmation_token_repo.get_person_id(confirmation_token=self.confirmation_token)
        except EntityDoesNotExistException:
            raise InvalidEntityException(source='confirmation_token', code='invalid',
                                         message='Invalid confirmation token')

        if person_id != self.logged_person_id:
            raise InvalidEntityException(source='confirmation_token', code='invalid',
                                         message='Invalid confirmation token')

        self.confirmation_token_repo.delete_confirmation_tokens(person_id=person_id)

        person = self.person_repo.get_person(id=self.logged_person_id)
        updated_person = Person(id=person.id, is_registered=person.is_registered,
                                username=person.username, email=person.email,
                                is_email_confirmed=True)
        updated_person = self.person_repo.update_person(updated_person)

        return updated_person
from mock import Mock
from abidria.exceptions import InvalidEntityException, EntityDoesNotExistException, ConflictException, \
        NoLoggedException
from people.entities import Person, AuthToken
from people.interactors import CreateGuestPersonAndReturnAuthTokenInteractor, AuthenticateInteractor, \
        RegisterUsernameAndEmailInteractor, ConfirmEmailInteractor
                    .assert_called_once_with(person_id=self.second_person.id)
            return self

        def then_should_send_email_with_confirmation_token(self):
            self.mailer_service.send_ask_confirmation_mail.assert_called_once_with(
                    confirmation_token=self.confirmation_token,
                    username=self.second_person.username,
                    email=self.second_person.email)
            return self

        def then_result_should_be_second_person(self):
            assert self.result == self.second_person
            return self

        def then_should_not_call_repo_update(self):
            self.person_repo.update_person.assert_not_called()
            return self

        def then_should_not_delete_previous_person_confirmation_tokens(self):
            self.confirmation_token_repo.delete_confirmation_tokens.assert_not_called()
            return self

        def then_should_not_create_confirmation_token(self):
            self.confirmation_token_repo.create_confirmation_token.assert_not_called()
            return self

        def then_should_not_send_email_with_confirmation_token(self):
            self.mailer_service.send_ask_confirmation_mail.assert_not_called()
            return self

        def then_should_raise_invalid_entity_exception(self):
            assert type(self.error) is InvalidEntityException
            assert self.error.source == 'username'
            assert self.error.code == 'already_used'
            assert str(self.error) == 'Username already used'
            return self

        def then_should_raise_conflict_exception(self):
            assert type(self.error) is ConflictException
            assert self.error.source == 'person'
            assert self.error.code == 'already_registered'
            assert str(self.error) == 'Person already registered'
            return self

        def then_should_raise_unauthorized_exception(self):
            assert type(self.error) is NoLoggedException
            return self


class TestConfirmEmailInteractor:

    def test_confirm_email_returns_person_confirmed(self):
        TestConfirmEmailInteractor.ScenarioMaker() \
                .given_a_logged_person_id() \
                .given_a_confirmation_token() \
                .given_a_confirmation_token_repo_that_returns_that_confirmation_token() \
                .given_an_updated_person() \
                .given_a_person() \
                .given_a_person_repo_that_returns_those_persons_on_get_and_update() \
                .when_confirm_email_interactor_is_executed() \
                .then_should_call_confirmation_token_repo_get_person_id_with_confirmation_token() \
                .then_should_delete_all_confirmation_tokens_for_that_person() \
                .then_should_call_person_repo_get() \
                .then_should_call_person_repo_update_with_is_email_confirmed_true() \
                .then_should_return_person_confirmed()

    def test_unauthenticated_raises_unauthorized(self):
        TestConfirmEmailInteractor.ScenarioMaker() \
                .when_confirm_email_interactor_is_executed() \
                .then_should_raise_unauthorized() \
                .then_should_not_delete_all_confirmation_tokens_for_that_person() \
                .then_should_not_call_person_repo_update()

    def test_no_confirmation_token_raises_unauthorized(self):
        TestConfirmEmailInteractor.ScenarioMaker() \
                .given_a_logged_person_id() \
                .given_a_confirmation_token_repo_that_raises_entity_does_not_exist() \
                .when_confirm_email_interactor_is_executed() \
                .then_should_raise_invalid_params_for_wrong_confirmation_token() \
                .then_should_not_delete_all_confirmation_tokens_for_that_person() \
                .then_should_not_call_person_repo_update()

    def test_not_coincident_person_id_raises_unauthorized(self):
        TestConfirmEmailInteractor.ScenarioMaker() \
                .given_a_logged_person_id() \
                .given_a_confirmation_token_repo_that_returns_another_person_id() \
                .when_confirm_email_interactor_is_executed() \
                .then_should_raise_invalid_params_for_wrong_confirmation_token() \
                .then_should_not_delete_all_confirmation_tokens_for_that_person() \
                .then_should_not_call_person_repo_update()

    class ScenarioMaker:

        def __init__(self):
            self.result = None
            self.error = None
            self.updated_person = None
            self.person = None
            self.logged_person_id = None
            self.confirmation_token = None
            self.confirmation_token_repo = Mock()
            self.person_repo = Mock()

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

        def given_a_confirmation_token(self):
            self.confirmation_token = 'ABC'
            return self

        def given_a_confirmation_token_repo_that_returns_that_confirmation_token(self):
            self.confirmation_token_repo.get_person_id.return_value = self.logged_person_id
            return self

        def given_a_confirmation_token_repo_that_returns_another_person_id(self):
            self.confirmation_token_repo.get_person_id.return_value = '99'
            return self

        def given_a_confirmation_token_repo_that_raises_entity_does_not_exist(self):

            self.confirmation_token_repo.get_person_id.side_effect = EntityDoesNotExistException()