From 8be59499b6074efd8f657c2d88a236d7d0646ec9 Mon Sep 17 00:00:00 2001 From: Levi Date: Thu, 12 May 2016 01:57:34 -0500 Subject: [PATCH] Added based view test, Added billing address form test, payment view test , Added test for hetzner pricing calculation, Added test for bern pricing calculation, Fixed script to load initial pricing data --- .../management/commands/create_vm_types.py | 4 +- hosting/migrations/0015_auto_20160512_0448.py | 20 +++ hosting/models.py | 6 +- hosting/templates/hosting/order_detail.html | 10 +- hosting/templates/hosting/orders.html | 2 +- .../hosting/virtual_machine_detail.html | 2 +- hosting/test_models.py | 75 ++++++++ hosting/test_views.py | 164 +++++++++++++++++- hosting/views.py | 5 +- utils/test_forms.py | 26 ++- utils/tests.py | 65 ++++++- 11 files changed, 360 insertions(+), 19 deletions(-) create mode 100644 hosting/migrations/0015_auto_20160512_0448.py create mode 100644 hosting/test_models.py diff --git a/hosting/management/commands/create_vm_types.py b/hosting/management/commands/create_vm_types.py index 23961bae..44995634 100644 --- a/hosting/management/commands/create_vm_types.py +++ b/hosting/management/commands/create_vm_types.py @@ -10,8 +10,8 @@ class Command(BaseCommand): hetzner = { 'base_price': 10, 'core_price': 10, - 'memory_price': 10, - 'disk_size_price': 10, + 'memory_price': 5, + 'disk_size_price': 1, 'description': 'VM auf einzelner HW, Raid1, kein HA' } diff --git a/hosting/migrations/0015_auto_20160512_0448.py b/hosting/migrations/0015_auto_20160512_0448.py new file mode 100644 index 00000000..16380513 --- /dev/null +++ b/hosting/migrations/0015_auto_20160512_0448.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-05-12 04:48 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('hosting', '0014_auto_20160505_0541'), + ] + + operations = [ + migrations.RenameField( + model_name='hostingorder', + old_name='VMPlan', + new_name='vm_plan', + ), + ] diff --git a/hosting/models.py b/hosting/models.py index dd686312..131a51d8 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -106,7 +106,7 @@ class HostingOrder(models.Model): ORDER_APPROVED_STATUS = 'Approved' ORDER_DECLINED_STATUS = 'Declined' - VMPlan = models.ForeignKey(VirtualMachinePlan, related_name='hosting_orders') + vm_plan = models.ForeignKey(VirtualMachinePlan, related_name='hosting_orders') customer = models.ForeignKey(StripeCustomer) billing_address = models.ForeignKey(BillingAddress) created_at = models.DateTimeField(auto_now_add=True) @@ -120,8 +120,8 @@ class HostingOrder(models.Model): return self.ORDER_APPROVED_STATUS if self.approved else self.ORDER_DECLINED_STATUS @classmethod - def create(cls, VMPlan=None, customer=None, billing_address=None): - instance = cls.objects.create(VMPlan=VMPlan, customer=customer, + def create(cls, vm_plan=None, customer=None, billing_address=None): + instance = cls.objects.create(vm_plan=vm_plan, customer=customer, billing_address=billing_address) return instance diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 74b8d5bb..f7c31eff 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -48,15 +48,15 @@

Order summary


-

Type {{object.VMPlan.hosting_company_name}}

+

Type {{object.vm_plan.hosting_company_name}}


-

Cores {{object.VMPlan.cores}}

+

Cores {{object.vm_plan.cores}}


-

Memory {{object.VMPlan.memory}} GiB

+

Memory {{object.vm_plan.memory}} GiB


-

Disk space {{object.VMPlan.disk_size}} GiB

+

Disk space {{object.vm_plan.disk_size}} GiB


-

Total

{{object.VMPlan.price}} CHF

+

Total

{{object.vm_plan.price}} CHF

diff --git a/hosting/templates/hosting/orders.html b/hosting/templates/hosting/orders.html index 99c6464f..ce408783 100644 --- a/hosting/templates/hosting/orders.html +++ b/hosting/templates/hosting/orders.html @@ -23,7 +23,7 @@ {{order.id}} {{order.created_at}} - {{order.VMPlan.price}} CHF + {{order.vm_plan.price}} CHF {% if order.approved %} Approved {% else%} diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index e51e1d4e..55d07b86 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -104,7 +104,7 @@ {{order.id}} {{order.created_at}} - {{order.VMPlan.price}} CHF + {{order.vm_plan.price}} CHF {% if order.approved %} Approved {% else%} diff --git a/hosting/test_models.py b/hosting/test_models.py new file mode 100644 index 00000000..0e80ce31 --- /dev/null +++ b/hosting/test_models.py @@ -0,0 +1,75 @@ +from django.test import TestCase + +from model_mommy import mommy +from django.core.management import call_command + + +from .models import VirtualMachineType + + +class VirtualMachineTypeModelTest(TestCase): + + def setUp(self): + self.HETZNER_NUG_NAME = 'hetzner_nug' + self.HETZNER_NAME = 'hetzner' + self.HETZNER_RAID6_NAME = 'hetzner_raid6' + self.HETZNER_GLUSTERFS_NAME = 'hetzner_glusterfs' + self.BERN_NAME = 'bern' + self.HETZNER_NUG_EXPECTED_PRICE = 79 + self.HETZNER_EXPECTED_PRICE = 180 + self.HETZNER_RAID6_EXPECTED_PRICE = 216 + self.HETZNER_GLUSTERFS_EXPECTED_PRICE = 252 + self.BERN_EXPECTED_PRICE = 202 + call_command('create_vm_types') + + def test_calculate_price(self): + + # hetzner_nug + specifications = { + 'cores': 2, + 'memory': 10, + 'disk_size': 100 + } + vm_type = VirtualMachineType.objects.get(hosting_company=self.HETZNER_NUG_NAME) + calculated_price = vm_type.calculate_price(specifications) + self.assertEqual(calculated_price, self.HETZNER_NUG_EXPECTED_PRICE) + + # hetzner + specifications = { + 'cores': 2, + 'memory': 10, + 'disk_size': 100 + } + vm_type = VirtualMachineType.objects.get(hosting_company=self.HETZNER_NAME) + calculated_price = vm_type.calculate_price(specifications) + self.assertEqual(calculated_price, self.HETZNER_EXPECTED_PRICE) + + # hetzner_raid6 + specifications = { + 'cores': 2, + 'memory': 10, + 'disk_size': 100 + } + vm_type = VirtualMachineType.objects.get(hosting_company=self.HETZNER_RAID6_NAME) + calculated_price = vm_type.calculate_price(specifications) + self.assertEqual(calculated_price, self.HETZNER_RAID6_EXPECTED_PRICE) + + # hetzner_glusterfs + specifications = { + 'cores': 2, + 'memory': 10, + 'disk_size': 100 + } + vm_type = VirtualMachineType.objects.get(hosting_company=self.HETZNER_GLUSTERFS_NAME) + calculated_price = vm_type.calculate_price(specifications) + self.assertEqual(calculated_price, self.HETZNER_GLUSTERFS_EXPECTED_PRICE) + + # bern + specifications = { + 'cores': 2, + 'memory': 10, + 'disk_size': 100 + } + vm_type = VirtualMachineType.objects.get(hosting_company=self.BERN_NAME) + calculated_price = vm_type.calculate_price(specifications) + self.assertEqual(calculated_price, self.BERN_EXPECTED_PRICE) diff --git a/hosting/test_views.py b/hosting/test_views.py index b2dfe9ae..161e95a5 100644 --- a/hosting/test_views.py +++ b/hosting/test_views.py @@ -1,8 +1,17 @@ -from django.test import TestCase +from unittest import mock +from django.conf import settings +from django.test import TestCase, RequestFactory from django.core.urlresolvers import reverse from django.core.urlresolvers import resolve -from .models import VirtualMachineType -from .views import DjangoHostingView, RailsHostingView, NodeJSHostingView + +from model_mommy import mommy + + +from membership.models import CustomUser, StripeCustomer +from .models import VirtualMachineType, HostingOrder +from .views import DjangoHostingView, RailsHostingView, NodeJSHostingView, LoginView, SignupView,\ + PaymentVMView +from utils.tests import BaseTestCase class ProcessVMSelectionTestMixin(object): @@ -70,3 +79,152 @@ class NodeJSHostingViewTest(TestCase, ProcessVMSelectionTestMixin): 'email': "info@node-hosting.ch", 'vm_types': VirtualMachineType.get_serialized_vm_types(), } + + +class PaymentVMViewTest(BaseTestCase): + + def setUp(self): + super(PaymentVMViewTest, self).setUp() + + self.view = PaymentVMView + + # VM + self.vm = mommy.make(VirtualMachineType, base_price=10000, + memory_price=100, + core_price=1000, + disk_size_price=1) + + # post data + self.billing_address = { + 'street_address': 'street name', + 'city': 'MyCity', + 'postal_code': '32123123123123', + 'country': 'VE', + 'token': 'a23kfmslwxhkwis' + } + + # urls + self.url = reverse('hosting:payment') + + # Session data + self.session_data = { + 'vm_specs': { + 'hosting_company': self.vm.hosting_company, + 'cores': 1, + 'memory': 10, + 'disk_size': 10000, + 'price': 22000, + } + } + + session = self.customer_client.session + session.update(self.session_data) + session.save() + + def test_url_resolve_to_view_correctly(self): + found = resolve(self.url) + self.assertEqual(found.func.__name__, self.view.__name__) + + @mock.patch('utils.stripe_utils.StripeUtils.create_customer') + def test_post(self, stripe_mocked_call): + + # Anonymous user should get redirect to login + response = self.client.post(self.url) + expected_url = "%s?next=%s" % (reverse('hosting:login'), reverse('hosting:payment')) + self.assertRedirects(response, expected_url=expected_url, + status_code=302, target_status_code=200) + + # Customer user should be able to pay + stripe_mocked_call.return_value = { + 'paid': True, + 'response_object': self.stripe_mocked_customer, + 'error': None + } + response = self.customer_client.post(self.url, self.billing_address) + self.assertEqual(response.status_code, 200) + self.assertTrue(StripeCustomer.objects.filter(user__email=self.customer.email).exists()) + stripe_customer = StripeCustomer.objects.get(user__email=self.customer.email) + self.assertEqual(stripe_customer.user, self.customer) + self.assertTrue(HostingOrder.objects.filter(customer=stripe_customer).exists()) + hosting_order = HostingOrder.objects.filter(customer=stripe_customer)[0] + vm_plan = { + 'cores': hosting_order.vm_plan.cores, + 'memory': hosting_order.vm_plan.memory, + 'disk_size': hosting_order.vm_plan.disk_size, + 'price': hosting_order.vm_plan.price, + 'hosting_company': hosting_order.vm_plan.vm_type.hosting_company + } + self.assertEqual(vm_plan, self.session_data.get('vm_specs')) + + def test_get(self): + + # Anonymous user should get redirect to login + response = self.client.get(self.url) + expected_url = "%s?next=%s" % (reverse('hosting:login'), reverse('hosting:payment')) + self.assertRedirects(response, expected_url=expected_url, + status_code=302, target_status_code=200) + + # Logged user should get the page + response = self.customer_client.get(self.url, follow=True) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['stripe_key'], + settings.STRIPE_API_PUBLIC_KEY) + + +class LoginViewTest(TestCase): + + def setUp(self): + self.url = reverse('hosting:login') + self.view = LoginView + self.expected_template = 'hosting/login.html' + self.user = mommy.make('membership.CustomUser') + self.password = 'fake_password' + self.user.set_password(self.password) + self.user.save() + + def test_url_resolve_to_view_correctly(self): + found = resolve(self.url) + self.assertEqual(found.func.__name__, self.view.__name__) + + def test_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, self.expected_template) + + def test_anonymous_user_can_login(self): + data = { + 'email': self.user.email, + 'password': self.password + } + response = self.client.post(self.url, data=data, follow=True) + self.assertEqual(response.context['user'], self.user) + self.assertEqual(response.status_code, 200) + + +class SignupViewTest(TestCase): + + def setUp(self): + self.url = reverse('hosting:signup') + self.expected_template = 'hosting/signup.html' + self.view = SignupView + self.signup_data = { + 'name': 'ungleich', + 'email': 'test@ungleich.com', + 'password': 'fake_password', + 'confirm_password': 'fake_password', + } + + def test_url_resolve_to_view_correctly(self): + found = resolve(self.url) + self.assertEqual(found.func.__name__, self.view.__name__) + + def test_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, self.expected_template) + + def test_anonymous_user_can_signup(self): + response = self.client.post(self.url, data=self.signup_data, follow=True) + self.user = CustomUser.objects.get(email=self.signup_data.get('email')) + self.assertEqual(response.context['user'], self.user) + self.assertEqual(response.status_code, 200) diff --git a/hosting/views.py b/hosting/views.py index b0e76028..2819104f 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -143,8 +143,9 @@ class SignupView(CreateView): return HttpResponseRedirect(self.get_success_url()) -class PaymentVMView(FormView): +class PaymentVMView(LoginRequiredMixin, FormView): template_name = 'hosting/payment.html' + login_url = reverse_lazy('hosting:login') form_class = BillingAddressForm def get_context_data(self, **kwargs): @@ -183,7 +184,7 @@ class PaymentVMView(FormView): billing_address = form.save() # Create a Hosting Order - order = HostingOrder.create(VMPlan=plan, customer=customer, + order = HostingOrder.create(vm_plan=plan, customer=customer, billing_address=billing_address) # Make stripe charge to a customer diff --git a/utils/test_forms.py b/utils/test_forms.py index 3e5701a7..dc6d9fcc 100644 --- a/utils/test_forms.py +++ b/utils/test_forms.py @@ -1,5 +1,5 @@ from django.test import TestCase -from .forms import ContactUsForm +from .forms import ContactUsForm, BillingAddressForm class ContactUsFormTest(TestCase): @@ -23,3 +23,27 @@ class ContactUsFormTest(TestCase): def test_invalid_form(self): form = ContactUsForm(data=self.incompleted_data) self.assertFalse(form.is_valid()) + + +class BillingAddressFormTest(TestCase): + + def setUp(self): + self.completed_data = { + 'street_address': 'street name', + 'city': 'MyCity', + 'postal_code': '32123123123123', + 'country': 'VE', + 'token': 'a23kfmslwxhkwis' + } + + self.incompleted_data = { + 'street_address': 'test', + } + + def test_valid_form(self): + form = BillingAddressForm(data=self.completed_data) + self.assertTrue(form.is_valid()) + + def test_invalid_form(self): + form = BillingAddressForm(data=self.incompleted_data) + self.assertFalse(form.is_valid()) diff --git a/utils/tests.py b/utils/tests.py index 7ce503c2..83b87a85 100644 --- a/utils/tests.py +++ b/utils/tests.py @@ -1,3 +1,66 @@ from django.test import TestCase +from django.test import Client +from model_mommy import mommy -# Create your tests here. + +class BaseTestCase(TestCase): + """ + Base class to initialize the test cases + """ + + def setUp(self): + + # Password + self.dummy_password = 'test_password' + + # Users + self.customer, self.another_customer = mommy.make('membership.CustomUser', + _quantity=2) + self.customer.set_password(self.dummy_password) + self.customer.save() + self.another_customer.set_password(self.dummy_password) + self.another_customer.save() + + # Stripe mocked data + self.stripe_mocked_customer = self.customer_stripe_mocked_data() + + # Clients + self.customer_client = self.get_client(self.customer) + self.another_customer_client = self.get_client(self.another_customer) + + def get_client(self, user): + """ + Authenticate a user and return the client + """ + client = Client() + client.login(email=user.email, password=self.dummy_password) + return client + + def customer_stripe_mocked_data(self): + return { + "id": "cus_8R1y9UWaIIjZqr", + "object": "customer", + "currency": "usd", + "default_source": "card_18A9up2eZvKYlo2Cq2RJMGeF", + "email": "vmedixtodd+1@gmail.com", + "livemode": False, + "metadata": { + }, + "shipping": None, + "sources": { + "object": "list", + "data": [{ + "id": "card_18A9up2eZvKYlo2Cq2RJMGeF", + "object": "card", + "brand": "Visa", + "country": "US", + "customer": "cus_8R1y9UWaIIjZqr", + "cvc_check": "pass", + "dynamic_last4": None, + "exp_month": 12, + "exp_year": 2018, + "funding": "credit", + "last4": "4242", + }] + } + }