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 import json chocolate_product_config = { 'features': { 'gramm': { 'min': 100, 'max': 5000, 'one_time_price_per_unit': 0.2, 'recurring_price_per_unit': 0 }, }, } chocolate_order_config = { 'features': { 'gramm': 500, } } chocolate_one_time_price = chocolate_order_config['features']['gramm'] * chocolate_product_config['features']['gramm']['one_time_price_per_unit'] vm_product_config = { 'features': { 'cores': { 'min': 1, 'max': 48, 'one_time_price_per_unit': 0, 'recurring_price_per_unit': 4 }, 'ram_gb': { 'min': 1, 'max': 256, 'one_time_price_per_unit': 0, 'recurring_price_per_unit': 4 }, }, } vm_order_config = { 'features': { 'cores': 2, 'ram_gb': 2 } } 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.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 """ o = Order.objects.create(owner=self.user, billing_address=self.ba, product=self.product, config=vm_order_config) def test_order_product(self): """ Order a product, ensure the order has correct price setup """ o = Order.objects.create(owner=self.user, billing_address=self.ba, product=self.product) self.assertEqual(o.one_time_price, 0) self.assertEqual(o.recurring_price, 16) 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, product=self.product, 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) 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=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) class BillTestCase(TestCase): """ Test aspects of billing / creating a bill """ def setUp(self): RecurringPeriod.populate_db_defaults() 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) 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.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 }) # 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, recurring_period=RecurringPeriod.objects.get(name="Onetime"), 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) 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, recurring_period=RecurringPeriod.objects.get(name="Onetime"), 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) 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) 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.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 """ 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(uncloud_pay.models.BillingAddress.DoesNotExist, BillingAddress.get_address_for, self.user)