from django.test import TestCase from django.contrib.auth import get_user_model from datetime import datetime, date, timedelta from django.utils import timezone from .models import * from uncloud_service.models import GenericServiceProduct from uncloud.models import UncloudProvider, UncloudNetwork import json vm_product_config = { 'features': { 'cores': { 'min': 1, 'max': 48 }, 'ram_gb': { 'min': 1, 'max': 256 }, }, } vm_order_config = json.dumps({ 'cores': 1, 'memory': 2, 'storage': 100 }) vm_order_downgrade_config = { 'features': { 'cores': 1, 'ram_gb': 1 } } vm_order_upgrade_config = { 'features': { 'cores': 4, 'ram_gb': 4 } } 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) RecurringPeriod.populate_db_defaults() self.default_recurring_period = RecurringPeriod.objects.get(name="Per 30 days") def test_create_product(self): """ Create a sample product """ p = Product.objects.create(name="Testproduct", description="Only for testing", config=vm_product_config) p.recurring_periods.add(self.default_recurring_period, through_defaults= { 'is_default': True }) class OrderTestCase(TestCase): """ The heart of ordering products """ def setUp(self): self.pricing_plan = PricingPlan.objects.create(name="PricingSample", set_up_fees=35, cores_unit_price=3, ram_unit_price=4, storage_unit_price=0.02) 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) self.product = Product.objects.create(name="Testproduct", description="Only for testing", config=vm_product_config) RecurringPeriod.populate_db_defaults() self.default_recurring_period = RecurringPeriod.objects.get(name="Per 30 days") 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 """ order_config = json.dumps({ 'cores': 1, 'memory':2, 'storage': 100 }) o = Order.objects.create(owner=self.user, billing_address=self.ba, pricing_plan = self.pricing_plan, product=self.product, config=order_config) def test_order_product(self): """ Order a product, ensure the order has correct price setup """ order_config = json.dumps({ 'cores': 1, 'memory':2, 'storage': 100 }) o = Order.objects.create(owner=self.user, billing_address=self.ba, pricing_plan = self.pricing_plan, product=self.product, config=order_config) self.assertEqual(o.one_time_price, 0) self.assertEqual(o.recurring_price, 13.0) def test_change_order(self): """ Change an order and ensure that - a new order is created - the price is correct in the new order """ order_config = json.dumps({ 'cores': 2, 'memory':4, 'storage': 200, }) order1 = Order.objects.create(owner=self.user, billing_address=self.ba, pricing_plan = self.pricing_plan, product=self.product, config=order_config) self.assertEqual(order1.one_time_price, 0) self.assertEqual(order1.recurring_price, 26.0) 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.pricing_plan = PricingPlan.objects.create(name="PricingSample", set_up_fees=35, cores_unit_price=3, ram_unit_price=4, storage_unit_price=0.02) self.order1_config = json.dumps({ 'cores': 2, 'memory':4, 'storage': 200 }) self.order2_config = json.dumps({ 'cores': 1, 'memory':2, 'storage': 100 }) self.ba = BillingAddress.objects.create( owner=self.user, organization = 'Test org', street="unknown", city="unknown", postal_code="somewhere else", active=True) self.product = Product.objects.create(name="Testproduct", description="Only for testing", config=vm_product_config) RecurringPeriod.populate_db_defaults() self.default_recurring_period = RecurringPeriod.objects.get(name="Per 30 days") self.product.recurring_periods.add(self.default_recurring_period, through_defaults= { 'is_default': True }) 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=self.order1_config, pricing_plan=self.pricing_plan, starting_date=starting_date) order1.update_order(self.order2_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, pricing_plan=self.pricing_plan, config=self.order1_config, starting_date=starting_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), 1) self.assertEqual(bill_records[0].starting_date, starting_date) order1.update_order(self.order2_config, starting_date=change1_date) bills = Bill.create_next_bills_for_user(user, ending_date=bill_ending_date) bill_records = BillRecord.objects.filter(bill=bill) self.assertEqual(len(bill_records), 2) self.assertEqual(bill_records[0].order.ending_date.date(), change1_date.date()) class BillTestCase(TestCase): """ Test aspects of billing / creating a bill """ def setUp(self): RecurringPeriod.populate_db_defaults() self.pricing_plan = PricingPlan.objects.create(name="PricingSample", set_up_fees=35, cores_unit_price=3, ram_unit_price=4, storage_unit_price=0.02) 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': '' } self.product = Product.objects.create(name="Product Sample", description="Not only for testing, but for joy", config=vm_product_config) self.vm = Product.objects.create(name="Super Fast VM", description="Zooooom", config=vm_product_config) 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.product.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 }) # 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_product(self): return Order.objects.create( owner=self.user, recurring_period=RecurringPeriod.objects.get(name="Onetime"), product=self.product, billing_address=BillingAddress.get_address_for(self.user), starting_date=self.order_meta[1]['starting_date'], ending_date=self.order_meta[1]['ending_date'], pricing_plan=self.pricing_plan, config=vm_order_config) 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, pricing_plan=self.pricing_plan, billing_address=BillingAddress.get_address_for(self.recurring_user), starting_date=timezone.make_aware(datetime.datetime(2020,3,3)), ) def test_bill_one_time_with_recurring(self): """ Validate that if the order contains one_time_price and recurring_pricing One Bill records should be created """ order = Order.objects.create( owner=self.user, product=self.vm, config=vm_order_config, pricing_plan=self.pricing_plan, one_time_price = 35, billing_address=BillingAddress.get_address_for(self.user), starting_date=timezone.make_aware(datetime.datetime(2020,3,3)), ) bill = Bill.create_next_bill_for_user_address(self.user_addr) self.assertEqual(order.billrecord_set.count(), 1) record = order.billrecord_set.first() self.assertEqual(record.is_recurring_record, False) self.assertEqual(record.price, 35) self.assertEqual(record.quantity, 1) self.assertEqual(record.sum, 35) #close the bill as it has been paid bill.close() bill2 = Bill.create_next_bill_for_user_address(self.user_addr) self.assertNotEqual(bill.id, bill2.id) self.assertEqual(order.billrecord_set.count(), 2) record = BillRecord.objects.filter(bill=bill2, order=order).first() self.assertEqual(record.is_recurring_record, True) self.assertEqual(record.price, 13) self.assertEqual(record.quantity, 1) self.assertEqual(record.sum, 13) def test_bill_one_time_one_bill_record(self): """ Ensure there is only 1 bill record per order """ order = self.order_product() 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_product() self.assertEqual(order.recurring_price, 13.0) bill = Bill.create_next_bill_for_user_address(self.user_addr) self.assertEqual(order.billrecord_set.count(), 1) record = order.billrecord_set.first() self.assertEqual(record.price, 13) self.assertEqual(record.quantity, 1) self.assertEqual(bill.sum, 13) def test_bill_creates_record_for_recurring_order(self): """ Ensure there is only 1 bill record per order """ order = self.order_vm() bill = Bill.create_next_bill_for_user_address(self.recurring_user_addr) self.assertEqual(order.billrecord_set.count(), 1) self.assertEqual(bill.bill_records.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 """ order = self.order_vm() for ending_date in self.bill_dates: b = Bill.create_next_bill_for_user_address(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) 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(BillingAddress.DoesNotExist, BillingAddress.get_address_for, self.user) class VATRatesTestCase(TestCase): def setUp(self): self.user = get_user_model().objects.create( username='random_user', email='jane.random@domain.tld') self.user_addr = BillingAddress.objects.create( owner=self.user, organization = 'Test org', street="unknown", city="unknown", postal_code="unknown", active=True) UncloudNetwork.populate_db_defaults() UncloudProvider.populate_db_defaults() def test_get_rate_for_user(self): """ Raise an error, when there is no address """