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