Skip to content
This repository has been archived by the owner on Aug 1, 2023. It is now read-only.

Commit

Permalink
Fix tests to be compatible with latest versions of recurly
Browse files Browse the repository at this point in the history
  • Loading branch information
yorinasub17 committed May 10, 2018
1 parent 7c4a8c2 commit 873c2ac
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 52 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -2,7 +2,7 @@ language: python
python:
- "2.7"
install:
- pip install -r requirements.txt --use-mirrors
- pip install -r requirements.txt
- pip install python-coveralls coverage
script:
- coverage run --source mocurly setup.py test
Expand Down
55 changes: 43 additions & 12 deletions mocurly/core.py
Expand Up @@ -152,16 +152,25 @@ def _register(self):
for endpoint in endpoints:
# register list views
list_uri = recurly.base_uri() + endpoint.base_uri
list_uri_re = re.compile(list_uri + r'$')

def list_callback(request, uri, headers, endpoint=endpoint):
xml, item_count = endpoint.list()
headers['X-Records'] = item_count
return 200, headers, xml
HTTPretty.register_uri(HTTPretty.GET, list_uri, body=_callback(self)(list_callback), content_type="application/xml")
HTTPretty.register_uri(
HTTPretty.GET,
list_uri_re,
body=_callback(self)(list_callback),
content_type="application/xml")

def create_callback(request, uri, headers, endpoint=endpoint):
return 200, headers, endpoint.create(deserialize(request.body)[1])
HTTPretty.register_uri(HTTPretty.POST, list_uri, body=_callback(self)(create_callback), content_type="application/xml")
HTTPretty.register_uri(
HTTPretty.POST,
list_uri_re,
body=_callback(self)(create_callback),
content_type="application/xml")

# register details views
detail_uri = recurly.base_uri() + endpoint.base_uri + r'/([^/ ]+)'
Expand All @@ -170,27 +179,48 @@ def create_callback(request, uri, headers, endpoint=endpoint):
def retrieve_callback(request, uri, headers, endpoint=endpoint, detail_uri_re=detail_uri_re):
pk = detail_uri_re.match(uri).group(1)
return 200, headers, endpoint.retrieve(pk)
HTTPretty.register_uri(HTTPretty.GET, detail_uri_re, body=_callback(self)(retrieve_callback), content_type="application/xml")
HTTPretty.register_uri(
HTTPretty.GET,
detail_uri_re,
body=_callback(self)(retrieve_callback),
content_type="application/xml")

def update_callback(request, uri, headers, endpoint=endpoint, detail_uri_re=detail_uri_re):
pk = detail_uri_re.match(uri).group(1)
return 200, headers, endpoint.update(pk, deserialize(request.body)[1])
HTTPretty.register_uri(HTTPretty.PUT, detail_uri_re, body=_callback(self)(update_callback), content_type="application/xml")
HTTPretty.register_uri(
HTTPretty.PUT,
detail_uri_re,
body=_callback(self)(update_callback),
content_type="application/xml")

def delete_callback(request, uri, headers, endpoint=endpoint, detail_uri_re=detail_uri_re):
parsed_url = urlparse(uri)
pk = detail_uri_re.match('{0}://{1}{2}'.format(parsed_url.scheme, parsed_url.netloc, parsed_url.path)).group(1)
url_domain_part = '{0}://{1}{2}'.format(parsed_url.scheme, parsed_url.netloc, parsed_url.path)
pk = detail_uri_re.match(url_domain_part).group(1)
endpoint.delete(pk, **parse_qs(parsed_url.query))
return 204, headers, ''
HTTPretty.register_uri(HTTPretty.DELETE, detail_uri_re, body=_callback(self)(delete_callback))
HTTPretty.register_uri(
HTTPretty.DELETE,
detail_uri_re,
body=_callback(self)(delete_callback))

# register extra views
for method in filter(lambda method: callable(method) and getattr(method, 'is_route', False), (getattr(endpoint, m) for m in dir(endpoint))):
extra_views = filter(
lambda method: callable(method) and getattr(method, 'is_route', False),
(getattr(endpoint, m)
for m in dir(endpoint)))
for method in extra_views:
uri = detail_uri + '/' + method.uri
uri_re = re.compile(uri)

