def test_assopy_paypal(client): """ This tests two views – paypal_feedback_ok and paypal_cancel. """ user = auth_factories.UserFactory(email='*****@*****.**', is_active=True) assopy_user = AssopyUserFactory(user=user) order_code = 'asdf' order = Order(user=assopy_user, code=order_code, method='paypal') order.save() paypal_feedback_ok_url = reverse("assopy-paypal-feedback-ok", kwargs={'code': order_code}) paypal_cancel_url = reverse("assopy-paypal-feedback-cancel", kwargs={'code': order_code}) response = client.get(paypal_cancel_url) make_sure_root_template_is_used(response, "assopy/paypal_cancel.html") make_sure_root_template_is_used(response, "assopy/base.html") make_sure_root_template_is_used(response, "p3/base.html") # @login_required is not enforced on any of those views, however feedback # has some manual checks builtin, so we need to to log in as a correct # user. client.login(email='*****@*****.**', password='******') response = client.get(paypal_feedback_ok_url) make_sure_root_template_is_used(response, "assopy/paypal_feedback_ok.html") make_sure_root_template_is_used(response, "assopy/base.html") make_sure_root_template_is_used(response, "p3/base.html")
def setUp(self): self.conference = Conference.objects.create( code=settings.CONFERENCE_CONFERENCE, name=settings.CONFERENCE_CONFERENCE, # by default start with open CFP cfp_start=timezone.now() - timedelta(days=2), cfp_end=timezone.now() + timedelta(days=2)) # default password is 'password123' per django_factory_boy admin = User.objects.create_superuser('admin', '*****@*****.**', 'admin') AssopyUserFactory(user=admin) self.user = auth_factories.UserFactory(email='*****@*****.**', is_active=True) AssopyUserFactory(user=self.user) self.form_url = reverse("conference-paper-submission")
def test_change_password(client): """ Testing full change password flow, 1. Log in 2. Look for change password url on the profile page 3. Change the password. 4. Log in with new password """ # default password is 'password123' per django_factory_boy user = auth_factories.UserFactory(email='*****@*****.**', is_active=True) # both are required to access user profile page. AssopyUserFactory(user=user) AttendeeProfile.objects.create(user=user, slug='foobar') client.login(email='*****@*****.**', password='******') user_profile_url = reverse("assopy-profile") change_password_url = reverse("password_change") response = client.get(user_profile_url) assert "Change your password" in response.content.decode('utf-8') assert change_password_url in response.content.decode('utf-8') assert template_used(response, 'assopy/profile.html') response = client.get(change_password_url) assert template_used(response, "registration/password_change_form.html") assert 'Django Administration' not in response.content response = client.post(change_password_url, { 'old_password': '******', 'new_password1': 'pwd345', 'new_password2': 'pwd345', }, follow=True) assert template_used(response, "registration/password_change_done.html") assert user_profile_url in response.content assert 'Password change successful' in response.content assert 'Go back to your profile' in response.content client.logout() can_log_in_with_new_password = client.login(email='*****@*****.**', password='******') assert can_log_in_with_new_password
def setUp(self): self.conference = Conference.objects.create( code=settings.CONFERENCE_CONFERENCE, name=settings.CONFERENCE_CONFERENCE, # by default start with open CFP cfp_start=timezone.now() - timedelta(days=2), cfp_end=timezone.now() + timedelta(days=2), voting_start=timezone.now() - timedelta(days=2), voting_end=timezone.now() + timedelta(days=2) ) self.user = auth_factories.UserFactory( email='*****@*****.**', is_active=True) AssopyUserFactory(user=self.user) self.talk = TalkFactory() TalkSpeakerFactory(speaker=SpeakerFactory(user=self.user)) P3TalkFactory(talk=self.talk)
def test_assopy_profile(client): profile_url = reverse('assopy-profile') user = auth_factories.UserFactory(email='*****@*****.**', is_active=True) # both are required to access user profile page. AssopyUserFactory(user=user) AttendeeProfile.objects.create(user=user, slug='foobar') client.login(email='*****@*****.**', password='******') response = client.get(profile_url) make_sure_root_template_is_used(response, "assopy/profile.html") make_sure_root_template_is_used(response, "assopy/base.html") make_sure_root_template_is_used(response, "p3/base.html") # there are some includes used in this template, namely # profile_{email_contact,personal_data,spam_control}.html # those templates are also used in specific p3 views make_sure_root_template_is_used(response, "assopy/profile_email_contact.html") make_sure_root_template_is_used(response, "assopy/profile_personal_data.html") make_sure_root_template_is_used(response, "assopy/profile_spam_control.html") # also those templates are used in some p3 views, so adding some additional # checks for them. p3_account_spam_control_url = reverse('p3-account-spam-control') response = client.get(p3_account_spam_control_url) make_sure_root_template_is_used(response, "assopy/profile_spam_control.html") p3_account_email_url = reverse('p3-account-email') response = client.get(p3_account_email_url) make_sure_root_template_is_used(response, "assopy/profile_email_contact.html") p3_account_data_url = reverse('p3-account-data') response = client.get(p3_account_data_url) make_sure_root_template_is_used(response, "assopy/profile_personal_data.html")
def test_assopy_invoice(client): # default password is 'password123' per django_factory_boy user = auth_factories.UserFactory(email='*****@*****.**', is_active=True) assopy_user = AssopyUserFactory(user=user) client.login(email='*****@*****.**', password='******') # invoice_code must be validated via ASSOPY_IS_REAL_INVOICE invoice_code, order_code = 'I123', 'asdf' # FYI(artcz): Order.objects.create is overloaded method on OrderManager, # that sets up a lot of unused stuff, going with manual .save(). order = Order(user=assopy_user, code=order_code) order.save() # create some random Vat instance to the invoice creation works vat_10 = Vat.objects.create(value=10) Invoice.objects.create( code=invoice_code, order=order, emit_date=timezone.now().date(), price=Decimal(1337), vat=vat_10, html='Here goes full html', exchange_rate_date=date.today(), ) invoice_url = reverse('assopy-invoice-html', kwargs={ 'order_code': order_code, 'code': invoice_code, }) response = client.get(invoice_url) # TODO(artcz) after we changed to pre-rendering and storing full html of # the invoice we no longer use a template in this view. # TBD if we need that test here anymore, since it supposed to test # templates temporarily anyway. assert response.content == 'Here goes full html'
def test_invoices_from_buying_tickets(client): """ This is an example of a full flow, of creating and buying a new ticket. """ # because of 2018 we need to make sure that ECB rates are in place responses.add(responses.GET, DAILY_ECB_URL, body=EXAMPLE_ECB_DAILY_XML) fetch_and_store_latest_ecb_exrates() assert settings.P3_FARES_ENABLED # 1. First create a user with complete profile. # default password is 'password123' per django_factory_boy user = auth_factories.UserFactory(email='*****@*****.**', is_active=True) # both are required to access user profile page. AssopyUserFactory(user=user) AttendeeProfile.objects.create(user=user, slug='foobar') client.login(email='*****@*****.**', password='******') # 2. Let's start with checking if no tickets are available at first cart_url = reverse('p3-cart') response = client.get(cart_url) assert template_used(response, "p3/cart.html") assert 'Sorry, no tickets are available' in response.content # 3. p3/cart.html is using {% fares_available %} assignment tag to display # fares. For more details about fares check conference/fares.py ticket_price = Decimal(100) ticket_amount = 20 social_event_price = Decimal(10) social_event_amount = 5 vat_rate_10, _ = Vat.objects.get_or_create(value=10) vat_rate_20, _ = Vat.objects.get_or_create(value=20) today = date.today() yesterday, tomorrow = today - timedelta(days=1), today + timedelta(days=1) CONFERENCE = settings.CONFERENCE_CONFERENCE create_fare_for_conference( code="TRSP", # Ticket Regular Standard Personal conference=CONFERENCE, price=ticket_price, start_validity=yesterday, end_validity=tomorrow, vat_rate=vat_rate_10) create_fare_for_conference(code=SOCIAL_EVENT_FARE_CODE, conference=CONFERENCE, price=social_event_price, start_validity=yesterday, end_validity=tomorrow, vat_rate=vat_rate_20) # 4. If Fare is created we should have one input on the cart. response = client.get(cart_url) assert template_used(response, "p3/cart.html") _response_content = response.content.decode('utf-8') assert 'Sorry, no tickets are available' not in _response_content assert 'Buy tickets (1 of 2)' in _response_content # There are plenty of tds but only TRSP should have data-fare set assert 'td class="fare" data-fare="TRSP">' in _response_content assert 'td class="fare" data-fare="TDCP">' not in _response_content assert 'td class="fare" data-fare="">' in _response_content # social events assert 'td class="fare" data-fare="VOUPE03">' in _response_content # and one input for TRSP where you can specify how many tickets # TODO: maybe it should have a different type than text? assert '<input type="text" size="2" name="TRSP"' in _response_content # 5. Try buying some tickets # FIXME: looks like the max_tickets is enforced only with javascript assert ticket_amount > conference_settings.MAX_TICKETS response = client.post( cart_url, { 'order_type': 'non-deductible', # == Personal 'TRSP': ticket_amount, 'VOUPE03': social_event_amount, }, follow=True) billing_url = reverse('p3-billing') assert response.status_code == 200 assert response.request['PATH_INFO'] == billing_url assert 'Buy tickets (2 of 2)' in response.content.decode('utf-8') # unless you POST to the billing page the Order is not created assert Order.objects.count() == 0 Country.objects.create(iso='PL', name='Poland') response = client.post(billing_url, { 'card_name': 'Joe Doe', 'payment': 'cc', 'country': 'PL', 'address': 'Random 42', 'cf_code': '31447', 'code_conduct': True, }, follow=True) assert response.status_code == 200 assert response.request['PATH_INFO'] == '/accounts/stripe/checkout/1/' order = Order.objects.get() # FIXME: confirming that max_tickets is only enforced in javascript assert order.orderitem_set.all().count() ==\ ticket_amount + social_event_amount # need to create an email template that's used in the purchasing process Email.objects.create(code='purchase-complete') # no invoices assert Invoice.objects.all().count() == 0 # static date, because of #592 choosing something in 2018 SOME_RANDOM_DATE = date(2018, 1, 1) order.confirm_order(SOME_RANDOM_DATE) assert order.payment_date == SOME_RANDOM_DATE # # multiple items per invoice, one invoice per vat rate. # # 2 invoices but they are both placeholders assert Invoice.objects.all().count() == 2 assert Invoice.objects.filter( html=VAT_NOT_AVAILABLE_PLACEHOLDER).count() == 2 # # and we can then upgrade all invoices to non-placeholders for _invoice in Invoice.objects.all(): upgrade_invoice_placeholder_to_real_invoice(_invoice) assert Invoice.objects.all().count() == 2 assert Invoice.objects.filter( html=VAT_NOT_AVAILABLE_PLACEHOLDER).count() == 0 invoice_vat_10 = Invoice.objects.get(vat__value=10) invoice_vat_20 = Invoice.objects.get(vat__value=20) # only one orderitem_set instance because they are grouped by fare_code # items are ordered desc by price. expected_invoice_items_vat_10 = [ { 'count': ticket_amount, 'price': ticket_price * ticket_amount, 'code': u'TRSP', 'description': u'ep2018 - Regular Standard Personal' }, ] expected_invoice_items_vat_20 = [ { 'count': social_event_amount, 'price': social_event_price * social_event_amount, 'code': SOCIAL_EVENT_FARE_CODE, 'description': u'ep2018 - Social Event' }, ] assert sequence_equals(invoice_vat_10.invoice_items(), expected_invoice_items_vat_10) assert sequence_equals(invoice_vat_20.invoice_items(), expected_invoice_items_vat_20) # check numbers for vat 10% gross_price_vat_10 = ticket_price * ticket_amount net_price_vat_10 = normalize_price(gross_price_vat_10 / Decimal('1.1')) vat_value_vat_10 = gross_price_vat_10 - net_price_vat_10 assert invoice_vat_10.price == gross_price_vat_10 assert invoice_vat_10.net_price() == net_price_vat_10 assert invoice_vat_10.vat_value() == vat_value_vat_10 assert invoice_vat_10.html.startswith('<!DOCTYPE') assert len(invoice_vat_10.html) > 1000 # large html blob # check numbers for vat 20% gross_price_vat_20 = social_event_price * social_event_amount net_price_vat_20 = normalize_price(gross_price_vat_20 / Decimal('1.2')) vat_value_vat_20 = gross_price_vat_20 - net_price_vat_20 assert invoice_vat_20.price == gross_price_vat_20 assert invoice_vat_20.net_price() == net_price_vat_20 assert invoice_vat_20.vat_value() == vat_value_vat_20 assert invoice_vat_20.html.startswith('<!DOCTYPE') assert len(invoice_vat_20.html) > 1000 # large html blob # each OrderItem should have a corresponding Ticket assert Ticket.objects.all().count() == ticket_amount + social_event_amount # Check if user profile has the tickets and invoices available profile_url = reverse('assopy-profile') response = client.get(profile_url) # order code depends on when this test is run, but invoice code should # default to whatever payment_date is (in this case 2018, 1, 1) # TODO: currently this test is under freezegun, but we may want to remove # it later and replace with APIs that allows to control/specify date for # order and invoice. assert 'O/18.0001' in response.content.decode('utf-8') # there is only one order but two invoices assert 'I/18.0001' in response.content.decode('utf-8') assert 'I/18.0002' in response.content.decode('utf-8')
def test_592_dont_display_invoices_for_years_before_2018(client): """ https://github.com/EuroPython/epcon/issues/592 Temporary(?) test for #592, until #591 is fixed. """ # default password is 'password123' per django_factory_boy user = auth_factories.UserFactory(email='*****@*****.**', is_active=True) # both are required to access user profile page. assopy_user = AssopyUserFactory(user=user) AttendeeProfile.objects.create(user=user, slug='foobar') client.login(email='*****@*****.**', password='******') # create some random Vat instance to the invoice creation works vat_10 = Vat.objects.create(value=10) # invoice_code must be validated via ASSOPY_IS_REAL_INVOICE invoice_code_2017, order_code_2017 = 'I2017', 'O2017' invoice_code_2018, order_code_2018 = 'I2018', 'O2018' order2017 = Order(user=assopy_user, code=order_code_2017) order2017.save() order2017.created = date(2017, 12, 31) order2017.save() order2018 = Order(user=assopy_user, code=order_code_2018) order2018.save() order2018.created = date(2018, 1, 1) order2018.save() Invoice.objects.create( code=invoice_code_2017, order=order2017, emit_date=date(2017, 3, 13), price=Decimal(1337), vat=vat_10, exchange_rate_date=date.today(), ) # Doesn't matter when the invoice was issued (invoice.emit_date), # it only matters what's the Order.created date Invoice.objects.create( code=invoice_code_2018, order=order2018, emit_date=date(2017, 3, 13), price=Decimal(1337), vat=vat_10, exchange_rate_date=date.today(), ) user_profile_url = reverse("assopy-profile") response = client.get(user_profile_url) assert invoice_code_2017 not in response.content.decode('utf-8') assert order_code_2017 not in response.content.decode('utf-8') assert invoice_code_2018 in response.content.decode('utf-8') assert order_code_2018 in response.content.decode('utf-8') assert reverse("assopy-invoice-pdf", kwargs={ 'code': invoice_code_2018, 'order_code': order_code_2018, }) in response.content.decode('utf-8') assert template_used(response, 'assopy/profile.html')
def make_user(email='*****@*****.**', **kwargs): user = auth_factories.UserFactory(email=email, is_active=True, **kwargs) AssopyUserFactory(user=user) AttendeeProfile.objects.getOrCreateForUser(user=user) return user