class RedisClusterCache(object):
    """
    Class used for accessing the redis cluster used for caching.
    """
    def __init__(self):
        self.client = self._create_client()

    def _create_client(self):
        """
        Function to connect to the redis cluster and init the client.
        """
        server_list = settings.REDIS_SERVER_LIST.replace(' ', '').split(',')

        nodes = []
        for server in server_list:
            if ':' not in server:
                continue
            host, port = server.split(':')
            nodes.append({'host': host, 'port': port})

        return StrictRedisCluster(startup_nodes=nodes, decode_responses=True)

    def get(self, key):
        return self.client.get(key)

    def exists(self, key):
        return self.client.exists(key)

    def set(self, key, value, timeout=DEFAULT_TIMEOUT):
        self.client.set(key, value, timeout) class App(models.Model):
    """
    Model that contains information about the supported apps by the middleware.
    """
    platform = models.CharField(choices=PLATFORM_CHOICES, max_length=10)
    app_id = models.CharField(max_length=255)

    push_key = models.CharField(max_length=255)
    logentries_token = models.CharField(max_length=255, blank=False, null=False, default='')
    partner_logentries_token = models.CharField(max_length=255, blank=True, null=True, default='')

    def __str__(self):
        return '{0} for {1}'.format(self.app_id, self.platform)

    class Meta:
        unique_together = ('app_id', 'platform') class Device(models.Model):
    """
    Model for all device who register at the middleware.
    """
    # FIXME: We need this to be backwards compatible for one release.
    id = models.CharField(max_length=255, unique=True)

    name = models.CharField(max_length=255, blank=True, null=True)
    sip_user_id = models.CharField(max_length=255, unique=True, primary_key=True)
    os_version = models.CharField(max_length=255, blank=True, null=True)
    client_version = models.CharField(max_length=255, blank=True, null=True)
    token = models.CharField(max_length=250)
    sandbox = models.BooleanField(default=False)
    last_seen = models.DateTimeField(blank=True, null=True)
    app = models.ForeignKey(App)
    remote_logging_id = models.CharField(max_length=255, blank=True, null=True)

    def __str__(self):
        return '{0} - {1}'.format(self.sip_user_id, self.name)

    # FIXME: We need this to be backwards compatible for one release.
    def save(self, *args, **kwargs):
        """
        Make sure the id fields stays the same as sip_user_id so we are
        backwards compatible.
        """
        if self.sip_user_id:
            self.id = self.sip_user_id
            obj = super(Device, self).save(*args, **kwargs)
            return obj class ResponseLog(models.Model):
    """
    Model for logging info about the device response.
    """
    platform = models.CharField(choices=PLATFORM_CHOICES, max_length=10)
    roundtrip_time = models.FloatField()
    available = models.BooleanField()
    date = models.DateTimeField(auto_now_add=True) APP_VERSION_KEY = 'app_version' CALL_SETUP_SUCCESSFUL_KEY = 'call_setup_successful' CODEC_KEY = 'codec' CONNECTION_TYPE_KEY = 'connection_type' DIRECTION_KEY = 'direction' FAILED_REASON_KEY = 'failed_reason' HANGUP_REASON_KEY = 'hangup_reason' MOS_KEY = 'mos' NETWORK_KEY = 'network' NETWORK_OPERATOR_KEY = 'network_operator' OS_KEY = 'os' OS_VERSION_KEY = 'os_version' def mocked_send_apns_message(device, app, message_type, data=None):
    cache.set('attempts', data.get('attempt', 1), 300)
    print('WORKED APNS')
    print(data.get('attempt', 1)) def mocked_send_fcm_message(device, app, message_type, data=None):
    cache.set('attempts', data.get('attempt', 1), 300)
    print('WORKED FCM')
    print(data.get('attempt', 1)) class ThreadWithReturn(Thread):
    def __init__(self, *args, **kwargs):
        super(ThreadWithReturn, self).__init__(*args, **kwargs)

        self._return = None

    def run(self):
        if self._target:
            self._return = self._target(*self._args, **self._kwargs)

    def join(self, *args, **kwargs):
        super(ThreadWithReturn, self).join(*args, **kwargs)

        return self._return
from ast import literal_eval
from datetime import datetime, timedelta
from unittest import mock
from django.conf import settings
from django.core.cache import cache
from django.test import TestCase, TransactionTestCase
from freezegun import freeze_time
from rest_framework.test import APIClient
from testfixtures import LogCapture
from app.cache import RedisClusterCache
from app.models import App, Device, ResponseLog
from main.prometheus.consts import (
    APP_VERSION_KEY,
    CALL_SETUP_SUCCESSFUL_KEY,
    CODEC_KEY,
    CONNECTION_TYPE_KEY,
    DIRECTION_KEY,
    FAILED_REASON_KEY,
    HANGUP_REASON_KEY,
    MOS_KEY,
    NETWORK_KEY,
    NETWORK_OPERATOR_KEY,
    OS_KEY,
    OS_VERSION_KEY)