def extra_route_callback(request, uri, headers, method=method, uri_re=uri_re):
pk = uri_re.match(uri).group(1)
def extra_route_callback(
request,
uri,
headers,
method=method,
uri_re=uri_re):
uri_args = uri_re.match(uri).groups()
if method.method == 'DELETE':
status = 204
else:
Expand All @@ -199,13 +229,14 @@ def extra_route_callback(request, uri, headers, method=method, uri_re=uri_re):
post_data = request.querystring.copy()
if request.body:
post_data.update(deserialize(request.body)[1])
result = method(pk, post_data)
method_args = uri_args + (post_data,)
result = method(*method_args)
elif method.is_list:
result = method(pk, filters=request.querystring)
result = method(*uri_args, filters=request.querystring)
headers['X-Records'] = result[1]
result = result[0]
else:
result = method(pk)
result = method(*uri_args)
return status, headers, result
if method.method == 'DELETE':
HTTPretty.register_uri(HTTPretty.DELETE, uri_re, body=_callback(self)(extra_route_callback))
Expand Down
86 changes: 48 additions & 38 deletions mocurly/endpoints.py
Expand Up @@ -138,7 +138,7 @@ def uris(self, obj):
if billing_info_backend.has_object(obj[AccountsEndpoint.pk_attr]):
uri_out['billing_info_uri'] = uri_out['object_uri'] + '/billing_info'
uri_out['invoices_uri'] = uri_out['object_uri'] + '/invoices'
uri_out['redemption_uri'] = uri_out['object_uri'] + '/redemption'
uri_out['redemption_uri'] = uri_out['object_uri'] + '/redemptions'
uri_out['subscriptions_uri'] = uri_out['object_uri'] + '/subscriptions'
uri_out['transactions_uri'] = uri_out['object_uri'] + '/transactions'
return uri_out
Expand Down Expand Up @@ -228,30 +228,21 @@ def filter_subscriptions(subscription):
out = SubscriptionsEndpoint.backend.list_objects(filter_subscriptions)
return subscriptions_endpoint.serialize(out, format=format)

@details_route('GET', 'redemption')
def get_coupon_redemption_view(self, account_code, format=BaseRecurlyEndpoint.XML):
coupon_redemption = self.get_coupon_redemption(account_code)
if coupon_redemption is None:
raise ResponseError(404, '')
return coupons_endpoint.serialize_coupon_redemption(coupon_redemption, format=format)

def get_coupon_redemption(self, account_code):
@details_route('GET', 'redemptions$', is_list=True)
def get_coupon_redemptions(self, account_code, filters=None, format=BaseRecurlyEndpoint.XML):
account_coupon_redemptions = coupon_redemptions_backend.list_objects(lambda redemption: redemption['account_code'] == account_code)
if len(account_coupon_redemptions) == 0:
return None

assert len(account_coupon_redemptions) == 1
coupon_redemption = account_coupon_redemptions[0]
return coupons_endpoint.hydrate_coupon_redemption_foreign_keys(coupon_redemption)

@details_route('DELETE', 'redemption')
def delete_coupon_redemption(self, account_code, format=BaseRecurlyEndpoint.XML):
coupon_redemption = self.get_coupon_redemption(account_code)
if coupon_redemption is None:
return coupons_endpoint.serialize_coupon_redemption(account_coupon_redemptions, format=format)

@details_route('DELETE', 'redemptions/([^/ ]+)')
def delete_coupon_redemption(self, account_code, redemption_uuid, format=BaseRecurlyEndpoint.XML):
account_coupon_redemptions = coupon_redemptions_backend.list_objects(
lambda redemption: \
(redemption['account_code'] == account_code and
coupons_endpoint.generate_coupon_redemption_uuid(redemption['coupon'], redemption['account_code']) == redemption_uuid))
if not account_coupon_redemptions:
raise ResponseError(404, '')

coupon_redemption_uuid = coupons_endpoint.generate_coupon_redemption_uuid(coupon_redemption['coupon']['coupon_code'], account_code)
coupon_redemptions_backend.delete_object(coupon_redemption_uuid)
coupon_redemptions_backend.delete_object(redemption_uuid)
return ''


Expand Down Expand Up @@ -648,27 +639,29 @@ def hydrate_coupon_redemption_foreign_keys(self, obj):
return obj

def coupon_redemption_uris(self, obj):
uuid = self.generate_coupon_redemption_uuid(obj['coupon']['coupon_code'], obj['account_code'])
uri_out = {}
uri_out['coupon_uri'] = coupons_endpoint.get_object_uri(obj['coupon'])
pseudo_account_object = {}
pseudo_account_object[AccountsEndpoint.pk_attr] = obj['account_code']
uri_out['account_uri'] = accounts_endpoint.get_object_uri(pseudo_account_object)
uri_out['object_uri'] = uri_out['account_uri'] + '/redemption'
uri_out['object_uri'] = uri_out['account_uri'] + '/redemptions/' + uuid
return uri_out

def serialize_coupon_redemption(self, obj, format=BaseRecurlyEndpoint.XML):
if format == BaseRecurlyEndpoint.RAW:
obj = self.hydrate_coupon_redemption_foreign_keys(obj)
return obj

if type(obj) == list:
if isinstance(obj, list):
obj = [self.hydrate_coupon_redemption_foreign_keys(o) for o in obj]
for o in obj:
o['uris'] = self.coupon_redemption_uris(o)
return serialize_list('redemption.xml', 'redemptions', 'redemption', obj)
else:
obj = self.hydrate_coupon_redemption_foreign_keys(obj)
obj['uris'] = self.coupon_redemption_uris(obj)

