source: main/waeup.ikoba/trunk/src/waeup/ikoba/payments/tests/test_paypal.py @ 12444

Last change on this file since 12444 was 12326, checked in by uli, 10 years ago

We cannot do creditcard payments w/o creditcards.

  • Property svn:keywords set to Id
File size: 38.4 KB
Line 
1## $Id: test_paypal.py 12326 2014-12-26 11:28:59Z uli $
2##
3## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18import grok
19import decimal
20import os
21import re
22import shutil
23import tempfile
24import unittest
25import paypalrestsdk
26from zope.component import (
27    getGlobalSiteManager, queryUtility,
28    )
29from zope.component.hooks import setSite, clearSite
30from zope.i18nmessageid.message import Message as i18nMessage
31from zope.interface import implements
32from zope.interface.verify import verifyObject, verifyClass
33from zope.schema.interfaces import IVocabularyTokenized, ITerm
34from waeup.ikoba.interfaces import IPayPalConfig
35from waeup.ikoba.payments.interfaces import (
36    IPaymentGatewayService, IPayer, IPayee, IPaymentItem,
37    )
38from waeup.ikoba.payments.paypal import (
39    get_paypal_config_file_path, parse_paypal_config, get_access_token,
40    configure_sdk, get_payment, Payer, PayerInfo, ShippingAddress,
41    ADDRESS_TYPES_VOCAB, IShippingAddress, Address, IAddress, to_dict,
42    CreditCard, CREDIT_CARD_TYPES_VOCAB, CREDIT_CARD_STATUS_VOCAB,
43    ICreditCard, ICreditCardToken, CreditCardToken, FundingInstrument,
44    AmountDetails, IAmount, Amount, IItem, Item, STOCK_KEEPING_UNITS_VOCAB,
45    IItemList, ItemList, IPaymentOptions, PaymentOptions, ITransaction,
46    Transaction, PAYMENT_OPTION_METHODS_VOCAB, PayPalCreditCardService,
47    PayPalRegularPaymentService,
48    )
49from waeup.ikoba.testing import (
50    FunctionalLayer, FunctionalTestCase,
51    )
52
53
54#
55# PayPal test config
56#
57
58EXTERNAL_PAYPAL_TESTS = False
59
60#
61# End of PayPal test config
62#
63
64
65def external_paypal_test(func):
66    """A decorator that can block test functions.
67    """
68    if not EXTERNAL_PAYPAL_TESTS:
69        myself = __file__
70        if myself.endswith('.pyc'):
71            myself = myself[:-2]
72        print "WARNING: external paypal tests are skipped!"
73        print "WARNING: edit %s to enable them." % myself
74        return
75    return func
76
77
78class HelperTests(unittest.TestCase):
79
80    def setUp(self):
81        self.workdir = tempfile.mkdtemp()
82
83    def tearDown(self):
84        shutil.rmtree(self.workdir)
85        # unregister any remaining utils registered during tests
86        util = queryUtility(IPayPalConfig)
87        if util is not None:
88            getGlobalSiteManager().unregisterUtility(util, IPayPalConfig)
89
90    def test_get_paypal_config_file_path(self):
91        # we can get a config file path (if registered)
92        path = os.path.join(self.workdir, 'sample.cfg')
93        getGlobalSiteManager().registerUtility(
94            {'path': path, }, IPayPalConfig, '')
95        result = get_paypal_config_file_path()
96        assert result == path
97
98    def test_get_paypal_config_file_path_no_file(self):
99        # without IPayPalConfig util set, we get `None`
100        result = get_paypal_config_file_path()
101        assert result is None
102
103    def test_parse_paypal_config(self):
104        # we can parse paypal configs
105        path = os.path.join(self.workdir, 'sample.cfg')
106        open(path, 'w').write(
107            "[rest-client]\n"
108            "id = myid\n"
109            "secret = mysecret\n"
110            "mode = live\n")
111        result = parse_paypal_config(path)
112        assert result['client_id'] == u'myid'
113        assert result['client_secret'] == u'mysecret'
114        assert result['mode'] == 'live'
115
116    def test_parse_paypal_config_defaults(self):
117        # we have fallback values (defaults) in place
118        path = os.path.join(self.workdir, 'sample.cfg')
119        open(path, 'w').write(
120            "[rest-client]\n"
121            )
122        result = parse_paypal_config(path)
123        assert result['client_id'] is None
124        assert result['client_secret'] is None
125        assert result['mode'] == "sandbox"
126
127    def test_configure_sdk(self):
128        # we can configure the paypal sdk
129        path = os.path.join(self.workdir, 'sample.cfg')
130        open(path, 'w').write(
131            "[rest-client]\n"
132            "id = my-special-id\n"
133            )
134        result = configure_sdk(path)
135        assert result['client_id'] == 'my-special-id'
136        assert result['client_secret'] is None
137        assert result['mode'] == "sandbox"
138        assert paypalrestsdk.api.__api__.mode == 'sandbox'
139        assert paypalrestsdk.api.__api__.client_id == 'my-special-id'
140
141    def test_get_payment_invalid_intent(self):
142        # only 'sale' is currently allowed
143        self.assertRaises(
144            ValueError, get_payment, intent='invalid')
145        self.assertRaises(
146            ValueError, get_payment, intent='order')
147
148    def test_to_dict(self):
149        # we can turn objects into dicts
150        class C1(object):
151            a = 1
152            b = "somestring"
153            c = u"unicodestring"
154            d = None
155        obj = C1()
156        self.assertEqual(
157            to_dict(obj),
158            {'a': u"1",
159             'b': u"somestring",
160             'c': u"unicodestring",
161             }
162            )
163
164    def test_to_dict_map(self):
165        # we can map attribute names
166        class C1(object):
167            a = 1
168            b = "somestring"
169        obj = C1()
170        self.assertEqual(
171            to_dict(obj, name_map={'a': 'replaced_a'}),
172            {'replaced_a': u"1",
173             'b': u"somestring",
174             })
175
176    def test_to_dict_lists(self):
177        # to_dict can handle lists
178        class C1(object):
179            a = 1
180
181            def to_dict(self):
182                return to_dict(self)
183
184        obj1 = C1()
185        obj2 = C1()
186        obj2.a = 2
187        obj3 = C1()
188        obj3.a = 3
189        obj1.foo = [obj2, obj3]
190        self.assertEqual(
191            to_dict(obj1),
192            {
193                'a': u'1',
194                'foo': [
195                    {'a': u'2'},
196                    {'a': u'3'},
197                    ]
198                }
199            )
200
201    def test_to_dict_decimals(self):
202        # decimals are converted to numbers with 2 decimals
203        class C1(object):
204            a = decimal.Decimal("0.1")
205        self.assertEqual(
206            to_dict(C1()),
207            {
208                'a': u"0.10",
209                }
210            )
211
212
213class PayerTests(unittest.TestCase):
214
215    def test_create(self):
216        # we can create payer objects
217        payer = Payer(payment_method='paypal')
218        assert payer.payment_method == 'paypal'
219        assert payer.funding_instruments == []
220        assert payer.payer_info is None
221        assert payer.status is None
222
223    def test_init_invalid_payment_meth(self):
224        # we must provide a valid payment method
225        payer = Payer(payment_method='paypal')
226        assert payer is not None
227        payer = Payer(payment_method='credit_card')
228        assert payer is not None
229        self.assertRaises(
230            ValueError, Payer, payment_method='invalid')
231
232    def test_init_invalid_payer_state(self):
233        # only certain values are allowed as payer states
234        payer = Payer(payment_method='paypal', status='VERIFIED')
235        assert payer is not None
236        payer = Payer(payment_method='paypal', status='UNVERIFIED')
237        assert payer is not None
238        self.assertRaises(
239            ValueError, Payer, payment_method='paypal', status='InVaLiD')
240
241
242class PayerInfoTests(unittest.TestCase):
243
244    def test_create(self):
245        # we can create payer infos
246        info = PayerInfo()
247        assert info.email == ''
248        assert info.first_name == ''
249        assert info.last_name == ''
250        assert info.payer_id == ''
251        assert info.phone == ''
252        assert info.shipping_address is None
253        assert info.tax_id_type == ''
254        assert info.tax_id == ''
255
256    def test_init_invalid_tax_id_type(self):
257        # onyl certain tax id types are allowed
258        info = PayerInfo(tax_id_type='BR_CPF')
259        assert info is not None
260        info = PayerInfo(tax_id_type='BR_CNPJ')
261        assert info is not None
262        self.assertRaises(
263            ValueError, PayerInfo, tax_id_type='INVALID_TYPE')
264
265
266class CreditCardTests(unittest.TestCase):
267
268    def test_iface(self):
269        # we fullfill any interface contracts
270        credit_card = CreditCard(
271            number=u"12345678",
272            credit_card_type="visa",
273            expire_month=4,
274            expire_year=2012,
275            )
276        verifyClass(ICreditCard, CreditCard)
277        verifyObject(ICreditCard, credit_card)
278
279    def test_create(self):
280        # we can create CreditCard objects
281        credit_card = CreditCard(
282            number=u"12345678",
283            credit_card_type="visa",
284            expire_month=4,
285            expire_year=2012,
286            )
287        assert credit_card.paypal_id is None
288        assert credit_card.payer_id is not None
289        assert credit_card.number == u"12345678"
290        assert credit_card.credit_card_type == "visa"
291        assert credit_card.expire_month == 4
292        assert credit_card.expire_year == 2012
293        assert credit_card.cvv2 is None
294        assert credit_card.first_name is None
295        assert credit_card.last_name is None
296        assert credit_card.billing_address is None
297        assert credit_card.state is None
298        assert credit_card.paypal_valid_until is None
299
300    def test_payer_id_given(self):
301        # we do not override given payer ids
302        credit_card = CreditCard(
303            number=u"12345678",
304            credit_card_type="visa",
305            expire_month=4,
306            expire_year=2012,
307            payer_id=u'MySpecialPayerId',
308            )
309        assert credit_card.payer_id == u'MySpecialPayerId'
310
311    def test_payer_id_not_given(self):
312        # in case no payer id is given, we generate one
313        credit_card = CreditCard(
314            number=u"12345678",
315            credit_card_type="visa",
316            expire_month=4,
317            expire_year=2012,
318            )
319        # our payer ids contain a leading 'PAYER_' and 32 hex digits
320        assert re.match('PAYER_[0-9a-f]{32}$', credit_card.payer_id)
321
322    def test_number_is_checked(self):
323        # we do not accept invalid numbers
324        self.assertRaises(
325            ValueError, CreditCard,
326            number=u"not-a-number",
327            credit_card_type="visa",
328            expire_month=4,
329            expire_year=2012,
330            )
331
332    def test_to_str(self):
333        # we can turn CreditCard objects into dicts.
334        addr = Address(
335            line1=u"52 N Main ST",
336            city=u"Johnstown",
337            state=u"OH",
338            postal_code=u"43210",
339            country_code=u"US")
340        credit_card = CreditCard(
341            credit_card_type=u"visa",
342            payer_id=u"PAYER_0123456789012345678901",
343            number=u"4417119669820331",
344            expire_month=11,
345            expire_year=2018,
346            cvv2=u"874",
347            first_name=u"Joe",
348            last_name=u"Shopper",
349            billing_address=addr)
350        self.assertEqual(
351            credit_card.to_dict(),
352            {
353                "type": u"visa",
354                "number": u"4417119669820331",
355                "payer_id": u"PAYER_0123456789012345678901",
356                "expire_month": u"11",
357                "expire_year": u"2018",
358                "cvv2": u"874",
359                "first_name": u"Joe",
360                "last_name": u"Shopper",
361                "billing_address": {
362                    "line1": u"52 N Main ST",
363                    "city": u"Johnstown",
364                    "state": u"OH",
365                    "postal_code": u"43210",
366                    "country_code": u"US"}
367                }
368            )
369
370
371class CreditCardTokenTests(unittest.TestCase):
372
373    def test_iface(self):
374        # we fullfill any interface contracts
375        token = CreditCardToken(
376            credit_card_id=u"12345678",
377            )
378        verifyClass(ICreditCardToken, CreditCardToken)
379        verifyObject(ICreditCardToken, token)
380
381    def test_create(self):
382        # we can create CreditCardToken objects
383        token = CreditCardToken(
384            credit_card_id=u"12345678",
385            )
386        assert token.credit_card_id == u"12345678"
387        assert token.payer_id is None
388        assert token.credit_card_type is None
389        assert token.expire_month is None
390        assert token.expire_year is None
391        assert token.last4 is None
392
393    def test_payer_id_given(self):
394        # we do not override given payer ids
395        token = CreditCardToken(
396            credit_card_id=u"12345678",
397            payer_id=u'MySpecialPayerId',
398            )
399        assert token.payer_id == u'MySpecialPayerId'
400
401    def test_to_str(self):
402        # we can turn CreditCardToken objects into dicts.
403        token = CreditCardToken(
404            credit_card_type=u"visa",
405            payer_id=u"PAYER_0123456789012345678901",
406            credit_card_id=u"12345678",
407            last4="8901",
408            expire_month=11,
409            expire_year=2018,
410            )
411        self.assertEqual(
412            token.to_dict(),
413            {
414                "credit_card_id": u"12345678",
415                "payer_id": u"PAYER_0123456789012345678901",
416                "last4": u"8901",
417                "type": u"visa",
418                "expire_month": u"11",
419                "expire_year": u"2018",
420                }
421            )
422
423
424class FundingInstrumentTests(unittest.TestCase):
425
426    def test_create(self):
427        # we can create FundingInstrument objects
428        token = CreditCardToken(
429            credit_card_id=u"12345678",
430            )
431        instr = FundingInstrument(credit_card_token=token)
432        assert instr.credit_card_token is token
433
434    def test_require_credit_card_or_token(self):
435        # we require a credit card object or a token.
436        credit_card = CreditCard(
437            number=u"12345678",
438            credit_card_type="visa",
439            expire_month=4,
440            expire_year=2012,
441            )
442        token = CreditCardToken(
443            credit_card_id=u"12345678",
444            )
445        self.assertRaises(
446            ValueError, FundingInstrument,
447            credit_card=credit_card,
448            credit_card_token=token
449            )
450        self.assertRaises(
451            ValueError, FundingInstrument
452            )
453
454    def test_to_dict(self):
455        # we can turn Funding instruments into dicts
456        token = CreditCardToken(
457            credit_card_type=u"visa",
458            payer_id=u"PAYER_0123456789012345678901",
459            credit_card_id=u"12345678",
460            last4="8901",
461            expire_month=11,
462            expire_year=2018,
463            )
464        instr = FundingInstrument(credit_card_token=token)
465        result = instr.to_dict()
466        self.assertEqual(
467            result,
468            {
469                "credit_card_token": {
470                    "credit_card_id": u"12345678",
471                    "payer_id": u"PAYER_0123456789012345678901",
472                    "last4": u"8901",
473                    "type": u"visa",
474                    "expire_month": u"11",
475                    "expire_year": u"2018",
476                    }
477                }
478            )
479
480
481class AddressTests(unittest.TestCase):
482
483    def test_iface(self):
484        # we fullfill any interface contracts
485        addr = Address(
486            line1=u'Address Line 1',
487            city=u'Somecity',
488            country_code=u'AT',
489            )
490        verifyClass(IAddress, Address)
491        verifyObject(IAddress, addr)
492
493    def test_create(self):
494        # we can create addresses
495        addr = Address(
496            line1=u'Honey Street 1',
497            city=u'Beartown',
498            country_code=u'GB',
499            )
500        assert addr.line1 == u'Honey Street 1'
501        assert addr.line2 is None
502        assert addr.city == u'Beartown'
503        assert addr.country_code == u'GB'
504        assert addr.postal_code is None
505        assert addr.state is None
506        assert addr.phone is None
507
508    def test_to_dict(self):
509        # we can turn addresses into dicts
510        addr = Address(
511            line1=u'Honey Street 1',
512            city=u'Beartown',
513            country_code=u'GB',
514            )
515        self.assertEqual(
516            addr.to_dict(),
517            {
518                'line1': u'Honey Street 1',
519                'city': u'Beartown',
520                'country_code': u'GB',
521                }
522            )
523        addr.line2 = u"Behind little tree"
524        self.assertEqual(
525            addr.to_dict(),
526            {
527                'line1': u'Honey Street 1',
528                'line2': u'Behind little tree',
529                'city': u'Beartown',
530                'country_code': u'GB',
531                }
532            )
533
534
535class ShippingAddressTests(unittest.TestCase):
536
537    def test_iface(self):
538        # we fullfill any interface contracts
539        addr = ShippingAddress(
540            recipient_name=u'Foo Bar',
541            line1=u'Address Line 1',
542            city=u'Somecity',
543            country_code=u'AT',
544            )
545        verifyClass(IShippingAddress, ShippingAddress)
546        verifyObject(IShippingAddress, addr)
547
548    def test_create(self):
549        # we can create shipping addresses
550        addr = ShippingAddress(
551            recipient_name=u'Rob Receiver',
552            line1=u'Honey Street 1',
553            city=u'Beartown',
554            country_code=u'GB',
555            )
556        assert addr.recipient_name == u'Rob Receiver'
557        assert addr.type == u'residential'
558        assert addr.line1 == u'Honey Street 1'
559        assert addr.line2 is None
560        assert addr.city == u'Beartown'
561        assert addr.country_code == u'GB'
562        assert addr.postal_code is None
563        assert addr.state is None
564        assert addr.phone is None
565
566    def test_to_dict(self):
567        # we can turn shipping addresses into dicts
568        addr = ShippingAddress(
569            recipient_name=u'Rob Receiver',
570            line1=u'Honey Street 1',
571            city=u'Beartown',
572            country_code=u'GB',
573            )
574        self.assertEqual(
575            addr.to_dict(),
576            {
577                'recipient_name': u'Rob Receiver',
578                'type': u'residential',
579                'line1': u'Honey Street 1',
580                'city': u'Beartown',
581                'country_code': u'GB',
582                }
583            )
584        addr.line2 = u"Behind little tree"
585        self.assertEqual(
586            addr.to_dict(),
587            {
588                'recipient_name': u'Rob Receiver',
589                'type': u'residential',
590                'line1': u'Honey Street 1',
591                'line2': u'Behind little tree',
592                'city': u'Beartown',
593                'country_code': u'GB',
594                }
595            )
596
597
598class AmountDetailsTests(unittest.TestCase):
599
600    def test_create(self):
601        # we can create AmountDetail objects
602        details = AmountDetails()
603        assert details.shipping is None
604        assert details.subtotal is None
605        assert details.tax is None
606        assert details.fee is None
607        assert details.handling_fee is None
608        assert details.insurance is None
609        assert details.shipping_discount is None
610
611    def test_to_dict(self):
612        # we can turn AmountDetails into a dict
613        details = AmountDetails(
614            shipping=decimal.Decimal("0.10"),
615            tax=decimal.Decimal("0.30"),
616            fee=decimal.Decimal("0.40"),
617            handling_fee=decimal.Decimal("0.50"),
618            insurance=decimal.Decimal("0.60"),
619            shipping_discount=decimal.Decimal("0.70")
620            )
621        self.assertEqual(
622            details.to_dict(),
623            {
624                'shipping': u"0.10",
625                'subtotal': u"1.20",
626                'tax': u"0.30",
627                'fee': u"0.40",
628                'handling_fee': u"0.50",
629                'insurance': u"0.60",
630                'shipping_discount': u"0.70"
631                }
632            )
633
634    def test_subtotal_all_none(self):
635        # if all items are none, also subtotal is none
636        details = AmountDetails(
637            shipping=None, tax=None, fee=None, handling_fee=None,
638            insurance=None, shipping_discount=None,
639            )
640        assert details.subtotal is None
641        details.shipping_discount = decimal.Decimal("1.00")
642        assert details.subtotal == decimal.Decimal("-1.00")
643
644    def test_subtotal_sum(self):
645        # subtotal sums up correctly
646        details = AmountDetails(
647            shipping=decimal.Decimal("0.05"),
648            tax=decimal.Decimal("0.40"),
649            fee=decimal.Decimal("3.00"),
650            handling_fee=decimal.Decimal("20.00"),
651            insurance=decimal.Decimal("100.00"),
652            shipping_discount=None
653            )
654        self.assertEqual(details.subtotal, decimal.Decimal("123.45"))
655        details.shipping_discount = decimal.Decimal("0.00")
656        self.assertEqual(details.subtotal, decimal.Decimal("123.45"))
657        details.shipping_discount = decimal.Decimal("23.45")
658        self.assertEqual(details.subtotal, decimal.Decimal("100.00"))
659
660
661class AmountTests(unittest.TestCase):
662
663    def test_iface(self):
664        # we fullfill any interface contracts.
665        amount = Amount()
666        verifyClass(IAmount, Amount)
667        verifyObject(IAmount, amount)
668
669    def test_create(self):
670        # we can create amount objects
671        details = AmountDetails(
672            shipping=decimal.Decimal("0.05"),
673            tax=decimal.Decimal("0.40"),
674            fee=decimal.Decimal("3.00"),
675            handling_fee=decimal.Decimal("20.00"),
676            insurance=decimal.Decimal("100.00"),
677            shipping_discount=None
678            )
679        amount = Amount(
680            total=decimal.Decimal("12.12"),
681            currency="USD",
682            details=details
683            )
684        assert amount.total == decimal.Decimal("12.12")
685        assert amount.currency == "USD"
686        assert amount.details is details
687
688    def test_to_dict(self):
689        # we can turn Amount objects into dicts
690        self.maxDiff = None
691        details = AmountDetails(
692            shipping=decimal.Decimal("0.05"),
693            tax=decimal.Decimal("0.40"),
694            fee=decimal.Decimal("3.00"),
695            handling_fee=decimal.Decimal("20.00"),
696            insurance=decimal.Decimal("100.00"),
697            shipping_discount=None
698            )
699        amount = Amount(
700            total=decimal.Decimal("12.12"),
701            currency="USD",
702            details=details
703            )
704        self.assertEqual(
705            amount.to_dict(),
706            {
707                'total': u'12.12',
708                'currency': u'USD',
709                'details': {
710                    'shipping': u'0.05',
711                    'subtotal': u'123.45',
712                    'tax': u'0.40',
713                    'fee': u'3.00',
714                    'handling_fee': u'20.00',
715                    'insurance': u'100.00',
716                    }
717                }
718            )
719
720
721class ItemTests(unittest.TestCase):
722
723    def test_iface(self):
724        # we fullfill all interface contracts
725        item = Item(name=u"Splendid Item")
726        verifyClass(IItem, Item)
727        verifyObject(IItem, item)
728
729    def test_create(self):
730        # we can create Item objects
731        item = Item(
732            quantity=3,
733            name=u"Splendid Thing",
734            price=decimal.Decimal("1.1"),
735            currency="USD",
736            sku="pcs",
737            description=u"Soo splendid!",
738            tax=decimal.Decimal("0.1"),
739            )
740        assert item.quantity == 3
741        assert item.name == u"Splendid Thing"
742        assert item.price == decimal.Decimal("1.1")
743        assert item.currency == "USD"
744        assert item.sku == "pcs"
745        assert item.description == u"Soo splendid!"
746        assert item.tax == decimal.Decimal("0.1")
747
748    def test_to_dict(self):
749        # we can turn Item objects into dicts
750        item = Item(
751            quantity=3,
752            name=u"Splendid Thing",
753            price=decimal.Decimal("1.1"),
754            currency="USD",
755            sku="pcs",
756            description=u"Soo splendid!",
757            tax=decimal.Decimal("0.1"),
758            )
759        self.assertEqual(
760            item.to_dict(),
761            {
762                "quantity": u"3",
763                "name": u"Splendid Thing",
764                "price": u"1.10",
765                "currency": u"USD",
766                "sku": u"pcs",
767                "description": u"Soo splendid!",
768                "tax": u"0.10"
769                }
770            )
771
772
773class ItemListTests(unittest.TestCase):
774
775    def test_iface(self):
776        # we fullfill all interface contracts
777        item_list = ItemList()
778        verifyClass(IItemList, ItemList)
779        verifyObject(IItemList, item_list)
780
781    def test_create_minimal(self):
782        # we can create ItemLists with a minimum of params
783        item_list = ItemList()
784        assert item_list.shipping_address is None
785        assert item_list.items == []
786
787    def test_create(self):
788        # we can create ItemLists
789        item1 = Item(
790            name=u"Splendid Thing",
791            )
792        item2 = Item(
793            name=u"Other Splendid Thing",
794            )
795        addr = ShippingAddress(
796            recipient_name=u'Rob Receiver',
797            line1=u'Honey Street 1',
798            city=u'Beartown',
799            country_code=u'GB',
800            )
801        item_list = ItemList(
802            shipping_address=addr,
803            items=[item1, item2])
804        assert item_list.shipping_address is addr
805        assert item_list.items == [item1, item2]
806
807    def test_to_dict(self):
808        # we can turn ITemLists into dicts
809        item = Item(
810            quantity=3,
811            name=u"Splendid Thing",
812            price=decimal.Decimal("1.1"),
813            currency="USD",
814            sku="pcs",
815            description=u"Soo splendid!",
816            tax=decimal.Decimal("0.1"),
817            )
818        addr = ShippingAddress(
819            recipient_name=u'Rob Receiver',
820            line1=u'Honey Street 1',
821            city=u'Beartown',
822            country_code=u'GB',
823            )
824        item_list = ItemList(items=[item, ], shipping_address=addr)
825        self.assertEqual(
826            item_list.to_dict(),
827            {
828                "items": [
829                    {
830                        "quantity": u"3",
831                        "name": u"Splendid Thing",
832                        "price": u"1.10",
833                        "currency": u"USD",
834                        "sku": u"pcs",
835                        "description": u"Soo splendid!",
836                        "tax": u"0.10"
837                        }
838                    ],
839                "shipping_address":
840                {
841                    'recipient_name': u'Rob Receiver',
842                    'type': u'residential',
843                    'line1': u'Honey Street 1',
844                    'city': u'Beartown',
845                    'country_code': u'GB',
846                    }
847                }
848            )
849
850
851class PaymentOptionsTests(unittest.TestCase):
852
853    def test_iface(self):
854        # we fullfill all interface contracts
855        opts = PaymentOptions()
856        verifyClass(IPaymentOptions, PaymentOptions)
857        verifyObject(IPaymentOptions, opts)
858
859    def test_create(self):
860        # we can create PaymentOptions objects
861        opts = PaymentOptions()
862        assert opts.allowed_payment_method is None
863
864    def test_allowed_payment_method_checked_in_init(self):
865        # any value apart from None, INSTANT... is rejected in __init__
866        self.assertRaises(
867            ValueError,
868            PaymentOptions, allowed_payment_method='NoTvAlID')
869
870    def test_to_dict(self):
871        # we can turn PaymentOptions into dicts
872        opts = PaymentOptions(
873            allowed_payment_method="INSTANT_FUNDING_SOURCE")
874        self.assertEqual(
875            opts.to_dict(),
876            {
877                'allowed_payment_method': "INSTANT_FUNDING_SOURCE",
878                }
879            )
880
881
882class TransactionTests(unittest.TestCase):
883
884    def test_iface(self):
885        # we fullfill all interface contracts
886        amount = Amount()
887        transaction = Transaction(amount=amount)
888        verifyClass(ITransaction, Transaction)
889        verifyObject(ITransaction, transaction)
890
891    def test_create(self):
892        # we can create transacions
893        amount = Amount()
894        transaction = Transaction(amount=amount)
895        assert transaction.amount is amount
896        assert transaction.description is None
897        assert transaction.item_list is None
898        assert transaction.related_resources == []
899        assert transaction.invoice_number is None
900        assert transaction.custom is None
901        assert transaction.soft_descriptor is None
902        assert transaction.payment_options is None
903
904    def test_to_dict(self):
905        # we can turn Transaction objects into dicts
906        transaction = Transaction(
907            amount=Amount(),
908            description=u"My description",
909            item_list=ItemList(),
910            related_resources=[],
911            invoice_number=u"12345",
912            custom=u"Some custom remark",
913            soft_descriptor=u"softdescriptor?",
914            payment_options=PaymentOptions(
915                allowed_payment_method="INSTANT_FUNDING_SOURCE"),
916            )
917        self.assertEqual(
918            transaction.to_dict(), {
919                'amount': {'currency': u'USD', 'total': u'0.00'},
920                'custom': u'Some custom remark',
921                'description': u'My description',
922                'invoice_number': u'12345',
923                'item_list': {
924                    'items': []
925                    },
926                'payment_options': {
927                    'allowed_payment_method': u'INSTANT_FUNDING_SOURCE'
928                    },
929                'related_resources': [],
930                'soft_descriptor': u'softdescriptor?'
931                }
932            )
933
934
935class AddressTypesVocabTests(unittest.TestCase):
936
937    def test_address_types_vocab_tokenized(self):
938        # we can get a countries source suitable for forms etc.
939        verifyObject(IVocabularyTokenized, ADDRESS_TYPES_VOCAB)
940
941    def test_address_types_vocab_i18nized(self):
942        # vocab titles are i18nized
943        result = ADDRESS_TYPES_VOCAB.getTerm('residential')
944        assert ITerm.providedBy(result)
945        self.assertEqual(result.title, u'residential')
946        assert isinstance(result.title, i18nMessage)
947
948    def test_address_types_vocab_tokens_are_string(self):
949        # vocab tokens are simple strings
950        result = ADDRESS_TYPES_VOCAB.getTerm('residential')
951        assert ITerm.providedBy(result)
952        assert result.token == result.value
953        assert result.value == 'residential'
954        assert isinstance(result.token, str)
955        assert isinstance(result.value, str)
956
957
958class CreditCardTypesVocabTests(unittest.TestCase):
959
960    def test_credit_card_types_vocab_tokenized(self):
961        # we can get a countries source suitable for forms etc.
962        verifyObject(IVocabularyTokenized, CREDIT_CARD_TYPES_VOCAB)
963
964    def test_credit_cards_types_vocab_i18nized(self):
965        # vocab titles are i18nized
966        result = CREDIT_CARD_TYPES_VOCAB.getTerm('visa')
967        assert ITerm.providedBy(result)
968        self.assertEqual(result.title, u'visa')
969        assert isinstance(result.title, i18nMessage)
970
971    def test_credit_cards_types_vocab_tokens_are_string(self):
972        # vocab tokens are simple strings
973        result = CREDIT_CARD_TYPES_VOCAB.getTerm('visa')
974        assert ITerm.providedBy(result)
975        assert result.token == result.value
976        assert result.value == 'visa'
977        assert isinstance(result.token, str)
978        assert isinstance(result.value, str)
979
980
981class CreditcardstatusVocabTests(unittest.TestCase):
982
983    def test_credit_card_status_vocab_tokenized(self):
984        # we can get a countries source suitable for forms etc.
985        verifyObject(IVocabularyTokenized, CREDIT_CARD_STATUS_VOCAB)
986
987    def test_credit_cards_status_vocab_i18nized(self):
988        # vocab titles are i18nized
989        result = CREDIT_CARD_STATUS_VOCAB.getTerm('ok')
990        assert ITerm.providedBy(result)
991        self.assertEqual(result.title, u'ok')
992        assert isinstance(result.title, i18nMessage)
993
994    def test_credit_cards_status_vocab_tokens_are_string(self):
995        # vocab tokens are simple strings
996        result = CREDIT_CARD_STATUS_VOCAB.getTerm('expired')
997        assert ITerm.providedBy(result)
998        assert result.token == result.value
999        assert result.value == 'expired'
1000        assert isinstance(result.token, str)
1001        assert isinstance(result.value, str)
1002
1003
1004class StockKeepingUnitsVocabTests(unittest.TestCase):
1005
1006    def test_sku_vocab_tokenized(self):
1007        # we can get a tokenzed vocab for stock keeping units
1008        verifyObject(IVocabularyTokenized, STOCK_KEEPING_UNITS_VOCAB)
1009
1010    def test_sku_vocab_i18nized(self):
1011        # vocab titles are i18nized
1012        result = STOCK_KEEPING_UNITS_VOCAB.getTerm('pcs')
1013        assert ITerm.providedBy(result)
1014        self.assertEqual(result.title, u'pieces')
1015        assert isinstance(result.title, i18nMessage)
1016
1017    def test_sku_vocab_tokens_are_string(self):
1018        # vocab tokens are simple strings
1019        result = STOCK_KEEPING_UNITS_VOCAB.getTerm('pcs')
1020        assert ITerm.providedBy(result)
1021        assert result.token == result.value
1022        assert result.value == 'pcs'
1023        assert isinstance(result.token, str)
1024        assert isinstance(result.value, str)
1025
1026
1027class PaymentOptionMethodsVocabTests(unittest.TestCase):
1028
1029    def test_payment_option_methods_vocab_tokenized(self):
1030        # we can get a countries source suitable for forms etc.
1031        verifyObject(IVocabularyTokenized, PAYMENT_OPTION_METHODS_VOCAB)
1032
1033    def test_payment_option_methods_vocab_i18nized(self):
1034        # vocab titles are i18nized
1035        result = PAYMENT_OPTION_METHODS_VOCAB.getTerm('INSTANT_FUNDING_SOURCE')
1036        assert ITerm.providedBy(result)
1037        self.assertEqual(result.title, u'INSTANT_FUNDING_SOURCE')
1038        assert isinstance(result.title, i18nMessage)
1039
1040    def test_payment_option_methods_vocab_tokens_are_string(self):
1041        # vocab tokens are simple strings
1042        result = PAYMENT_OPTION_METHODS_VOCAB.getTerm('INSTANT_FUNDING_SOURCE')
1043        assert ITerm.providedBy(result)
1044        assert result.token == result.value
1045        assert result.value == 'INSTANT_FUNDING_SOURCE'
1046        assert isinstance(result.token, str)
1047        assert isinstance(result.value, str)
1048
1049
1050class FakePayer(object):
1051
1052    implements(IPayer)
1053
1054    payer_id = 'PAYER-123'
1055    first_name = u'Joe'
1056    last_name = u'Shopper'
1057
1058
1059class FakePayee(object):
1060
1061    implements(IPayee)
1062
1063    payee_id = 'PAYEE-456'
1064
1065
1066class FakePaymentItem(object):
1067
1068    implements(IPaymentItem)
1069
1070    title = u'Fake Item'
1071    item_id = 'BILL-123456'
1072    amount = decimal.Decimal("12.10")
1073    currency = 'EUR'
1074
1075
1076# Make sure all fake classes (and objects made of them) are up-to-date
1077verifyClass(IPayer, FakePayer)
1078verifyObject(IPayer, FakePayer())
1079verifyClass(IPayee, FakePayee)
1080verifyObject(IPayee, FakePayee())
1081verifyClass(IPaymentItem, FakePaymentItem)
1082verifyObject(IPaymentItem, FakePaymentItem())
1083
1084
1085class PayPalCreditCardServiceTests(FunctionalTestCase):
1086
1087    layer = FunctionalLayer
1088
1089    def tearDown(self):
1090        super(PayPalCreditCardServiceTests, self).tearDown()
1091        clearSite()
1092
1093    def create_site(self):
1094        # create a simple site in root.
1095        class MyApp(grok.Application, grok.Container):
1096            pass
1097        site = MyApp()
1098        self.getRootFolder()['app'] = site
1099        setSite(site)
1100        return site
1101
1102    def test_iface(self):
1103        # we fullfill all interface contracts
1104        service = PayPalCreditCardService()
1105        verifyClass(IPaymentGatewayService, PayPalCreditCardService)
1106        verifyObject(IPaymentGatewayService, service)
1107
1108    def test_get_credit_card_no_site(self):
1109        # we get simply None if no site is registered
1110        service = PayPalCreditCardService()
1111        assert service.get_credit_card('not-a-valid-payer-id') is None
1112
1113    def test_get_credit_card_site_not_a_container(self):
1114        # we get simply None if site is not a container
1115        site = grok.Application()  # does not provide IContainer
1116        self.getRootFolder()['app'] = site
1117        setSite(site)
1118        service = PayPalCreditCardService()
1119        assert service.get_credit_card('not-a-valid-payer-id') is None
1120
1121    def test_get_credit_card_no_container(self):
1122        # we get simply None if no 'creditcards' container is in site
1123        self.create_site()
1124        service = PayPalCreditCardService()
1125        assert service.get_credit_card('not-a-valid-payer-id') is None
1126
1127    def test_get_credit_card(self):
1128        # we can get a credit card, if one was stored
1129        class MyCard(grok.Model):
1130            myid = 'CARD1'
1131
1132            def __eq__(self, obj):
1133                return self.myid == obj.myid
1134
1135        site = self.create_site()
1136        site['creditcards'] = grok.Container()
1137        card = MyCard()
1138        card.myid = 'CHANGED ID'
1139        site['creditcards'][u'CARD1'] = card
1140        service = PayPalCreditCardService()
1141        assert service.get_credit_card(u'CARD1') == card
1142        assert service.get_credit_card(u'CARD2') is None
1143
1144    def test_create_payment_no_credictard(self):
1145        # trying to create a payment without credit card raises an exception
1146        service = PayPalCreditCardService()
1147        exception = None
1148        try:
1149            service.create_payment(
1150                payer=FakePayer(),
1151                payment_item=FakePaymentItem(),
1152                payee=FakePayee()
1153                )
1154        except (ValueError, ) as err:
1155            exception = err
1156        assert exception.message == 'Payer PAYER-123 has no credit card.'
1157
1158
1159class FunctionalPaypalTests(FunctionalTestCase):
1160
1161    layer = FunctionalLayer
1162
1163    @external_paypal_test
1164    def test_get_access_token(self):
1165        # we can get an access token
1166        result = get_access_token()
1167        assert isinstance(result, unicode)
1168
1169    @external_paypal_test
1170    def test_get_payment(self):
1171        # we can construct (paypal-)payment objects
1172        payment = get_payment()
1173        assert isinstance(payment, paypalrestsdk.Payment)
1174        result = payment.create()
1175        assert result is True
1176
1177    def test_paypal_services_registered(self):
1178        # the PayPal gateway services are all registered
1179        creditcard_service = queryUtility(
1180            IPaymentGatewayService, name=u'paypal_creditcard')
1181        assert creditcard_service is not None
1182        assert isinstance(creditcard_service, PayPalCreditCardService)
1183        paypal_regular_service = queryUtility(
1184            IPaymentGatewayService, name=u'paypal_regular')
1185        assert paypal_regular_service is not None
1186        assert isinstance(paypal_regular_service, PayPalRegularPaymentService)
Note: See TracBrowser for help on using the repository browser.