コード例 #1
0
    def setUp(self):

        self.factory = RequestFactory()

        # Create a couple of images to use for the downloadable work and BuskerFile objects
        self.img = Image.new("RGB", (1200, 1200), "#990000")
        self.img_file = tempfile.NamedTemporaryFile(suffix=".jpg",
                                                    delete=False)
        self.img_basename = os.path.split(self.img_file.name)[-1]
        self.img.save(self.img_file, format="JPEG")

        self.img2 = Image.new("RGB", (5000, 5000), "#336699")
        self.img2_file = tempfile.NamedTemporaryFile(suffix=".png",
                                                     delete=False)
        self.img2_basename = os.path.split(self.img2_file.name)[-1]
        self.img2.save(self.img2_file, format="PNG")

        self.artist = Artist.objects.create(name="Conrad Poohs",
                                            url="https://magicians.band")
        self.work_file = File(self.img_file)
        self.work = DownloadableWork(artist=self.artist,
                                     title="Dancing Teeth",
                                     published=True,
                                     image=self.work_file)
        self.work.image.save(name=self.img_basename, content=self.img_file)
        self.work.save()

        self.busker_file_attachment = File(self.img2_file)
        self.busker_file = BuskerFile(work=self.work,
                                      description="",
                                      file=self.busker_file_attachment)
        self.busker_file.file.save(name=self.img2_basename,
                                   content=self.img2_file)
        self.busker_file.save()
        self.batch = Batch.objects.create(
            work=self.work,
            label="Conrad Poohs Test Batch",
            private_note="Batch for unit testing",
            public_message=
            "#Thank You\nThis is a message with *markdown* **formatting**.",
            number_of_codes=10)

        self.unpub_work = DownloadableWork.objects.create(title="Unpublished",
                                                          artist=self.artist,
                                                          published=False)
        self.unpub_batch = Batch.objects.create(label="unpublished",
                                                work=self.unpub_work,
                                                number_of_codes=1,
                                                public_message='test')

        self.unpub_code = DownloadCode.objects.get(batch=self.unpub_batch)
        self.used_code = DownloadCode.objects.create(batch=self.batch,
                                                     max_uses=1,
                                                     times_used=1)
コード例 #2
0
class FormattersTestCase(TestCase):
    def setUp(self):
        # Create a couple of images to use for the downloadable work and BuskerFile objects
        self.img = Image.new("RGB", (1200, 1200), "#990000")
        self.img_file = tempfile.NamedTemporaryFile(suffix=".jpg",
                                                    delete=False)
        self.img_basename = os.path.split(self.img_file.name)[-1]
        self.img.save(self.img_file, format="JPEG")

        self.img2 = Image.new("RGB", (5000, 5000), "#336699")
        self.img2_file = tempfile.NamedTemporaryFile(suffix=".png",
                                                     delete=False)
        self.img2_basename = os.path.split(self.img2_file.name)[-1]
        self.img2.save(self.img2_file, format="PNG")

        self.artist = Artist.objects.create(name="Conrad Poohs",
                                            url="https://magicians.band")
        self.work_file = File(self.img_file)
        self.work = DownloadableWork(artist=self.artist,
                                     title="Dancing Teeth",
                                     published=True,
                                     image=self.work_file)
        self.work.image.save(name=self.img_basename, content=self.img_file)
        self.work.save()

        self.busker_file_attachment = File(self.img2_file)
        self.busker_file = BuskerFile(work=self.work,
                                      description="",
                                      file=self.busker_file_attachment)
        self.busker_file.file.save(name=self.img2_basename,
                                   content=self.img2_file)
        self.busker_file.save()
        self.batch = Batch.objects.create(
            work=self.work,
            label="Conrad Poohs Test Batch",
            private_note="Batch for unit testing",
            public_message=
            "#Thank You\nThis is a message with *markdown* **formatting**.",
            number_of_codes=10)

    def tearDown(self):
        os.unlink(self.img_file.name)
        os.unlink(self.img2_file.name)

    def test_csv_formatter(self):
        # TODO would be nice to test the actual expected CSV output
        codes = DownloadCode.objects.filter(batch=self.batch)
        response = format_codes_csv(codes)
        self.assertIsInstance(response, StreamingHttpResponse)
