2020-02-21 15:33:37 +00:00
|
|
|
from django.test import TestCase
|
2020-03-09 10:30:11 +00:00
|
|
|
from django.contrib.auth import get_user_model
|
|
|
|
from datetime import datetime, date, timedelta
|
2020-09-28 18:44:50 +00:00
|
|
|
from django.utils import timezone
|
2020-02-21 15:33:37 +00:00
|
|
|
|
2020-03-09 10:30:11 +00:00
|
|
|
from .models import *
|
2020-04-18 07:02:33 +00:00
|
|
|
from uncloud_service.models import GenericServiceProduct
|
2020-03-09 10:30:11 +00:00
|
|
|
|
2020-09-28 19:34:24 +00:00
|
|
|
import json
|
2020-08-09 09:02:45 +00:00
|
|
|
|
2020-09-28 21:16:17 +00:00
|
|
|
chocolate_product_config = {
|
|
|
|
'features': {
|
|
|
|
'gramm':
|
|
|
|
{ 'min': 100,
|
|
|
|
'max': 5000,
|
2020-10-06 21:14:32 +00:00
|
|
|
'one_time_price_per_unit': 0.2,
|
|
|
|
'recurring_price_per_unit': 0
|
2020-09-28 21:16:17 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
chocolate_order_config = {
|
|
|
|
'features': {
|
|
|
|
'gramm': 500,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-06 21:14:32 +00:00
|
|
|
chocolate_one_time_price = chocolate_order_config['features']['gramm'] * chocolate_product_config['features']['gramm']['one_time_price_per_unit']
|
2020-09-28 21:16:17 +00:00
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
vm_product_config = {
|
2020-09-28 19:59:35 +00:00
|
|
|
'features': {
|
|
|
|
'cores':
|
|
|
|
{ 'min': 1,
|
|
|
|
'max': 48,
|
2020-10-06 21:14:32 +00:00
|
|
|
'one_time_price_per_unit': 0,
|
|
|
|
'recurring_price_per_unit': 4
|
2020-09-28 19:59:35 +00:00
|
|
|
},
|
|
|
|
'ram_gb':
|
|
|
|
{ 'min': 1,
|
|
|
|
'max': 256,
|
2020-10-06 21:14:32 +00:00
|
|
|
'one_time_price_per_unit': 0,
|
|
|
|
'recurring_price_per_unit': 4
|
2020-09-28 19:59:35 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
vm_order_config = {
|
2020-09-28 19:59:35 +00:00
|
|
|
'features': {
|
|
|
|
'cores': 2,
|
|
|
|
'ram_gb': 2
|
|
|
|
}
|
|
|
|
}
|
2020-08-09 09:02:45 +00:00
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
vm_order_downgrade_config = {
|
|
|
|
'features': {
|
|
|
|
'cores': 1,
|
|
|
|
'ram_gb': 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vm_order_upgrade_config = {
|
|
|
|
'features': {
|
|
|
|
'cores': 4,
|
|
|
|
'ram_gb': 4
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-28 19:34:24 +00:00
|
|
|
class ProductTestCase(TestCase):
|
|
|
|
"""
|
|
|
|
Test products and products <-> order interaction
|
|
|
|
"""
|
2020-08-27 12:45:37 +00:00
|
|
|
|
2020-08-08 21:02:24 +00:00
|
|
|
def setUp(self):
|
|
|
|
self.user = get_user_model().objects.create(
|
|
|
|
username='random_user',
|
|
|
|
email='jane.random@domain.tld')
|
|
|
|
|
2020-09-28 19:34:24 +00:00
|
|
|
self.ba = BillingAddress.objects.create(
|
2020-08-08 21:02:24 +00:00
|
|
|
owner=self.user,
|
|
|
|
organization = 'Test org',
|
|
|
|
street="unknown",
|
|
|
|
city="unknown",
|
|
|
|
postal_code="somewhere else",
|
|
|
|
active=True)
|
|
|
|
|
2020-10-06 16:53:13 +00:00
|
|
|
RecurringPeriod.populate_db_defaults()
|
|
|
|
self.default_recurring_period = RecurringPeriod.objects.get(name="Per 30 days")
|
|
|
|
|
2020-09-28 19:34:24 +00:00
|
|
|
def test_create_product(self):
|
|
|
|
"""
|
|
|
|
Create a sample product
|
|
|
|
"""
|
|
|
|
|
2020-09-28 19:59:35 +00:00
|
|
|
p = Product.objects.create(name="Testproduct",
|
|
|
|
description="Only for testing",
|
2020-10-06 21:14:32 +00:00
|
|
|
config=vm_product_config)
|
|
|
|
|
|
|
|
p.recurring_periods.add(self.default_recurring_period,
|
|
|
|
through_defaults= { 'is_default': True })
|
2020-09-28 19:59:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
class OrderTestCase(TestCase):
|
|
|
|
"""
|
|
|
|
The heart of ordering products
|
|
|
|
"""
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.user = get_user_model().objects.create(
|
|
|
|
username='random_user',
|
|
|
|
email='jane.random@domain.tld')
|
|
|
|
|
|
|
|
self.ba = BillingAddress.objects.create(
|
|
|
|
owner=self.user,
|
|
|
|
organization = 'Test org',
|
|
|
|
street="unknown",
|
|
|
|
city="unknown",
|
|
|
|
postal_code="somewhere else",
|
|
|
|
active=True)
|
|
|
|
|
2020-10-06 21:14:32 +00:00
|
|
|
self.product = Product.objects.create(name="Testproduct",
|
|
|
|
description="Only for testing",
|
|
|
|
config=vm_product_config)
|
|
|
|
|
2020-10-06 16:53:13 +00:00
|
|
|
RecurringPeriod.populate_db_defaults()
|
|
|
|
self.default_recurring_period = RecurringPeriod.objects.get(name="Per 30 days")
|
|
|
|
|
2020-10-06 21:14:32 +00:00
|
|
|
self.product.recurring_periods.add(self.default_recurring_period,
|
|
|
|
through_defaults= { 'is_default': True })
|
|
|
|
|
|
|
|
|
|
|
|
def test_order_invalid_recurring_period(self):
|
|
|
|
"""
|
|
|
|
Order a products with a recurringperiod that is not added to the product
|
|
|
|
"""
|
|
|
|
|
|
|
|
o = Order.objects.create(owner=self.user,
|
|
|
|
billing_address=self.ba,
|
|
|
|
product=self.product,
|
|
|
|
config=vm_order_config)
|
|
|
|
|
2020-10-06 16:53:13 +00:00
|
|
|
|
2020-09-28 19:59:35 +00:00
|
|
|
def test_order_product(self):
|
|
|
|
"""
|
|
|
|
Order a product, ensure the order has correct price setup
|
|
|
|
"""
|
2020-08-08 21:02:24 +00:00
|
|
|
|
2020-09-28 19:59:35 +00:00
|
|
|
o = Order.objects.create(owner=self.user,
|
|
|
|
billing_address=self.ba,
|
2020-10-06 21:14:32 +00:00
|
|
|
product=self.product)
|
2020-08-08 21:02:24 +00:00
|
|
|
|
2020-09-28 19:59:35 +00:00
|
|
|
self.assertEqual(o.one_time_price, 0)
|
|
|
|
self.assertEqual(o.recurring_price, 16)
|
2020-08-08 21:02:24 +00:00
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
def test_change_order(self):
|
|
|
|
"""
|
|
|
|
Change an order and ensure that
|
|
|
|
- a new order is created
|
|
|
|
- the price is correct in the new order
|
|
|
|
"""
|
|
|
|
order1 = Order.objects.create(owner=self.user,
|
|
|
|
billing_address=self.ba,
|
2020-10-06 21:14:32 +00:00
|
|
|
product=self.product,
|
2020-10-06 13:46:22 +00:00
|
|
|
config=vm_order_config)
|
|
|
|
|
|
|
|
|
|
|
|
self.assertEqual(order1.one_time_price, 0)
|
|
|
|
self.assertEqual(order1.recurring_price, 16)
|
|
|
|
|
|
|
|
|
|
|
|
class ModifyOrderTestCase(TestCase):
|
|
|
|
"""
|
|
|
|
Test typical order flows like
|
|
|
|
- cancelling
|
|
|
|
- downgrading
|
|
|
|
- upgrading
|
|
|
|
"""
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.user = get_user_model().objects.create(
|
|
|
|
username='random_user',
|
|
|
|
email='jane.random@domain.tld')
|
|
|
|
|
|
|
|
self.ba = BillingAddress.objects.create(
|
|
|
|
owner=self.user,
|
|
|
|
organization = 'Test org',
|
|
|
|
street="unknown",
|
|
|
|
city="unknown",
|
|
|
|
postal_code="somewhere else",
|
|
|
|
active=True)
|
|
|
|
|
2020-10-06 21:14:32 +00:00
|
|
|
self.product = Product.objects.create(name="Testproduct",
|
|
|
|
description="Only for testing",
|
|
|
|
config=vm_product_config)
|
|
|
|
|
2020-10-06 16:53:13 +00:00
|
|
|
RecurringPeriod.populate_db_defaults()
|
|
|
|
self.default_recurring_period = RecurringPeriod.objects.get(name="Per 30 days")
|
|
|
|
|
2020-10-06 21:14:32 +00:00
|
|
|
self.product.recurring_periods.add(self.default_recurring_period,
|
|
|
|
through_defaults= { 'is_default': True })
|
2020-10-06 13:46:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_change_order(self):
|
|
|
|
"""
|
|
|
|
Test changing an order
|
|
|
|
|
|
|
|
Expected result:
|
|
|
|
|
|
|
|
- Old order should be closed before new order starts
|
|
|
|
- New order should start at starting data
|
|
|
|
"""
|
|
|
|
|
|
|
|
user = self.user
|
|
|
|
|
|
|
|
starting_price = 16
|
|
|
|
downgrade_price = 8
|
|
|
|
|
|
|
|
starting_date = timezone.make_aware(datetime.datetime(2019,3,3))
|
|
|
|
ending1_date = starting_date + datetime.timedelta(days=15)
|
|
|
|
change1_date = start_after(ending1_date)
|
|
|
|
|
|
|
|
bill_ending_date = change1_date + datetime.timedelta(days=1)
|
|
|
|
|
|
|
|
|
|
|
|
order1 = Order.objects.create(owner=self.user,
|
|
|
|
billing_address=BillingAddress.get_address_for(self.user),
|
|
|
|
product=self.product,
|
|
|
|
config=vm_order_config,
|
|
|
|
starting_date=starting_date)
|
|
|
|
|
|
|
|
order1.update_order(vm_order_downgrade_config, starting_date=change1_date)
|
|
|
|
|
|
|
|
bills = Bill.create_next_bills_for_user(user, ending_date=bill_ending_date)
|
|
|
|
|
|
|
|
bill = bills[0]
|
|
|
|
bill_records = BillRecord.objects.filter(bill=bill)
|
|
|
|
|
|
|
|
self.assertEqual(len(bill_records), 2)
|
|
|
|
|
|
|
|
self.assertEqual(bill_records[0].starting_date, starting_date)
|
|
|
|
self.assertEqual(bill_records[0].ending_date, ending1_date)
|
|
|
|
|
|
|
|
self.assertEqual(bill_records[1].starting_date, change1_date)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_downgrade_product(self):
|
|
|
|
"""
|
|
|
|
Test downgrading behaviour:
|
|
|
|
|
|
|
|
We create a recurring product (recurring time: 30 days) and downgrade after 15 days.
|
|
|
|
|
|
|
|
We create the bill right AFTER the end of the first order.
|
|
|
|
|
|
|
|
Expected result:
|
|
|
|
|
|
|
|
- First bill record for 30 days
|
|
|
|
- Second bill record starting after 30 days
|
|
|
|
- Bill contains two bill records
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
user = self.user
|
|
|
|
|
|
|
|
starting_price = 16
|
|
|
|
downgrade_price = 8
|
|
|
|
|
|
|
|
starting_date = timezone.make_aware(datetime.datetime(2019,3,3))
|
|
|
|
first_order_should_end_at = starting_date + datetime.timedelta(days=30)
|
|
|
|
change1_date = start_after(starting_date + datetime.timedelta(days=15))
|
|
|
|
bill_ending_date = change1_date + datetime.timedelta(days=1)
|
|
|
|
|
|
|
|
order1 = Order.objects.create(owner=self.user,
|
|
|
|
billing_address=BillingAddress.get_address_for(self.user),
|
|
|
|
product=self.product,
|
|
|
|
config=vm_order_config,
|
|
|
|
starting_date=starting_date)
|
|
|
|
|
|
|
|
order1.update_order(vm_order_downgrade_config, starting_date=change1_date)
|
|
|
|
|
|
|
|
bills = Bill.create_next_bills_for_user(user, ending_date=bill_ending_date)
|
|
|
|
|
|
|
|
bill = bills[0]
|
|
|
|
bill_records = BillRecord.objects.filter(bill=bill)
|
|
|
|
|
|
|
|
self.assertEqual(len(bill_records), 2)
|
|
|
|
|
|
|
|
self.assertEqual(bill_records[0].starting_date, starting_date)
|
|
|
|
self.assertEqual(bill_records[0].order.ending_date, first_order_should_end_at)
|
|
|
|
|
2020-09-28 21:16:17 +00:00
|
|
|
|
|
|
|
class BillTestCase(TestCase):
|
|
|
|
"""
|
|
|
|
Test aspects of billing / creating a bill
|
|
|
|
"""
|
|
|
|
|
|
|
|
def setUp(self):
|
2020-11-15 14:43:11 +00:00
|
|
|
RecurringPeriod.populate_db_defaults()
|
|
|
|
|
2020-09-28 21:16:17 +00:00
|
|
|
self.user_without_address = get_user_model().objects.create(
|
|
|
|
username='no_home_person',
|
|
|
|
email='far.away@domain.tld')
|
|
|
|
|
|
|
|
self.user = get_user_model().objects.create(
|
|
|
|
username='jdoe',
|
|
|
|
email='john.doe@domain.tld')
|
|
|
|
|
|
|
|
self.recurring_user = get_user_model().objects.create(
|
|
|
|
username='recurrent_product_user',
|
|
|
|
email='jane.doe@domain.tld')
|
|
|
|
|
|
|
|
self.user_addr = BillingAddress.objects.create(
|
|
|
|
owner=self.user,
|
|
|
|
organization = 'Test org',
|
|
|
|
street="unknown",
|
|
|
|
city="unknown",
|
|
|
|
postal_code="unknown",
|
|
|
|
active=True)
|
|
|
|
|
|
|
|
self.recurring_user_addr = BillingAddress.objects.create(
|
|
|
|
owner=self.recurring_user,
|
|
|
|
organization = 'Test org',
|
|
|
|
street="Somewhere",
|
|
|
|
city="Else",
|
|
|
|
postal_code="unknown",
|
|
|
|
active=True)
|
|
|
|
|
|
|
|
self.order_meta = {}
|
|
|
|
self.order_meta[1] = {
|
|
|
|
'starting_date': timezone.make_aware(datetime.datetime(2020,3,3)),
|
|
|
|
'ending_date': timezone.make_aware(datetime.datetime(2020,4,17)),
|
|
|
|
'price': 15,
|
|
|
|
'description': 'One chocolate bar'
|
|
|
|
}
|
|
|
|
|
|
|
|
self.chocolate = Product.objects.create(name="Swiss Chocolate",
|
|
|
|
description="Not only for testing, but for joy",
|
|
|
|
config=chocolate_product_config)
|
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
|
|
|
|
self.vm = Product.objects.create(name="Super Fast VM",
|
|
|
|
description="Zooooom",
|
|
|
|
config=vm_product_config)
|
|
|
|
|
2020-09-28 21:16:17 +00:00
|
|
|
|
2020-10-06 21:14:32 +00:00
|
|
|
RecurringPeriod.populate_db_defaults()
|
|
|
|
self.default_recurring_period = RecurringPeriod.objects.get(name="Per 30 days")
|
|
|
|
|
|
|
|
self.onetime_recurring_period = RecurringPeriod.objects.get(name="Onetime")
|
|
|
|
|
|
|
|
self.chocolate.recurring_periods.add(self.onetime_recurring_period,
|
|
|
|
through_defaults= { 'is_default': True })
|
|
|
|
|
|
|
|
self.vm.recurring_periods.add(self.default_recurring_period,
|
|
|
|
through_defaults= { 'is_default': True })
|
|
|
|
|
|
|
|
|
2020-09-28 21:16:17 +00:00
|
|
|
# used for generating multiple bills
|
|
|
|
self.bill_dates = [
|
|
|
|
timezone.make_aware(datetime.datetime(2020,3,31)),
|
|
|
|
timezone.make_aware(datetime.datetime(2020,4,30)),
|
|
|
|
timezone.make_aware(datetime.datetime(2020,5,31)),
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
def order_chocolate(self):
|
|
|
|
return Order.objects.create(
|
|
|
|
owner=self.user,
|
2020-11-15 14:43:11 +00:00
|
|
|
recurring_period=RecurringPeriod.objects.get(name="Onetime"),
|
2020-09-28 21:16:17 +00:00
|
|
|
product=self.chocolate,
|
|
|
|
billing_address=BillingAddress.get_address_for(self.user),
|
|
|
|
starting_date=self.order_meta[1]['starting_date'],
|
|
|
|
ending_date=self.order_meta[1]['ending_date'],
|
|
|
|
config=chocolate_order_config)
|
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
def order_vm(self, owner=None):
|
|
|
|
|
|
|
|
if not owner:
|
|
|
|
owner = self.recurring_user
|
|
|
|
|
|
|
|
return Order.objects.create(
|
|
|
|
owner=owner,
|
|
|
|
product=self.vm,
|
|
|
|
config=vm_order_config,
|
|
|
|
billing_address=BillingAddress.get_address_for(self.recurring_user),
|
|
|
|
starting_date=timezone.make_aware(datetime.datetime(2020,3,3)),
|
|
|
|
)
|
|
|
|
|
|
|
|
return Order.objects.create(
|
|
|
|
owner=self.user,
|
2020-11-15 14:43:11 +00:00
|
|
|
recurring_period=RecurringPeriod.objects.get(name="Onetime"),
|
2020-10-06 13:46:22 +00:00
|
|
|
product=self.chocolate,
|
|
|
|
billing_address=BillingAddress.get_address_for(self.user),
|
|
|
|
starting_date=self.order_meta[1]['starting_date'],
|
|
|
|
ending_date=self.order_meta[1]['ending_date'],
|
|
|
|
config=chocolate_order_config)
|
|
|
|
|
2020-09-28 21:16:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_bill_one_time_one_bill_record(self):
|
|
|
|
"""
|
|
|
|
Ensure there is only 1 bill record per order
|
|
|
|
"""
|
|
|
|
|
|
|
|
order = self.order_chocolate()
|
|
|
|
|
|
|
|
bill = Bill.create_next_bill_for_user_address(self.user_addr)
|
|
|
|
|
|
|
|
self.assertEqual(order.billrecord_set.count(), 1)
|
|
|
|
|
|
|
|
def test_bill_sum_onetime(self):
|
|
|
|
"""
|
|
|
|
Check the bill sum for a single one time order
|
|
|
|
"""
|
|
|
|
|
|
|
|
order = self.order_chocolate()
|
|
|
|
bill = Bill.create_next_bill_for_user_address(self.user_addr)
|
|
|
|
self.assertEqual(bill.sum, chocolate_one_time_price)
|
|
|
|
|
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
def test_bill_creates_record_for_recurring_order(self):
|
|
|
|
"""
|
|
|
|
Ensure there is only 1 bill record per order
|
|
|
|
"""
|
2020-09-28 21:16:17 +00:00
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
order = self.order_vm()
|
|
|
|
bill = Bill.create_next_bill_for_user_address(self.recurring_user_addr)
|
2020-09-28 21:16:17 +00:00
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
self.assertEqual(order.billrecord_set.count(), 1)
|
|
|
|
self.assertEqual(bill.billrecord_set.count(), 1)
|
2020-09-28 21:16:17 +00:00
|
|
|
|
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
def test_new_bill_after_closing(self):
|
|
|
|
"""
|
|
|
|
After closing a bill and the user has a recurring product,
|
|
|
|
the next bill run should create e new bill
|
|
|
|
"""
|
2020-09-28 21:16:17 +00:00
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
order = self.order_vm()
|
2020-09-28 21:16:17 +00:00
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
for ending_date in self.bill_dates:
|
|
|
|
b = Bill.create_next_bill_for_user_address(self.recurring_user_addr, ending_date)
|
|
|
|
b.close()
|
2020-09-28 21:16:17 +00:00
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
bill_count = Bill.objects.filter(owner=self.recurring_user).count()
|
2020-09-28 21:16:17 +00:00
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
self.assertEqual(len(self.bill_dates), bill_count)
|
2020-09-28 21:16:17 +00:00
|
|
|
|
2020-09-28 19:34:24 +00:00
|
|
|
|
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
class BillingAddressTestCase(TestCase):
|
|
|
|
def setUp(self):
|
|
|
|
self.user = get_user_model().objects.create(
|
|
|
|
username='random_user',
|
|
|
|
email='jane.random@domain.tld')
|
2020-08-09 08:14:49 +00:00
|
|
|
|
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
def test_user_no_address(self):
|
|
|
|
"""
|
|
|
|
Raise an error, when there is no address
|
|
|
|
"""
|
2020-08-09 08:14:49 +00:00
|
|
|
|
2020-10-06 13:46:22 +00:00
|
|
|
self.assertRaises(uncloud_pay.models.BillingAddress.DoesNotExist,
|
|
|
|
BillingAddress.get_address_for,
|
|
|
|
self.user)
|