Repository: joke2k/faker

Issue:
Canadian SSN Provider returns a string nothing like a Canadian SIN under python3
Canadian SSN Provider returns a string nothing like a Canadian SIN under python3. There are also no tests for this provider -- if there were, the change would have been detected.

### Steps to reproduce

```
Python 3.6.5 (default, Apr 25 2018, 14:23:58)
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from faker import Faker
>>> print(Faker('en-Ca').ssn())
37.09 8.002.0 83.08
```
### Expected behavior

An expected result follows the pattern `### ### ###`.  

### Actual behavior

Something else entirely. Here, it's `37.09 8.002.0 83.08`.

### Explanation

This is due to the integer division operator change between python2 and python3.

Canadian SSN Provider returns a string nothing like a Canadian SIN under python3
Canadian SSN Provider returns a string nothing like a Canadian SIN under python3. There are also no tests for this provider -- if there were, the change would have been detected.

### Steps to reproduce

```
Python 3.6.5 (default, Apr 25 2018, 14:23:58)
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from faker import Faker
>>> print(Faker('en-Ca').ssn())
37.09 8.002.0 83.08
```
### Expected behavior

An expected result follows the pattern `### ### ###`.  

### Actual behavior

Something else entirely. Here, it's `37.09 8.002.0 83.08`.

### Explanation

This is due to the integer division operator change between python2 and python3.



Discussion:

Resolving patch:
diff --git a/faker/providers/ssn/en_CA/__init__.py b/faker/providers/ssn/en_CA/__init__.py
--- a/faker/providers/ssn/en_CA/__init__.py
+++ b/faker/providers/ssn/en_CA/__init__.py
@@ -2,36 +2,81 @@
 from __future__ import unicode_literals
 from .. import Provider as SsnProvider
 
+def checksum(sin):
+    """
+    Determine validity of a Canadian Social Insurance Number.
+    Validation is performed using a modified Luhn Algorithm.  To check
+    the Every second digit of the SIN is doubled and the result is
+    summed.  If the result is a multiple of ten, the Social Insurance
+    Number is considered valid.
+
+    https://en.wikipedia.org/wiki/Social_Insurance_Number
+    """
+
+    # Remove spaces and create a list of digits.
+    checksumCollection = list(sin.replace(' ', ''))
+    checksumCollection = [int(i) for i in checksumCollection]
+
+    # Discard the last digit, we will be calculating it later.
+    checksumCollection[-1] = 0
+
+    # Iterate over the provided SIN and double every second digit.
+    # In the case that doubling that digit results in a two-digit
+    # number, then add the two digits together and keep that sum.
+
+    for i in range(1, len(checksumCollection), 2):
+        result = checksumCollection[i] * 2
+        if result < 10:
+            checksumCollection[i] = result
+        else:
+            checksumCollection[i] = result - 10 + 1
+
+    # The appropriate checksum digit is the value that, when summed
+    # with the first eight values, results in a value divisible by 10
+
+    check_digit = 10 - (sum(checksumCollection) % 10)
+    check_digit = (0 if check_digit == 10 else check_digit)
+
+    return check_digit
 
 class Provider(SsnProvider):
 
-    # in order to create a valid SIN we need to provide a number that passes a simple modified Luhn Algorithmn checksum
-    # this function essentially reverses the checksum steps to create a random
-    # valid SIN (Social Insurance Number)
+    # In order to create a valid SIN we need to provide a number that 
+    # passes a simple modified Luhn Algorithm checksum. 
+    # 
+    # This function reverses the checksum steps to create a random
+    # valid nine-digit Canadian SIN (Social Insurance Number) in the
+    # format '### ### ###'.
     def ssn(self):
 
-        # create an array of 8 elements initialized randomly
-        digits = self.generator.random.sample(range(10), 8)
+        # Create an array of 8 elements initialized randomly.
+        digits = self.generator.random.sample(range(9), 8)
+
+        # The final step of the validation requires that all of the 
+        # digits sum to a multiple of 10. First, sum the first 8 and
+        # set the 9th to the value that results in a multiple of 10.
+        check_digit = 10 - (sum(digits) % 10)
+        check_digit = (0 if check_digit == 10 else check_digit)
 
