BASE_DIR = os.path.dirname(os.path.dirname(__file__))
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(PROJECT_ROOT, STATIC_URL.strip("/"))
MEDIA_URL = STATIC_URL + "media/"
MEDIA_ROOT = os.path.join(PROJECT_ROOT, *MEDIA_URL.strip("/").split("/"))
SECRET_KEY = 'N/A'
DEBUG = True
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(PROJECT_ROOT, "templates"),
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                # Insert your TEMPLATE_CONTEXT_PROCESSORS here or use this
                # list if you haven't customized them:
                'django.contrib.auth.context_processors.auth',
                'django.template.context_processors.debug',
                'django.template.context_processors.i18n',
                'django.template.context_processors.media',
                'django.template.context_processors.static',
                'django.template.context_processors.tz',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
ALLOWED_HOSTS = []
SITE_ID = 1
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    "django.contrib.redirects",
    'django.contrib.sessions',
    "django.contrib.sites",
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'oauth2_provider',
    'rest_framework',
    'social_django',

    'yak.rest_user',
    'yak.rest_social_auth',
    'yak.rest_social_network',
    'yak.rest_notifications',
    'test_project.test_app',
)
MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
ROOT_URLCONF = 'test_project.urls'
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
YAK = {
    'USER_APP_LABEL': 'test_app',
    'USER_MODEL': 'user',
    'USER_SERIALIZER': "test_project.test_app.api.serializers.ProjectUserSerializer",
    'API_SCHEMA': 'test_project/api-schema-1.0.json',
    'SOCIAL_MODEL': "test_project.test_app.models.Post",
    'EMAIL_NOTIFICATION_SUBJECT': 'Notification',
    'ALLOW_EMAIL': True,
    'ALLOW_PUSH': True,
    'PUSHWOOSH_AUTH_TOKEN': "",
    'PUSHWOOSH_APP_CODE': "",
    'USE_FACEBOOK_OG': True,
    'FACEBOOK_OG_NAMESPACE': "yakexample",
    'SERIALIZER_MAPPING': {
        "test_project.test_app.models.Post": "test_project.test_app.api.serializers.PostSerializer",
        "test_project.test_app.models.Article": "test_project.test_app.api.serializers.ArticleSerializer"
    },
    'FLAKE8_CONFIG': {
        'ignore': ['E123', 'E128'],
        'max_line_length': 120,
        'exclude': ['*/migrations/*', '*/test_project/settings.py'],
    },
}
AUTH_USER_MODEL = "{}.{}".format(YAK['USER_APP_LABEL'], YAK['USER_MODEL'].capitalize())
PACKAGES_TO_TEST = ['test_project', 'yak']
AUTHENTICATION_BACKENDS = (
    "yak.rest_social_auth.backends.yak_instagram.Instagram",
    "yak.rest_social_auth.backends.yak_soundcloud.Soundcloud",
    "yak.rest_social_auth.backends.yak_tumblr.Tumblr",
    "yak.rest_social_auth.backends.yak_facebook.Facebook",
    "yak.rest_social_auth.backends.yak_twitter.Twitter",
    "yak.rest_user.backends.CaseInsensitiveBackend",
)
SOCIAL_AUTH_INSTAGRAM_KEY = ''
SOCIAL_AUTH_INSTAGRAM_SECRET = ''
SOCIAL_AUTH_SOUNDCLOUD_KEY = ''
SOCIAL_AUTH_SOUNDCLOUD_SECRET = ''
SOCIAL_AUTH_FACEBOOK_KEY = ''
SOCIAL_AUTH_FACEBOOK_SECRET = ''
SOCIAL_AUTH_FACEBOOK_APP_TOKEN = ''
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email', 'publish_actions', 'user_about_me', 'user_location']
SOCIAL_AUTH_TWITTER_KEY = ''
SOCIAL_AUTH_TWITTER_SECRET = ''
SOCIAL_AUTH_PIPELINE = (
    # Get the information we can about the user and return it in a simple
    # format to create the user instance later. On some cases the details are
    # already part of the auth response from the provider, but sometimes this
    # could hit a provider API.
    'social.pipeline.social_auth.social_details',

    # Get the social uid from whichever service we're authing thru. The uid is
    # the unique identifier of the given user in the provider.
    'social.pipeline.social_auth.social_uid',

    # Verifies that the current auth process is valid within the current
    # project, this is were emails and domains whitelists are applied (if
    # defined).
    'social.pipeline.social_auth.auth_allowed',

    # If used, replaces default 'social.pipeline.social_auth.social_user'
    # 'yak.rest_social_auth.pipeline.social_auth_user',

    # Checks if the current social-account is already associated in the site.
    'social.pipeline.social_auth.social_user',

    # Make up a username for this person, appends a random string at the end if
    # there's any collision.
    'social.pipeline.user.get_username',

    # Send a validation email to the user to verify its email address.
    # Disabled by default.
    # 'social.pipeline.mail.mail_validation',

    # Associates the current social details with another user account with
    # a similar email address. Disabled by default.
    'social.pipeline.social_auth.associate_by_email',

    # Create a user account if we haven't found one yet.
    'social.pipeline.user.create_user',

    # Create the record that associated the social account with this user.
    'social.pipeline.social_auth.associate_user',

    # Populate the extra_data field in the social record with the values
    # specified by settings (and the default ones like access_token, etc).
    'social.pipeline.social_auth.load_extra_data',

    # Update the user record with any changed info from the auth service.
    'social.pipeline.user.user_details',

    'yak.rest_social_auth.pipeline.save_extra_data',
    'yak.rest_social_auth.pipeline.save_profile_image',
)
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'yak.rest_core.permissions.IsOwnerOrReadOnly',
    ),
    'PAGINATE_BY': 20,
    'PAGE_SIZE': 20,
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
        'rest_framework.authentication.SessionAuthentication',
    ),
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
        'rest_framework.filters.OrderingFilter'
    ),
    'TEST_REQUEST_DEFAULT_FORMAT': 'json',
}
OAUTH2_PROVIDER = {
    # this is the list of available scopes
    'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'groups': 'Access to your groups'}
}
ACCESS_TOKEN_EXPIRE_SECONDS = 7776000  # 90 days
CACHE_MIDDLEWARE_SECONDS = 60
CACHE_MIDDLEWARE_KEY_PREFIX = "YAK" class Post(BaseSocialModel):
    user = models.ForeignKey(User, related_name='posts', on_delete=models.CASCADE)
    title = models.CharField(max_length=100, null=True, blank=True)
    description = models.TextField(blank=True, null=True)
    thumbnail = models.ImageField(upload_to="post_photos/thumbnail/", blank=True, null=True)

    TAG_FIELD = 'description'

    likes = GenericRelation(Like)
    flags = GenericRelation(Flag)
    shares = GenericRelation(Share)
    related_tags = models.ManyToManyField(Tag, blank=True)
    comments = GenericRelation(Comment)
    notifications = GenericRelation(Notification)

    def likes_count(self):
        return self.likes.count()

    def comments_count(self):
        return self.comments.count()

    def identifier(self):
        return "{}".format(self.title)

    def __unicode__(self):
        return "{}".format(self.title) if self.title else "Untitled"

    def url(self):
        current_site = Site.objects.get_current()
        return "http://{0}/{1}/{2}/".format(current_site.domain, "post", base62.encode(self.pk))

    def facebook_og_info(self):
        return {'action': 'post', 'object': 'cut', 'url': self.url()}

    def create_social_message(self, provider):
        message = "{} published by {} on Test Project".format(self.title, self.user.username)

        # TODO: Sending of messages to post on social media is broken and convoluted at this point, need to refactor
        if provider == "twitter":
            return "{}".format(message.encode('utf-8'))
        else:
            return "{} {}".format(message.encode('utf-8'), self.url()) class Article(CoreModel):
    title = models.CharField(max_length=60)
    body = models.TextField()
    thumbnail = models.ImageField(upload_to="article_photos/thumbnail/", blank=True, null=True)
    likes = GenericRelation(Like)
    notifications = GenericRelation(Notification)

    def __unicode__(self):
        return "{}".format(self.title) if self.title else "Untitled"

    def identifier(self):
        return "{}".format(self.title) class PostFactory(factory.DjangoModelFactory):
    class Meta:
        model = Post

    user = factory.SubFactory(UserFactory)
    title = "Factory-farmed post"
    description = "I love Yeti App Kit!" class UserFactory(factory.DjangoModelFactory):
    class Meta:
        model = User

    email = factory.Sequence(lambda n: 'person{0}@example.com'.format(n))
    username = factory.Sequence(lambda n: 'person{0}'.format(n))

    @staticmethod
    def get_test_application():
        application, created = Application.objects.get_or_create(
            client_type=Application.CLIENT_PUBLIC,
            name="Test App",
            authorization_grant_type=Application.GRANT_PASSWORD,
        )
        return application

    @classmethod
    def _create(cls, model_class, *args, **kwargs):
        user = super(UserFactory, cls)._create(model_class, *args, **kwargs)
        application = cls.get_test_application()
        AccessToken.objects.create(
            user=user,
            application=application,
            token='token{}'.format(user.id),
            expires=now() + datetime.timedelta(days=1)
        )
        return user class SchemaTestCase(APITestCaseWithAssertions):
    def setUp(self):
        super(SchemaTestCase, self).setUp()

        # Parse schema objects for use later
        self.schema_objects = {}
        with open(yak_settings.API_SCHEMA) as file:
            schema_data = json.loads(file.read())
            self.schema_objects = schema_data['objects']

    def check_schema_keys(self, data_object, schema_fields):
        """
        `data_object` is the actual JSON being sent or received
        `schema_fields` is the expected JSON based on the schema file
        """
        required_fields = []

        for schema_field, schema_type in schema_fields.items():
            # If this field is actually another related object, then check that object's fields as well
            schema_parts = schema_type.split(',')
            is_list = False
            is_optional = False
            new_schema_object = None
            for part in schema_parts:
                # Parse through all parts, regardless of ordering
                if part in ["array", "O2M", "M2M"]:
                    is_list = True
                elif part == "optional":
                    is_optional = True
                elif part.startswith('$'):
                    new_schema_object = part

            if not is_optional:
                required_fields.append(schema_field)

            if new_schema_object:
                if schema_field not in data_object or data_object[schema_field] is None:
                    # If our new object to check is None and optional then continue, else raise an error
                    if is_optional:
                        continue
                    else:
                        raise self.failureException("No data for object {0}".format(new_schema_object))

                new_data_object = data_object[schema_field]
                if is_list:
                    # If our new object to check is a list of these objects, continue if we don't have any data
                    # Else grab the first one in the list
                    if len(new_data_object) == 0:
                        continue
                    new_data_object = new_data_object[0]

                self.check_schema_keys(new_data_object, self.schema_objects[new_schema_object])

        set_required_fields = set(required_fields)
        set_data_object = set(data_object)
        set_schema_fields = set(schema_fields)
        # The actual `data_object` contains every required field
        self.assertTrue(set_required_fields.issubset(set_data_object),
                        "Data did not match schema.\nMissing fields: {}".format(
                            set_required_fields.difference(set_data_object)))

        # The actual `data_object` contains no extraneous fields not found in the schema
        self.assertTrue(set_data_object.issubset(set_schema_fields),
                        "Data did not match schema.\nExtra fields: {}".format(
                            set_data_object.difference(set_schema_fields)))

    def add_credentials(self, user):
        """
        Adds OAuth2.0 authentication as specified in `rest_user`
        If no user is specified, clear any existing credentials (allows us to
        check unauthorized requests)
        """
        if user:
            token = user.oauth2_provider_accesstoken.first()
            self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + token.token)
        else:
            self.client.credentials()

    def check_response_data(self, response, response_object_name):
        results_data = response.data

        if "results" in response.data or isinstance(response.data, list):  # If multiple objects returned
            if "results" in response.data:
                results_data = response.data["results"]
            else:  # A plain list is returned, i.e. from a bulk update request
                results_data = response.data

            if len(results_data) == 0:
                raise self.failureException("No data to compare response")
            results_data = results_data[0]

        self.check_schema_keys(results_data, self.schema_objects[response_object_name])

    def assertSchemaGet(
            self,
            url,
            parameters,
            response_object_name,
            user,
            unauthorized=False):
        """
        Checks GET parameters and results match the schema
        """
        self.add_credentials(user)
        response = self.client.get(url, parameters)
        if unauthorized:
            self.assertHttpNotAllowed(response)
        else:
            self.assertValidJSONResponse(response)
            self.check_response_data(response, response_object_name)

        return response

    def assertSchemaPost(
            self,
            url,
            request_object_name,
            response_object_name,
            data,
            user,
            format="json",
            unauthorized=False,
            status_OK=False):
        """
        Checks POST data and results match schema

        status_OK: used for non-standard POST requests that do not return 201,
            e.g. if creating a custom route that uses POST
        """
        if isinstance(data, list):  # Check first object if this is a bulk create
            self.check_schema_keys(data[0], self.schema_objects[request_object_name])
        else:
            self.check_schema_keys(data, self.schema_objects[request_object_name])

        self.add_credentials(user)
        response = self.client.post(url, data, format=format)
        if unauthorized:
            self.assertHttpNotAllowed(response)
        elif status_OK:
            self.assertHttpOK(response)
            self.assertTrue(response['Content-Type'].startswith('application/json'))
            self.check_response_data(response, response_object_name)
        else:
            self.assertHttpCreated(response)
            self.assertTrue(response['Content-Type'].startswith('application/json'))
            self.check_response_data(response, response_object_name)

        return response

    def assertSchemaPatch(
            self,
            url,
            request_object_name,
            response_object_name,
            data,
            user,
            format="json",
            unauthorized=False):
        """
        Checks PATCH data and results match schema
        """
        self.check_schema_keys(data, self.schema_objects[request_object_name])

        self.add_credentials(user)
        response = self.client.patch(url, data, format=format)
        if unauthorized:
            self.assertHttpNotAllowed(response)
        else:
            self.assertValidJSONResponse(response)
            self.check_response_data(response, response_object_name)

        return response

    def assertSchemaPut(
            self,
            url,
            request_object_name,
            response_object_name,
            data,
            user,
            format="json",
            unauthorized=False,
            forbidden=False):
        """
        Assumes PUT is used for bulk updates, not single updates.
        Runs a PUT request and checks the PUT data and results match the
        schema for bulk updates. Assumes that all objects sent in a bulk
        update are identical, and hence only checks that the first one
        matches the schema.
        """
        self.check_schema_keys(data[0], self.schema_objects[request_object_name])

        self.add_credentials(user)
        response = self.client.put(url, data, format=format)
        if forbidden:
            # Attempting to update an object that isn't yours means it isn't in the queryset. DRF reads this as
            # creating, not updating. Since we have the `allow_add_remove` option set to False, creating isn't
            # allowed. So, instead of being rejected with a 403, server returns a 400 Bad Request.
            self.assertHttpBadRequest(response)
        elif unauthorized:
            self.assertHttpUnauthorized(response)
        else:
            self.assertValidJSONResponse(response)
            self.check_response_data(response, response_object_name)

        return response

    def assertSchemaDelete(
            self,
            url,
            user,
            unauthorized=False):
        """
        Checks DELETE
        """
        self.add_credentials(user)
        response = self.client.delete(url)

        if unauthorized:
            self.assertHttpNotAllowed(response)
        else:
            self.assertHttpAccepted(response)

        return response

    def assertPhotoUpload(self):
        pass

    def assertVideoUpload(
            self,
            url,
            obj_to_update,
            user,
            path_to_video,
            path_to_thumbnail,
            related_media_model=None,
            related_name=None,
            unauthorized=False
    ):
        """
        Checks that the video is uploaded and saved
        If the model being 'updated' is not the model that actually stores
        files (e.g., there is a Media model that has a relation to the model
        being updated), pass that model and the keyword field on that model
        that relates to the model being updated
        """
        self.add_credentials(user)
        kwargs = {
            "data": {
                'video_file': open(settings.PROJECT_ROOT + path_to_video, 'rb')
            },
            'format': 'multipart'
        }
        response = self.client.post(url, **kwargs)

        if unauthorized:
            self.assertHttpForbidden(response)
        else:
            self.assertHttpCreated(response)
            self.assertTrue(response['Content-Type'].startswith('application/json'))

            # Check the video and thumbnail are saved
            if related_media_model and related_name:
                filters = {
                    related_name: obj_to_update
                }
                obj_to_update = related_media_model.objects.filter(**filters)[0]
            else:
                obj_to_update = obj_to_update.__class__.objects.get(pk=obj_to_update.pk)
            original_file_field_name = getattr(obj_to_update, "original_file_name", "original_file")
            original_file = getattr(obj_to_update, original_file_field_name)
            self.assertEqual(
                original_file.file.read(),
                open(settings.PROJECT_ROOT + path_to_video, 'r').read()
            )
            self.assertEqual(
                obj_to_update.thumbnail.file.read(),
                open(settings.PROJECT_ROOT + path_to_thumbnail, 'r').read()
            ) @task
