Add tests

This commit is contained in:
Nathan Chapman 2022-05-05 18:17:53 -06:00
parent 46bc3ff0ff
commit 98b6f5e9c4
12 changed files with 197 additions and 63 deletions

View File

@ -0,0 +1 @@
[{"model": "accounts.address", "pk": 1, "fields": {"first_name": "Nathan", "last_name": "Chapman", "street_address_1": "1504 N 230 E", "street_address_2": "", "city": "North Logan", "state": "UT", "postal_code": "84341"}}, {"model": "accounts.address", "pk": 2, "fields": {"first_name": "Nathan", "last_name": "Chapman", "street_address_1": "1125 W 400 N", "street_address_2": "", "city": "Logan", "state": "UT", "postal_code": "84321"}}, {"model": "accounts.user", "pk": 1, "fields": {"password": "pbkdf2_sha256$320000$VLksxVkUXOtoAEthHFi6DB$xj+81uh6NwPfZFP+7agf2UAJQZN89j5Wt38gUXeJ3z0=", "last_login": "2022-05-03T23:58:18.358Z", "is_superuser": true, "username": "nathanchapman", "first_name": "Nathan", "last_name": "Chapman", "email": "contact@nathanjchapman.com", "is_staff": true, "is_active": true, "date_joined": "2022-04-28T01:24:47.591Z", "default_shipping_address": 1, "default_billing_address": null, "groups": [], "user_permissions": [], "addresses": []}}, {"model": "accounts.user", "pk": 13, "fields": {"password": "pbkdf2_sha256$320000$L6WDkOMJwmkjR9OVsXfsIj$otr4goV5Tz5Hy5l24UkSYcH0L9Y5hDD89GKYD6LGcZo=", "last_login": null, "is_superuser": false, "username": "john", "first_name": "John", "last_name": "Doe", "email": "john@example.com", "is_staff": false, "is_active": true, "date_joined": "2022-05-04T00:00:11Z", "default_shipping_address": null, "default_billing_address": null, "groups": [], "user_permissions": [], "addresses": []}}]

View File

