~bzr-pqm/bzr/bzr.dev

2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2006 Canonical Ltd
0.4.1 by Martin Pool
Start lp-register command
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
0.4.8 by Martin Pool
More instrumentation of xmlrpc requests
17
import base64
0.4.13 by Martin Pool
Update xmlrpc api to pass product name as a parameter.
18
import os
0.4.8 by Martin Pool
More instrumentation of xmlrpc requests
19
from StringIO import StringIO
2978.5.1 by John Arbash Meinel
Fix bug #162494, 'bzr register-branch' needs proper auth handling.
20
import urlparse
0.4.5 by Martin Pool
Add structured test for parameters passed through xmlrpc
21
import xmlrpclib
22
2978.5.1 by John Arbash Meinel
Fix bug #162494, 'bzr register-branch' needs proper auth handling.
23
from bzrlib import (
24
    config,
25
    osutils,
26
    tests,
27
    ui,
28
    )
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
29
from bzrlib.tests import TestCaseWithTransport, TestSkipped
0.4.1 by Martin Pool
Start lp-register command
30
0.4.5 by Martin Pool
Add structured test for parameters passed through xmlrpc
31
# local import
2898.3.8 by James Henstridge
Get rid of relative imports in Launchpad plugin.
32
from bzrlib.plugins.launchpad.lp_registration import (
0.4.26 by Martin Pool
(register-branch) Add test for link_branch_to_bug and fix its parameters
33
        BaseRequest,
34
        BranchBugLinkRequest,
35
        BranchRegistrationRequest,
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
36
        ResolveLaunchpadPathRequest,
0.4.26 by Martin Pool
(register-branch) Add test for link_branch_to_bug and fix its parameters
37
        LaunchpadService,
38
        )
0.4.5 by Martin Pool
Add structured test for parameters passed through xmlrpc
39
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
40
0.4.12 by Martin Pool
doc
41
# TODO: Test that the command-line client, making sure that it'll pass the
42
# request through to a dummy transport, and that the transport will validate
43
# the results passed in.  Not sure how to get the transport object back out to
44
# validate that its OK - may not be necessary.
45
46
# TODO: Add test for (and implement) other command-line options to set
0.4.14 by Martin Pool
Update xmlrpc api
47
# project, author_email, description.
0.4.12 by Martin Pool
doc
48
49
# TODO: project_id is not properly handled -- must be passed in rpc or path.
50
0.4.8 by Martin Pool
More instrumentation of xmlrpc requests
51
class InstrumentedXMLRPCConnection(object):
52
    """Stands in place of an http connection for the purposes of testing"""
53
54
    def __init__(self, testcase):
55
        self.testcase = testcase
56
57
    def getreply(self):
58
        """Fake the http reply.
59
60
        :returns: (errcode, errmsg, headers)
61
        """
62
        return (200, 'OK', [])
63
64
    def getfile(self):
65
        """Return a fake file containing the response content."""
66
        return StringIO('''\
67
<?xml version="1.0" ?>
68
<methodResponse>
0.4.11 by Martin Pool
Check the correct params are seen by the server
69
    <params>
70
        <param>
71
            <value>
72
                <string>victoria dock</string>
73
            </value>
74
        </param>
75
    </params>
0.4.8 by Martin Pool
More instrumentation of xmlrpc requests
76
</methodResponse>''')
77
78
79
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
80
class InstrumentedXMLRPCTransport(xmlrpclib.Transport):
81
2027.2.2 by Marien Zwart
Fixes for python 2.5.
82
    # Python 2.5's xmlrpclib looks for this.
83
    _use_datetime = False
84
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
85
    def __init__(self, testcase, expect_auth):
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
86
        self.testcase = testcase
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
87
        self.expect_auth = expect_auth
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
88
89
    def make_connection(self, host):
90
        host, http_headers, x509 = self.get_host_info(host)
91
        test = self.testcase
0.4.10 by Martin Pool
Move test-specific values out of InstrumentedXMLRPCTransport
92
        self.connected_host = host
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
93
        if self.expect_auth:
94
            auth_hdrs = [v for k,v in http_headers if k == 'Authorization']
95
            assert len(auth_hdrs) == 1
96
            authinfo = auth_hdrs[0]
97
            expected_auth = 'testuser@launchpad.net:testpassword'
98
            test.assertEquals(authinfo,
99
                    'Basic ' + base64.encodestring(expected_auth).strip())
100
        else:
101
            assert not http_headers
0.4.8 by Martin Pool
More instrumentation of xmlrpc requests
102
        return InstrumentedXMLRPCConnection(test)
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
103
104
    def send_request(self, connection, handler_path, request_body):
105
        test = self.testcase
106
        self.got_request = True
107
108
    def send_host(self, conn, host):
109
        pass
110
111
    def send_user_agent(self, conn):
