# datacenterlight/tests/test_views.py import json from unittest.mock import patch from django.contrib.messages import get_messages from django.contrib.sessions.middleware import SessionMiddleware from django.contrib.auth.models import AnonymousUser from django.core.exceptions import ValidationError from django.test import RequestFactory, TestCase from django.urls import reverse from datacenterlight.forms import ContactForm from datacenterlight.views import ( ContactUsView, IndexView, OrderConfirmationView, PaymentOrderView, WhyDataCenterLightView, create_incomplete_intent_request, get_error_response_dict, show_error, ) from utils.forms import BillingAddressFormSignup from factories import CMSIntegrationFactory class SessionRequestMixin: """ Adds a real Django session to RequestFactory requests. """ def add_session(self, request): middleware = SessionMiddleware(lambda request: None) middleware.process_request(request) request.session.save() return request class ContactUsViewTests(TestCase, SessionRequestMixin): def setUp(self): self.factory = RequestFactory() def test_get_redirects_to_index(self): response = self.client.get( reverse("datacenterlight:contact_us") ) self.assertRedirects( response, reverse("datacenterlight:index"), fetch_redirect_response=False, ) def test_form_valid_renders_index_template(self): request = self.factory.post( reverse("datacenterlight:contact_us") ) view = ContactUsView() view.setup(request) form = ContactForm() response = view.form_valid(form) self.assertEqual(response.status_code, 200) self.assertContains(response, "Thank you") def test_form_invalid_renders_index_template(self): request = self.factory.post( reverse("datacenterlight:contact_us") ) view = ContactUsView() view.setup(request) # Bind the form with invalid/missing data. form = ContactForm(data={}) self.assertFalse(form.is_valid()) response = view.form_invalid(form) self.assertEqual(response.status_code, 200) self.assertContains(response, "index") class IndexViewTests(TestCase, SessionRequestMixin): def setUp(self): self.factory = RequestFactory() def test_get_redirects_to_cms_index(self): response = self.client.get( reverse("datacenterlight:whydatacenterlight") ) self.assertRedirects( response, reverse("datacenterlight:cms_index"), fetch_redirect_response=False, ) def test_index_t_redirect_chain(self): response = self.client.get( reverse("datacenterlight:index_t"), follow=True, ) self.assertEqual(response.status_code, 200) self.assertEqual( response.redirect_chain[0][0], reverse("datacenterlight:cms_index"), ) def test_index_g_uses_index_view(self): response = self.client.get( reverse("datacenterlight:index_g"), follow=True, ) self.assertEqual(response.status_code, 200) self.assertEqual( response.redirect_chain[0][0], reverse("datacenterlight:cms_index"), ) def test_index_f_uses_index_view(self): response = self.client.get( reverse("datacenterlight:index_f"), follow=True, ) self.assertEqual(response.status_code, 200) self.assertEqual( response.redirect_chain[0][0], reverse("datacenterlight:cms_index"), ) def test_index_l_uses_index_view(self): response = self.client.get( reverse("datacenterlight:index_l"), follow=True, ) self.assertEqual(response.status_code, 200) self.assertEqual( response.redirect_chain[0][0], reverse("datacenterlight:cms_index"), ) def test_cms_index_redirects_to_cms(self): response = self.client.get( reverse("datacenterlight:cms_index"), follow=True, ) def test_get_success_url(self): request = self.factory.get( reverse("datacenterlight:index") ) view = IndexView() view.setup(request) self.assertEqual( view.get_success_url(), reverse("datacenterlight:index") + "#requestform", ) def test_get_context_data_contains_base_url(self): request = self.factory.get( reverse("datacenterlight:index") ) view = IndexView() view.setup(request) with patch( "datacenterlight.views.CreateView.get_context_data", return_value={}, ): context = view.get_context_data() self.assertIn("base_url", context) self.assertIn("contact_form", context) self.assertIs(context["contact_form"], ContactForm) self.assertEqual( context["base_url"], f"{request.scheme}://{request.get_host()}", ) # ------------------------------------------------------------------ # validate_cores # ------------------------------------------------------------------ def test_validate_cores_accepts_one(self): request = self.factory.post("/") view = IndexView() view.setup(request) self.assertIsNone(view.validate_cores(1)) def test_validate_cores_accepts_48(self): request = self.factory.post("/") view = IndexView() view.setup(request) self.assertIsNone(view.validate_cores(48)) def test_validate_cores_rejects_zero(self): request = self.factory.post("/") view = IndexView() view.setup(request) with self.assertRaises(ValidationError): view.validate_cores(0) def test_validate_cores_rejects_more_than_48(self): request = self.factory.post("/") view = IndexView() view.setup(request) with self.assertRaises(ValidationError): view.validate_cores(49) def test_validate_cores_rejects_negative_number(self): request = self.factory.post("/") view = IndexView() view.setup(request) with self.assertRaises(ValidationError): view.validate_cores(-1) # ------------------------------------------------------------------ # validate_memory # ------------------------------------------------------------------ def test_validate_memory_requires_pid(self): request = self.factory.post( "/", { "ram": "1", }, ) view = IndexView() view.setup(request) with self.assertRaises(ValidationError): view.validate_memory(1) def test_validate_memory_rejects_unknown_plugin(self): request = self.factory.post( "/", { "pid": "999999999", }, ) view = IndexView() view.setup(request) with self.assertRaises(ValidationError): view.validate_memory(1) # ------------------------------------------------------------------ # validate_storage # ------------------------------------------------------------------ def test_validate_storage_accepts_10(self): request = self.factory.post("/") view = IndexView() view.setup(request) self.assertIsNone(view.validate_storage(10)) def test_validate_storage_accepts_2000(self): request = self.factory.post("/") view = IndexView() view.setup(request) self.assertIsNone(view.validate_storage(2000)) def test_validate_storage_rejects_below_10(self): request = self.factory.post("/") view = IndexView() view.setup(request) with self.assertRaises(ValidationError): view.validate_storage(9) def test_validate_storage_rejects_above_2000(self): request = self.factory.post("/") view = IndexView() view.setup(request) with self.assertRaises(ValidationError): view.validate_storage(2001) # ------------------------------------------------------------------ # POST without a valid pricing name # ------------------------------------------------------------------ def test_post_with_unknown_pricing_name_redirects_to_referer(self): response = self.client.post( reverse("datacenterlight:index"), { "cpu": "2", "ram": "4", "storage": "20", "config": "1", "pricing_name": "definitely-does-not-exist", }, HTTP_REFERER="/some-page/", ) self.assertEqual(response.status_code, 302) self.assertEqual( response["Location"], "/some-page/#order_form", ) messages = list( get_messages(response.wsgi_request) ) self.assertTrue(messages) self.assertIn( "Incorrect pricing name", str(messages[0]), ) class WhyDataCenterLightViewTests(TestCase): def test_template_name(self): view = WhyDataCenterLightView() self.assertEqual( view.template_name, "datacenterlight/whydatacenterlight.html", ) def test_get_redirects_to_cms_index(self): response = self.client.get( reverse("datacenterlight:whydatacenterlight") ) self.assertRedirects( response, reverse("datacenterlight:cms_index"), fetch_redirect_response=False, ) class PaymentOrderViewTests(TestCase, SessionRequestMixin): def setUp(self): self.factory = RequestFactory() CMSIntegrationFactory.create(name="default", domain=None) def set_payment_session(self, **extra): session = self.client.session session["vat_validation_status"] = "not_needed" session["card_id"] = None session["token"] = None session["id_payment_method"] = None session.update(extra) session.save() def test_product_lookup_with_unknown_product_returns_empty_json(self): self.set_payment_session() response = self.client.post( reverse("datacenterlight:payment"), { "product": "1", "generic_payment_form-product_name": "999999999", }, ) self.assertEqual(response.status_code, 200) self.assertEqual(response.json(), {}) def test_get_form_class_for_anonymous_user(self): request = self.factory.get( reverse("datacenterlight:payment") ) request.user = AnonymousUser() view = PaymentOrderView() view.setup(request) form_class = view.get_form_class() self.assertIsNotNone(form_class) self.assertIs(form_class, BillingAddressFormSignup) def test_generic_payment_page_renders(self): self.set_payment_session() response = self.client.get( reverse("datacenterlight:payment"), { "type": "generic", }, ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed( response, "datacenterlight/landing_payment.html", ) class OrderConfirmationViewTests(TestCase, SessionRequestMixin): def setUp(self): self.factory = RequestFactory() CMSIntegrationFactory.create(name="default", domain=None) def test_get_without_payment_data_redirects_to_index(self): response = self.client.get( reverse("datacenterlight:order_confirmation") ) self.assertRedirects( response, reverse("datacenterlight:index"), fetch_redirect_response=False, ) def test_post_without_payment_method_returns_error(self): response = self.client.post( reverse("datacenterlight:order_confirmation") ) self.assertEqual(response.status_code, 200) data = response.json() self.assertIn("status", data) self.assertFalse(data["status"]) def test_order_confirmation_url_exists(self): response = self.client.get( reverse("datacenterlight:order_confirmation") ) self.assertIn( response.status_code, (200, 302, 404), ) @patch( "datacenterlight.views.StripeUtils." "get_cards_details_from_payment_method" ) def test_generic_recurring_payment_renders_confirmation( self, mock_get_card_details, ): mock_get_card_details.return_value = { "response_object": { "brand": "visa", "last4": "4242", "exp_month": 12, "exp_year": 2030, } } session = self.client.session session["generic_payment_type"] = "generic" session["generic_payment_details"] = { "amount": 100, "amount_before_vat": 100, "vat_rate": 0, "vat_amount": 0, "product_name": "Test Product", "product_id": 1, "product_slug": "test-product", "recurring": True, "description": "Test product", "recurring_interval": "month", "exclude_vat_calculations": False, } session["id_payment_method"] = "pm_test" session["customer"] = "cus_test" session["vat_validation_status"] = "not_needed" session.save() response = self.client.get( reverse("datacenterlight:order_confirmation") ) self.assertEqual(response.status_code, 200) self.assertTemplateUsed( response, "datacenterlight/order_detail.html", ) self.assertIn( "generic_payment_details", response.context, ) mock_get_card_details.assert_called_once_with( "pm_test" ) class HelperFunctionTests(TestCase, SessionRequestMixin): def setUp(self): self.factory = RequestFactory() def test_create_incomplete_intent_request(self): request = self.factory.post( "/payment/", { "amount": "100", }, ) self.add_session(request) request.session["specs"] = { "cpu": 2, "ram": 4, "storage": 20, } request.session["template"] = { "id": 123, } request.session.save() result = create_incomplete_intent_request(request) self.assertIsInstance(result, str) def test_show_error_returns_json_response(self): request = self.factory.get( reverse("datacenterlight:payment") ) self.add_session(request) # Add messages middleware because show_error() # uses django.contrib.messages. from django.contrib.messages.middleware import MessageMiddleware MessageMiddleware( lambda request: None ).process_request(request) response = show_error( "Something went wrong", request, ) self.assertEqual( response.status_code, 200, ) data = json.loads( response.content.decode("utf-8") ) self.assertFalse(data["status"]) self.assertEqual( data["msg_title"], "Error.", )