コード例 #3
0
    def setUp(self):

        # Create a couple of images to use for the downloadable work and BuskerFile objects
        self.img = Image.new("RGB", (1200, 1200), "#990000")
        self.img_file = tempfile.NamedTemporaryFile(suffix=".jpg",
                                                    delete=False)
        self.img_basename = os.path.split(self.img_file.name)[-1]
        self.img.save(self.img_file, format="JPEG")

        self.img2 = Image.new("RGB", (5000, 5000), "#336699")
        self.img2_file = tempfile.NamedTemporaryFile(suffix=".png",
                                                     delete=False)
        self.img2_basename = os.path.split(self.img2_file.name)[-1]
        self.img2.save(self.img2_file, format="PNG")

        self.artist = Artist.objects.create(name="Conrad Poohs",
                                            url="https://magicians.band")
        self.work_file = File(self.img_file)
        self.work = DownloadableWork(artist=self.artist,
                                     title="Dancing Teeth",
                                     published=True,
                                     image=self.work_file)
        self.work.image.save(name=self.img_basename, content=self.img_file)
        self.work.save()

        self.busker_file_attachment = File(self.img2_file)
        self.busker_file = BuskerFile(work=self.work,
                                      description="",
                                      file=self.busker_file_attachment)
        self.busker_file.file.save(name=self.img2_basename,
                                   content=self.img2_file)
        self.busker_file.save()
        self.batch = Batch.objects.create(
            work=self.work,
            label="Conrad Poohs Test Batch",
            private_note="Batch for unit testing",
            public_message=
            "#Thank You\nThis is a message with *markdown* **formatting**.",
            number_of_codes=10)
        user = User.objects.create_superuser(
            username='******',
            password='******',
        )
        self.client.force_login(user)
コード例 #4
0
class RedeemCodeFormTest(TestCase):
    def setUp(self):
        # Create a couple of images to use for the downloadable work and BuskerFile objects
        self.img = Image.new("RGB", (1200, 1200), "#990000")
        self.img_file = tempfile.NamedTemporaryFile(suffix=".jpg",
                                                    delete=False)
        self.img_basename = os.path.split(self.img_file.name)[-1]
        self.img.save(self.img_file, format="JPEG")

        self.img2 = Image.new("RGB", (5000, 5000), "#336699")
        self.img2_file = tempfile.NamedTemporaryFile(suffix=".png",
                                                     delete=False)
        self.img2_basename = os.path.split(self.img2_file.name)[-1]
        self.img2.save(self.img2_file, format="PNG")

        self.artist = Artist.objects.create(name="Conrad Poohs",
                                            url="https://magicians.band")
        self.work_file = File(self.img_file)
        self.work = DownloadableWork(artist=self.artist,
                                     title="Dancing Teeth",
                                     published=True,
                                     image=self.work_file)
        self.work.image.save(name=self.img_basename, content=self.img_file)
        self.work.save()

        self.busker_file_attachment = File(self.img2_file)
        self.busker_file = BuskerFile(work=self.work,
                                      description="",
                                      file=self.busker_file_attachment)
        self.busker_file.file.save(name=self.img2_basename,
                                   content=self.img2_file)
        self.busker_file.save()
        self.batch = Batch.objects.create(
            work=self.work,
            label="Conrad Poohs Test Batch",
            private_note="Batch for unit testing",
            public_message=
            "#Thank You\nThis is a message with *markdown* **formatting**.",
            number_of_codes=10)

    def tearDown(self):
        os.unlink(self.img_file.name)
        os.unlink(self.img2_file.name)

    def test_valid_code(self):
        code = DownloadCode.objects.first()
        data = {'code': code.id}
        form = RedeemCodeForm(data)
        self.assertTrue(form.is_valid())

    def test_invalid_code(self):
        data = {'code': 'no_such_code'}
        form = RedeemCodeForm(data)
        self.assertFalse(form.is_valid())

    def test_used_code(self):
        """
        Test that a valid code with no remaining uses does not validate
        """
        code = DownloadCode.objects.create(batch=self.batch,
                                           max_uses=1,
                                           times_used=1)
        data = {'code': code.id}
        form = RedeemCodeForm(data)
        self.assertFalse(form.is_valid())

    def test_unlimited_code(self):
        """
        Test that times_used > max_uses validates when max_uses is 0
        """
        code = DownloadCode.objects.create(batch=self.batch,
                                           max_uses=0,
                                           times_used=500)
        data = {'code': code.id}
        form = RedeemCodeForm(data)
        self.assertTrue(form.is_valid())

    def test_unpublished_work(self):
        """
        Test that a valid code with remaining uses does not validate if the work it's related to is NOT published
        """
        code = DownloadCode.objects.first()
        self.work.published = False
        self.work.save()

        data = {'code': code.id}
        form = RedeemCodeForm(data)
        self.assertFalse(form.is_valid())
        self.work.published = True
        self.work.save()  # TODO is it necessary to reset this