def create_notification(receiver, reporter, content_object, notification_type, template_override=None, reply_to=None,
                        deep_link=None):
    # If the receiver of this notification is the same as the reporter or
    # if the user has blocked this type, then don't create
    if receiver == reporter:
        return

    notification = Notification.objects.create(user=receiver,
                                               reporter=reporter,
                                               content_object=content_object,
                                               notification_type=notification_type,
                                               template_override=template_override,
                                               deep_link=deep_link)
    notification.save()

    notification_setting = NotificationSetting.objects.get(notification_type=notification_type, user=receiver)
    if notification_setting.allow_push and yak_settings.ALLOW_PUSH:
        from .utils import send_push_notification
        send_push_notification(receiver, notification.push_message(), deep_link=deep_link)

    if notification_setting.allow_email and yak_settings.ALLOW_EMAIL and receiver.email:
        from .utils import send_email_notification
        send_email_notification(receiver, notification.email_message(), reply_to=reply_to) class Notification(CoreModel):
    PUSH = "push"
    EMAIL = "email"

    notification_type = models.ForeignKey(NotificationType, related_name="notifications", on_delete=models.CASCADE)
    template_override = models.CharField(max_length=100, blank=True, null=True)
    deep_link = models.CharField(max_length=100, blank=True, null=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="notifications_received", null=True, blank=True,
                             on_delete=models.CASCADE)
    reporter = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="notifications_sent", null=True, blank=True,
                                 on_delete=models.CASCADE)

    # The object that the notification is about, not the social model related to it
    # E.g., if you Like a Post, the content_object for the notification is the Post, not the Like
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField(db_index=True)
    content_object = GenericForeignKey()

    def message(self, location):
        """
        Takes our configured notifications and creates a message
        replacing the appropriate variables from the content object
        """

        data = defaultdict(str)
        # get the domain from Site
        data['domain'] = Site.objects.get_current().domain

        if self.content_object:
            data['identifier'] = self.content_object.identifier()
        if self.reporter:
            data['reporter'] = self.reporter.identifier()

        if hasattr(self.content_object, 'extra_notification_params'):
            data.update(self.content_object.extra_notification_params())

        configured_template_name = "{}.html".format(self.notification_type.slug)
        template_name = self.template_override if self.template_override else configured_template_name
        return render_to_string("notifications/{}/{}".format(location, template_name), data)

    def email_message(self):
        return self.message(Notification.EMAIL)

    def push_message(self):
        message = self.message(Notification.PUSH)
        if self.reporter:
            return "{0} {1}".format(self.reporter, message)
        else:
            return "{0}".format(message)

    @property
    def name(self):
        return "{}".format(self.notification_type)

    def __unicode__(self):
        return "{}: {}".format(self.user, self.notification_type)

    class Meta:
        ordering = ['-created'] class NotificationSetting(CoreModel):
    notification_type = models.ForeignKey(NotificationType, related_name="user_settings", on_delete=models.CASCADE)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='notification_settings', on_delete=models.CASCADE)
    allow_push = models.BooleanField(default=True)
    allow_email = models.BooleanField(default=True)

    class Meta:
        unique_together = ('notification_type', 'user')
        ordering = ['-created']

    def __unicode__(self):
        return "{}: {}".format(self.user, self.notification_type)

    def name(self):
        return "{}: {}".format(self.user, self.notification_type) class NotificationType(CachingMixin, CoreModel):
    name = models.CharField(max_length=32)
    slug = models.SlugField(unique=True)
    description = models.CharField(max_length=255)
    is_active = models.BooleanField(default=True)

    objects = CachingManager()

    def __unicode__(self):
        return "{}".format(self.name) def send_email_notification(receiver, message, reply_to=None):
    headers = {}
    if reply_to:
        headers['Reply-To'] = reply_to

    text_content = strip_tags(message)
    msg = EmailMultiAlternatives(yak_settings.EMAIL_NOTIFICATION_SUBJECT, text_content, settings.DEFAULT_FROM_EMAIL,
                                 [receiver.email], headers=headers)
    msg.attach_alternative(message, "text/html")
    msg.send() def send_push_notification(receiver, message, deep_link=None):
    notification_handler = import_string(yak_settings.PUSH_NOTIFICATION_HANDLER)
    return notification_handler(receiver, message, deep_link=None) def perform_import(val, setting_name):
    def __getattr__(self, attr):
