Merge lp:~elopio/u1-test-utils/pay_client_return_response into lp:u1-test-utils
- pay_client_return_response
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Leo Arias |
Approved revision: | 43 |
Merged at revision: | 40 |
Proposed branch: | lp:~elopio/u1-test-utils/pay_client_return_response |
Merge into: | lp:u1-test-utils |
Diff against target: |
297 lines (+123/-99) 5 files modified
requirements.txt (+1/-1) u1testutils/pay/__init__.py (+23/-0) u1testutils/pay/client.py (+59/-75) u1testutils/pay/data.py (+5/-0) u1testutils/pay/selftests/unit/test_pay_api_client.py (+35/-23) |
To merge this branch: | bzr merge lp:~elopio/u1-test-utils/pay_client_return_response |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Matias Bordese (community) | Approve | ||
Review via email: mp+155609@code.launchpad.net |
Commit message
Return the response from the pay api client, instead of a boolean.
Description of the change
With this change, the pay API test will be able to use the helpers, reducing duplication.
This branch requires that https:/
Leo Arias (elopio) wrote : | # |
<matiasb> pindonga: ok, I'll try the fix above (about create) but... if we are passing a CardRequest object as data, can we check using assert_called_with? and related to this, can you take a look at https:/
<matiasb> elopio: ^
<elopio> sounds fair :)
<pindonga> matiasb, you can assert that the card request object was the one passed in
<pindonga> and since you just set the value on that object, it can be assumed it will have the right value
<pindonga> matiasb, you mean that the mp there is duplicating some code by adding this wrapper?
<matiasb> pindonga: ok, I'll take a look, that may be possible; about that META dict, that is required when setting up the payment to add the credit card; the payment requires some session info, and session info is pulled from the request (that's the IP address taken from the request)
<pindonga> ah, ok
<matiasb> pindonga: I mean that it looks like we need to wrap the payclient calls to use them... and it sounds weird, have an API client that we can't use easily here, I think
<pindonga> matiasb, what I don't understand is why you say we *need* to wrap them
<pindonga> surely the api client can be used as it is
<matiasb> pindonga: right, more like a philosophical question, I guess; if you check l.114 of that MP, in this case, we are getting the shop_id, billing address and card info, and then reorganizing all that to create a CardRequest to pass to the client method as data... wondering if it won't be better at some point to be able to pass data as kwargs
<matiasb> or maybe we need more friendly ways to create/initialize the *Request objects (*: Payment, Card, etc)
<nessita> matiasb, pindonga: IMHO, we should tweak the payment client api so kwargs can be passed
<matiasb> I mean, we usually get the billing address, user info, and card info from different sources, but we need to join them all to create a request object to pass as data argument for api calls
<elopio> this are the kind of objects we use in acceptance testing, because we need easy ways to get a unique object and then edit some fields from it.
<nessita> instead of needing to create a FooRequest object each time, outside the call
<elopio> I'm not sure if the same typo of objects would make sense for the real software.
<nessita> elopio, why not?
<matiasb> elopio: yes, if you use the payclient, it will validate you are passing the right kind of object as arg to the method call
<nessita> elopio, as matiasb says, the FooRequest object have to be created by any software wanting to call the payclient library
<elopio> I think I should understand better what's your proposal :)
<elopio> matiasb, nessita: so, you are against the payclient receiving Request objects?
<pindonga> matiasb, I think I understand
<nessita> elopio, the payclient already does that, no?
<pindonga> so having a single CreditCardRequest object is "bad" as it forces us to create the object
<pindonga> it would be better to just have kwargs as params
<pindon...
Preview Diff
1 | === modified file 'requirements.txt' |
2 | --- requirements.txt 2013-03-26 05:16:33 +0000 |
3 | +++ requirements.txt 2013-03-27 20:56:21 +0000 |
4 | @@ -5,6 +5,6 @@ |
5 | pyflakes |
6 | Twisted |
7 | bzr+ssh://bazaar.launchpad.net/~bloodearnest/localmail/trunk |
8 | -bzr+http://bazaar.launchpad.net/~ubuntuone-hackers/payclient/trunk@1 |
9 | +bzr+http://bazaar.launchpad.net/~ubuntuone-hackers/payclient/trunk@3 |
10 | bzr+http://bazaar.launchpad.net/~canonical-isd-qa/selenium-simple-test/trunk@367 |
11 | bzr+http://bazaar.launchpad.net/~canonical-isd-hackers/canonical-identity-provider/ssoclient@3 |
12 | |
13 | === modified file 'u1testutils/pay/__init__.py' |
14 | --- u1testutils/pay/__init__.py 2013-03-26 05:48:26 +0000 |
15 | +++ u1testutils/pay/__init__.py 2013-03-27 20:56:21 +0000 |
16 | @@ -0,0 +1,23 @@ |
17 | +# Copyright 2013 Canonical Ltd. |
18 | +# |
19 | +# This program is free software: you can redistribute it and/or modify it |
20 | +# under the terms of the GNU Lesser General Public License version 3, as |
21 | +# published by the Free Software Foundation. |
22 | +# |
23 | +# This program is distributed in the hope that it will be useful, but |
24 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
25 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
26 | +# PURPOSE. See the GNU Lesser General Public License for more details. |
27 | +# |
28 | +# You should have received a copy of the GNU General Public License along |
29 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
30 | + |
31 | +__all__ = [ |
32 | + 'APIClient', |
33 | + 'data', |
34 | + 'environment', |
35 | +] |
36 | + |
37 | +import data |
38 | +import environment |
39 | +from client import APIClient |
40 | |
41 | === modified file 'u1testutils/pay/client.py' |
42 | --- u1testutils/pay/client.py 2013-03-26 06:19:46 +0000 |
43 | +++ u1testutils/pay/client.py 2013-03-27 20:56:21 +0000 |
44 | @@ -14,83 +14,67 @@ |
45 | |
46 | import payclient |
47 | from django.conf import settings |
48 | -from piston_mini_client import auth, failhandlers |
49 | |
50 | import u1testutils.pay.environment |
51 | - |
52 | - |
53 | -def get_pay_client(pay_server_url=None): |
54 | - """Return the Payment Service API client. |
55 | - |
56 | - pay_server_url -- The URL of the Pay server that provides the API. Default |
57 | - is None, which means that the URL will be taken from value of the |
58 | - Django settings PAY_SERVER_URL option. |
59 | +from piston_mini_client import auth |
60 | + |
61 | + |
62 | +class APIClient(object): |
63 | + """API client helper for payment tests. |
64 | + |
65 | + It extends the PaymentServiceAPI to take the credentials from the Django |
66 | + configuration file and to make it easier for the tests to call the API |
67 | + methods. |
68 | |
69 | """ |
70 | - user = settings.PAY_API_USERNAME |
71 | - password = settings.PAY_API_PASSWORD |
72 | - basic_auth = auth.BasicAuthorizer(user, password) |
73 | - pay_server_url = _get_pay_server_url(pay_server_url) |
74 | - pay_api_url = '{0}/api/2.0/'.format(pay_server_url) |
75 | - return payclient.PaymentServiceAPI( |
76 | - auth=basic_auth, service_root=pay_api_url) |
77 | - |
78 | - |
79 | -def _get_pay_server_url(pay_server_url=None): |
80 | - if pay_server_url is None: |
81 | - pay_server_url = \ |
82 | - u1testutils.pay.environment.get_pay_base_url() |
83 | - else: |
84 | - pay_server_url = pay_server_url.rstrip('/') |
85 | - return pay_server_url |
86 | - |
87 | - |
88 | -def add_new_credit_card(shop_id, user, credit_card, billing_address, |
89 | - pay_server_url=None): |
90 | - """Add a new credit card through the Pay API. |
91 | - |
92 | - Keyword arguments: |
93 | - shop_id -- The identifier of the shop that will add the credit card. |
94 | - user -- The user that will have the new credit card. It must have the |
95 | - openid attribute. |
96 | - credit_card -- The credit card information. It must have the attributes |
97 | + |
98 | + def __init__(self, pay_server_url=None): |
99 | + pay_server_url = self._get_pay_server_url(pay_server_url) |
100 | + user = settings.PAY_API_USERNAME |
101 | + password = settings.PAY_API_PASSWORD |
102 | + basic_auth = auth.BasicAuthorizer(user, password) |
103 | + pay_api_url = '{0}/api/2.0/'.format(pay_server_url) |
104 | + self.client = payclient.PaymentServiceAPI( |
105 | + auth=basic_auth, service_root=pay_api_url) |
106 | + |
107 | + def _get_pay_server_url(self, pay_server_url=None): |
108 | + if pay_server_url is None: |
109 | + pay_server_url = u1testutils.pay.environment.get_pay_base_url() |
110 | + else: |
111 | + pay_server_url = pay_server_url.rstrip('/') |
112 | + return pay_server_url |
113 | + |
114 | + def add_new_credit_card(self, shop_id, user, credit_card, billing_address): |
115 | + """Add a new credit card through the Pay API. |
116 | + |
117 | + Keyword arguments: |
118 | + shop_id -- The identifier of the shop that will add the credit card. |
119 | + user -- The user that will have the new credit card. It must have the |
120 | + openid attribute. |
121 | + credit_card -- The credit card information. It must have the attributes |
122 | card_type, name_on_cad, card_number, ccv_number, expiration_month and |
123 | - expiration_year. |
124 | - billing_address -- The billing address for the card. It must have the |
125 | - attributes street_line1, state, country and postal_code. The country |
126 | - is the name of the country in english. |
127 | - pay_server_url -- The URL of the Pay server that provides the API. Default |
128 | - is None. |
129 | - |
130 | - """ |
131 | - billing_address_dict = { |
132 | - 'street': billing_address.street_line1, |
133 | - 'state': billing_address.state, |
134 | - 'country': _get_country_code(billing_address.country), |
135 | - 'postal': billing_address.postal_code |
136 | - } |
137 | - kwargs = dict( |
138 | - shop_id=shop_id, |
139 | - open_id=user.openid, |
140 | - card_type=credit_card.card_type, |
141 | - card_holder=credit_card.name_on_card, |
142 | - card_number=credit_card.card_number, |
143 | - card_ccv=credit_card.ccv_number, |
144 | - card_expiration_month=credit_card.expiration_month, |
145 | - card_expiration_year=credit_card.expiration_year, |
146 | - billing_address=billing_address_dict |
147 | - ) |
148 | - credit_card_request = payclient.CreditCardRequest(**kwargs) |
149 | - pay_client = get_pay_client(pay_server_url) |
150 | - try: |
151 | - response = pay_client.new_credit_card(data=credit_card_request) |
152 | - return isinstance(response, payclient.CreditCardResponse) |
153 | - except failhandlers.APIError: |
154 | - # TODO log the error. |
155 | - return False |
156 | - |
157 | - |
158 | -def _get_country_code(country_name): |
159 | - # TODO add more? |
160 | - country_data = {'United States': 'US'} |
161 | - return country_data.get(country_name) |
162 | + expiration_year. |
163 | + billing_address -- The billing address for the card. It must have the |
164 | + attributes street_line1, state, country and postal_code. The |
165 | + country is the name of the country in english. |
166 | + |
167 | + """ |
168 | + billing_address_dict = { |
169 | + 'street': billing_address.street_line1, |
170 | + 'state': billing_address.state, |
171 | + 'country': billing_address.get_country_code(), |
172 | + 'postal': billing_address.postal_code |
173 | + } |
174 | + kwargs = dict( |
175 | + shop_id=shop_id, |
176 | + open_id=user.openid, |
177 | + card_type=credit_card.card_type, |
178 | + card_holder=credit_card.name_on_card, |
179 | + card_number=credit_card.card_number, |
180 | + card_ccv=credit_card.ccv_number, |
181 | + card_expiration_month=credit_card.expiration_month, |
182 | + card_expiration_year=credit_card.expiration_year, |
183 | + billing_address=billing_address_dict |
184 | + ) |
185 | + credit_card_request = payclient.CreditCardRequest(**kwargs) |
186 | + return self.client.new_credit_card(data=credit_card_request) |
187 | |
188 | === modified file 'u1testutils/pay/data.py' |
189 | --- u1testutils/pay/data.py 2013-03-26 05:48:26 +0000 |
190 | +++ u1testutils/pay/data.py 2013-03-27 20:56:21 +0000 |
191 | @@ -69,3 +69,8 @@ |
192 | country = "United States" |
193 | return cls( |
194 | street_line1, street_line2, city, state, postal_code, country) |
195 | + |
196 | + def get_country_code(self): |
197 | + # TODO add more? |
198 | + country_data = {'United States': 'US'} |
199 | + return country_data.get(self.country) |
200 | |
201 | === modified file 'u1testutils/pay/selftests/unit/test_pay_api_client.py' |
202 | --- u1testutils/pay/selftests/unit/test_pay_api_client.py 2013-03-26 15:11:44 +0000 |
203 | +++ u1testutils/pay/selftests/unit/test_pay_api_client.py 2013-03-27 20:56:21 +0000 |
204 | @@ -16,7 +16,6 @@ |
205 | |
206 | import mock |
207 | import payclient |
208 | -from piston_mini_client import failhandlers |
209 | |
210 | import u1testutils.django |
211 | from u1testutils.pay import client |
212 | @@ -27,34 +26,35 @@ |
213 | class APIClientTestCase(unittest.TestCase): |
214 | |
215 | def test_get_api_client(self): |
216 | - api_client = client.get_pay_client() |
217 | - self.assertTrue(isinstance(api_client, payclient.PaymentServiceAPI)) |
218 | + api_client = client.APIClient() |
219 | + self.assertTrue( |
220 | + isinstance(api_client.client, payclient.PaymentServiceAPI)) |
221 | |
222 | def test_get_api_client_with_default_url(self): |
223 | - api_client = client.get_pay_client() |
224 | + api_client = client.APIClient() |
225 | self.assertEquals( |
226 | - api_client._service_root, 'http://localhost:8000/api/2.0/') |
227 | + api_client.client._service_root, 'http://localhost:8000/api/2.0/') |
228 | |
229 | def test_get_api_client_with_settings_url(self): |
230 | with u1testutils.django.patch_settings( |
231 | PAY_SERVER_URL='http://settings.pay.local:8000'): |
232 | - api_client = client.get_pay_client() |
233 | + api_client = client.APIClient() |
234 | self.assertEquals( |
235 | - api_client._service_root, |
236 | + api_client.client._service_root, |
237 | 'http://settings.pay.local:8000/api/2.0/') |
238 | |
239 | def test_get_api_client_with_argument_url(self): |
240 | pay_server_url = 'http://argument.pay.local:8000' |
241 | - api_client = client.get_pay_client(pay_server_url) |
242 | + api_client = client.APIClient(pay_server_url) |
243 | self.assertEquals( |
244 | - api_client._service_root, |
245 | + api_client.client._service_root, |
246 | 'http://argument.pay.local:8000/api/2.0/') |
247 | |
248 | def test_get_api_client_with_argument_url_and_trailing_slash(self): |
249 | pay_server_url = 'http://argument.pay.local:8000/' |
250 | - api_client = client.get_pay_client(pay_server_url) |
251 | + api_client = client.APIClient(pay_server_url) |
252 | self.assertEquals( |
253 | - api_client._service_root, |
254 | + api_client.client._service_root, |
255 | 'http://argument.pay.local:8000/api/2.0/') |
256 | |
257 | |
258 | @@ -67,15 +67,27 @@ |
259 | self.billing_address = pay_data.Address.make_unique() |
260 | |
261 | def test_new_credit_card_added(self): |
262 | - with mock.patch('payclient.PaymentServiceAPI.new_credit_card', |
263 | - return_value=payclient.CreditCardResponse()): |
264 | - added = client.add_new_credit_card( |
265 | - 'TEST', self.user, self.credit_card, self.billing_address) |
266 | - self.assertTrue(added) |
267 | - |
268 | - def test_add_new_credit_card_failed(self): |
269 | - with mock.patch('payclient.PaymentServiceAPI.new_credit_card', |
270 | - side_effect=failhandlers.APIError('test')): |
271 | - added = client.add_new_credit_card( |
272 | - 'TEST', self.user, self.credit_card, self.billing_address) |
273 | - self.assertFalse(added) |
274 | + shop_id = 'TEST' |
275 | + api_client = client.APIClient() |
276 | + with mock.patch('payclient.PaymentServiceAPI.new_credit_card') \ |
277 | + as mock_api_call: |
278 | + api_client.add_new_credit_card( |
279 | + shop_id, self.user, self.credit_card, self.billing_address) |
280 | + expected_params = { |
281 | + 'shop_id': shop_id, |
282 | + 'open_id': self.user.openid, |
283 | + 'card_type': self.credit_card.card_type, |
284 | + 'card_holder': self.credit_card.name_on_card, |
285 | + 'card_number': self.credit_card.card_number, |
286 | + 'card_ccv': self.credit_card.ccv_number, |
287 | + 'card_expiration_month': self.credit_card.expiration_month, |
288 | + 'card_expiration_year': self.credit_card.expiration_year, |
289 | + 'billing_address': { |
290 | + 'street': self.billing_address.street_line1, |
291 | + 'state': self.billing_address.state, |
292 | + 'postal': self.billing_address.postal_code, |
293 | + 'country': 'US', |
294 | + } |
295 | + } |
296 | + mock_api_call.assert_called_with( |
297 | + data=payclient.CreditCardRequest(**expected_params)) |
+1