コード例 #5
0
class DownloadViewTest(TestCase):
    def setUp(self):

        self.factory = RequestFactory()

        # Create a couple of images to use for the downloadable work and BuskerFile objects
        self.img = Image.new("RGB", (1200, 1200), "#990000")
        self.img_file = tempfile.NamedTemporaryFile(suffix=".jpg",
                                                    delete=False)
        self.img_basename = os.path.split(self.img_file.name)[-1]
        self.img.save(self.img_file, format="JPEG")

        self.img2 = Image.new("RGB", (5000, 5000), "#336699")
        self.img2_file = tempfile.NamedTemporaryFile(suffix=".png",
                                                     delete=False)
        self.img2_basename = os.path.split(self.img2_file.name)[-1]
        self.img2.save(self.img2_file, format="PNG")

        self.artist = Artist.objects.create(name="Conrad Poohs",
                                            url="https://magicians.band")
        self.work_file = File(self.img_file)
        self.work = DownloadableWork(artist=self.artist,
                                     title="Dancing Teeth",
                                     published=True,
                                     image=self.work_file)
        self.work.image.save(name=self.img_basename, content=self.img_file)
        self.work.save()

        self.busker_file_attachment = File(self.img2_file)
        self.busker_file = BuskerFile(work=self.work,
                                      description="",
                                      file=self.busker_file_attachment)
        self.busker_file.file.save(name=self.img2_basename,
                                   content=self.img2_file)
        self.busker_file.save()
        self.batch = Batch.objects.create(
            work=self.work,
            label="Conrad Poohs Test Batch",
            private_note="Batch for unit testing",
            public_message=
            "#Thank You\nThis is a message with *markdown* **formatting**.",
            number_of_codes=10)

        self.unpub_work = DownloadableWork.objects.create(title="Unpublished",
                                                          artist=self.artist,
                                                          published=False)
        self.unpub_batch = Batch.objects.create(label="unpublished",
                                                work=self.unpub_work,
                                                number_of_codes=1,
                                                public_message='test')

        self.unpub_code = DownloadCode.objects.get(batch=self.unpub_batch)
        self.used_code = DownloadCode.objects.create(batch=self.batch,
                                                     max_uses=1,
                                                     times_used=1)

    def test_valid_token(self):
        """
        Valid token should return file as an attachment
        """
        token = token_hex(16)
        session = self.client.session
        session[
            'busker_download_token'] = token  # Simulate the session token that gets set after a code is redeemed
        session.save()
        url = reverse('busker:download',
                      kwargs={'file_id': self.busker_file.id}) + f"?t={token}"
        response = self.client.get(url, HTTP_USER_AGENT=__name__)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.get('Content-Type'), 'image/png')
        self.assertEqual(
            response.get('Content-Disposition'),
            f'attachment; filename="{self.busker_file.filename}"')

    def test_invalid_token(self):
        """
        Invalid/missing token should return 401
        """
        # No token
        response = self.client.get(
            reverse('busker:download', kwargs={'file_id':
                                               self.busker_file.id}))
        self.assertEqual(response.status_code, 401)

        # Bogus token
        response = self.client.get(
            reverse('busker:download', kwargs={'file_id': self.busker_file.id})
            + '?t=1234567890abcdefgh')
        self.assertEqual(response.status_code, 401)

    def test_invalid_file(self):
        """
        Test behavior if user has a valid token but requests a non-existent file ID. (This should not normally happen
        but maybe they're fooling around with URLs)
        """
        token = token_hex(16)
        session = self.client.session
        session[
            'busker_download_token'] = token  # Simulate the session token that gets set after a code is redeemed
        session.save()

        # However vanishingly unlikely, loop to ensure the fake file ID we use does not exist
        fake_file_id = None
        while fake_file_id is None:
            id = str(uuid4())
            try:
                BuskerFile.objects.get(pk=id)
            except BuskerFile.DoesNotExist as e:
                fake_file_id = id

        url = reverse('busker:download', kwargs={'file_id': fake_file_id
                                                 }) + f"?t={token}"
        response = self.client.get(url, HTTP_USER_AGENT=__name__)
        self.assertEqual(response.status_code, 404)