@ -16,9 +16,10 @@ def get_or_create_customer(request, form, shipping_address):
if request.user.is_authenticated:
user = request.user
user.addresses.add(address)
if not user.default_shipping_address:
user.default_shipping_address = address
user.save()
user.save()
else:
user, u_created = User.objects.get_or_create(
email=form.cleaned_data['email'],
@ -35,6 +36,7 @@ def get_or_create_customer(request, form, shipping_address):
if u_created:
password = User.objects.make_random_password()
user.set_password(password)
user.addresses.add(address)
user.save()
EmailAddress.objects.create(user=user, email=user.email, primary=True, verified=False)

View File

@ -0,0 +1 @@
[{"model": "core.product", "pk": 1, "fields": {"name": "Ethiopia", "description": "Spicy espresso reminiscent of Northern Italy, on the mild side. Perfect for espresso, and steamed milk drinks. Also, a full-bodied, earthy sweet drip or Americano. Contains organic beans from Indonesia, Africa and America.", "sku": "23468", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 4, "created_at": "2022-02-19T20:15:36.292Z", "updated_at": "2022-03-28T17:29:26.300Z"}}, {"model": "core.product", "pk": 2, "fields": {"name": "Sumatra", "description": "Dark heavy-bodied roast with a lingering chocolatey taste. Organic Single origin.", "sku": "89765", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 3, "created_at": "2022-02-19T20:15:59.741Z", "updated_at": "2022-03-28T17:29:08.706Z"}}, {"model": "core.product", "pk": 3, "fields": {"name": "Pantomime", "description": "Very Dark French Roast\r\nOur darkest drip. A blend of five different beans roasted two ways. Organic Africa, Indonesia, and South and Central America.", "sku": "565656", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 1, "created_at": "2022-02-23T17:59:00.711Z", "updated_at": "2022-03-28T17:28:58.670Z"}}, {"model": "core.product", "pk": 4, "fields": {"name": "Decaf", "description": "French Roast (Water Processed)\r\n\r\n“I cant believe its decaf!”. The best-tasting Swiss water process decaf we have developed over the past 30 years, for an unbelievable espresso or drip coffee. Organic Africa, Indonesia and South and Central America.", "sku": "566565", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 2, "created_at": "2022-02-23T17:59:32.099Z", "updated_at": "2022-03-28T17:28:45.512Z"}}, {"model": "core.product", "pk": 5, "fields": {"name": "Moka Java Blend", "description": "Dark Roast\r\n\r\nA classic Moka Java style blend dark roasted with organic beans for a perfect body and sweetness with a hint of citrus.", "sku": "56466", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": false, "sorting": 5, "created_at": "2022-02-23T18:05:41.742Z", "updated_at": "2022-03-28T17:29:39.761Z"}}, {"model": "core.product", "pk": 6, "fields": {"name": "Loop d Loop", "description": "Mild Dark Roast\r\n\r\nOur most popular blend reminiscent of Central Italy. A dark, chocolaty flavor perfect for espresso or drip. Its dark Vienna roast properties make it ideal for steamed milk drinks. Organic Indonesia, Africa and America.", "sku": "53264", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 6, "created_at": "2022-02-23T18:06:09.881Z", "updated_at": "2022-03-28T17:29:53.104Z"}}, {"model": "core.product", "pk": 7, "fields": {"name": "Dantes Tornado", "description": "Medium Roast\r\n\r\nFull City spicy espresso roast reminiscent of Northern Italy, on the mild side. A full- bodied, earthy sweet drip or Americano. Organic Indonesia, Africa and America.", "sku": "78945", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 7, "created_at": "2022-02-23T18:06:35.593Z", "updated_at": "2022-03-28T17:30:11.445Z"}}, {"model": "core.product", "pk": 8, "fields": {"name": "Nicaragua", "description": "Mild Roast\r\n\r\nOur mildest roast with sweet and fruity notes, containing organic beans from Nicaragua. Single origin.", "sku": "12365", "price": "13.40", "weight": "16.0:oz", "visible_in_listings": true, "sorting": 8, "created_at": "2022-02-23T18:06:57.624Z", "updated_at": "2022-03-28T17:30:20.941Z"}}, {"model": "core.productphoto", "pk": 1, "fields": {"product": 1, "image": "products/images/slice2.png"}}, {"model": "core.productphoto", "pk": 2, "fields": {"product": 2, "image": "products/images/slice1.png"}}, {"model": "core.productphoto", "pk": 5, "fields": {"product": 5, "image": "products/images/moka_java.png"}}, {"model": "core.productphoto", "pk": 6, "fields": {"product": 6, "image": "products/images/loop_d_loop.png"}}, {"model": "core.productphoto", "pk": 7, "fields": {"product": 7, "image": "products/images/dantes_tornado.png"}}, {"model": "core.productphoto", "pk": 8, "fields": {"product": 8, "image": "products/images/nicaragua.png"}}, {"model": "core.productphoto", "pk": 15, "fields": {"product": 3, "image": "products/images/pantomime_800.png"}}, {"model": "core.productphoto", "pk": 16, "fields": {"product": 3, "image": "products/images/pantomime_beans.png"}}, {"model": "core.productphoto", "pk": 18, "fields": {"product": 2, "image": "products/images/pantomime_beans_J2bFBiH.png"}}, {"model": "core.productphoto", "pk": 19, "fields": {"product": 4, "image": "products/images/decaf_800.png"}}, {"model": "core.productphoto", "pk": 20, "fields": {"product": 4, "image": "products/images/pantomime_beans_Lo0hJRx.png"}}]

1
src/fixtures/db.json Normal file

File diff suppressed because one or more lines are too long

View File

View File

@ -0,0 +1,32 @@
import os, time
from selenium.webdriver.firefox.webdriver import WebDriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import WebDriverException
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
class HomeTests(StaticLiveServerTestCase):
fixtures = ['accounts.json', 'products.json']
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.browser = WebDriver()
@classmethod
def tearDownClass(cls):
cls.browser.quit()
super().tearDownClass()
def test_home_page_has_product_list(self):
self.login()
self.assertTrue(
self.browser.find_element_by_css_selector('.product__list')
)
def login(self):
self.browser.get('%s%s' % (self.live_server_url, '/accounts/login/'))
username_input = self.browser.find_element_by_name("login")
username_input.send_keys('john@example.com')
password_input = self.browser.find_element_by_name("password")
password_input.send_keys('Bf25XBdP4vdt2X9L')
self.browser.find_element_by_xpath('//input[@value="Login"]').click()

View File

@ -172,7 +172,10 @@ class Cart:
def create_order(self):
params = self.build_order_params()
logger.info(f'\nParams: {params}\n')
response = CreateOrder().create_order(params, debug=True)
if settings.DEBUG:
response = CreateOrder().create_order(params, debug=True)
else:
response = CreateOrder().create_order(params)
return response
def build_bulk_list(self, order):

View File

@ -11,6 +11,8 @@ from django.conf import settings
from django.contrib.sites.models import Site
from django.urls import reverse_lazy
from core import CoffeeGrind
logger = logging.getLogger(__name__)
class PayPalClient:
@ -74,9 +76,11 @@ class CreateOrder(PayPalClient):
# Shows within upper-right dropdown during payment approval
'name': f'{item["product"]}',
# Item details will also be in the completed paypal.com transaction view
'description': 'Coffee',
'description': ', '.join([next((f"{value['quantity']} x {v[1]}" for i, v in enumerate(CoffeeGrind.GRIND_CHOICES) if v[0] == key), None)
for key, value in item['variations'].items()]),
'unit_amount': {
'currency_code': 'USD',
'currency_code': settings.DEFAULT_CURRENCY,
'value': f'{item["price"]}'
},
'quantity': f'{item["quantity"]}'
@ -143,18 +147,16 @@ class CreateOrder(PayPalClient):
request.request_body(self.build_request_body(params))
response = self.client.execute(request)
if debug:
logger.info(f'\nStatus Code: {response.status_code}', )
logger.info(f'\nStatus: {response.result.status}', )
logger.info(f'\nOrder ID: {response.result.id}', )
logger.info(f'\nIntent: {response.result.intent}', )
logger.info(f"\njson_data: {response.result}")
logger.debug(f'\nStatus Code: {response.status_code}', )
logger.debug(f'\nStatus: {response.result.status}', )
logger.debug(f'\nOrder ID: {response.result.id}', )
logger.debug(f'\nIntent: {response.result.intent}', )
logger.debug(f"\njson_data: {response.result}")
return response
class CaptureOrder(PayPalClient):
"""this is the sample function performing payment capture on the order. Approved Order id should be passed as an argument to this function"""
def capture_order(self, order_id, debug=False):
"""Method to capture order using order_id"""
request = OrdersCaptureRequest(order_id)

View File

@ -0,0 +1,43 @@
class RequestFaker:
REQUEST_BODY = {
'intent': 'CAPTURE',
'application_context': {
'return_url': 'https://example.com/done/',
'cancel_url': 'https://example.com/canceled/',
'brand_name': 'example.com',
},
'purchase_units': [
{
'description': 'Coffee',
'amount': {
'currency_code': 'USD',
'value': '13.40',
'breakdown': {
'item_total': {'currency_code': 'USD', 'value': '13.40'},
'shipping': {'currency_code': 'USD', 'value': '0.00'},
'tax_total': {'currency_code': 'USD', 'value': '0'},
'discount': {'currency_code': 'USD', 'value': '0'},
},
},
'items': [
{
'name': 'Decaf',
'description': '1 x Whole Beans, 2 x Percolator',
'unit_amount': {'currency_code': 'USD', 'value': '13.40'},
'quantity': '3',
}
],
'shipping': {
'method': 'US POSTAL SERVICE',
'address': {
'address_line_1': '1504 N 230 E',
'address_line_2': '',
'admin_area_2': 'North Logan',
'admin_area_1': 'UT',
'postal_code': '84341',
'country_code': 'US',
},
},
}
],
}

View File

@ -10,22 +10,12 @@ from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment
from accounts.models import User, Address
from core.models import Product, Order
from .views import OrderCreateView
from .cart import Cart
from core import CoffeeGrind
from storefront.views import OrderCreateView
from storefront.cart import Cart
logger = logging.getLogger(__name__)
WHOLE = 'whole-beans'
ESPRESSO = 'espresso'
CONE_DRIP = 'cone-drip'
BASKET_DRIP = 'basket-drip'
FRENCH_PRESS = 'french-press'
STOVETOP_ESPRESSO = 'stovetop-espresso'
AEROPRESS = 'aeropress'
PERCOLATOR = 'percolator'
CAFE_STYLE = 'cafe-style'
class CartTest(TestCase):
def setUp(self):
self.client = Client()
@ -48,35 +38,21 @@ class CartTest(TestCase):
)
self.client.force_login(self.customer)
def test_post_checkout_form(self):
url = reverse('storefront:order-create')
response = self.client.get(url)
self.assertTemplateUsed(response, 'storefront/order_form.html')
request = response.wsgi_request
cart = Cart(request)
cart.add(
request,
product=self.product,
quantity=1,
grind=WHOLE,
update_quantity=False
)
params = {
'email': 'nathanchapman@hey.com',
self.client.session['shipping_address'] = {
'first_name': 'Nathan',
'last_name': 'Chapman',
'total_net_amount': 26.80
'email': 'contact@nathanjchapman.com',
'street_address_1': '1504 N 230 E',
'street_address_2': '',
'city': 'North Logan',
'state': 'UT',
'postal_code': '84341'
}
response = self.client.post(url, params)
self.assertContains(response, 'Checkout', status_code=200)
def test_cart_item_variations(self):
cart_detail_url = reverse('storefront:cart-detail')
response = self.client.get(cart_detail_url)
response = self.client.get(cart_detail_url, follow=True)
logger.debug(response.context['messages'])
request = response.wsgi_request
cart = Cart(request)
@ -85,14 +61,14 @@ class CartTest(TestCase):
request,
product=self.product,
quantity=1,
grind=WHOLE,
grind=CoffeeGrind.WHOLE,
update_quantity=False
)
cart.add(
request,
product=self.product,
quantity=1,
grind=ESPRESSO,
grind=CoffeeGrind.ESPRESSO,
update_quantity=False
)
for item in cart.cart.values():
@ -109,11 +85,11 @@ class CartTest(TestCase):
request,
product=self.product,
quantity=1,
grind=WHOLE,
grind=CoffeeGrind.WHOLE,
update_quantity=False
)
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][WHOLE]['quantity'], 1)
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][CoffeeGrind.WHOLE]['quantity'], 1)
self.assertEqual(len(cart), 1)
self.assertEqual(sum(cart.get_item_prices()), Decimal('13.4'))
self.assertEqual(cart.get_total_price(), Decimal('13.4'))
@ -121,20 +97,20 @@ class CartTest(TestCase):
request,
product=self.product,
quantity=1,
grind=WHOLE,
grind=CoffeeGrind.WHOLE,
update_quantity=False
)
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][WHOLE]['quantity'], 2)
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][CoffeeGrind.WHOLE]['quantity'], 2)
self.assertEqual(len(cart), 2)
cart.add(
request,
product=self.product,
quantity=3,
grind=ESPRESSO,
grind=CoffeeGrind.ESPRESSO,
update_quantity=False
)
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][ESPRESSO]['quantity'], 3)
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][CoffeeGrind.ESPRESSO]['quantity'], 3)
self.assertEqual(len(cart), 5)
self.assertEqual(cart.get_total_price(), Decimal('67'))
@ -149,19 +125,19 @@ class CartTest(TestCase):
request,
product=self.product,
quantity=3,
grind=WHOLE,
grind=CoffeeGrind.WHOLE,
update_quantity=False
)
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][WHOLE]['quantity'], 3)
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][CoffeeGrind.WHOLE]['quantity'], 3)
cart.add(
request,
product=self.product,
quantity=1,
grind=WHOLE,
grind=CoffeeGrind.WHOLE,
update_quantity=True
)
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][WHOLE]['quantity'], 1)
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][CoffeeGrind.WHOLE]['quantity'], 1)
def test_cart_remove_item(self):
cart_detail_url = reverse('storefront:cart-detail')
@ -174,7 +150,7 @@ class CartTest(TestCase):
request,
product=self.product,
quantity=3,
grind=WHOLE,
grind=CoffeeGrind.WHOLE,
update_quantity=False
)
self.assertEqual(len(cart), 3)
@ -192,9 +168,10 @@ class CartTest(TestCase):
request,
product=self.product,
quantity=3,
grind=WHOLE,
grind=CoffeeGrind.WHOLE,
update_quantity=False
)
self.assertEqual(cart.get_total_weight(), Decimal(48))
# 96oz
def test_cart_(self):
pass