112
        # TODO: send special user agent string, including bzrlib version
113
        # number
114
        pass
115
116
    def send_content(self, conn, request_body):
117
        unpacked, method = xmlrpclib.loads(request_body)
0.4.10 by Martin Pool
Move test-specific values out of InstrumentedXMLRPCTransport
118
        assert None not in unpacked, \
119
                "xmlrpc result %r shouldn't contain None" % (unpacked,)
120
        self.sent_params = unpacked
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
121
122
0.4.22 by Martin Pool
(register-branch) Update tests to be in-process calls to a mock server.
123
class MockLaunchpadService(LaunchpadService):
124
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
125
    def send_request(self, method_name, method_params, authenticated):
0.4.22 by Martin Pool
(register-branch) Update tests to be in-process calls to a mock server.
126
        """Stash away the method details rather than sending them to a real server"""
127
        self.called_method_name = method_name
128
        self.called_method_params = method_params
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
129
        self.called_authenticated = authenticated
0.4.22 by Martin Pool
(register-branch) Update tests to be in-process calls to a mock server.
130
131
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
132
class TestBranchRegistration(TestCaseWithTransport):
0.4.4 by Martin Pool
Start forming xmlrpc requests
133
    SAMPLE_URL = 'http://bazaar-vcs.org/bzr/bzr.dev/'
0.4.6 by Martin Pool
Put the rest of the parameters into the registration request.
134
    SAMPLE_OWNER = 'jhacker@foo.com'
135
    SAMPLE_BRANCH_ID = 'bzr.dev'
0.4.4 by Martin Pool
Start forming xmlrpc requests
136
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
137
    def setUp(self):
138
        super(TestBranchRegistration, self).setUp()
139
        # make sure we have a reproducible standard environment
2978.5.1 by John Arbash Meinel
Fix bug #162494, 'bzr register-branch' needs proper auth handling.
140
        self._captureVar('BZR_LP_XMLRPC_URL', None)
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
141
0.4.22 by Martin Pool
(register-branch) Update tests to be in-process calls to a mock server.
142
    def test_register_help(self):
143
        """register-branch accepts --help"""
2581.1.4 by Martin Pool
fixup run_bzr syntax in launchpad plugin
144
        out, err = self.run_bzr(['register-branch', '--help'])
0.4.2 by Martin Pool
Rename command to 'register-branch'
145
        self.assertContainsRe(out, r'Register a branch')
146
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
147
    def test_register_no_url_no_branch(self):
0.4.22 by Martin Pool
(register-branch) Update tests to be in-process calls to a mock server.
148
        """register-branch command requires parameters"""
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
149
        self.make_repository('.')
150
        self.run_bzr_error(
151
            ['register-branch requires a public branch url - '
152
             'see bzr help register-branch'],
153
            'register-branch')
154
155
    def test_register_no_url_in_published_branch_no_error(self):
156
        b = self.make_branch('.')
157
        b.set_public_branch('http://test-server.com/bzr/branch')
158
        out, err = self.run_bzr(['register-branch', '--dry-run'])
159
        self.assertEqual('Branch registered.\n', out)
160
        self.assertEqual('', err)
161
162
    def test_register_no_url_in_unpublished_branch_errors(self):
163
        b = self.make_branch('.')
164
        out, err = self.run_bzr_error(['no public branch'],
165
            ['register-branch', '--dry-run'])
166
        self.assertEqual('', out)
0.4.3 by Martin Pool
More command line processing
167
1668.1.12 by Martin Pool
(launchpad plugin) Improved --dry-run that uses a dummy xmlrpc service.
168
    def test_register_dry_run(self):
2581.1.4 by Martin Pool
fixup run_bzr syntax in launchpad plugin
169
        out, err = self.run_bzr(['register-branch',
1668.1.12 by Martin Pool
(launchpad plugin) Improved --dry-run that uses a dummy xmlrpc service.
170
                                'http://test-server.com/bzr/branch',
2581.1.4 by Martin Pool
fixup run_bzr syntax in launchpad plugin
171
                                '--dry-run'])
1668.1.12 by Martin Pool
(launchpad plugin) Improved --dry-run that uses a dummy xmlrpc service.
172
        self.assertEquals(out, 'Branch registered.\n')
0.4.4 by Martin Pool
Start forming xmlrpc requests
173
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
174
    def test_onto_transport(self):
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
175
        """Test how the request is sent by transmitting across a mock Transport"""
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
176
        # use a real transport, but intercept at the http/xml layer
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
177
        transport = InstrumentedXMLRPCTransport(self, expect_auth=True)
0.4.30 by Martin Pool
Fix wierd syntax errors in test
178
        service = LaunchpadService(transport)
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
179
        service.registrant_email = 'testuser@launchpad.net'