コード例 #6
0
class RedeemViewTest(TestCase):
    def setUp(self):
        self.factory = RequestFactory()

        # Create a couple of images to use for the downloadable work and BuskerFile objects
        self.img = Image.new("RGB", (1200, 1200), "#990000")
        self.img_file = tempfile.NamedTemporaryFile(suffix=".jpg",
                                                    delete=False)
        self.img_basename = os.path.split(self.img_file.name)[-1]
        self.img.save(self.img_file, format="JPEG")

        self.img2 = Image.new("RGB", (5000, 5000), "#336699")
        self.img2_file = tempfile.NamedTemporaryFile(suffix=".png",
                                                     delete=False)
        self.img2_basename = os.path.split(self.img2_file.name)[-1]
        self.img2.save(self.img2_file, format="PNG")

        self.artist = Artist.objects.create(name="Conrad Poohs",
                                            url="https://magicians.band")
        self.work_file = File(self.img_file)
        self.work = DownloadableWork(artist=self.artist,
                                     title="Dancing Teeth",
                                     published=True,
                                     image=self.work_file)
        self.work.image.save(name=self.img_basename, content=self.img_file)
        self.work.save()

        self.busker_file_attachment = File(self.img2_file)
        self.busker_file = BuskerFile(work=self.work,
                                      description="",
                                      file=self.busker_file_attachment)
        self.busker_file.file.save(name=self.img2_basename,
                                   content=self.img2_file)
        self.busker_file.save()
        self.batch = Batch.objects.create(
            work=self.work,
            label="Conrad Poohs Test Batch",
            private_note="Batch for unit testing",
            public_message=
            "#Thank You\nThis is a message with *markdown* **formatting**.",
            number_of_codes=10)

        self.unpub_work = DownloadableWork.objects.create(title="Unpublished",
                                                          artist=self.artist,
                                                          published=False)
        self.unpub_batch = Batch.objects.create(label="unpublished",
                                                work=self.unpub_work,
                                                number_of_codes=1,
                                                public_message='test')

        self.unpub_code = DownloadCode.objects.get(batch=self.unpub_batch)
        self.used_code = DownloadCode.objects.create(batch=self.batch,
                                                     max_uses=1,
                                                     times_used=1)

    def test_redeem_view_valid(self):
        """
        Test the redeem URL with a valid code; should respond with the confirm form
        """
        code = self.batch.codes.first()
        response = self.client.get(reverse('busker:redeem',
                                           kwargs={'download_code': code.id}),
                                   HTTP_USER_AGENT=__name__)
        # TODO support i18n for button label
        #  test response content for 'has [n] uses left' string
        #  test response content for work image thumbnail URI
        for expected in {
                'Continue', code.batch.work.artist, code.batch.work.title,
                code.batch.public_message_rendered
        }:
            self.assertContains(response, expected, status_code=200)

    def test_redeem_view_invalid(self):
        """
        Test the redeem URL with various invalid codes; should respond with 404
        """

        for test_code in {
                'this code is too long',
                generate_code(
                ),  # (Will always return a code that does not exist in the db)
                self.unpub_code.id,
                self.used_code.id
        }:
            response = self.client.get(
                reverse('busker:redeem', kwargs={'download_code': test_code}))
            self.assertEqual(response.status_code, 404)

    def test_redeem_confirm_valid(self):
        """
        Test that clicking 'confirm' from the redeem view displays the expected list of files. There is currently no
        cleaning or validation on the confirm form, thus no corresponding 'invalid' test.
        """
        code = self.batch.codes.first()
        data = {
            'code': code.id,
            'submit': 'Continue'
        }  # TODO make sure 'Continue button' is i18n friendly
        response = self.client.post(reverse('busker:redeem',
                                            kwargs={'download_code': code.id}),
                                    data=data,
                                    HTTP_USER_AGENT=__name__)
        self.assertEqual(response.status_code, 200)
        for file in code.batch.work.files.all():
            self.assertContains(response, file.filename)
        self.assertTrue('busker_download_token' in self.client.session)
