1# -*- encoding: utf8 -*-
2"""Tests for distutils.command.register."""
3import os
4import unittest
5import getpass
6import urllib2
7import warnings
8
9from test.test_support import check_warnings, run_unittest
10
11from distutils.command import register as register_module
12from distutils.command.register import register
13from distutils.errors import DistutilsSetupError
14
15from distutils.tests.test_config import PyPIRCCommandTestCase
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 RawInputs(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):
63        self.reqs.append(req)
64        return self
65
66    def read(self):
67        return 'xxx'
68
69class RegisterTestCase(PyPIRCCommandTestCase):
70
71    def setUp(self):
72        super(RegisterTestCase, self).setUp()
73        # patching the password prompt
74        self._old_getpass = getpass.getpass
75        def _getpass(prompt):
76            return 'password'
77        getpass.getpass = _getpass
78        self.old_opener = urllib2.build_opener
79        self.conn = urllib2.build_opener = FakeOpener()
80
81    def tearDown(self):
82        getpass.getpass = self._old_getpass
83        urllib2.build_opener = self.old_opener
84        super(RegisterTestCase, self).tearDown()
85
86    def _get_cmd(self, metadata=None):
87        if metadata is None:
88            metadata = {'url': 'xxx', 'author': 'xxx',
89                        'author_email': 'xxx',
90                        'name': 'xxx', 'version': 'xxx'}
91        pkg_info, dist = self.create_dist(**metadata)
92        return register(dist)
93
94    def test_create_pypirc(self):
95        # this test makes sure a .pypirc file
96        # is created when requested.
97
98        # let's create a register instance
99        cmd = self._get_cmd()
100
101        # we shouldn't have a .pypirc file yet
102        self.assertFalse(os.path.exists(self.rc))
103
104        # patching raw_input and getpass.getpass
105        # so register gets happy
106        #
107        # Here's what we are faking :
108        # use your existing login (choice 1.)
109        # Username : 'tarek'
110        # Password : 'password'
111        # Save your login (y/N)? : 'y'
112        inputs = RawInputs('1', 'tarek', 'y')
113        register_module.raw_input = inputs.__call__
114        # let's run the command
115        try:
116            cmd.run()
117        finally:
118            del register_module.raw_input
119
120        # we should have a brand new .pypirc file
121        self.assertTrue(os.path.exists(self.rc))
122
123        # with the content similar to WANTED_PYPIRC
124        f = open(self.rc)
125        try:
126            content = f.read()
127            self.assertEqual(content, WANTED_PYPIRC)
128        finally:
129            f.close()
130
131        # now let's make sure the .pypirc file generated
132        # really works : we shouldn't be asked anything
133        # if we run the command again
134        def _no_way(prompt=''):
135            raise AssertionError(prompt)
136        register_module.raw_input = _no_way
137
138        cmd.show_response = 1
139        cmd.run()
140
141        # let's see what the server received : we should
142        # have 2 similar requests
143        self.assertEqual(len(self.conn.reqs), 2)
144        req1 = dict(self.conn.reqs[0].headers)
145        req2 = dict(self.conn.reqs[1].headers)
146        self.assertEqual(req2['Content-length'], req1['Content-length'])
147        self.assertIn('xxx', self.conn.reqs[1].data)
148
149    def test_password_not_in_file(self):
150
151        self.write_file(self.rc, PYPIRC_NOPASSWORD)
152        cmd = self._get_cmd()
153        cmd._set_config()
154        cmd.finalize_options()
155        cmd.send_metadata()
156
157        # dist.password should be set
158        # therefore used afterwards by other commands
159        self.assertEqual(cmd.distribution.password, 'password')
160
161    def test_registering(self):
162        # this test runs choice 2
163        cmd = self._get_cmd()
164        inputs = RawInputs('2', 'tarek', 'tarek@ziade.org')
165        register_module.raw_input = inputs.__call__
166        try:
167            # let's run the command
168            cmd.run()
169        finally:
170            del register_module.raw_input
171
172        # we should have send a request
173        self.assertEqual(len(self.conn.reqs), 1)
174        req = self.conn.reqs[0]
175        headers = dict(req.headers)
176        self.assertEqual(headers['Content-length'], '608')
177        self.assertIn('tarek', req.data)
178
179    def test_password_reset(self):
180        # this test runs choice 3
181        cmd = self._get_cmd()
182        inputs = RawInputs('3', 'tarek@ziade.org')
183        register_module.raw_input = inputs.__call__
184        try:
185            # let's run the command
186            cmd.run()
187        finally:
188            del register_module.raw_input
189
190        # we should have send a request
191        self.assertEqual(len(self.conn.reqs), 1)
192        req = self.conn.reqs[0]
193        headers = dict(req.headers)
194        self.assertEqual(headers['Content-length'], '290')
195        self.assertIn('tarek', req.data)
196
197    @unittest.skipUnless(docutils is not None, 'needs docutils')
198    def test_strict(self):
199        # testing the script option
200        # when on, the register command stops if
201        # the metadata is incomplete or if
202        # long_description is not reSt compliant
203
204        # empty metadata
205        cmd = self._get_cmd({})
206        cmd.ensure_finalized()
207        cmd.strict = 1
208        self.assertRaises(DistutilsSetupError, cmd.run)
209
210        # metadata are OK but long_description is broken
211        metadata = {'url': 'xxx', 'author': 'xxx',
212                    'author_email': u'éxéxé',
213                    'name': 'xxx', 'version': 'xxx',
214                    'long_description': 'title\n==\n\ntext'}
215
216        cmd = self._get_cmd(metadata)
217        cmd.ensure_finalized()
218        cmd.strict = 1
219        self.assertRaises(DistutilsSetupError, cmd.run)
220
221        # now something that works
222        metadata['long_description'] = 'title\n=====\n\ntext'
223        cmd = self._get_cmd(metadata)
224        cmd.ensure_finalized()
225        cmd.strict = 1
226        inputs = RawInputs('1', 'tarek', 'y')
227        register_module.raw_input = inputs.__call__
228        # let's run the command
229        try:
230            cmd.run()
231        finally:
232            del register_module.raw_input
233
234        # strict is not by default
235        cmd = self._get_cmd()
236        cmd.ensure_finalized()
237        inputs = RawInputs('1', 'tarek', 'y')
238        register_module.raw_input = inputs.__call__
239        # let's run the command
240        try:
241            cmd.run()
242        finally:
243            del register_module.raw_input
244
245        # and finally a Unicode test (bug #12114)
246        metadata = {'url': u'xxx', 'author': u'\u00c9ric',
247                    'author_email': u'xxx', u'name': 'xxx',
248                    'version': u'xxx',
249                    'description': u'Something about esszet \u00df',
250                    'long_description': u'More things about esszet \u00df'}
251
252        cmd = self._get_cmd(metadata)
253        cmd.ensure_finalized()
254        cmd.strict = 1
255        inputs = RawInputs('1', 'tarek', 'y')
256        register_module.raw_input = inputs.__call__
257        # let's run the command
258        try:
259            cmd.run()
260        finally:
261            del register_module.raw_input
262
263    @unittest.skipUnless(docutils is not None, 'needs docutils')
264    def test_register_invalid_long_description(self):
265        description = ':funkie:`str`'  # mimic Sphinx-specific markup
266        metadata = {'url': 'xxx', 'author': 'xxx',
267                    'author_email': 'xxx',
268                    'name': 'xxx', 'version': 'xxx',
269                    'long_description': description}
270        cmd = self._get_cmd(metadata)
271        cmd.ensure_finalized()
272        cmd.strict = True
273        inputs = RawInputs('2', 'tarek', 'tarek@ziade.org')
274        register_module.raw_input = inputs
275        self.addCleanup(delattr, register_module, 'raw_input')
276        self.assertRaises(DistutilsSetupError, cmd.run)
277
278    def test_check_metadata_deprecated(self):
279        # makes sure make_metadata is deprecated
280        cmd = self._get_cmd()
281        with check_warnings() as w:
282            warnings.simplefilter("always")
283            cmd.check_metadata()
284            self.assertEqual(len(w.warnings), 1)
285
286def test_suite():
287    return unittest.makeSuite(RegisterTestCase)
288
289if __name__ == "__main__":
290    run_unittest(test_suite())
291