From 3a2531a4f43c81a0c41fcbdc27684733c6922ae6 Mon Sep 17 00:00:00 2001 From: Steven Ly <6807939+orcsly@users.noreply.github.com> Date: Tue, 25 Aug 2020 09:25:05 -0700 Subject: [PATCH] DIV-1041: Add delete doc to poc and unit tests. --- edivorce/apps/core/tests/test_storage.py | 94 +++++++++++++++++++ edivorce/apps/poc/models.py | 10 ++ .../poc/document_confirm_delete.html | 27 ++++++ edivorce/apps/poc/templates/storage.html | 7 +- edivorce/apps/poc/urls.py | 3 +- edivorce/apps/poc/views.py | 7 +- 6 files changed, 143 insertions(+), 5 deletions(-) create mode 100644 edivorce/apps/core/tests/test_storage.py create mode 100644 edivorce/apps/poc/templates/poc/document_confirm_delete.html diff --git a/edivorce/apps/core/tests/test_storage.py b/edivorce/apps/core/tests/test_storage.py new file mode 100644 index 00000000..c3ed3809 --- /dev/null +++ b/edivorce/apps/core/tests/test_storage.py @@ -0,0 +1,94 @@ +from unittest import mock + +from django.core.files.uploadedfile import SimpleUploadedFile +from django.test import TransactionTestCase +from redis.exceptions import ConnectionError + +from edivorce.apps.core.redis import generate_unique_filename +from edivorce.apps.poc.models import Document + + +class UploadStorageTests(TransactionTestCase): + + @mock.patch('redis.connection.ConnectionPool.get_connection') + def test_storage_connection_error(self, mock_redis): + mock_redis.side_effect = ConnectionError() + + original_count = Document.objects.count() + connection_error = False + + try: + file = SimpleUploadedFile('file.txt', b'this is some content') + test = Document() + test.file = file + test.save() + except ConnectionError: + connection_error = True + + self.assertTrue(connection_error) + self.assertEqual(Document.objects.count(), original_count) + + @mock.patch('edivorce.apps.core.redis.RedisStorage.get_available_name') + @mock.patch('edivorce.apps.core.redis.RedisStorage._save') + def test_storage_file_name_match(self, mock_redis_an, mock_redis_save): + mock_redis_an.return_value = 'file.txt' + mock_redis_save.return_value = 'file.txt' + + file = SimpleUploadedFile('file.txt', b'this is some content') + test = Document() + test.file = file + test.save() + + self.assertTrue(mock_redis_save.called) + self.assertEqual(test.filename, test.file.name) + + @mock.patch('edivorce.apps.core.redis.RedisStorage.get_available_name') + @mock.patch('edivorce.apps.core.redis.RedisStorage._save') + def test_storage_redis_storage(self, mock_redis_an, mock_redis_save): + mock_redis_an.return_value = '6061bebb-f2be-4a74-8757-c4063f6f6993_file_txt' + mock_redis_save.return_value = 'file.txt' + + file = SimpleUploadedFile('file.txt', b'this is some content') + test = Document() + test.file = file + test.save() + + self.assertTrue(mock_redis_save.called) + self.assertEqual(Document.objects.count(), 1) + test = Document.objects.get(id=test.id) + self.assertEqual(test.filename, 'file.txt') + self.assertNotEqual(test.file.name, 'file.txt') + + def test_storage_redis_key(self): + name = 'file.txt' + self.assertNotEqual(generate_unique_filename(None, name), name) + + name = '../../../etc/passwd' + self.assertNotEqual(generate_unique_filename(None, name), name) + self.assertFalse('../../' in generate_unique_filename(None, name)) + + name = '../../../etc/passwd%00.png' + self.assertNotEqual(generate_unique_filename(None, name), name) + self.assertFalse('../../' in generate_unique_filename(None, name)) + + name = '..%2F..%2F..%2Fetc%2F' + self.assertNotEqual(generate_unique_filename(None, name), name) + self.assertFalse('../../' in generate_unique_filename(None, name)) + + @mock.patch('edivorce.apps.core.redis.RedisStorage.get_available_name') + @mock.patch('edivorce.apps.core.redis.RedisStorage._save') + @mock.patch('edivorce.apps.core.redis.RedisStorage.delete') + def test_storage_redis_delete(self, mock_redis_an, mock_redis_save, mock_redis_delete): + mock_redis_an.return_value = '6061bebb-f2be-4a74-8757-c4063f6f6993_file_txt' + mock_redis_save.return_value = 'file.txt' + mock_redis_delete.return_value = True + + file = SimpleUploadedFile('file.txt', b'this is some content') + test = Document() + test.file = file + test.save() + + test.delete() + + self.assertTrue(mock_redis_delete.called) + diff --git a/edivorce/apps/poc/models.py b/edivorce/apps/poc/models.py index 79b94bab..dbfc7980 100644 --- a/edivorce/apps/poc/models.py +++ b/edivorce/apps/poc/models.py @@ -15,3 +15,13 @@ class Document(models.Model): self.filename = self.file.name super(Document, self).save(*args, **kwargs) + + def delete(self, **kwargs): + """ + Override delete so we can delete the Redis object when this instance is deleted. + :param kwargs: + :return: + """ + self.file.delete(save=False) + + super(Document, self).delete(**kwargs) diff --git a/edivorce/apps/poc/templates/poc/document_confirm_delete.html b/edivorce/apps/poc/templates/poc/document_confirm_delete.html new file mode 100644 index 00000000..d4ad7ed9 --- /dev/null +++ b/edivorce/apps/poc/templates/poc/document_confirm_delete.html @@ -0,0 +1,27 @@ +{% extends 'base.html' %} +{% load input_field %} +{% load step_order %} +{% load load_json %} + +{% block title %}{{ block.super }}: POC{% endblock %} + +{% block progress %}{% include "partials/progress.html" %}{% endblock %} + +{% block content %} +
{% csrf_token %} +