コード例 #7
0
class RedeemFormViewTest(TestCase):
    def setUp(self):
        self.factory = RequestFactory()

        # Create a couple of images to use for the downloadable work and BuskerFile objects
        self.img = Image.new("RGB", (1200, 1200), "#990000")
        self.img_file = tempfile.NamedTemporaryFile(suffix=".jpg",
                                                    delete=False)
        self.img_basename = os.path.split(self.img_file.name)[-1]
        self.img.save(self.img_file, format="JPEG")

        self.img2 = Image.new("RGB", (5000, 5000), "#336699")
        self.img2_file = tempfile.NamedTemporaryFile(suffix=".png",
                                                     delete=False)
        self.img2_basename = os.path.split(self.img2_file.name)[-1]
        self.img2.save(self.img2_file, format="PNG")

        self.artist = Artist.objects.create(name="Conrad Poohs",
                                            url="https://magicians.band")
        self.work_file = File(self.img_file)
        self.work = DownloadableWork(artist=self.artist,
                                     title="Dancing Teeth",
                                     published=True,
                                     image=self.work_file)
        self.work.image.save(name=self.img_basename, content=self.img_file)
        self.work.save()

        self.busker_file_attachment = File(self.img2_file)
        self.busker_file = BuskerFile(work=self.work,
                                      description="",
                                      file=self.busker_file_attachment)
        self.busker_file.file.save(name=self.img2_basename,
                                   content=self.img2_file)
        self.busker_file.save()
        self.batch = Batch.objects.create(
            work=self.work,
            label="Conrad Poohs Test Batch",
            private_note="Batch for unit testing",
            public_message=
            "#Thank You\nThis is a message with *markdown* **formatting**.",
            number_of_codes=10)

        self.unpub_work = DownloadableWork.objects.create(title="Unpublished",
                                                          artist=self.artist,
                                                          published=False)
        self.unpub_batch = Batch.objects.create(label="unpublished",
                                                work=self.unpub_work,
                                                number_of_codes=1,
                                                public_message='test')

        self.unpub_code = DownloadCode.objects.get(batch=self.unpub_batch)
        self.used_code = DownloadCode.objects.create(batch=self.batch,
                                                     max_uses=1,
                                                     times_used=1)

    def tearDown(self):
        os.unlink(self.img_file.name)
        os.unlink(self.img2_file.name)

    def test_redeem_form_valid(self):
        """
        Test expected behavior when submitting the RedeemFormView with valid code (redirects to RedeemView with 302)
        """
        code = DownloadCode.objects.create(batch=self.batch, max_uses=3)
        data = {
            'code': code.id,
            'submit': 'Redeem Code'
        }  # TODO make sure submit value is i18n friendly
        response = self.client.post(reverse('busker:redeem_form'),
                                    data=data,
                                    follow=True)
        self.assertRedirects(
            response,
            reverse('busker:redeem', kwargs={'download_code': code.id}))

    def test_redeem_form_invalid(self):
        """
        Test expected behavior when submitting RedeemFormView with various invalid code values
         (content should indicate error)
        """

        code = generate_code(
        )  # (Will always return a code that does not exist in the db)

        for test_code in {
                '', 'this code is too long', code, self.unpub_code.id,
                self.used_code.id
        }:
            data = {'code': test_code}
            response = self.client.post(reverse('busker:redeem_form'),
                                        data=data)
            self.assertContains(response, 'error')