180
        service.registrant_password = 'testpassword'
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
181
        rego = BranchRegistrationRequest('http://test-server.com/bzr/branch',
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
182
                'branch-id',
183
                'my test branch',
184
                'description',
185
                'author@launchpad.net',
186
                'product')
0.4.20 by Bjorn Tillenius
try to fix the tests.
187
        rego.submit(service)
3200.2.2 by Robert Collins
* The launchpad plugin now uses the ``edge`` xmlrpc server to avoid
188
        self.assertEquals(transport.connected_host, 'xmlrpc.edge.launchpad.net')
0.4.14 by Martin Pool
Update xmlrpc api
189
        self.assertEquals(len(transport.sent_params), 6)
0.4.11 by Martin Pool
Check the correct params are seen by the server
190
        self.assertEquals(transport.sent_params,
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
191
                ('http://test-server.com/bzr/branch',  # branch_url
192
                 'branch-id',                          # branch_name
193
                 'my test branch',                     # branch_title
194
                 'description',
195
                 'author@launchpad.net',
196
                 'product'))
0.4.8 by Martin Pool
More instrumentation of xmlrpc requests
197
        self.assertTrue(transport.got_request)
0.4.13 by Martin Pool
Update xmlrpc api to pass product name as a parameter.
198
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
199
    def test_onto_transport_unauthenticated(self):
200
        """Test how an unauthenticated request is transmitted across a mock Transport"""
201
        transport = InstrumentedXMLRPCTransport(self, expect_auth=False)
202
        service = LaunchpadService(transport)
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
203
        resolve = ResolveLaunchpadPathRequest('bzr')
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
204
        resolve.submit(service)
3200.2.2 by Robert Collins
* The launchpad plugin now uses the ``edge`` xmlrpc server to avoid
205
        self.assertEquals(transport.connected_host, 'xmlrpc.edge.launchpad.net')
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
206
        self.assertEquals(len(transport.sent_params), 1)
207
        self.assertEquals(transport.sent_params, ('bzr', ))
208
        self.assertTrue(transport.got_request)
209
0.4.22 by Martin Pool
(register-branch) Update tests to be in-process calls to a mock server.
210
    def test_subclass_request(self):
211
        """Define a new type of xmlrpc request"""
212
        class DummyRequest(BaseRequest):
213
            _methodname = 'dummy_request'
214
            def _request_params(self):
215
                return (42,)
216
217
        service = MockLaunchpadService()
218
        service.registrant_email = 'test@launchpad.net'
219
        service.registrant_password = ''
220
        request = DummyRequest()
221
        request.submit(service)
222
        self.assertEquals(service.called_method_name, 'dummy_request')
223
        self.assertEquals(service.called_method_params, (42,))
224
0.4.24 by Martin Pool
(register-branch) additional test case against mock server
225
    def test_mock_server_registration(self):
226
        """Send registration to mock server"""
227
        test_case = self
228
        class MockRegistrationService(MockLaunchpadService):
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
229
            def send_request(self, method_name, method_params, authenticated):
0.4.24 by Martin Pool
(register-branch) additional test case against mock server
230
                test_case.assertEquals(method_name, "register_branch")
231
                test_case.assertEquals(list(method_params),
232
                        ['url', 'name', 'title', 'description', 'email', 'name'])
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
233
                test_case.assertEquals(authenticated, True)
0.4.24 by Martin Pool
(register-branch) additional test case against mock server
234
                return 'result'
235
        service = MockRegistrationService()
236
        rego = BranchRegistrationRequest('url', 'name', 'title',
237
                        'description', 'email', 'name')
238
        result = rego.submit(service)
239
        self.assertEquals(result, 'result')
0.4.25 by Martin Pool
(register-branch) additional test for registration with defaults
240
241
    def test_mock_server_registration_with_defaults(self):
242
        """Send registration to mock server"""
243
        test_case = self
244
        class MockRegistrationService(MockLaunchpadService):
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
245
            def send_request(self, method_name, method_params, authenticated):
0.4.25 by Martin Pool
(register-branch) additional test for registration with defaults
246
                test_case.assertEquals(method_name, "register_branch")
247
                test_case.assertEquals(list(method_params),
248
                        ['http://server/branch', 'branch', '', '', '', ''])
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
249
                test_case.assertEquals(authenticated, True)
0.4.25 by Martin Pool
(register-branch) additional test for registration with defaults
250
                return 'result'
251
        service = MockRegistrationService()
252
        rego = BranchRegistrationRequest('http://server/branch')
253
        result = rego.submit(service)
254
        self.assertEquals(result, 'result')
0.4.26 by Martin Pool
(register-branch) Add test for link_branch_to_bug and fix its parameters
255
256
    def test_mock_bug_branch_link(self):
257
        """Send bug-branch link to mock server"""
258
        test_case = self
259
        class MockService(MockLaunchpadService):
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
260
            def send_request(self, method_name, method_params, authenticated):
