~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
= Subscription Vouchers =

Subscription vouchers are items sold in the Canonical store.  When
redeemed against a commercial project in Launchpad they give that
project the right to use all of the services of Launchpad, just like
open source projects.

For testing purposes we use a custom XML-RPC transport which
implements a mock proxy in the transport and avoids network traffic.

    >>> from lp.services.salesforce.tests.proxy import (
    ...     SalesforceXMLRPCTestTransport)
    >>> import xmlrpclib
    >>> test_transport = SalesforceXMLRPCTestTransport()
    >>> server = xmlrpclib.ServerProxy("http://example.com",
    ...                                transport=test_transport)

Let's create a convenience function for displaying our voucher lists,
which are just dictionaries in the transport.

    >>> def print_vouchers(vouchers):
    ...     for voucher in vouchers:
    ...         print "%s,%s" % (voucher['voucher_id'], voucher['status'])


== Test the XMLRPC transport ==

All of the sample voucher data in the transport can be shown.

    >>> for voucher in test_transport.vouchers:
    ...     print voucher
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000001,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000002,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000003,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000004,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000005,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-bac000000001,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-bac000000002,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-bac000000003,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-bac000000004,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-bac000000005,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-com000000001,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-com000000002,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-com000000003,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-com000000004,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-com000000005,Reserved

We can retrieve all unredeemed vouchers for a person.

    >>> from lp.registry.interfaces.person import (
    ...     IPersonSet)
    >>> from lp.registry.interfaces.product import (
    ...     IProductSet)
    >>> from zope.security.proxy import removeSecurityProxy
    >>> mark = getUtility(IPersonSet).getByName('mark')
    >>> mark_identifier = removeSecurityProxy(
    ...     mark.account).openid_identifiers.any().identifier

    >>> vouchers = server.getUnredeemedVouchers(mark_identifier)
    >>> print_vouchers(vouchers)
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000001,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000002,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000003,Reserved

A single voucher can be retrieved using the `getVoucher` method.
Unlike the other retrieval methods this one does the lookup based on
the voucher id, not the owner's id.

    >>> voucher = server.getVoucher(
    ...     'LPCBS12-f78df324-0cc2-11dd-8b6b-000000000001')
    >>> print_vouchers([voucher])
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000001,Reserved

Attempting to retrieve a non-existent voucher raises a `NotFound` fault.

    >>> voucher = server.getVoucher('LPCBS12-nonexistent')
    Traceback (most recent call last):
      ...
    Fault: <Fault NotFound: 'The voucher LPCBS12-nonexistent was not found.'>

A single voucher can be redeemed, but must be associated with a project.

    >>> firefox = getUtility(IProductSet).getByName('firefox')
    >>> voucher = vouchers[0]['voucher_id']
    >>> result = server.redeemVoucher(
    ...     voucher, mark_identifier,
    ...     firefox.id, firefox.displayname)
    >>> print result
    True
    >>> vouchers = server.getUnredeemedVouchers(mark_identifier)
    >>> print_vouchers(vouchers)
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000002,Reserved
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000003,Reserved

The test transport can be forced to return a fault for testing.  The
class attribute `forced_fault` can be set to a tuple of (method name,
fault, message).  Once set, any call to the specified method will
return the given fault.

    >>> SalesforceXMLRPCTestTransport.forced_fault = (
    ...     'getServerStatus', 'DeadServer', "He's dead, Jim.")
    >>> status = server.getServerStatus()
    Traceback (most recent call last):
      ...
    Fault: <Fault DeadServer: "He's dead, Jim.">

The forced fault is set or `redeeemVoucher` and to return a `NotFound`
fault.

    >>> SalesforceXMLRPCTestTransport.forced_fault = (
    ...     'redeemVoucher', 'NotFound','Something was not found.')
    >>> voucher = vouchers[0]['voucher_id']
    >>> result = server.redeemVoucher(
    ...     voucher, mark_identifier,
    ...     firefox.id, firefox.displayname)
    Traceback (most recent call last):
      ...
    Fault: <Fault NotFound: 'Something was not found.'>

Calls to other methods work as usual.

    >>> status = server.getServerStatus()
    >>> print status
    Server is running normally

Reset the class variable to disable forced faults.

    >>> SalesforceXMLRPCTestTransport.forced_fault = None


== Test the wrapper ==

The interface `ISalesforceVoucherProxy` defines the interaction with
the Salesforce proxy.

    >>> from lp.services.salesforce.interfaces import (
    ...     ISalesforceVoucher, ISalesforceVoucherProxy)
    >>> from canonical.launchpad.webapp.testing import verifyObject
    >>> voucher_proxy = getUtility(ISalesforceVoucherProxy)
    >>> verifyObject(ISalesforceVoucherProxy, voucher_proxy)
    True

The status of the proxy server can be checked.

    >>> status = voucher_proxy.getServerStatus()
    >>> print status
    Server is running normally

All of the unredeemed vouchers for a Launchpad user can be retrieved.

If the user has no matching vouchers an empty list is returned.

    >>> foobar = getUtility(IPersonSet).getByName('name16')
    >>> vouchers = voucher_proxy.getUnredeemedVouchers(foobar)
    >>> len(vouchers)
    0