コード例 #8
0
class DownloadCodeAdminTestCase(TestCase):
    def setUp(self):
        # Create a couple of images to use for the downloadable work and BuskerFile objects
        self.img = Image.new("RGB", (1200, 1200), "#990000")
        self.img_file = tempfile.NamedTemporaryFile(suffix=".jpg",
                                                    delete=False)
        self.img_basename = os.path.split(self.img_file.name)[-1]
        self.img.save(self.img_file, format="JPEG")

        self.img2 = Image.new("RGB", (5000, 5000), "#336699")
        self.img2_file = tempfile.NamedTemporaryFile(suffix=".png",
                                                     delete=False)
        self.img2_basename = os.path.split(self.img2_file.name)[-1]
        self.img2.save(self.img2_file, format="PNG")

        self.artist = Artist.objects.create(name="Conrad Poohs",
                                            url="https://magicians.band")
        self.work_file = File(self.img_file)
        self.work = DownloadableWork(artist=self.artist,
                                     title="Dancing Teeth",
                                     published=True,
                                     image=self.work_file)
        self.work.image.save(name=self.img_basename, content=self.img_file)
        self.work.save()

        self.busker_file_attachment = File(self.img2_file)
        self.busker_file = BuskerFile(work=self.work,
                                      description="",
                                      file=self.busker_file_attachment)
        self.busker_file.file.save(name=self.img2_basename,
                                   content=self.img2_file)
        self.busker_file.save()
        self.batch = Batch.objects.create(
            work=self.work,
            label="Conrad Poohs Test Batch",
            private_note="Batch for unit testing",
            public_message=
            "#Thank You\nThis is a message with *markdown* **formatting**.",
            number_of_codes=10)
        user = User.objects.create_superuser(
            username='******',
            password='******',
        )
        self.client.force_login(user)

    def test_code_admin_work_published(self):
        code_admin = DownloadCodeAdmin(model=DownloadCode,
                                       admin_site=AdminSite())
        # Test Code for published work
        code = DownloadCode.objects.filter(
            batch=self.batch, batch__work__published=True).first()
        self.assertTrue(code_admin.work_published(instance=code))

        # Test Code  for unpublished work
        work = DownloadableWork.objects.create(title="Unpublished",
                                               artist=self.artist,
                                               published=False)
        unpub_batch = Batch.objects.create(work=work,
                                           label="Unpublished test",
                                           public_message="")
        code2 = DownloadCode.objects.create(batch=unpub_batch)
        self.assertFalse(code_admin.work_published(instance=code2))

    def test_code_admin_download_as_csv(self):
        """
        Tests that the 'Download CSV' batch admin option returns CSV data.
        TODO: Would be good to test the actual data
        """
        to_download = DownloadCode.objects.values_list('pk', flat=True)
        data = {'action': 'download_as_csv', '_selected_action': to_download}
        url = reverse('admin:busker_downloadcode_changelist')
        response = self.client.post(url, data)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.get('Content-type'), 'text/csv')
        self.assertEqual(response.get('Content-Disposition'),
                         'attachment; filename=downloadcode_export.csv;')