-        # All of the digits must sum to a multiple of 10.
-        # sum the first 8 and set 9th to the value to get to a multiple of 10
-        digits.append(10 - (sum(digits) % 10))
+        digits.append(check_digit)
 
-        # digits is now the digital root of the number we want multiplied by the magic number 121 212 121
-        # reverse the multiplication which occurred on every other element
+        # digits is now the digital root of the number we want 
+        # multiplied by the magic number 121 212 121. The next step is
+        # to reverse the multiplication which occurred on every other
+        # element.
         for i in range(1, len(digits), 2):
             if digits[i] % 2 == 0:
-                digits[i] = (digits[i] / 2)
+                digits[i] = (digits[i] // 2)
             else:
-                digits[i] = (digits[i] + 9) / 2
+                digits[i] = (digits[i] + 9) // 2
 
-        # build the resulting SIN string
+        # Build the resulting SIN string.
         sin = ""
-        for i in range(0, len(digits), 1):
+        for i in range(0, len(digits)):
             sin += str(digits[i])
-            # add a space to make it conform to normal standards in Canada
-            if i % 3 == 2:
+            # Add a space to make it conform to Canadian formatting.
+            if i in (2,5):
                 sin += " "
 
-        # finally return our random but valid SIN
+        # Finally return our random but valid SIN.
         return sin


Test patch:
diff --git a/tests/providers/test_ssn.py b/tests/providers/test_ssn.py
--- a/tests/providers/test_ssn.py
+++ b/tests/providers/test_ssn.py
@@ -7,13 +7,29 @@
 from datetime import datetime
 
 from faker import Faker
+from faker.providers.ssn.en_CA import checksum as ca_checksum
 from faker.providers.ssn.et_EE import checksum as et_checksum
 from faker.providers.ssn.fi_FI import Provider as fi_Provider
 from faker.providers.ssn.hr_HR import checksum as hr_checksum
 from faker.providers.ssn.pt_BR import checksum as pt_checksum
 from faker.providers.ssn.pl_PL import checksum as pl_checksum, calculate_month as pl_calculate_mouth
-from faker.providers.ssn.no_NO import checksum as no_checksum, Provider
+from faker.providers.ssn.no_NO import checksum as no_checksum, Provider as no_Provider
 
+class TestEnCA(unittest.TestCase):
+    def setUp(self):
+        self.factory = Faker('en_CA')
+        self.factory.seed(0)
+
+    def test_ssn(self):
+        for _ in range(100):
+            sin = self.factory.ssn()
+
+            # Ensure that generated SINs are 11 characters long
+            # including spaces, consist of spaces and digits only, and
+            # satisfy the validation algorithm. 
+            assert len(sin) == 11
+            assert sin.replace(' ','').isdigit()
+            assert ca_checksum(sin) == int(sin[-1])
 
 class TestEtEE(unittest.TestCase):
     """ Tests SSN in the et_EE locale """
@@ -170,8 +186,8 @@ def setUp(self):
         self.factory = Faker('no_NO')
 
     def test_no_NO_ssn_checksum(self):
-        self.assertEqual(no_checksum([0, 1, 0, 2, 0, 3, 9, 8, 7], Provider.scale1), 6)
-        self.assertEqual(no_checksum([0, 1, 0, 2, 0, 3, 9, 8, 7, 6], Provider.scale2), 7)
+        self.assertEqual(no_checksum([0, 1, 0, 2, 0, 3, 9, 8, 7], no_Provider.scale1), 6)
+        self.assertEqual(no_checksum([0, 1, 0, 2, 0, 3, 9, 8, 7, 6], no_Provider.scale2), 7)
 
     def test_no_NO_ssn(self):
         for _ in range(100):