Repository: joke2k/faker

Issue:
zh_CN (and possible other providers) return invalid results for domain_name
Brief summary of the issue goes here.

### Steps to reproduce

f = Faker('zh_CN')
f.domain_name()
>he.cn

### Expected behavior

it should return valid domain names

### Actual behavior

he.cn is not a valid domain. he is in the list of second-level domains (https://en.wikipedia.org/wiki/.cn#Second-level_domains_of_provinces) and will cause failures in places where domains are being validated.

it's unclear where this change should/could be made after briefly reviewing the source code.



Discussion:
I may be able to open a PR to add second level domains.  The file in question is in `faker/provider/internet/zh_CN/__init__.py` :)

Resolving patch:
diff --git a/faker/providers/internet/zh_CN/__init__.py b/faker/providers/internet/zh_CN/__init__.py
--- a/faker/providers/internet/zh_CN/__init__.py
+++ b/faker/providers/internet/zh_CN/__init__.py
@@ -1,5 +1,6 @@
 # coding=utf-8
 from __future__ import unicode_literals
+from collections import OrderedDict
 from .. import Provider as InternetProvider
 from faker.utils.decorators import slugify
 
@@ -11,8 +12,54 @@ class Provider(InternetProvider):
         '{{first_romanized_name}}##',
         '?{{last_romanized_name}}',
     )
-    tlds = ('com', 'com', 'com', 'net', 'org', 'cn', 'cn', 'cn')
+
+    tlds = OrderedDict((
+        ('cn', 0.8),
+        ('net', 0.1),
+        ('com', 0.05),
+        ('org', 0.05),
+    ))
+
+    second_level_domains = ('ac', 'com', 'edu', 'gov', 'mil', 'net', 'org',
+                            'ah', 'bj', 'cq', 'fj', 'gd', 'gs', 'gz', 'gx',
+                            'ha', 'hb', 'he', 'hi', 'hk', 'hl', 'hn', 'jl',
+                            'js', 'jx', 'ln', 'mo', 'nm', 'nx', 'qh', 'sc',
+                            'sd', 'sh', 'sn', 'sx', 'tj', 'xj', 'xz', 'yn', 'zj')
+
+    domain_formats = (
+        '##', '??',
+        '{{first_romanized_name}}',
+        '{{last_romanized_name}}',
+        '{{first_romanized_name}}{{last_romanized_name}}',
+        '{{last_romanized_name}}{{last_romanized_name}}',
+        '{{first_romanized_name}}{{first_romanized_name}}',
+    )
 
     @slugify
     def domain_word(self):
-        return self.generator.format('last_romanized_name')
+        pattern = self.random_element(self.domain_formats)
+        if '#' in pattern or '?' in pattern:
+            return self.bothify(pattern)
+        else:
+            return self.generator.parse(pattern)
+
+    def domain_name(self, levels=1):
+        if levels < 1:
+            raise ValueError("levels must be greater than or equal to 1")
+        if levels == 1:
+            domain_word = self.domain_word()
+            # Avoids he.cn as seen in issue #687
+            while domain_word in self.second_level_domains:
+                domain_word = self.domain_word()
+            return domain_word + '.' + self.tld()
+        elif levels == 2:
+            my_tld = self.tld()
+            my_second_level = ''
+            if my_tld == 'cn':
+                my_second_level = self.random_element(self.second_level_domains)
+            else:
+                my_second_level = self.domain_word()
+            return self.domain_word() + '.' + my_second_level + '.' + my_tld
+        else:
+            return self.domain_word() + '.' + self.domain_name(levels - 1)
+


Test patch:
diff --git a/tests/providers/test_internet.py b/tests/providers/test_internet.py
--- a/tests/providers/test_internet.py
+++ b/tests/providers/test_internet.py
@@ -95,6 +95,31 @@ def test_email(self):
         email = self.factory.email()
         validate_email(email, check_deliverability=False)
 
+    def test_domain_word(self):
+        domain_word = self.factory.domain_word()
+        self.assertGreater(len(domain_word), 1)
+
+    @mock.patch(
+        'faker.providers.internet.Provider.tld',
+        lambda x: 'cn'
+    )
+    def test_domain_name(self):
+        domain_name_1_level = self.factory.domain_name(levels=1)
+        domain_parts = domain_name_1_level.split(".")
+        self.assertEqual(len(domain_parts), 2)
+        self.assertEqual(domain_parts[-1], 'cn')
+        domain_name_2_level = self.factory.domain_name(levels=2)
+        domain_parts = domain_name_2_level.split(".")
+        self.assertEqual(len(domain_parts), 3)
+        self.assertEqual(domain_parts[-1], 'cn')
+        self.assertIn(domain_parts[1], ['ac', 'com', 'edu', 'gov', 'mil',
+                                        'net', 'org', 'ah', 'bj', 'cq',
+                                        'fj', 'gd', 'gs', 'gz', 'gx', 'ha',
+                                        'hb', 'he', 'hi', 'hk', 'hl', 'hn',
+                                        'jl', 'js', 'jx', 'ln', 'mo', 'nm',
+                                        'nx', 'qh', 'sc', 'sd', 'sh', 'sn',
+                                        'sx', 'tj', 'xj', 'xz', 'yn', 'zj'])
+
 
 class TestZhTW(unittest.TestCase):