コード例 #9
0
class BuskerTestCase(TestCase):
    def setUp(self):
        # Create a couple of images to use for the downloadable work and BuskerFile objects
        self.img = Image.new("RGB", (1200, 1200), "#990000")
        self.img_file = tempfile.NamedTemporaryFile(suffix=".jpg",
                                                    delete=False)
        self.img_basename = os.path.split(self.img_file.name)[-1]
        self.img.save(self.img_file, format="JPEG")

        self.img2 = Image.new("RGB", (5000, 5000), "#336699")
        self.img2_file = tempfile.NamedTemporaryFile(suffix=".png",
                                                     delete=False)
        self.img2_basename = os.path.split(self.img2_file.name)[-1]
        self.img2.save(self.img2_file, format="PNG")

        self.artist = Artist.objects.create(name="Conrad Poohs",
                                            url="https://magicians.band")
        self.work_file = File(self.img_file)
        self.work = DownloadableWork(artist=self.artist,
                                     title="Dancing Teeth",
                                     published=True,
                                     image=self.work_file)
        self.work.image.save(name=self.img_basename, content=self.img_file)
        self.work.save()

        self.busker_file_attachment = File(self.img2_file)
        self.busker_file = BuskerFile(work=self.work,
                                      description="",
                                      file=self.busker_file_attachment)
        self.busker_file.file.save(name=self.img2_basename,
                                   content=self.img2_file)
        self.busker_file.save()
        self.batch = Batch.objects.create(
            work=self.work,
            label="Conrad Poohs Test Batch",
            private_note="Batch for unit testing",
            public_message=
            "#Thank You\nThis is a message with *markdown* **formatting**.",
            number_of_codes=10)

    def tearDown(self):
        os.unlink(self.img_file.name)
        os.unlink(self.img2_file.name)

    def test_work_image_path(self):
        """
        Tests the expected upload_to value when attaching an image to a DownloadableWork object.
        """
        # (File objects' upload_to method gets called with the base filename)
        img_base_name = os.path.split(self.img_file.name)[-1]
        expected_path = f"busker/downloadable_work_images/{self.work.id}/{img_base_name}"
        self.assertEquals(
            work_image_path(self.work, img_base_name), expected_path,
            "Image upload paths should use the format `busker/downloadable_work_images/[downloadable "
            "work id]/[uploaded img file name]`")

    def test_work_file_path(self):
        """
        Tests the expected upload_to value when attaching a file to a BuskerFile object.
        """
        # (File objects' upload_to method gets called with the base filename)
        file_base_name = os.path.split(self.busker_file_attachment.name)[-1]
        expected_path = f"busker/files/{self.busker_file.id}/{file_base_name}"
        self.assertEquals(
            work_file_path(self.busker_file, file_base_name), expected_path,
            "BuskerFile upload paths should use the format `busker/files/[busker file object id]/["
            "uploaded img file name]`")

    def test_validate_code(self):
        """
        Tests expected behavior of the models.validate_code() method.
        """
        code = self.batch.codes.first()
        # TODO test various conditions (code already used, work is not published)
        self.assertEqual(
            validate_code(code), code,
            f"validate_code should have returned the DownloadCode instance "
            f"with an id of `{code.id}`")

        self.assertEqual(
            validate_code("This is definitely not a valid code"), False,
            f"validate_code should return "
            f"boolean False when given an "
            f"invalid code.")

    def test_generate_code(self):
        """
        Verifies that a sampling of codes returned by models.generate_code do not exist in the database.
        """
        for i in range(0, 100):
            code = generate_code()
            with self.assertRaises(DownloadCode.DoesNotExist):
                DownloadCode.objects.get(pk=code)

    def test_artist_str(self):
        self.assertEqual(
            self.artist.__str__(), self.artist.name,
            "Artist.__str__() should return the value of the "
            "name field.")

    def test_downloadable_work_str(self):
        self.assertEqual(
            self.work.__str__(), self.work.title,
            "DownloadableWork.__str__() should return the value of "
            "the title field")

    def test_busker_file_str(self):
        self.assertEqual(
            self.busker_file.__str__(),
            os.path.basename(self.busker_file.file.name),
            "BuskerFile.__str__() "
            "should return the base "
            "value of the file attached "
            "to the 'file' field")

    def test_busker_filename(self):
        self.assertEqual(
            self.busker_file.filename,
            os.path.basename(self.busker_file.file.name),
            "BuskerFile.filename() "
            "should return the base "
            "value of the file attached "
            "to the 'file' field")

    def test_batch_str(self):
        expected_value = f"{self.batch.label} -- {self.batch.work.title} by {self.batch.work.artist.name}"
        self.assertEqual(
            self.batch.__str__(), expected_value,
            "Batch.__str__() should follow the pattern {label} -- "
            "{work.title} by {work.artist.name}")

    def test_code_remaining_uses(self):
        used = randint(0, 10)
        code = DownloadCode.objects.create(batch=self.batch,
                                           max_uses=10,
                                           times_used=used)
        self.assertEqual(
            code.remaining_uses, 10 - used,
            "DownloadCode.remaining_uses should return the value of max_uses minus times_used"
        )
        code.delete()

    def test_code_redeem_uri(self):
        code = DownloadCode.objects.create(batch=self.batch)
        self.assertEqual(
            code.redeem_uri,
            reverse('busker:redeem', kwargs={'download_code': code.id}))
        code.delete()