View File

@ -0,0 +1,73 @@
import logging
from decimal import Decimal
from measurement.measures import Weight
from django.test import TestCase, Client, RequestFactory
from django.urls import reverse
from django.conf import settings
from django.contrib.sessions.middleware import SessionMiddleware
from paypalcheckoutsdk.orders import OrdersCreateRequest, OrdersCaptureRequest
from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment
from accounts.models import User, Address
from core.models import Product, Order
from core import CoffeeGrind
from storefront.views import OrderCreateView
from storefront.cart import Cart
from storefront.payments import CreateOrder
from . import RequestFaker
logger = logging.getLogger(__name__)
class CreateOrderTest(TestCase):
def setUp(self):
self.client = Client()
self.product = Product.objects.create(
name='Decaf',
description='Coffee',
sku='23987',
price=Decimal('13.40'),
weight=Weight(oz=16),
visible_in_listings=True
)
def test_build_request_body(self):
product_list_url = reverse('storefront:product-list')
response = self.client.get(product_list_url, follow=True)
request = response.wsgi_request
cart = Cart(request)
cart.add(
request,
product=self.product,
quantity=1,
grind=CoffeeGrind.WHOLE,
update_quantity=False
)
cart.add(
request,
product=self.product,
quantity=2,
grind=CoffeeGrind.PERCOLATOR,
update_quantity=False
)
params = {
'items': cart,
'total_price': '13.40',
'item_total': '13.40',
'discount': '0',
'shipping_price': '0.00',
'tax_total': '0',
'shipping_method': 'US POSTAL SERVICE',
'shipping_address': {
'address_line_1': '1504 N 230 E',
'address_line_2': '',
'admin_area_2': 'North Logan',
'admin_area_1': 'UT',
'postal_code': '84341',
'country_code': 'US',
},
}
request_body = CreateOrder().build_request_body(params)
self.assertDictEqual(RequestFaker.REQUEST_BODY, request_body)

View File

@ -231,7 +231,6 @@ class OrderCreateView(CreateView):
return context
def form_valid(self, form):
# TODO: make order status "Draft" and then in the PP capture set status appropriately
cart = Cart(self.request)
shipping_address = self.request.session.get('shipping_address')
form.instance.customer, form.instance.shipping_address = get_or_create_customer(self.request, form, shipping_address)