class YAKAPISettings(APISettings):
USER_SETTINGS = getattr(settings, 'YAK', None)
DEFAULTS = {
    'USER_APP_LABEL': 'test_app',
    'USER_MODEL': 'user',
    'USER_SERIALIZER': "test_app.api.serializers.UserSerializer",
    'API_SCHEMA': 'api-schema-1.0.json',
    'SOCIAL_MODEL': "test_app.models.Post",
    'ALLOW_EMAIL': True,
    'ALLOW_PUSH': True,
    'EMAIL_NOTIFICATION_SUBJECT': 'Test Project Notification',
    'PUSHWOOSH_AUTH_TOKEN': "",
    'PUSHWOOSH_APP_CODE': "",
    'PUSH_NOTIFICATION_HANDLER': "yak.rest_notifications.utils.send_pushwoosh_notification",
    'SOCIAL_SHARE_DELAY': 60,
    'USE_FACEBOOK_OG': False,
    'FACEBOOK_OG_NAMESPACE': "",
    'SERIALIZER_MAPPING': {},
    'FLAKE8_CONFIG': None,
}
IMPORT_STRINGS = (
    "USER_SERIALIZER",
    "SOCIAL_MODEL",
    "SERIALIZER_MAPPING",
)
from unittest import mock
from unittest.mock import patch
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core import mail
from rest_framework.reverse import reverse
from test_project import settings
from test_project.test_app.models import Post, Article
from test_project.test_app.tests.factories import PostFactory, UserFactory
from yak.rest_core.test import SchemaTestCase
from yak.rest_notifications.models import create_notification, Notification, NotificationSetting, NotificationType
from yak.rest_notifications.utils import send_email_notification, send_push_notification
from yak.settings import yak_settings