Similarly the call to get all of a user's vouchers returns the empty
list when she has none.

    >>> vouchers = voucher_proxy.getAllVouchers(foobar)
    >>> len(vouchers)
    0

Get a single voucher by id.

    >>> voucher = voucher_proxy.getVoucher(
    ...     'LPCBS12-f78df324-0cc2-11dd-8b6b-000000000003')
    >>> print voucher
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000003,Reserved,12,unassigned
    >>> verifyObject(ISalesforceVoucher, voucher)
    True

Mark has some vouchers and they can be retrieved.

    >>> vouchers = voucher_proxy.getUnredeemedVouchers(mark)
    >>> for voucher in vouchers:
    ...     print voucher
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000001,Reserved,12,unassigned
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000002,Reserved,12,unassigned
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000003,Reserved,12,unassigned

Get a single voucher with an invalid id.

    >>> voucher = voucher_proxy.getVoucher('LPCBS12-nonexistent')
    Traceback (most recent call last):
      ...
    SVPNotFoundException: The voucher LPCBS12-nonexistent was not found.

Redeem the first voucher for firefox.

    >>> voucher_id = vouchers[0].voucher_id
    >>> result = voucher_proxy.redeemVoucher(voucher_id, mark, firefox)
    >>> print result
    True
    >>> vouchers = voucher_proxy.getUnredeemedVouchers(mark)
    >>> for voucher in vouchers:
    ...     print voucher
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000002,Reserved,12,unassigned
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000003,Reserved,12,unassigned

If we get all of the vouchers the redeemed one is shown.

    >>> vouchers = voucher_proxy.getAllVouchers(mark)
    >>> for voucher in vouchers:
    ...     print voucher
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000001,Redeemed,12,firefox
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000002,Reserved,12,unassigned
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000003,Reserved,12,unassigned

Attempting to redeem an already redeemed voucher is unsuccessful.

    >>> result = voucher_proxy.redeemVoucher(voucher_id, mark, firefox)
    Traceback (most recent call last):
      ...
    SVPAlreadyRedeemedException: Voucher LPCBS12-f78df324-0cc2-11dd-8b6b-000000000001 is already redeemed

Attempting to redeem a bogus voucher is also unsuccessful.

    >>> voucher_id = "hownowbrowncow"
    >>> result = voucher_proxy.redeemVoucher(voucher_id, mark, firefox)
    Traceback (most recent call last):
      ...
    SVPNotFoundException: No such voucher hownowbrowncow

A user cannot redeem a voucher if it is owned by someone else.

    >>> cprov = getUtility(IPersonSet).getByName('cprov')
    >>> vouchers = voucher_proxy.getUnredeemedVouchers(cprov)
    >>> voucher = vouchers[0]
    >>> result = voucher_proxy.redeemVoucher(voucher.voucher_id, mark, firefox)
    Traceback (most recent call last):
      ...
    SVPNotAllowedException: Voucher is not owned by named user

Using None for the project results in an error.

    >>> result = voucher_proxy.redeemVoucher(voucher_id, mark, None)
    Traceback (most recent call last):
      ...
    AttributeError:...

Using None for the person results in an error.

    >>> result = voucher_proxy.redeemVoucher(voucher_id, None, firefox)
    Traceback (most recent call last):
      ...
    AttributeError:...

If a project's name changes we can communicate that to Salesforce.

    >>> from zope.security.proxy import removeSecurityProxy
    >>> naked_firefox = removeSecurityProxy(firefox)
    >>> print naked_firefox.displayname
    Mozilla Firefox
    >>> naked_firefox.displayname = 'Super Mozilla Firefox'
    >>> print naked_firefox.displayname
    Super Mozilla Firefox
    >>> result = voucher_proxy.updateProjectName(naked_firefox)
    >>> print result
    1

Attempting to rename a project that does not exist in the Salesforce
voucher data results in an error.

    >>> jokosher = getUtility(IProductSet).getByName('jokosher')
    >>> result = voucher_proxy.updateProjectName(jokosher)
    Traceback (most recent call last):
      ...
    SVPNotFoundException: No vouchers matching product id...

A voucher can be created and assigned to a user.

    >>> vouchers = voucher_proxy.getUnredeemedVouchers(cprov)
    >>> for voucher in vouchers:
    ...     print voucher
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000004,Reserved,12,unassigned
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000005,Reserved,12,unassigned

    >>> voucher_id = voucher_proxy.grantVoucher(mark, mark, cprov, 12)
    >>> print voucher_id
    LPCBS12-f78df324-0cc2-11dd-0000-000000000001

    >>> voucher_id = voucher_proxy.grantVoucher(mark, mark, cprov, 6)
    >>> print voucher_id
    LPCBS06-f78df324-0cc2-11dd-0000-000000000002

    >>> vouchers = voucher_proxy.getUnredeemedVouchers(cprov)
    >>> for voucher in vouchers:
    ...     print voucher
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000004,Reserved,12,unassigned
    LPCBS12-f78df324-0cc2-11dd-8b6b-000000000005,Reserved,12,unassigned
    LPCBS12-f78df324-0cc2-11dd-0000-000000000001,Reserved,12,unassigned
    LPCBS06-f78df324-0cc2-11dd-0000-000000000002,Reserved,6,unassigned