from io import StringIO import os import shutil import tempfile from zipfile import ZipFile from django.conf import settings from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError from django.test import TestCase from django.test.utils import override_settings from django.urls import reverse from pycon.sponsorship.models import Benefit, Sponsor, SponsorBenefit,\ SponsorLevel from symposion.conference.models import current_conference User = get_user_model() class TestSponsorZipDownload(TestCase): def setUp(self): self.user = User.objects.create_user(username='joe', email='joe@example.com', password='joe') self.user.is_staff = True self.user.save() self.url = reverse("sponsor_zip_logos") self.assertTrue(self.client.login(username='joe@example.com', password='joe')) # we need a sponsor conference = current_conference() self.sponsor_level = SponsorLevel.objects.create( conference=conference, name="Lead", cost=1) self.sponsor = Sponsor.objects.create( name="Big Daddy", level=self.sponsor_level, active=True, ) # Create our benefits, of various types self.text_benefit = Benefit.objects.create(name="text", type="text") self.file_benefit = Benefit.objects.create(name="file", type="file") # These names must be spelled exactly this way: self.weblogo_benefit = Benefit.objects.create(name="Web logo", type="weblogo") self.printlogo_benefit = Benefit.objects.create(name="Print logo", type="file") self.advertisement_benefit = Benefit.objects.create(name="Advertisement", type="file") def validate_response(self, rsp, names_and_sizes): # Ensure a response from the view looks right, contains a valid # zip archive, has files with the right names and sizes. self.assertEqual("application/zip", rsp['Content-type']) prefix = settings.CONFERENCE_URL_PREFIXES[settings.CONFERENCE_ID] self.assertEqual( 'attachment; filename="pycon_%s_sponsorlogos.zip"' % prefix, rsp['Content-Disposition']) zipfile = ZipFile(StringIO(rsp.content), "r") # Check out the zip - testzip() returns None if no errors found self.assertIsNone(zipfile.testzip()) # Compare contents to what is expected infolist = zipfile.infolist() self.assertEqual(len(names_and_sizes), len(infolist)) for info, name_and_size in zip(infolist, names_and_sizes): name, size = name_and_size self.assertEqual(name, info.filename) self.assertEqual(size, info.file_size) def make_temp_file(self, name, size=0): # Create a temp file with the given name and size under self.temp_dir path = os.path.join(self.temp_dir, name) with open(path, "wb") as f: f.write(size * "x") def test_must_be_logged_in(self): # Must be logged in to use the view # If not logged in, doesn't redirect, just serves up a login view self.client.logout() rsp = self.client.get(self.url) self.assertEqual(200, rsp.status_code) self.assertIn("""""", rsp.content) def test_must_be_staff(self): # Only staff can use the view # If not staff, doesn't show error, just serves up a login view # Also, the dashboard doesn't show the download button self.user.is_staff = False self.user.save() rsp = self.client.get(self.url) self.assertEqual(200, rsp.status_code) self.assertIn("""""", rsp.content) rsp = self.client.get(reverse('dashboard')) self.assertNotIn(self.url, rsp.content) def test_no_files(self): # If there are no sponsor files, we still work # And the dashboard shows our download button rsp = self.client.get(self.url) self.validate_response(rsp, []) rsp = self.client.get(reverse('dashboard')) self.assertIn(self.url, rsp.content) def test_different_benefit_types(self): # We only get files from the benefits named "Print logo" and "Web logo" # And we ignore any non-existent files try: # Create a temp dir for media files self.temp_dir = tempfile.mkdtemp() with override_settings(MEDIA_ROOT=self.temp_dir): # Give our sponsor some benefits SponsorBenefit.objects.create( sponsor=self.sponsor, benefit=self.text_benefit, text="Foo!" ) self.make_temp_file("file1", 10) SponsorBenefit.objects.create( sponsor=self.sponsor, benefit=self.file_benefit, upload="file1" ) self.make_temp_file("file2", 20) SponsorBenefit.objects.create( sponsor=self.sponsor, benefit=self.weblogo_benefit, upload="file2" ) # Benefit whose file is missing from the disk SponsorBenefit.objects.create( sponsor=self.sponsor, benefit=self.weblogo_benefit, upload="file3" ) # print logo benefit self.make_temp_file("file4", 40) SponsorBenefit.objects.create( sponsor=self.sponsor, benefit=self.printlogo_benefit, upload="file4" ) self.make_temp_file("file5", 50) SponsorBenefit.objects.create( sponsor=self.sponsor, benefit=self.advertisement_benefit, upload="file5" ) rsp = self.client.get(self.url) expected = [ ('web_logos/lead/big_daddy/file2', 20), ('print_logos/lead/big_daddy/file4', 40), ('advertisement/lead/big_daddy/file5', 50) ] self.validate_response(rsp, expected) finally: if hasattr(self, 'temp_dir'): # Clean up any temp media files shutil.rmtree(self.temp_dir) def test_file_org(self): # The zip file is organized into directories: # {print_logos,web_logos,advertisement}/// # Add another sponsor at a different sponsor level conference = current_conference() self.sponsor_level2 = SponsorLevel.objects.create( conference=conference, name="Silly putty", cost=1) self.sponsor2 = Sponsor.objects.create( name="Big Mama", level=self.sponsor_level2, active=True, ) # try: # Create a temp dir for media files self.temp_dir = tempfile.mkdtemp() with override_settings(MEDIA_ROOT=self.temp_dir): # Give our sponsors some benefits self.make_temp_file("file1", 10) SponsorBenefit.objects.create( sponsor=self.sponsor, benefit=self.weblogo_benefit, upload="file1" ) # print logo benefit self.make_temp_file("file2", 20) SponsorBenefit.objects.create( sponsor=self.sponsor, benefit=self.printlogo_benefit, upload="file2" ) # Sponsor 2 self.make_temp_file("file3", 30) SponsorBenefit.objects.create( sponsor=self.sponsor2, benefit=self.weblogo_benefit, upload="file3" ) # print logo benefit self.make_temp_file("file4", 42) SponsorBenefit.objects.create( sponsor=self.sponsor2, benefit=self.printlogo_benefit, upload="file4" ) # ad benefit self.make_temp_file("file5", 55) SponsorBenefit.objects.create( sponsor=self.sponsor2, benefit=self.advertisement_benefit, upload="file5" ) rsp = self.client.get(self.url) expected = [ ('web_logos/lead/big_daddy/file1', 10), ('web_logos/silly_putty/big_mama/file3', 30), ('print_logos/lead/big_daddy/file2', 20), ('print_logos/silly_putty/big_mama/file4', 42), ('advertisement/silly_putty/big_mama/file5', 55), ] self.validate_response(rsp, expected) finally: if hasattr(self, 'temp_dir'): # Clean up any temp media files shutil.rmtree(self.temp_dir) class TestBenefitValidation(TestCase): """ It should not be possible to save a SponsorBenefit if it has the wrong kind of data in it - e.g. a text-type benefit cannot have an uploaded file, and vice-versa. """ def setUp(self): # we need a sponsor conference = current_conference() self.sponsor_level = SponsorLevel.objects.create( conference=conference, name="Lead", cost=1) self.sponsor = Sponsor.objects.create( name="Big Daddy", level=self.sponsor_level, ) # Create our benefit types self.text_type = Benefit.objects.create(name="text", type="text") self.file_type = Benefit.objects.create(name="file", type="file") self.weblogo_type = Benefit.objects.create(name="log", type="weblogo") self.simple_type = Benefit.objects.create(name="simple", type="simple") def validate(self, should_work, benefit_type, upload, text): obj = SponsorBenefit( benefit=benefit_type, sponsor=self.sponsor, upload=upload, text=text ) if should_work: obj.save() else: with self.assertRaises(ValidationError): obj.save() def test_text_has_text(self): self.validate(True, self.text_type, upload=None, text="Some text") def test_text_has_upload(self): self.validate(False, self.text_type, upload="filename", text='') def test_text_has_both(self): self.validate(False, self.text_type, upload="filename", text="Text") def test_file_has_text(self): self.validate(False, self.file_type, upload=None, text="Some text") def test_file_has_upload(self): self.validate(True, self.file_type, upload="filename", text='') def test_file_has_both(self): self.validate(False, self.file_type, upload="filename", text="Text") def test_weblogo_has_text(self): self.validate(False, self.weblogo_type, upload=None, text="Some text") def test_weblogo_has_upload(self): self.validate(True, self.weblogo_type, upload="filename", text='') def test_weblogo_has_both(self): self.validate(False, self.weblogo_type, upload="filename", text="Text") def test_simple_has_neither(self): self.validate(True, self.simple_type, upload=None, text='') def test_simple_has_text(self): self.validate(True, self.simple_type, upload=None, text="Some text") def test_simple_has_upload(self): self.validate(False, self.simple_type, upload="filename", text='') def test_simple_has_both(self): self.validate(False, self.simple_type, upload="filename", text="Text")