User = get_user_model()


def mockPushNotificationHandler(receiver, message, deep_link=None):
    return {"hello": "world"}


class NotificationsTestCase(SchemaTestCase):
    def setUp(self):
        super(NotificationsTestCase, self).setUp()
        self.social_obj = PostFactory()
        self.receiver = UserFactory()
        self.reporter = UserFactory()
        self.notification_type = NotificationType.objects.get(slug="comment")
        patcher = patch('yak.rest_notifications.utils.submit_to_pushwoosh')
        self.addCleanup(patcher.stop)
        self.mock_submit_to_pushwoosh = patcher.start()
        self.mock_submit_to_pushwoosh.return_value = {"status_code": 200}

    @mock.patch('pypushwoosh.client.PushwooshClient.invoke')
    def test_create_pushwoosh_token(self, mock_pushwoosh_client):
        mock_pushwoosh_client.return_value = {"status_code": 200}

        url = reverse("pushwoosh_token")
        data = {
            "token": "ABC123",
            "language": "en",
            "hwid": "XYZ456",
            "platform": "ios"
        }
        self.assertSchemaPost(url, "$pushwooshTokenRequest", "$pushwooshTokenResponse", data, self.receiver)

        # Non-authenticated users can't create a token
        self.assertSchemaPost(url, "$pushwooshTokenRequest", "$pushwooshTokenResponse", data, None, unauthorized=True)

        # Can't create token if write-only language and hwid data is missing
        bad_data = {
            "token": "ABC123"
        }
        self.add_credentials(self.receiver)
        response = self.client.post(url, bad_data, format="json")
        self.assertHttpBadRequest(response)

    def test_email_notification_sent(self):
        message = "<h1>You have a notification!</h1>"

        send_email_notification(self.receiver, message)