1"""Tests for distutils.command.register."""
2import os
3import unittest
4import getpass
5import urllib
6import warnings
7
8from test.support import check_warnings, run_unittest
9
10from distutils.command import register as register_module
11from distutils.command.register import register
12from distutils.errors import DistutilsSetupError
13from distutils.log import INFO
14
15from distutils.tests.test_config import BasePyPIRCCommandTestCase
16
17try:
18    import docutils
19except ImportError:
20    docutils = None
21
22PYPIRC_NOPASSWORD = """\
23[distutils]
24
25index-servers =
26    server1
27
28[server1]
29username:me
30"""
31
32WANTED_PYPIRC = """\
33[distutils]
34index-servers =
35    pypi
36
37[pypi]
38username:tarek
39password:password
40"""
41
42class Inputs(object):
43    """Fakes user inputs."""
44    def __init__(self, *answers):
45        self.answers = answers
46        self.index = 0
47
48    def __call__(self, prompt=''):
49        try:
50            return self.answers[self.index]
51        finally:
52            self.index += 1
53
54class FakeOpener(object):
55    """Fakes a PyPI server"""
56    def __init__(self):
57        self.reqs = []
58
59    def __call__(self, *args):
60        return self
61
62    def open(self, req, data=None, timeout=None):
63        self.reqs.append(req)
64        return self
65
66    def read(self):
67        return b'xxx'
68
69    def getheader(self, name, default=None):
70        return {
71            'content-type': 'text/plain; charset=utf-8',
72            }.get(name.lower(), default)
73
74
75class RegisterTestCase(BasePyPIRCCommandTestCase):
76
77    def setUp(self):
78        super(RegisterTestCase, self).setUp()
79        # patching the password prompt
80        self._old_getpass = getpass.getpass
81        def _getpass(prompt):
82            return 'password'
83        getpass.getpass = _getpass
84        urllib.request._opener = None
85        self.old_opener = urllib.request.build_opener
86        self.conn = urllib.request.build_opener = FakeOpener()
87
88    def tearDown(self):
89        getpass.getpass = self._old_getpass
90        urllib.request._opener = None
91        urllib.request.build_opener = self.old_opener
92        super(RegisterTestCase, self).tearDown()
93
94    def _get_cmd(self, metadata=None):
95        if metadata is None:
96            metadata = {'url': 'xxx', 'author': 'xxx',
97                        'author_email': 'xxx',
98                        'name': 'xxx', 'version': 'xxx'}
99        pkg_info, dist = self.create_dist(**metadata)
100        return register(dist)
101
102    def test_create_pypirc(self):
103        # this test makes sure a .pypirc file
104        # is created when requested.
105
106        # let's create a register instance
107        cmd = self._get_cmd()
108
109        # we shouldn't have a .pypirc file yet
110        self.assertFalse(os.path.exists(self.rc))
111
112        # patching input and getpass.getpass
113        # so register gets happy
114        #
115        # Here's what we are faking :
116        # use your existing login (choice 1.)
117        # Username : 'tarek'
118        # Password : 'password'
119        # Save your login (y/N)? : 'y'
120        inputs = Inputs('1', 'tarek', 'y')
121        register_module.input = inputs.__call__
122        # let's run the command
123        try:
124            cmd.run()
125        finally:
126            del register_module.input
127
128        # we should have a brand new .pypirc file
129        self.assertTrue(os.path.exists(self.rc))
130
131        # with the content similar to WANTED_PYPIRC
132        f = open(self.rc)
133        try:
134            content = f.read()
135            self.assertEqual(content, WANTED_PYPIRC)
136        finally:
137            f.close()
138
139        # now let's make sure the .pypirc file generated
140        # really works : we shouldn't be asked anything
141        # if we run the command again
142        def _no_way(prompt=''):
143            raise AssertionError(prompt)
144        register_module.input = _no_way
145
146        cmd.show_response = 1
147        cmd.run()
148
149        # let's see what the server received : we should
150        # have 2 similar requests
151        self.assertEqual(len(self.conn.reqs), 2)
152        req1 = dict(self.conn.reqs[0].headers)
153        req2 = dict(self.conn.reqs[1].headers)
154
155        self.assertEqual(req1['Content-length'], '1374')
156        self.assertEqual(req2['Content-length'], '1374')
157        self.assertIn(b'xxx', self.conn.reqs[1].data)
158
159    def test_password_not_in_file(self):
160
161        self.write_file(self.rc, PYPIRC_NOPASSWORD)
162        cmd = self._get_cmd()
163        cmd._set_config()
164        cmd.finalize_options()
165        cmd.send_metadata()
166
167        # dist.password should be set
168        # therefore used afterwards by other commands
169        self.assertEqual(cmd.distribution.password, 'password')
170
171    def test_registering(self):
172        # this test runs choice 2
173        cmd = self._get_cmd()
174        inputs = Inputs('2', 'tarek', 'tarek@ziade.org')
175        register_module.input = inputs.__call__
176        try:
177            # let's run the command
178            cmd.run()
179        finally:
180            del register_module.input
181
182        # we should have send a request
183        self.assertEqual(len(self.conn.reqs), 1)
184        req = self.conn.reqs[0]
185        headers = dict(req.headers)
186        self.assertEqual(headers['Content-length'], '608')
187        self.assertIn(b'tarek', req.data)
188
189    def test_password_reset(self):
190        # this test runs choice 3
191        cmd = self._get_cmd()
192        inputs = Inputs('3', 'tarek@ziade.org')
193        register_module.input = inputs.__call__
194        try:
195            # let's run the command
196            cmd.run()
197        finally:
198            del register_module.input
199
200        # we should have send a request
201        self.assertEqual(len(self.conn.reqs), 1)
202        req = self.conn.reqs[0]
203        headers = dict(req.headers)
204        self.assertEqual(headers['Content-length'], '290')
205        self.assertIn(b'tarek', req.data)
206
207    @unittest.skipUnless(docutils is not None, 'needs docutils')
208    def test_strict(self):
209        # testing the script option
210        # when on, the register command stops if
211        # the metadata is incomplete or if
212        # long_description is not reSt compliant
213
214        # empty metadata
215        cmd = self._get_cmd({})
216        cmd.ensure_finalized()
217        cmd.strict = 1
218        self.assertRaises(DistutilsSetupError, cmd.run)
219
220        # metadata are OK but long_description is broken
221        metadata = {'url': 'xxx', 'author': 'xxx',
222                    'author_email': 'éxéxé',
223                    'name': 'xxx', 'version': 'xxx',
224                    'long_description': 'title\n==\n\ntext'}
225
226        cmd = self._get_cmd(metadata)
227        cmd.ensure_finalized()
228        cmd.strict = 1
229        self.assertRaises(DistutilsSetupError, cmd.run)
230
231        # now something that works
232        metadata['long_description'] = 'title\n=====\n\ntext'
233        cmd = self._get_cmd(metadata)
234        cmd.ensure_finalized()
235        cmd.strict = 1
236        inputs = Inputs('1', 'tarek', 'y')
237        register_module.input = inputs.__call__
238        # let's run the command
239        try:
240            cmd.run()
241        finally:
242            del register_module.input
243
244        # strict is not by default
245        cmd = self._get_cmd()
246        cmd.ensure_finalized()
247        inputs = Inputs('1', 'tarek', 'y')
248        register_module.input = inputs.__call__
249        # let's run the command
250        try:
251            cmd.run()
252        finally:
253            del register_module.input
254
255        # and finally a Unicode test (bug #12114)
256        metadata = {'url': 'xxx', 'author': '\u00c9ric',
257                    'author_email': 'xxx', 'name': 'xxx',
258                    'version': 'xxx',
259                    'description': 'Something about esszet \u00df',
260                    'long_description': 'More things about esszet \u00df'}
261
262        cmd = self._get_cmd(metadata)
263        cmd.ensure_finalized()
264        cmd.strict = 1
265        inputs = Inputs('1', 'tarek', 'y')
266        register_module.input = inputs.__call__
267        # let's run the command
268        try:
269            cmd.run()
270        finally:
271            del register_module.input
272
273    @unittest.skipUnless(docutils is not None, 'needs docutils')
274    def test_register_invalid_long_description(self):
275        description = ':funkie:`str`'  # mimic Sphinx-specific markup
276        metadata = {'url': 'xxx', 'author': 'xxx',
277                    'author_email': 'xxx',
278                    'name': 'xxx', 'version': 'xxx',
279                    'long_description': description}
280        cmd = self._get_cmd(metadata)
281        cmd.ensure_finalized()
282        cmd.strict = True
283        inputs = Inputs('2', 'tarek', 'tarek@ziade.org')
284        register_module.input = inputs
285        self.addCleanup(delattr, register_module, 'input')
286
287        self.assertRaises(DistutilsSetupError, cmd.run)
288
289    def test_check_metadata_deprecated(self):
290        # makes sure make_metadata is deprecated
291        cmd = self._get_cmd()
292        with check_warnings() as w:
293            warnings.simplefilter("always")
294            cmd.check_metadata()
295            self.assertEqual(len(w.warnings), 1)
296
297    def test_list_classifiers(self):
298        cmd = self._get_cmd()
299        cmd.list_classifiers = 1
300        cmd.run()
301        results = self.get_logs(INFO)
302        self.assertEqual(results, ['running check', 'xxx'])
303
304    def test_show_response(self):
305        # test that the --show-response option return a well formatted response
306        cmd = self._get_cmd()
307        inputs = Inputs('1', 'tarek', 'y')
308        register_module.input = inputs.__call__
309        cmd.show_response = 1
310        try:
311            cmd.run()
312        finally:
313            del register_module.input
314
315        results = self.get_logs(INFO)
316        self.assertEqual(results[3], 75 * '-' + '\nxxx\n' + 75 * '-')
317
318
319def test_suite():
320    return unittest.makeSuite(RegisterTestCase)
321
322if __name__ == "__main__":
323    run_unittest(test_suite())
324