text: # -*- coding: utf-8 -*-
  from __future__ import unicode_literals
  
  import sys
  import unittest
  
  from django.test import TestCase
  from django.utils import six
  from django.utils.six.moves.urllib.parse import parse_qsl, urlparse
  
  from two_factor.models import PhoneDevice
  from two_factor.utils import (
      backup_phones, default_device, get_otpauth_url, totp_digits,
  )
  
  from .utils import UserMixin
  
  
  class UtilsTest(UserMixin, TestCase):
      def test_default_device(self):
          user = self.create_user()
          self.assertEqual(default_device(user), None)
  
          user.phonedevice_set.create(name='backup', number='+1')
          self.assertEqual(default_device(user), None)
  
          default = user.phonedevice_set.create(name='default', number='+1')
          self.assertEqual(default_device(user).pk, default.pk)
  
      def test_backup_phones(self):
          self.assertQuerysetEqual(list(backup_phones(None)),
                                   list(PhoneDevice.objects.none()))
          user = self.create_user()
          user.phonedevice_set.create(name='default', number='+1')
          backup = user.phonedevice_set.create(name='backup', number='+1')
          phones = backup_phones(user)
  
          self.assertEqual(len(phones), 1)
          self.assertEqual(phones[0].pk, backup.pk)
  
      @unittest.skipIf((3, 2) <= sys.version_info < (3, 3), "Python 3.2's urlparse is broken")
      @unittest.skipIf(sys.version_info < (2, 7), "Python 2.6 not supported")
      def test_get_otpauth_url(self):
          for num_digits in (6, 8):
              self.assertEqualUrl(
                  'otpauth://totp/bouke%40example.com?secret=abcdef123&digits=' + str(num_digits),
                  get_otpauth_url(accountname='bouke@example.com', secret='abcdef123',
                                  digits=num_digits))
  
              self.assertEqualUrl(
                  'otpauth://totp/Bouke%20Haarsma?secret=abcdef123&digits=' + str(num_digits),
                  get_otpauth_url(accountname='Bouke Haarsma', secret='abcdef123',
                                  digits=num_digits))
  
              self.assertEqualUrl(
                  'otpauth://totp/example.com%3A%20bouke%40example.com?'
                  'secret=abcdef123&digits=' + str(num_digits) + '&issuer=example.com',
                  get_otpauth_url(accountname='bouke@example.com', issuer='example.com',
                                  secret='abcdef123', digits=num_digits))
  
              self.assertEqualUrl(
                  'otpauth://totp/My%20Site%3A%20bouke%40example.com?'
                  'secret=abcdef123&digits=' + str(num_digits) + '&issuer=My+Site',
                  get_otpauth_url(accountname='bouke@example.com', issuer='My Site',
                                  secret='abcdef123', digits=num_digits))
  
              self.assertEqualUrl(
                  'otpauth://totp/%E6%B5%8B%E8%AF%95%E7%BD%91%E7%AB%99%3A%20'
                  '%E6%88%91%E4%B8%8D%E6%98%AF%E9%80%97%E6%AF%94?'
                  'secret=abcdef123&digits=' + str(num_digits) + '&issuer=测试网站',
                  get_otpauth_url(accountname='我不是逗比',
                                  issuer='测试网站',
                                  secret='abcdef123', digits=num_digits))
  
      def assertEqualUrl(self, lhs, rhs):
          """
          Asserts whether the URLs are canonically equal.
          """
          if six.PY2:
              # See those Chinese characters above? Those are quite difficult
              # to match against the generated URLs in portable code. True,
              # this solution is not the nicest, but it works. And it's test
              # code after all.
              lhs = lhs.encode('utf8')
  
          lhs = urlparse(lhs)
          rhs = urlparse(rhs)
          self.assertEqual(lhs.scheme, rhs.scheme)
          self.assertEqual(lhs.netloc, rhs.netloc)
          self.assertEqual(lhs.path, rhs.path)
          self.assertEqual(lhs.fragment, rhs.fragment)
  
          # We used parse_qs before, but as query parameter order became
          # significant with Microsoft Authenticator and possibly other
          # authenticator apps, we've switched to parse_qsl.
          self.assertEqual(parse_qsl(lhs.query), parse_qsl(rhs.query))
  
      def test_get_totp_digits(self):
          # test that the default is 6 if TWO_FACTOR_TOTP_DIGITS is not set
          self.assertEqual(totp_digits(), 6)
  
          for no_digits in (6, 8):
              with self.settings(TWO_FACTOR_TOTP_DIGITS=no_digits):
                  self.assertEqual(totp_digits(), no_digits)
  