9211894b23
Signed-off-by: Nico Schottelius <nico@nico-notebook.schottelius.org>
575 lines
20 KiB
Python
575 lines
20 KiB
Python
from django.test import TestCase
|
|
from django.contrib.auth import get_user_model
|
|
from datetime import datetime, date, timedelta
|
|
|
|
from .models import *
|
|
from uncloud_service.models import GenericServiceProduct
|
|
|
|
|
|
class ProductTestCase(TestCase):
|
|
"""
|
|
Test products and products <-> order interaction
|
|
"""
|
|
|
|
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)
|
|
|
|
def test_create_one_time_product(self):
|
|
"""
|
|
One time payment products cannot be updated - can they?
|
|
"""
|
|
|
|
p = SampleOneTimeProduct.objects.create(owner=self.user)
|
|
|
|
self.assertEqual(p.one_time_price, 5)
|
|
self.assertEqual(p.recurring_price, 0)
|
|
|
|
def test_create_product_without_active_billing_address(self):
|
|
"""
|
|
Fail to create a product without an active billing address
|
|
"""
|
|
|
|
self.ba.active = False
|
|
self.ba.save()
|
|
|
|
with self.assertRaises(ValidationError):
|
|
p = SampleOneTimeProduct.objects.create(owner=self.user)
|
|
|
|
def test_create_product_without_billing_address(self):
|
|
"""
|
|
Fail to create a product without a billing address
|
|
"""
|
|
|
|
user2 = get_user_model().objects.create(
|
|
username='random_user2',
|
|
email='jane.randomly@domain.tld')
|
|
|
|
with self.assertRaises(ValidationError):
|
|
p = SampleOneTimeProduct.objects.create(owner=user2)
|
|
|
|
|
|
def test_create_order_creates_correct_order_count(self):
|
|
"""
|
|
Ensure creating orders from product only creates 1 order
|
|
"""
|
|
|
|
# One order
|
|
p = SampleOneTimeProduct.objects.create(owner=self.user)
|
|
p.create_order(timezone.make_aware(datetime.datetime(2020,3,3)))
|
|
|
|
order_count = Order.objects.filter(owner=self.user).count()
|
|
self.assertEqual(order_count, 1)
|
|
|
|
# One more order
|
|
p = SampleRecurringProduct.objects.create(owner=self.user)
|
|
p.create_order(timezone.make_aware(datetime.datetime(2020,3,3)))
|
|
|
|
order_count = Order.objects.filter(owner=self.user).count()
|
|
self.assertEqual(order_count, 2)
|
|
|
|
# Should create 2 orders
|
|
p = SampleRecurringProductOneTimeFee.objects.create(owner=self.user)
|
|
p.create_order(timezone.make_aware(datetime.datetime(2020,3,3)))
|
|
|
|
order_count = Order.objects.filter(owner=self.user).count()
|
|
self.assertEqual(order_count, 4)
|
|
|
|
|
|
def test_update_recurring_order(self):
|
|
"""
|
|
Ensure creating orders from product only creates 1 order
|
|
"""
|
|
|
|
p = SampleRecurringProduct.objects.create(owner=self.user)
|
|
p.create_order(timezone.make_aware(datetime.datetime(2020,3,3)))
|
|
|
|
p.create_or_update_recurring_order(timezone.make_aware(datetime.datetime(2020,3,3)))
|
|
|
|
|
|
class BillingAddressTestCase(TestCase):
|
|
def setUp(self):
|
|
self.user = get_user_model().objects.create(
|
|
username='random_user',
|
|
email='jane.random@domain.tld')
|
|
|
|
|
|
def test_user_no_address(self):
|
|
"""
|
|
Raise an error, when there is no address
|
|
"""
|
|
|
|
self.assertRaises(uncloud_pay.models.BillingAddress.DoesNotExist,
|
|
BillingAddress.get_address_for,
|
|
self.user)
|
|
|
|
def test_user_only_inactive_address(self):
|
|
"""
|
|
Raise an error, when there is no active address
|
|
"""
|
|
|
|
ba = BillingAddress.objects.create(
|
|
owner=self.user,
|
|
organization = 'Test org',
|
|
street="unknown",
|
|
city="unknown",
|
|
postal_code="somewhere else",
|
|
active=False)
|
|
|
|
self.assertRaises(uncloud_pay.models.BillingAddress.DoesNotExist,
|
|
BillingAddress.get_address_for,
|
|
self.user)
|
|
|
|
def test_find_active_address(self):
|
|
"""
|
|
Find the active address
|
|
"""
|
|
|
|
ba = BillingAddress.objects.create(
|
|
owner=self.user,
|
|
organization = 'Test org',
|
|
street="unknown",
|
|
city="unknown",
|
|
postal_code="unknown",
|
|
active=True)
|
|
|
|
|
|
self.assertEqual(BillingAddress.get_address_for(self.user), ba)
|
|
|
|
def test_find_right_address_with_multiple_addresses(self):
|
|
"""
|
|
Find the active address only, skip inactive
|
|
"""
|
|
|
|
ba = BillingAddress.objects.create(
|
|
owner=self.user,
|
|
organization = 'Test org',
|
|
street="unknown",
|
|
city="unknown",
|
|
postal_code="unknown",
|
|
active=True)
|
|
|
|
ba2 = BillingAddress.objects.create(
|
|
owner=self.user,
|
|
organization = 'Test org',
|
|
street="unknown",
|
|
city="unknown",
|
|
postal_code="somewhere else",
|
|
active=False)
|
|
|
|
|
|
self.assertEqual(BillingAddress.get_address_for(self.user), ba)
|
|
|
|
def test_change_addresses(self):
|
|
"""
|
|
Switch the active address
|
|
"""
|
|
|
|
ba = BillingAddress.objects.create(
|
|
owner=self.user,
|
|
organization = 'Test org',
|
|
street="unknown",
|
|
city="unknown",
|
|
postal_code="unknown",
|
|
active=True)
|
|
|
|
self.assertEqual(BillingAddress.get_address_for(self.user), ba)
|
|
|
|
ba.active=False
|
|
ba.save()
|
|
|
|
ba2 = BillingAddress.objects.create(
|
|
owner=self.user,
|
|
organization = 'Test org',
|
|
street="unknown",
|
|
city="unknown",
|
|
postal_code="somewhere else",
|
|
active=True)
|
|
|
|
self.assertEqual(BillingAddress.get_address_for(self.user), ba2)
|
|
|
|
|
|
|
|
class BillTestCase(TestCase):
|
|
def setUp(self):
|
|
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.one_time_order = Order.objects.create(
|
|
owner=self.user,
|
|
starting_date=self.order_meta[1]['starting_date'],
|
|
ending_date=self.order_meta[1]['ending_date'],
|
|
recurring_period=RecurringPeriod.ONE_TIME,
|
|
price=self.order_meta[1]['price'],
|
|
description=self.order_meta[1]['description'],
|
|
billing_address=BillingAddress.get_address_for(self.user))
|
|
|
|
self.recurring_order = Order.objects.create(
|
|
owner=self.recurring_user,
|
|
starting_date=timezone.make_aware(datetime.datetime(2020,3,3)),
|
|
recurring_period=RecurringPeriod.PER_30D,
|
|
price=15,
|
|
description="A pretty VM",
|
|
billing_address=BillingAddress.get_address_for(self.recurring_user)
|
|
)
|
|
|
|
# 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 test_bill_one_time_one_bill_record(self):
|
|
"""
|
|
Ensure there is only 1 bill record per order
|
|
"""
|
|
|
|
bill = Bill.create_next_bill_for_user_address(self.user, self.user_addr)
|
|
|
|
self.assertEqual(self.one_time_order.billrecord_set.count(), 1)
|
|
|
|
def test_bill_sum_onetime(self):
|
|
"""
|
|
Check the bill sum for a single one time order
|
|
"""
|
|
|
|
bill = Bill.create_next_bill_for_user_address(self.user, self.user_addr)
|
|
self.assertEqual(bill.sum, self.order_meta[1]['price'])
|
|
|
|
|
|
def test_bill_creates_record_for_recurring_order(self):
|
|
"""
|
|
Ensure there is only 1 bill record per order
|
|
"""
|
|
|
|
bill = Bill.create_next_bill_for_user_address(self.recurring_user,
|
|
self.recurring_user_addr)
|
|
|
|
self.assertEqual(self.recurring_order.billrecord_set.count(), 1)
|
|
self.assertEqual(bill.billrecord_set.count(), 1)
|
|
|
|
|
|
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
|
|
"""
|
|
|
|
for ending_date in self.bill_dates:
|
|
b = Bill.create_next_bill_for_user_address(self.recurring_user, self.recurring_user_addr, ending_date)
|
|
b.close()
|
|
|
|
bill_count = Bill.objects.filter(owner=self.recurring_user).count()
|
|
|
|
self.assertEqual(len(self.bill_dates), bill_count)
|
|
|
|
def test_multi_addr_multi_bill(self):
|
|
"""
|
|
Ensure multiple bills are created if orders exist with different billing addresses
|
|
"""
|
|
|
|
username="lotsofplaces"
|
|
multi_addr_user = get_user_model().objects.create(
|
|
username=username,
|
|
email=f"{username}@example.org")
|
|
|
|
user_addr1 = BillingAddress.objects.create(
|
|
owner=multi_addr_user,
|
|
organization = 'Test org',
|
|
street="unknown",
|
|
city="unknown",
|
|
postal_code="unknown",
|
|
active=True)
|
|
|
|
order1 = Order.objects.create(
|
|
owner=multi_addr_user,
|
|
starting_date=self.order_meta[1]['starting_date'],
|
|
ending_date=self.order_meta[1]['ending_date'],
|
|
recurring_period=RecurringPeriod.ONE_TIME,
|
|
price=self.order_meta[1]['price'],
|
|
description=self.order_meta[1]['description'],
|
|
billing_address=BillingAddress.get_address_for(self.user))
|
|
|
|
# Make this address inactive
|
|
user_addr1.active = False
|
|
user_addr1.save()
|
|
|
|
user_addr2 = BillingAddress.objects.create(
|
|
owner=multi_addr_user,
|
|
organization = 'Test2 org',
|
|
street="unknown2",
|
|
city="unknown2",
|
|
postal_code="unknown2",
|
|
active=True)
|
|
|
|
order2 = Order.objects.create(
|
|
owner=multi_addr_user,
|
|
starting_date=self.order_meta[1]['starting_date'],
|
|
ending_date=self.order_meta[1]['ending_date'],
|
|
recurring_period=RecurringPeriod.ONE_TIME,
|
|
price=self.order_meta[1]['price'],
|
|
description=self.order_meta[1]['description'],
|
|
billing_address=BillingAddress.get_address_for(self.user))
|
|
|
|
|
|
bills = Bill.create_next_bills_for_user(multi_addr_user)
|
|
|
|
self.assertEqual(len(bills), 2)
|
|
|
|
|
|
# TO BE IMPLEMENTED -- once orders can be marked as "done" / "inactive" / "not for billing"
|
|
# def test_skip_disabled_orders(self):
|
|
# """
|
|
# Ensure that a bill only considers "active" orders
|
|
# """
|
|
|
|
# self.assertEqual(1, 2)
|
|
|
|
|
|
class ModifyProductTestCase(TestCase):
|
|
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)
|
|
|
|
def test_no_pro_rata_first_bill(self):
|
|
"""
|
|
The bill should NOT contain a partial amount -- this is a BILL TEST :-)
|
|
"""
|
|
|
|
price = 5
|
|
|
|
# Standard 30d recurring product
|
|
product = SampleRecurringProduct.objects.create(owner=self.user,
|
|
rc_price=price)
|
|
|
|
starting_date = timezone.make_aware(datetime.datetime(2020,3,3))
|
|
ending_date = timezone.make_aware(datetime.datetime(2020,3,31))
|
|
time_diff = (ending_date - starting_date).total_seconds()
|
|
|
|
product.create_order(starting_date)
|
|
|
|
bills = Bill.create_next_bills_for_user(self.user,
|
|
ending_date=ending_date)
|
|
|
|
|
|
# We expect 1 bill for 1 billing address and 1 time frame
|
|
self.assertEqual(len(bills), 1)
|
|
|
|
pro_rata_amount = time_diff / product.default_recurring_period.value
|
|
|
|
self.assertNotEqual(bills[0].sum, pro_rata_amount * price)
|
|
self.assertEqual(bills[0].sum, price)
|
|
|
|
def test_bill_for_modified_product(self):
|
|
"""
|
|
The bill should NOT contain a partial amount -- this is a BILL TEST :-)
|
|
"""
|
|
|
|
price = 5
|
|
|
|
# Standard 30d recurring product
|
|
product = SampleRecurringProduct.objects.create(owner=self.user,
|
|
rc_price=price)
|
|
|
|
starting_date = timezone.make_aware(datetime.datetime(2019,3,3))
|
|
ending_date = timezone.make_aware(datetime.datetime(2019,3,31))
|
|
change_date = timezone.make_aware(datetime.datetime(2019,4,17))
|
|
|
|
product.create_order(starting_date)
|
|
|
|
bills = Bill.create_next_bills_for_user(self.user,
|
|
ending_date=ending_date)
|
|
|
|
product.rc_price = 10
|
|
product.save()
|
|
|
|
product.create_or_update_recurring_order(when_to_start=change_date)
|
|
|
|
# expeted result:
|
|
# 1x 5 chf bill record
|
|
# 1x 5 chf bill record
|
|
# 1x 10 partial bill record
|
|
|
|
|
|
|
|
|
|
|
|
# class NotABillingTC(TestCase):
|
|
# #class BillingTestCase(TestCase):
|
|
# def setUp(self):
|
|
# self.user = get_user_model().objects.create(
|
|
# username='jdoe',
|
|
# email='john.doe@domain.tld')
|
|
# self.billing_address = BillingAddress.objects.create(
|
|
# owner=self.user,
|
|
# street="unknown",
|
|
# city="unknown",
|
|
# postal_code="unknown")
|
|
|
|
# def test_basic_monthly_billing(self):
|
|
# one_time_price = 10
|
|
# recurring_price = 20
|
|
# description = "Test Product 1"
|
|
|
|
# # Three months: full, full, partial.
|
|
# # starting_date = datetime.fromisoformat('2020-03-01')
|
|
# starting_date = datetime(2020,3,1)
|
|
# ending_date = datetime(2020,5,8)
|
|
|
|
# # Create order to be billed.
|
|
# order = Order.objects.create(
|
|
# owner=self.user,
|
|
# starting_date=starting_date,
|
|
# ending_date=ending_date,
|
|
# recurring_period=RecurringPeriod.PER_30D,
|
|
# recurring_price=recurring_price,
|
|
# one_time_price=one_time_price,
|
|
# description=description,
|
|
# billing_address=self.billing_address)
|
|
|
|
# # Generate & check bill for first month: full recurring_price + setup.
|
|
# first_month_bills = order.generate_initial_bill()
|
|
# self.assertEqual(len(first_month_bills), 1)
|
|
# self.assertEqual(first_month_bills[0].amount, one_time_price + recurring_price)
|
|
|
|
# # Generate & check bill for second month: full recurring_price.
|
|
# second_month_bills = Bill.generate_for(2020, 4, self.user)
|
|
# self.assertEqual(len(second_month_bills), 1)
|
|
# self.assertEqual(second_month_bills[0].amount, recurring_price)
|
|
|
|
# # Generate & check bill for third and last month: partial recurring_price.
|
|
# third_month_bills = Bill.generate_for(2020, 5, self.user)
|
|
# self.assertEqual(len(third_month_bills), 1)
|
|
# # 31 days in May.
|
|
# self.assertEqual(float(third_month_bills[0].amount),
|
|
# round(round((7/31), AMOUNT_DECIMALS) * recurring_price, AMOUNT_DECIMALS))
|
|
|
|
# # Check that running Bill.generate_for() twice does not create duplicates.
|
|
# self.assertEqual(len(Bill.generate_for(2020, 3, self.user)), 0)
|
|
|
|
# def test_basic_yearly_billing(self):
|
|
# one_time_price = 10
|
|
# recurring_price = 150
|
|
# description = "Test Product 1"
|
|
|
|
# starting_date = datetime.fromisoformat('2020-03-31T08:05:23')
|
|
|
|
# # Create order to be billed.
|
|
# order = Order.objects.create(
|
|
# owner=self.user,
|
|
# starting_date=starting_date,
|
|
# recurring_period=RecurringPeriod.PER_365D,
|
|
# recurring_price=recurring_price,
|
|
# one_time_price=one_time_price,
|
|
# description=description,
|
|
# billing_address=self.billing_address)
|
|
|
|
# # Generate & check bill for first year: recurring_price + setup.
|
|
# first_year_bills = order.generate_initial_bill()
|
|
# self.assertEqual(len(first_year_bills), 1)
|
|
# self.assertEqual(first_year_bills[0].starting_date.date(),
|
|
# date.fromisoformat('2020-03-31'))
|
|
# self.assertEqual(first_year_bills[0].ending_date.date(),
|
|
# date.fromisoformat('2021-03-30'))
|
|
# self.assertEqual(first_year_bills[0].amount,
|
|
# recurring_price + one_time_price)
|
|
|
|
# # Generate & check bill for second year: recurring_price.
|
|
# second_year_bills = Bill.generate_for(2021, 3, self.user)
|
|
# self.assertEqual(len(second_year_bills), 1)
|
|
# self.assertEqual(second_year_bills[0].starting_date.date(),
|
|
# date.fromisoformat('2021-03-31'))
|
|
# self.assertEqual(second_year_bills[0].ending_date.date(),
|
|
# date.fromisoformat('2022-03-30'))
|
|
# self.assertEqual(second_year_bills[0].amount, recurring_price)
|
|
|
|
# # Check that running Bill.generate_for() twice does not create duplicates.
|
|
# self.assertEqual(len(Bill.generate_for(2020, 3, self.user)), 0)
|
|
# self.assertEqual(len(Bill.generate_for(2020, 4, self.user)), 0)
|
|
# self.assertEqual(len(Bill.generate_for(2020, 2, self.user)), 0)
|
|
# self.assertEqual(len(Bill.generate_for(2021, 3, self.user)), 0)
|
|
|
|
# def test_basic_hourly_billing(self):
|
|
# one_time_price = 10
|
|
# recurring_price = 1.4
|
|
# description = "Test Product 1"
|
|
|
|
# starting_date = datetime.fromisoformat('2020-03-31T08:05:23')
|
|
# ending_date = datetime.fromisoformat('2020-04-01T11:13:32')
|
|
|
|
# # Create order to be billed.
|
|
# order = Order.objects.create(
|
|
# owner=self.user,
|
|
# starting_date=starting_date,
|
|
# ending_date=ending_date,
|
|
# recurring_period=RecurringPeriod.PER_HOUR,
|
|
# recurring_price=recurring_price,
|
|
# one_time_price=one_time_price,
|
|
# description=description,
|
|
# billing_address=self.billing_address)
|
|
|
|
# # Generate & check bill for first month: recurring_price + setup.
|
|
# first_month_bills = order.generate_initial_bill()
|
|
# self.assertEqual(len(first_month_bills), 1)
|
|
# self.assertEqual(float(first_month_bills[0].amount),
|
|
# round(16 * recurring_price, AMOUNT_DECIMALS) + one_time_price)
|
|
|
|
# # Generate & check bill for first month: recurring_price.
|
|
# second_month_bills = Bill.generate_for(2020, 4, self.user)
|
|
# self.assertEqual(len(second_month_bills), 1)
|
|
# self.assertEqual(float(second_month_bills[0].amount),
|
|
# round(12 * recurring_price, AMOUNT_DECIMALS))
|