if format == BaseRecurlyEndpoint.RAW:
return obj
elif isinstance(obj, list):
return serialize_list('redemption.xml', 'redemptions', 'redemption', obj)
else:
return serialize('redemption.xml', 'redemption', obj)

@details_route('GET', 'redemptions', is_list=True)
Expand All @@ -681,7 +674,9 @@ def redeem_coupon(self, pk, redeem_info, format=BaseRecurlyEndpoint.XML):
assert CouponsEndpoint.backend.has_object(pk)
redeem_info['coupon'] = pk
redeem_info['created_at'] = current_time().isoformat()
return self.serialize_coupon_redemption(coupon_redemptions_backend.add_object(self.generate_coupon_redemption_uuid(pk, redeem_info['account_code']), redeem_info), format=format)
redemption_uuid = self.generate_coupon_redemption_uuid(pk, redeem_info['account_code'])
new_redemption = coupon_redemptions_backend.add_object(redemption_uuid, redeem_info)
return self.serialize_coupon_redemption(new_redemption, format=format)

def determine_coupon_discount(self, coupon, charge):
type = coupon['discount_type']
Expand Down Expand Up @@ -858,10 +853,13 @@ def create(self, create_info, format=BaseRecurlyEndpoint.XML):

# If there are addons, make sure they exist in the system
if 'subscription_add_ons' in create_info:
for add_on in create_info['subscription_add_ons']:
add_ons = create_info['subscription_add_ons']
if isinstance(add_ons, dict):
add_ons = add_ons.values()
for add_on in add_ons:
add_on_uuid = plans_endpoint.generate_plan_add_on_uuid(create_info['plan_code'], add_on['add_on_code'])
assert plan_add_ons_backend.has_object(add_on_uuid)
create_info['subscription_add_ons'] = [add_on['add_on_code'] for add_on in create_info['subscription_add_ons']]
create_info['subscription_add_ons'] = [add_on['add_on_code'] for add_on in add_ons]

defaults = SubscriptionsEndpoint.defaults.copy()
defaults['unit_amount_in_cents'] = plan['unit_amount_in_cents'][create_info['currency']]
Expand Down Expand Up @@ -917,12 +915,10 @@ def create(self, create_info, format=BaseRecurlyEndpoint.XML):
adjustment_infos.append(plan_charge_line_item)

# now calculate discounts
coupon_redemption = accounts_endpoint.get_coupon_redemption(new_sub['account'])
if coupon_redemption:
for plan_charge_line_item in adjustment_infos:
discount = coupons_endpoint.determine_coupon_discount(coupon_redemption['coupon'], plan_charge_line_item['unit_amount_in_cents'])
plan_charge_line_item['discount_in_cents'] = discount
total -= plan_charge_line_item['discount_in_cents']
coupon_redemptions = accounts_endpoint.get_coupon_redemptions(
new_sub['account'], format=BaseRecurlyEndpoint.RAW)
if coupon_redemptions:
total -= self._apply_coupons(coupon_redemptions, adjustment_infos)

# create a transaction if the subscription is started
new_transaction = {}
Expand Down Expand Up @@ -999,6 +995,20 @@ def reactivate_subscription(self, pk, reactivate_info, format=format):
'canceled_at': None
}), format=format)

def _apply_coupons(self, coupon_redemptions, adjustment_infos):
total_discounts = 0
for redemption in coupon_redemptions:
for plan_charge_line_item in adjustment_infos:
discount = coupons_endpoint.determine_coupon_discount(
redemption['coupon'],
plan_charge_line_item['unit_amount_in_cents'])
if 'discount_in_cents' in plan_charge_line_item:
plan_charge_line_item['discount_in_cents'] += discount
else:
plan_charge_line_item['discount_in_cents'] = discount
total_discounts += discount
return total_discounts

accounts_endpoint = AccountsEndpoint()
adjustments_endpoint = AdjustmentsEndpoint()
transactions_endpoint = TransactionsEndpoint()
Expand Down
2 changes: 1 addition & 1 deletion mocurly/templates/account.xml
Expand Up @@ -4,7 +4,7 @@
<billing_info href="{{ account.uris.billing_info_uri }}"/>
{% endif %}
<invoices href="{{ account.uris.invoices_uri }}"/>
<redemption href="{{ account.uris.redemption_uri }}"/>
<redemptions href="{{ account.uris.redemption_uri }}"/>
<subscriptions href="{{ account.uris.subscriptions_uri }}"/>
<transactions href="{{ account.uris.transactions_uri }}"/>
<account_code>{{ account.account_code }}</account_code>
Expand Down

0 comments on commit 873c2ac

Please sign in to comment.