Are you sure you want to delete "{{ object }}"?

+ +
+{% endblock %} + +{% block formbuttons %} + +{% endblock %} + +{% block sidebarNav %} + +{% endblock %} + +{% block sidebar %} + +{% endblock %} diff --git a/edivorce/apps/poc/templates/storage.html b/edivorce/apps/poc/templates/storage.html index bf5d2270..369f6d62 100644 --- a/edivorce/apps/poc/templates/storage.html +++ b/edivorce/apps/poc/templates/storage.html @@ -10,7 +10,7 @@ {% block content %}

Proof of Concept:File storage

-
+ {% csrf_token %}
@@ -32,11 +32,12 @@

Stored documents

- +
+ @@ -48,6 +49,7 @@ + {% endfor %} @@ -55,7 +57,6 @@ - {% endblock %} {% block formbuttons %} diff --git a/edivorce/apps/poc/urls.py b/edivorce/apps/poc/urls.py index 3bbf5011..b5e53724 100644 --- a/edivorce/apps/poc/urls.py +++ b/edivorce/apps/poc/urls.py @@ -5,6 +5,7 @@ from ..core.decorators import bceid_required urlpatterns = [ url(r'scan', bceid_required(views.UploadScan.as_view()), name="poc-scan"), - url(r'storage/doc/(?P\d+)', views.view_document_file, name="poc-storage-download"), + url(r'storage/doc/(?P\d+)', bceid_required(views.view_document_file), name="poc-storage-download"), + url(r'storage/delete/(?P\d+)', bceid_required(views.UploadStorageDelete.as_view()), name="poc-storage-delete"), url(r'storage', bceid_required(views.UploadStorage.as_view()), name="poc-storage"), ] \ No newline at end of file diff --git a/edivorce/apps/poc/views.py b/edivorce/apps/poc/views.py index c6af8fee..d0744756 100644 --- a/edivorce/apps/poc/views.py +++ b/edivorce/apps/poc/views.py @@ -1,5 +1,5 @@ from django.shortcuts import render -from django.views.generic.edit import FormView, CreateView +from django.views.generic.edit import FormView, CreateView, DeleteView from django import forms from django.http import HttpResponse @@ -36,6 +36,11 @@ class UploadStorage(CreateView): return super(UploadStorage, self).get_context_data(**kwargs) +class UploadStorageDelete(DeleteView): + model = Document + success_url = '/poc/storage' + + def view_document_file(request, document_id): doc = Document.objects.get(id=document_id)
File name Redis key
{{ document.file.name }}Delete