from .utils import mocked_send_apns_message, mocked_send_fcm_message, ThreadWithReturn
import time
            client_version='1.0',
            app=self.ios_app,
        )
        self.data = {
            'sip_user_id': '123456789',
            'unique_key': 'sduiqayduiryqwuioeryqwer76789',
        }

        self.hangup_reason_url = '/api/hangup-reason/'

    @freeze_time('2018-01-01 12:00:00.133700')
    def test_if_the_reason_is_logged_correctly(self):
        """
        Test if the reason is logged correctly when doing a correct
        call.
        """
        self.data['reason'] = 'Device did not now answer'
        with LogCapture() as log:
            self.client.post(self.hangup_reason_url, self.data)
        log.check(
            (
                'django',
                'INFO',
                ('No remote logging ID - middleware - sduiqayduiryqwuioeryqwer76789 | APNS Device '
                 'not available because: Device did not now answer on 12:00:00.133700'),
            ),
        )

    def test_incorrect_sip_user_id_log(self):
        """
        Test if the warning message is logged when a wrong sip user id is given.
        """
        self.data['reason'] = 'Device did not now answer'
        self.data['sip_user_id'] = '987654321'
        with LogCapture() as log:
            self.client.post(self.hangup_reason_url, self.data)
        log.check(
            (
                'django',
                'WARNING',
                ('No remote logging ID - middleware - sduiqayduiryqwuioeryqwer76789 | Failed to '
                 'find a device for SIP_user_ID : 987654321'),
            ),
            (
                'django.request',
                'WARNING',
                'Not Found: /api/hangup-reason/',
            ),
        )


class LogMetricsTest(TestCase):
    def setUp(self):
        """
        Initialize the data we need for the tests.
        """
        super(LogMetricsTest, self).setUp()
        self.client = APIClient()

        self.ios_app, created = App.objects.get_or_create(platform='apns', app_id='com.voipgrid.vialer')
        Device.objects.create(
            name='test device',
            token='a652aee84bdec6c2859eec89a6e5b1a42c400fba43070f404148f27b502610b6',
            sip_user_id='123456789',
            os_version='8.3',
            client_version='1.0',
            app=self.ios_app,
        )
        self.data = {
            'sip_user_id': '123456789',
        }
        self.log_metrics_url = '/api/log-metrics/'
        self.redis_client = RedisClusterCache()
        self.success_redis_key = 'vialer_call_success_total'
        self.failed_redis_key = 'vialer_call_failure_total'
        self.hangup_redis_key = 'vialer_hangup_reason_total'

    def tearDown(self):
        super(LogMetricsTest, self).tearDown()
        self.redis_client.client.delete(self.success_redis_key)
        self.redis_client.client.delete(self.failed_redis_key)
        self.redis_client.client.delete(self.hangup_redis_key)

    def test_if_call_success_key_is_stored_in_redis_correctly(self):
        """
        Test if the data we send is stored with the call_success_key in Redis.
        """
        self.data[OS_KEY] = 'iOS'
        self.data[OS_VERSION_KEY] = '5.0.1'
        self.data[APP_VERSION_KEY] = '2.0'
        self.data[NETWORK_KEY] = 'WiFi'
        self.data[CONNECTION_TYPE_KEY] = 'TLS'
        self.data[DIRECTION_KEY] = 'Incoming'
        self.data[CALL_SETUP_SUCCESSFUL_KEY] = 'true'
        self.data[CODEC_KEY] = 'OPUS'
        self.data[MOS_KEY] = '4.50'
        response = self.client.post(self.log_metrics_url, self.data)
        self.assertEquals(response.status_code, 200)
        self.assertEquals(self.redis_client.client.llen(self.success_redis_key), 1)
        value_list = self.redis_client.client.lrange(self.success_redis_key, 0, -1)
        value_dict = literal_eval(value_list[0])
        self.assertEquals(self.data[OS_KEY], value_dict[OS_KEY])
        self.assertEquals(self.data[OS_VERSION_KEY], value_dict[OS_VERSION_KEY])
        self.assertEquals(self.data[APP_VERSION_KEY], value_dict[APP_VERSION_KEY])
        self.assertEquals(self.data[NETWORK_KEY], value_dict[NETWORK_KEY])
        self.assertEquals(self.data[CONNECTION_TYPE_KEY], value_dict[CONNECTION_TYPE_KEY])
        self.assertEquals(self.data[DIRECTION_KEY], value_dict[DIRECTION_KEY])
        self.assertEquals(self.data[CODEC_KEY], value_dict[CODEC_KEY])
        self.assertEquals(self.data[MOS_KEY], value_dict[MOS_KEY])

    def test_if_call_failure_key_is_stored_in_redis_correctly(self):
        """
        Test if the data we send is stored with the call_failure_key in Redis.
        """
        self.data[OS_KEY] = 'iOS'
        self.data[OS_VERSION_KEY] = '5.0.1'
        self.data[APP_VERSION_KEY] = '2.0'
        self.data[NETWORK_KEY] = 'WiFi'
        self.data[CONNECTION_TYPE_KEY] = 'TLS'
        self.data[DIRECTION_KEY] = 'Incoming'

        self.data[CALL_SETUP_SUCCESSFUL_KEY] = 'false'