0.4.26 by Martin Pool
(register-branch) Add test for link_branch_to_bug and fix its parameters
261
                test_case.assertEquals(method_name, "link_branch_to_bug")
262
                test_case.assertEquals(list(method_params),
263
                        ['http://server/branch', 1234, ''])
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
264
                test_case.assertEquals(authenticated, True)
0.4.26 by Martin Pool
(register-branch) Add test for link_branch_to_bug and fix its parameters
265
                return 'http://launchpad.net/bug/1234'
266
        service = MockService()
267
        rego = BranchBugLinkRequest('http://server/branch', 1234)
268
        result = rego.submit(service)
269
        self.assertEquals(result, 'http://launchpad.net/bug/1234')
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
270
271
    def test_mock_resolve_lp_url(self):
272
        test_case = self
273
        class MockService(MockLaunchpadService):
274
            def send_request(self, method_name, method_params, authenticated):
2898.4.7 by James Henstridge
Fix up tests.
275
                test_case.assertEquals(method_name, "resolve_lp_path")
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
276
                test_case.assertEquals(list(method_params), ['bzr'])
277
                test_case.assertEquals(authenticated, False)
2898.4.4 by James Henstridge
Changes to account for modifications to the XMLRPC API.
278
                return dict(urls=[
279
                        'bzr+ssh://bazaar.launchpad.net~bzr/bzr/trunk',
280
                        'sftp://bazaar.launchpad.net~bzr/bzr/trunk',
281
                        'bzr+http://bazaar.launchpad.net~bzr/bzr/trunk',
282
                        'http://bazaar.launchpad.net~bzr/bzr/trunk'])
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
283
        service = MockService()
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
284
        resolve = ResolveLaunchpadPathRequest('bzr')
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
285
        result = resolve.submit(service)
2898.4.4 by James Henstridge
Changes to account for modifications to the XMLRPC API.
286
        self.assertTrue('urls' in result)
287
        self.assertEquals(result['urls'], [
288
                'bzr+ssh://bazaar.launchpad.net~bzr/bzr/trunk',
289
                'sftp://bazaar.launchpad.net~bzr/bzr/trunk',
290
                'bzr+http://bazaar.launchpad.net~bzr/bzr/trunk',
291
                'http://bazaar.launchpad.net~bzr/bzr/trunk'])
2978.5.2 by John Arbash Meinel
merge bzr.dev 2991
292
2978.5.1 by John Arbash Meinel
Fix bug #162494, 'bzr register-branch' needs proper auth handling.
293
294
class TestGatherUserCredentials(tests.TestCaseInTempDir):
295
296
    def setUp(self):
297
        super(TestGatherUserCredentials, self).setUp()
298
        # make sure we have a reproducible standard environment
299
        self._captureVar('BZR_LP_XMLRPC_URL', None)
300
301
    def test_gather_user_credentials_has_password(self):
302
        service = LaunchpadService()
303
        service.registrant_password = 'mypassword'
304
        # This should be a basic no-op, since we already have the password
305
        service.gather_user_credentials()
306
        self.assertEqual('mypassword', service.registrant_password)
307
308
    def test_gather_user_credentials_from_auth_conf(self):
309
        auth_path = config.authentication_config_filename()
310
        service = LaunchpadService()
311
        g_conf = config.GlobalConfig()
312
        g_conf.set_user_option('email', 'Test User <test@user.com>')
313
        f = open(auth_path, 'wb')
314
        try:
315
            scheme, hostinfo = urlparse.urlsplit(service.service_url)[:2]
316
            f.write('[section]\n'
317
                    'scheme=%s\n'
318
                    'host=%s\n'
319
                    'user=test@user.com\n'
320
                    'password=testpass\n'
321
                    % (scheme, hostinfo))
322
        finally:
323
            f.close()
324
        self.assertIs(None, service.registrant_password)
325
        service.gather_user_credentials()
326
        self.assertEqual('test@user.com', service.registrant_email)
327
        self.assertEqual('testpass', service.registrant_password)
328
329
    def test_gather_user_credentials_prompts(self):
330
        service = LaunchpadService()
331
        self.assertIs(None, service.registrant_password)
332
        g_conf = config.GlobalConfig()
333
        g_conf.set_user_option('email', 'Test User <test@user.com>')
334
        stdout = tests.StringIOWrapper()
335
        ui.ui_factory = tests.TestUIFactory(stdin='userpass\n',
336
                                            stdout=stdout)
337
        self.assertIs(None, service.registrant_password)
338
        service.gather_user_credentials()
339
        self.assertEqual('test@user.com', service.registrant_email)
340
        self.assertEqual('userpass', service.registrant_password)
341
        self.assertContainsRe(stdout.getvalue(),
342
                             'launchpad.net password for test@user\\.com')
343