fix(Issue-27): Confirmations not triggered on models with validation failures (#30)
* feat(ISSUE-27): Added some validation integration tests * feat(ISSUE-27): Integration tests for validations * feat(ISSUE-27): Unable to select autocomplete with selenium * feat(ISSUE-27): Fix file caching with proper FileCache * feat(ISSUE-27): Some minor tweaks * feat(ISSUE-27): Try reviewdog * feat(ISSUE-27): Remove noisy linting * feat(ISSUE-27): Black format * feat(ISSUE-27): Adding some simple file cache tests * feat(ISSUE-27): Some cleaning up Co-authored-by: Thu Trang Pham <thu@joinmodernhealth.com>
This commit is contained in:
@@ -102,10 +102,6 @@ class TestConfirmSaveActions(AdminConfirmTestCase):
|
||||
# Should have cached the unsaved item
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNotNone(cached_item)
|
||||
self.assertIsNone(cached_item.id)
|
||||
self.assertEqual(cached_item.name, data["name"])
|
||||
self.assertEqual(cached_item.price, data["price"])
|
||||
self.assertEqual(cached_item.currency, data["currency"])
|
||||
|
||||
# Should not have saved the changes yet
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
@@ -171,16 +167,6 @@ class TestConfirmSaveActions(AdminConfirmTestCase):
|
||||
multipart_form=True,
|
||||
)
|
||||
|
||||
# Should have cached the unsaved item
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNotNone(cached_item)
|
||||
self.assertIsNone(cached_item.id)
|
||||
self.assertEqual(cached_item.name, data["name"])
|
||||
self.assertEqual(cached_item.price, data["price"])
|
||||
self.assertEqual(cached_item.currency, data["currency"])
|
||||
self.assertEqual(cached_item.file, data["file"])
|
||||
self.assertEqual(cached_item.image, data["image"])
|
||||
|
||||
# Should not have saved the item yet
|
||||
self.assertEqual(Item.objects.count(), 0)
|
||||
|
||||
@@ -205,13 +191,14 @@ class TestConfirmSaveActions(AdminConfirmTestCase):
|
||||
self.assertEqual(saved_item.name, data["name"])
|
||||
self.assertEqual(saved_item.price, data["price"])
|
||||
self.assertEqual(saved_item.currency, data["currency"])
|
||||
self.assertEqual(saved_item.file, data["file"])
|
||||
self.assertEqual(saved_item.image, data["image"])
|
||||
self.assertIsNotNone(saved_item.file)
|
||||
self.assertIsNotNone(saved_item.image)
|
||||
|
||||
self.assertEqual(saved_item.file.name, "test_file.jpg")
|
||||
self.assertEqual(saved_item.image.name, "test_image.jpg")
|
||||
self.assertRegex(saved_item.file.name, r"test_file.*\.jpg$")
|
||||
self.assertRegex(saved_item.image.name, r"test_image.*\.jpg$")
|
||||
|
||||
# Should have cleared cache
|
||||
self.assertEqual(len(ItemAdmin._file_cache.cached_keys), 0)
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
@@ -271,16 +258,6 @@ class TestConfirmSaveActions(AdminConfirmTestCase):
|
||||
multipart_form=True,
|
||||
)
|
||||
|
||||
# Should have cached the unsaved item
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNotNone(cached_item)
|
||||
self.assertIsNone(cached_item.id)
|
||||
self.assertEqual(cached_item.name, data["name"])
|
||||
self.assertEqual(cached_item.price, data["price"])
|
||||
self.assertEqual(cached_item.currency, data["currency"])
|
||||
self.assertFalse(cached_item.file.name)
|
||||
self.assertEqual(cached_item.image, i2)
|
||||
|
||||
# Should not have saved the changes yet
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
item.refresh_from_db()
|
||||
@@ -312,11 +289,11 @@ class TestConfirmSaveActions(AdminConfirmTestCase):
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.file)
|
||||
self.assertEqual(new_item.image, i2)
|
||||
self.assertIsNotNone(new_item.image)
|
||||
self.assertRegex(new_item.image.name, r"test_image2.*\.jpg$")
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
self.assertEqual(len(ItemAdmin._file_cache.cached_keys), 0)
|
||||
|
||||
def test_relations_add(self):
|
||||
gm = GeneralManager.objects.create(name="gm")
|
||||
@@ -379,6 +356,7 @@ class TestConfirmSaveActions(AdminConfirmTestCase):
|
||||
self.assertIn(shop, shops)
|
||||
|
||||
# Should have cleared cache
|
||||
self.assertEqual(len(ItemAdmin._file_cache.cached_keys), 0)
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
@@ -473,5 +451,4 @@ class TestConfirmSaveActions(AdminConfirmTestCase):
|
||||
self.assertIn(shop, shops2)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
self.assertEqual(len(ItemAdmin._file_cache.cached_keys), 0)
|
||||
|
||||
@@ -102,10 +102,6 @@ class TestConfirmationCache(AdminConfirmTestCase):
|
||||
# Should have cached the unsaved item
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNotNone(cached_item)
|
||||
self.assertIsNone(cached_item.id)
|
||||
self.assertEqual(cached_item.name, data["name"])
|
||||
self.assertEqual(cached_item.price, data["price"])
|
||||
self.assertEqual(cached_item.currency, data["currency"])
|
||||
|
||||
# Should not have saved the changes yet
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
@@ -204,11 +200,11 @@ class TestConfirmationCache(AdminConfirmTestCase):
|
||||
self.assertEqual(saved_item.name, data["name"])
|
||||
self.assertEqual(saved_item.price, data["price"])
|
||||
self.assertEqual(saved_item.currency, data["currency"])
|
||||
self.assertEqual(saved_item.file, data["file"])
|
||||
self.assertEqual(saved_item.image, data["image"])
|
||||
self.assertIsNotNone(saved_item.file)
|
||||
self.assertIsNotNone(saved_item.image)
|
||||
|
||||
self.assertEqual(saved_item.file.name, "test_file.jpg")
|
||||
self.assertEqual(saved_item.image.name, "test_image.jpg")
|
||||
self.assertRegex(saved_item.file.name, r"test_file.*\.jpg$")
|
||||
self.assertRegex(saved_item.image.name, r"test_image.*\.jpg$")
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
@@ -271,12 +267,6 @@ class TestConfirmationCache(AdminConfirmTestCase):
|
||||
# Should have cached the unsaved item
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNotNone(cached_item)
|
||||
self.assertIsNone(cached_item.id)
|
||||
self.assertEqual(cached_item.name, data["name"])
|
||||
self.assertEqual(cached_item.price, data["price"])
|
||||
self.assertEqual(cached_item.currency, data["currency"])
|
||||
self.assertFalse(cached_item.file.name)
|
||||
self.assertEqual(cached_item.image, i2)
|
||||
|
||||
# Should not have saved the changes yet
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
@@ -301,7 +291,9 @@ class TestConfirmationCache(AdminConfirmTestCase):
|
||||
self.assertEqual(saved_item.price, data["price"])
|
||||
self.assertEqual(saved_item.currency, data["currency"])
|
||||
self.assertFalse(saved_item.file)
|
||||
self.assertEqual(saved_item.image, i2)
|
||||
self.assertIsNotNone(saved_item.image)
|
||||
|
||||
self.assertRegex(saved_item.image.name, r"test_image2.*\.jpg$")
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
|
||||
@@ -0,0 +1,920 @@
|
||||
"""
|
||||
Ensure that files are saved during confirmation
|
||||
Without file changes, Django is relied on
|
||||
|
||||
With file changes, we cache the object, save it with
|
||||
the files if new, or add files to existing obj and save
|
||||
|
||||
Then send the rest of the changes to Django to handle
|
||||
|
||||
This is arguably the most we fiddle with the Django request
|
||||
Thus we should test it extensively
|
||||
"""
|
||||
from admin_confirm.utils import format_cache_key
|
||||
from admin_confirm.file_cache import FileCache
|
||||
import time
|
||||
from unittest import mock
|
||||
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.core.cache import cache
|
||||
|
||||
from admin_confirm.tests.helpers import AdminConfirmTestCase
|
||||
from admin_confirm.constants import CACHE_KEYS, CONFIRMATION_RECEIVED
|
||||
|
||||
from tests.market.admin import ItemAdmin
|
||||
from tests.market.models import Item, Shop
|
||||
from tests.factories import ItemFactory, ShopFactory
|
||||
|
||||
|
||||
class TestConfirmationUsingFileCache(AdminConfirmTestCase):
|
||||
def setUp(self):
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.confirm_change = True
|
||||
ItemAdmin.fields = ["name", "price", "file", "image", "currency"]
|
||||
ItemAdmin.save_as = True
|
||||
ItemAdmin.save_as_continue = True
|
||||
|
||||
self.image_path = "screenshot.png"
|
||||
f = SimpleUploadedFile(
|
||||
name="test_file.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
i = SimpleUploadedFile(
|
||||
name="test_image.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
self.item = ItemFactory(name="Not name", file=f, image=i)
|
||||
|
||||
return super().setUp()
|
||||
|
||||
def test_save_as_continue_true_should_not_redirect_to_changelist(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = True
|
||||
|
||||
# Upload new image and remove file
|
||||
i2 = SimpleUploadedFile(
|
||||
name="test_image2.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_saveasnew": True,
|
||||
}
|
||||
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"],
|
||||
price=data["price"],
|
||||
currency=data["currency"],
|
||||
image=i2,
|
||||
)
|
||||
file_cache = FileCache()
|
||||
file_cache.set(format_cache_key(model="Item", field="image"), i2)
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_item)
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertIn("You may edit it again below.", message)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, f"/admin/market/item/{self.item.id + 1}/change/")
|
||||
|
||||
# Should not have changed existing item
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertEqual(item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.file)
|
||||
self.assertEqual(new_item.image.name.count("test_image2"), 1)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_save_as_continue_false_should_redirect_to_changelist(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = False
|
||||
|
||||
# Upload new image and remove file
|
||||
i2 = SimpleUploadedFile(
|
||||
name="test_image2.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_saveasnew": True,
|
||||
}
|
||||
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"],
|
||||
price=data["price"],
|
||||
currency=data["currency"],
|
||||
image=i2,
|
||||
)
|
||||
file_cache = FileCache()
|
||||
file_cache.set(format_cache_key(model="Item", field="image"), i2)
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_item)
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertEqual(item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.file)
|
||||
self.assertEqual(new_item.image.name.count("test_image2"), 1)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_saveasnew_without_any_file_changes_should_save_new_instance_without_files(
|
||||
self,
|
||||
):
|
||||
item = self.item
|
||||
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"image": "",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_saveasnew": True,
|
||||
}
|
||||
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"],
|
||||
price=data["price"],
|
||||
currency=data["currency"],
|
||||
)
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_item)
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertIn("You may edit it again below.", message)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, f"/admin/market/item/{self.item.id + 1}/change/")
|
||||
|
||||
# Should not have changed existing item
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertEqual(item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
# In Django (by default), the save as new does not transfer over the files
|
||||
self.assertFalse(new_item.file)
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_add_with_upload_file_should_save_new_instance_with_files(self):
|
||||
# Upload new file
|
||||
f2 = SimpleUploadedFile(
|
||||
name="test_file2.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
|
||||
# Request.POST
|
||||
data = {
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"image": "",
|
||||
"file": f2,
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"], price=data["price"], currency=data["currency"], file=f2
|
||||
)
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_item)
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_add"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post("/admin/market/item/add/", data=data)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
self.item.refresh_from_db()
|
||||
self.assertEqual(self.item.name, "Not name")
|
||||
self.assertEqual(self.item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(self.item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(self.item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=self.item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertIsNotNone(new_item.file)
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
self.assertRegex(new_item.file.name, r"test_file2.*\.jpg$")
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_add_without_cached_post_should_save_new_instance_with_file(self):
|
||||
# Upload new file
|
||||
f2 = SimpleUploadedFile(
|
||||
name="test_file2.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
|
||||
# Request.POST
|
||||
data = {
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"image": "",
|
||||
"file": f2,
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"], price=data["price"], currency=data["currency"], file=f2
|
||||
)
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_item)
|
||||
# Make sure there's no post cached post
|
||||
cache.delete(CACHE_KEYS["post"])
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_add"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post("/admin/market/item/add/", data=data)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
self.item.refresh_from_db()
|
||||
self.assertEqual(self.item.name, "Not name")
|
||||
self.assertEqual(self.item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(self.item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(self.item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=self.item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
# Able to save the cached file since cached object was there even though cached post was not
|
||||
self.assertRegex(new_item.file.name, r"test_file2.*\.jpg$")
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_add_without_cached_object_should_save_new_instance_but_not_have_file(self):
|
||||
# Request.POST
|
||||
data = {
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"image": "",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
|
||||
# Make sure there's no post cached obj
|
||||
cache.delete(CACHE_KEYS["object"])
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_add"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post("/admin/market/item/add/", data=data)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
self.item.refresh_from_db()
|
||||
self.assertEqual(self.item.name, "Not name")
|
||||
self.assertEqual(self.item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(self.item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(self.item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=self.item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
# FAILED to save the file, because cached item was not there
|
||||
self.assertFalse(new_item.file)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_add_without_any_cache_should_save_new_instance_but_not_have_file(self):
|
||||
# Request.POST
|
||||
data = {
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"image": "",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
|
||||
# Make sure there's no cache
|
||||
cache.delete(CACHE_KEYS["object"])
|
||||
cache.delete(CACHE_KEYS["post"])
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_add"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post("/admin/market/item/add/", data=data)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
self.item.refresh_from_db()
|
||||
self.assertEqual(self.item.name, "Not name")
|
||||
self.assertEqual(self.item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(self.item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(self.item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=self.item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
# FAILED to save the file, because cached item was not there
|
||||
self.assertFalse(new_item.file)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_change_without_cached_post_should_save_file_changes(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = False
|
||||
|
||||
# Upload new image and remove file
|
||||
i2 = SimpleUploadedFile(
|
||||
name="test_image2.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"image": i2,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_saveasnew": True,
|
||||
}
|
||||
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"],
|
||||
price=data["price"],
|
||||
currency=data["currency"],
|
||||
image=i2,
|
||||
)
|
||||
file_cache = FileCache()
|
||||
file_cache.set(format_cache_key(model="Item", field="image"), i2)
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_item)
|
||||
# Ensure no cached post
|
||||
cache.delete(CACHE_KEYS["post"])
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
# Image would have been in FILES and not in POST
|
||||
del data["image"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertEqual(item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
# Should have cleared `file` since clear was selected
|
||||
self.assertFalse(new_item.file)
|
||||
self.assertIsNotNone(new_item.image)
|
||||
# Saved cached file from cached obj even if cached post was missing
|
||||
self.assertIn("test_image2", new_item.image.name)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_change_without_cached_object_should_save_but_without_file_changes(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = False
|
||||
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_saveasnew": True,
|
||||
}
|
||||
|
||||
# Ensure no cached obj
|
||||
cache.delete(CACHE_KEYS["object"])
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertEqual(item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.file)
|
||||
# FAILED to save image
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_change_without_any_cache_should_save_but_not_have_file_changes(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = False
|
||||
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_saveasnew": True,
|
||||
}
|
||||
|
||||
# Ensure no cache
|
||||
cache.delete(CACHE_KEYS["object"])
|
||||
cache.delete(CACHE_KEYS["post"])
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertEqual(item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.file)
|
||||
# FAILED to save image
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_change_without_changing_file_should_save_changes(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = False
|
||||
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"image": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_save": True,
|
||||
}
|
||||
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"],
|
||||
price=data["price"],
|
||||
currency=data["currency"],
|
||||
)
|
||||
|
||||
cache.get(CACHE_KEYS["object"], cache_item)
|
||||
cache.get(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/1/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should have changed existing item
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "name")
|
||||
# Should have cleared if requested
|
||||
self.assertFalse(item.file.name)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
@mock.patch("admin_confirm.admin.CACHE_TIMEOUT", 1)
|
||||
def test_old_cache_should_not_be_used(self):
|
||||
item = self.item
|
||||
|
||||
# Upload new image and remove file
|
||||
i2 = SimpleUploadedFile(
|
||||
name="test_image2.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
# Click "Save And Continue"
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"image": i2,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_continue": True,
|
||||
}
|
||||
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
|
||||
|
||||
# Should be shown confirmation page
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content,
|
||||
save_action="_continue",
|
||||
multipart_form=True,
|
||||
)
|
||||
|
||||
# Should have cached the unsaved item
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNotNone(cached_item)
|
||||
|
||||
# Should not have saved the changes yet
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertIsNotNone(item.file)
|
||||
self.assertIsNotNone(item.image)
|
||||
|
||||
# Wait for cache to time out
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# Check that it did time out
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNone(cached_item)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data["image"] = ""
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, f"/admin/market/item/{item.id}/change/")
|
||||
|
||||
# Should have saved item
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
saved_item = Item.objects.all().first()
|
||||
self.assertEqual(saved_item.name, data["name"])
|
||||
self.assertEqual(saved_item.price, data["price"])
|
||||
self.assertEqual(saved_item.currency, data["currency"])
|
||||
self.assertFalse(saved_item.file)
|
||||
|
||||
# SHOULD not have saved image since it was in the old cache
|
||||
self.assertNotIn("test_image2", saved_item.image)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_cache_with_incorrect_model_should_not_be_used(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = False
|
||||
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_save": True,
|
||||
}
|
||||
|
||||
# Set cache to incorrect model
|
||||
cache_obj = Shop(name="ShopName")
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_obj)
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/1/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should have changed existing item
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "name")
|
||||
# Should have cleared if requested
|
||||
self.assertFalse(item.file.name)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_form_without_files_should_not_use_cache(self):
|
||||
cache.delete_many(CACHE_KEYS.values())
|
||||
shop = ShopFactory()
|
||||
# Click "Save And Continue"
|
||||
data = {
|
||||
"id": shop.id,
|
||||
"name": "name",
|
||||
"_confirm_change": True,
|
||||
"_continue": True,
|
||||
}
|
||||
response = self.client.post(f"/admin/market/shop/{shop.id}/change/", data=data)
|
||||
|
||||
# Should be shown confirmation page
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content, save_action="_continue"
|
||||
)
|
||||
|
||||
# Should not have set cache since not multipart form
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
@@ -1,911 +1,33 @@
|
||||
"""
|
||||
Ensure that files are saved during confirmation
|
||||
Without file changes, Django is relied on
|
||||
|
||||
With file changes, we cache the object, save it with
|
||||
the files if new, or add files to existing obj and save
|
||||
|
||||
Then send the rest of the changes to Django to handle
|
||||
|
||||
This is arguably the most we fiddle with the Django request
|
||||
Thus we should test it extensively
|
||||
"""
|
||||
import time
|
||||
from unittest import mock
|
||||
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.core.cache import cache
|
||||
|
||||
from admin_confirm.tests.helpers import AdminConfirmTestCase
|
||||
from admin_confirm.constants import CACHE_KEYS, CONFIRMATION_RECEIVED
|
||||
|
||||
from tests.market.admin import ItemAdmin
|
||||
from tests.market.models import Item, Shop
|
||||
from tests.factories import ItemFactory, ShopFactory
|
||||
|
||||
|
||||
class TestFileCache(AdminConfirmTestCase):
|
||||
def setUp(self):
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.confirm_change = True
|
||||
ItemAdmin.fields = ["name", "price", "file", "image", "currency"]
|
||||
ItemAdmin.save_as = True
|
||||
ItemAdmin.save_as_continue = True
|
||||
|
||||
self.image_path = "screenshot.png"
|
||||
f = SimpleUploadedFile(
|
||||
name="test_file.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
i = SimpleUploadedFile(
|
||||
name="test_image.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
self.item = ItemFactory(name="Not name", file=f, image=i)
|
||||
|
||||
return super().setUp()
|
||||
|
||||
def test_save_as_continue_true_should_not_redirect_to_changelist(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = True
|
||||
|
||||
# Upload new image and remove file
|
||||
i2 = SimpleUploadedFile(
|
||||
name="test_image2.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_saveasnew": True,
|
||||
}
|
||||
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"],
|
||||
price=data["price"],
|
||||
currency=data["currency"],
|
||||
image=i2,
|
||||
)
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_item)
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertIn("You may edit it again below.", message)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, f"/admin/market/item/{self.item.id + 1}/change/")
|
||||
|
||||
# Should not have changed existing item
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertEqual(item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.file)
|
||||
self.assertEqual(new_item.image.name.count("test_image2"), 1)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_save_as_continue_false_should_redirect_to_changelist(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = False
|
||||
|
||||
# Upload new image and remove file
|
||||
i2 = SimpleUploadedFile(
|
||||
name="test_image2.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_saveasnew": True,
|
||||
}
|
||||
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"],
|
||||
price=data["price"],
|
||||
currency=data["currency"],
|
||||
image=i2,
|
||||
)
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_item)
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertEqual(item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.file)
|
||||
self.assertEqual(new_item.image.name.count("test_image2"), 1)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_saveasnew_without_any_file_changes_should_save_new_instance_without_files(
|
||||
self,
|
||||
):
|
||||
item = self.item
|
||||
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"image": "",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_saveasnew": True,
|
||||
}
|
||||
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"],
|
||||
price=data["price"],
|
||||
currency=data["currency"],
|
||||
)
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_item)
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertIn("You may edit it again below.", message)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, f"/admin/market/item/{self.item.id + 1}/change/")
|
||||
|
||||
# Should not have changed existing item
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertEqual(item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
# In Django (by default), the save as new does not transfer over the files
|
||||
self.assertFalse(new_item.file)
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_add_with_upload_file_should_save_new_instance_with_files(self):
|
||||
# Request.POST
|
||||
data = {
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"image": "",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
|
||||
# Upload new file
|
||||
f2 = SimpleUploadedFile(
|
||||
name="test_file2.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"], price=data["price"], currency=data["currency"], file=f2
|
||||
)
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_item)
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_add"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post("/admin/market/item/add/", data=data)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
self.item.refresh_from_db()
|
||||
self.assertEqual(self.item.name, "Not name")
|
||||
self.assertEqual(self.item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(self.item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(self.item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=self.item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertIn("test_file2", new_item.file.name)
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_add_without_cached_post_should_save_new_instance_with_file(self):
|
||||
# Request.POST
|
||||
data = {
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"image": "",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
|
||||
# Upload new file
|
||||
f2 = SimpleUploadedFile(
|
||||
name="test_file2.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"], price=data["price"], currency=data["currency"], file=f2
|
||||
)
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_item)
|
||||
# Make sure there's no post cached post
|
||||
cache.delete(CACHE_KEYS["post"])
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_add"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post("/admin/market/item/add/", data=data)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
self.item.refresh_from_db()
|
||||
self.assertEqual(self.item.name, "Not name")
|
||||
self.assertEqual(self.item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(self.item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(self.item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=self.item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
# Able to save the cached file since cached object was there even though cached post was not
|
||||
self.assertIn("test_file2", new_item.file.name)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_add_without_cached_object_should_save_new_instance_but_not_have_file(self):
|
||||
# Request.POST
|
||||
data = {
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"image": "",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
|
||||
# Make sure there's no post cached obj
|
||||
cache.delete(CACHE_KEYS["object"])
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_add"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post("/admin/market/item/add/", data=data)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
self.item.refresh_from_db()
|
||||
self.assertEqual(self.item.name, "Not name")
|
||||
self.assertEqual(self.item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(self.item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(self.item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=self.item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
# FAILED to save the file, because cached item was not there
|
||||
self.assertFalse(new_item.file)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_add_without_any_cache_should_save_new_instance_but_not_have_file(self):
|
||||
# Request.POST
|
||||
data = {
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"image": "",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
|
||||
# Make sure there's no cache
|
||||
cache.delete(CACHE_KEYS["object"])
|
||||
cache.delete(CACHE_KEYS["post"])
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_add"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post("/admin/market/item/add/", data=data)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
self.item.refresh_from_db()
|
||||
self.assertEqual(self.item.name, "Not name")
|
||||
self.assertEqual(self.item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(self.item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(self.item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=self.item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
# FAILED to save the file, because cached item was not there
|
||||
self.assertFalse(new_item.file)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_change_without_cached_post_should_save_file_changes(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = False
|
||||
|
||||
# Upload new image and remove file
|
||||
i2 = SimpleUploadedFile(
|
||||
name="test_image2.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"image": i2,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_saveasnew": True,
|
||||
}
|
||||
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"],
|
||||
price=data["price"],
|
||||
currency=data["currency"],
|
||||
image=i2,
|
||||
)
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_item)
|
||||
# Ensure no cached post
|
||||
cache.delete(CACHE_KEYS["post"])
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
# Image would have been in FILES and not in POST
|
||||
del data["image"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertEqual(item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
# Should have cleared `file` since clear was selected
|
||||
self.assertFalse(new_item.file)
|
||||
# Saved cached file from cached obj even if cached post was missing
|
||||
self.assertIn("test_image2", new_item.image.name)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_change_without_cached_object_should_save_but_without_file_changes(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = False
|
||||
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_saveasnew": True,
|
||||
}
|
||||
|
||||
# Ensure no cached obj
|
||||
cache.delete(CACHE_KEYS["object"])
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertEqual(item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.file)
|
||||
# FAILED to save image
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_change_without_any_cache_should_save_but_not_have_file_changes(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = False
|
||||
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_saveasnew": True,
|
||||
}
|
||||
|
||||
# Ensure no cache
|
||||
cache.delete(CACHE_KEYS["object"])
|
||||
cache.delete(CACHE_KEYS["post"])
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/2/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should not have changed existing item
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertEqual(item.file.name.count("test_file"), 1)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have saved new item
|
||||
self.assertEqual(Item.objects.count(), 2)
|
||||
new_item = Item.objects.filter(id=item.id + 1).first()
|
||||
self.assertIsNotNone(new_item)
|
||||
self.assertEqual(new_item.name, data["name"])
|
||||
self.assertEqual(new_item.price, data["price"])
|
||||
self.assertEqual(new_item.currency, data["currency"])
|
||||
self.assertFalse(new_item.file)
|
||||
# FAILED to save image
|
||||
self.assertFalse(new_item.image)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_change_without_changing_file_should_save_changes(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = False
|
||||
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"image": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_save": True,
|
||||
}
|
||||
|
||||
# Set cache
|
||||
cache_item = Item(
|
||||
name=data["name"],
|
||||
price=data["price"],
|
||||
currency=data["currency"],
|
||||
)
|
||||
|
||||
cache.get(CACHE_KEYS["object"], cache_item)
|
||||
cache.get(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/1/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should have changed existing item
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "name")
|
||||
# Should have cleared if requested
|
||||
self.assertFalse(item.file.name)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
@mock.patch("admin_confirm.admin.CACHE_TIMEOUT", 1)
|
||||
def test_old_cache_should_not_be_used(self):
|
||||
item = self.item
|
||||
|
||||
# Upload new image and remove file
|
||||
i2 = SimpleUploadedFile(
|
||||
name="test_image2.jpg",
|
||||
content=open(self.image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
# Click "Save And Continue"
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"image": i2,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_continue": True,
|
||||
}
|
||||
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
|
||||
|
||||
# Should be shown confirmation page
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content,
|
||||
save_action="_continue",
|
||||
multipart_form=True,
|
||||
)
|
||||
|
||||
# Should have cached the unsaved item
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNotNone(cached_item)
|
||||
self.assertIsNone(cached_item.id)
|
||||
self.assertEqual(cached_item.name, data["name"])
|
||||
self.assertEqual(cached_item.price, data["price"])
|
||||
self.assertEqual(cached_item.currency, data["currency"])
|
||||
self.assertFalse(cached_item.file.name)
|
||||
self.assertEqual(cached_item.image, i2)
|
||||
|
||||
# Should not have saved the changes yet
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
self.assertIsNotNone(item.file)
|
||||
self.assertIsNotNone(item.image)
|
||||
|
||||
# Wait for cache to time out
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# Check that it did time out
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNone(cached_item)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data["image"] = ""
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
response = self.client.post(f"/admin/market/item/{item.id}/change/", data=data)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, f"/admin/market/item/{item.id}/change/")
|
||||
|
||||
# Should have saved item
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
saved_item = Item.objects.all().first()
|
||||
self.assertEqual(saved_item.name, data["name"])
|
||||
self.assertEqual(saved_item.price, data["price"])
|
||||
self.assertEqual(saved_item.currency, data["currency"])
|
||||
self.assertFalse(saved_item.file)
|
||||
|
||||
# SHOULD not have saved image since it was in the old cache
|
||||
self.assertNotIn("test_image2", saved_item.image)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_cache_with_incorrect_model_should_not_be_used(self):
|
||||
item = self.item
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.save_as_continue = False
|
||||
|
||||
# Request.POST
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"file": "",
|
||||
"file-clear": "on",
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_change": True,
|
||||
"_save": True,
|
||||
}
|
||||
|
||||
# Set cache to incorrect model
|
||||
cache_obj = Shop(name="ShopName")
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_obj)
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
with mock.patch.object(ItemAdmin, "message_user") as message_user:
|
||||
response = self.client.post(
|
||||
f"/admin/market/item/{self.item.id}/change/", data=data
|
||||
)
|
||||
# Should show message to user with correct obj and path
|
||||
message_user.assert_called_once()
|
||||
message = message_user.call_args[0][1]
|
||||
self.assertIn("/admin/market/item/1/change/", message)
|
||||
self.assertIn(data["name"], message)
|
||||
self.assertNotIn("You may edit it again below.", message)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# Should have changed existing item
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "name")
|
||||
# Should have cleared if requested
|
||||
self.assertFalse(item.file.name)
|
||||
self.assertEqual(item.image.name.count("test_image2"), 0)
|
||||
self.assertEqual(item.image.name.count("test_image"), 1)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_form_without_files_should_not_use_cache(self):
|
||||
cache.delete_many(CACHE_KEYS.values())
|
||||
shop = ShopFactory()
|
||||
# Click "Save And Continue"
|
||||
data = {
|
||||
"id": shop.id,
|
||||
"name": "name",
|
||||
"_confirm_change": True,
|
||||
"_continue": True,
|
||||
}
|
||||
response = self.client.post(f"/admin/market/shop/{shop.id}/change/", data=data)
|
||||
|
||||
# Should be shown confirmation page
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content, save_action="_continue"
|
||||
)
|
||||
|
||||
# Should not have set cache since not multipart form
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
from admin_confirm.file_cache import FileCache
|
||||
|
||||
file = SimpleUploadedFile(
|
||||
name="test_file.jpg",
|
||||
content=open("screenshot.png", "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
|
||||
|
||||
def test_should_set_file_cache():
|
||||
file_cache = FileCache()
|
||||
file_cache.set("key", file)
|
||||
assert "key" in file_cache.cached_keys
|
||||
assert file_cache.get("key") is not None
|
||||
|
||||
|
||||
def test_should_delete_file_cache():
|
||||
file_cache = FileCache()
|
||||
file_cache.set("key", file)
|
||||
file_cache.delete("key")
|
||||
assert "key" not in file_cache.cached_keys
|
||||
assert file_cache.get("key") is None
|
||||
|
||||
|
||||
def test_should_delete_all_file_cache():
|
||||
file_cache = FileCache()
|
||||
file_cache.set("key", file)
|
||||
file_cache.set("key2", file)
|
||||
file_cache.delete_all()
|
||||
assert len(file_cache.cached_keys) == 0
|
||||
assert file_cache.get("key") is None
|
||||
assert file_cache.get("key2") is None
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
"""
|
||||
Ensures that confirmations work with validators on the Model and on the Modelform.
|
||||
|
||||
NOTE: These unit tests are not enough to ensure that confirmations work with validators.
|
||||
Please ensure to add integration tests too!
|
||||
"""
|
||||
|
||||
from admin_confirm.constants import CONFIRM_ADD
|
||||
from unittest import mock
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
@@ -80,27 +84,8 @@ class TestWithValidators(AdminConfirmTestCase):
|
||||
"_save": True,
|
||||
}
|
||||
response = self.client.post(reverse("admin:market_itemsale_add"), data)
|
||||
# Should not have been added yet
|
||||
self.assertEqual(ItemSale.objects.count(), 0)
|
||||
|
||||
# Ensure not redirected (confirmation page does not redirect)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected_templates = [
|
||||
"admin/market/itemsale/change_confirmation.html",
|
||||
"admin/market/change_confirmation.html",
|
||||
"admin/change_confirmation.html",
|
||||
]
|
||||
self.assertEqual(response.template_name, expected_templates)
|
||||
|
||||
self._assertSubmitHtml(rendered_content=response.rendered_content)
|
||||
|
||||
# Confirmation page would not have the _confirm_add sent on submit
|
||||
del data["_confirm_add"]
|
||||
# Selecting to "Yes, I'm sure" on the confirmation page
|
||||
# Would post to the same endpoint
|
||||
response = self.client.post(reverse("admin:market_itemsale_add"), data)
|
||||
|
||||
# Should not have redirected, since there was an error
|
||||
# Should show form with error and not confirmation page
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected_templates = [
|
||||
"admin/market/itemsale/change_form.html",
|
||||
@@ -109,8 +94,10 @@ class TestWithValidators(AdminConfirmTestCase):
|
||||
]
|
||||
self.assertEqual(response.template_name, expected_templates)
|
||||
self.assertEqual(ItemSale.objects.count(), 0)
|
||||
self.assertTrue("error" in str(response.content))
|
||||
self.assertTrue("Invalid Currency" in str(response.content))
|
||||
self.assertIn("error", str(response.rendered_content))
|
||||
self.assertIn("Invalid Currency", str(response.rendered_content))
|
||||
# Should still ask for confirmation
|
||||
self.assertIn(CONFIRM_ADD, response.rendered_content)
|
||||
|
||||
def test_can_confirm_for_models_with_clean_overridden(self):
|
||||
shop = ShopFactory()
|
||||
@@ -162,6 +149,7 @@ class TestWithValidators(AdminConfirmTestCase):
|
||||
item = ItemFactory()
|
||||
InventoryFactory(shop=shop, item=item, quantity=1)
|
||||
transaction = TransactionFactory(shop=shop)
|
||||
# Asking to buy more than the shop has in stock
|
||||
data = {
|
||||
"transaction": transaction.id,
|
||||
"item": item.id,
|
||||
@@ -176,6 +164,28 @@ class TestWithValidators(AdminConfirmTestCase):
|
||||
# Should not have been added yet
|
||||
self.assertEqual(ItemSale.objects.count(), 0)
|
||||
|
||||
# Ensure it shows the form and not the confirmation page
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected_templates = [
|
||||
"admin/market/itemsale/change_form.html",
|
||||
"admin/market/change_form.html",
|
||||
"admin/change_form.html",
|
||||
]
|
||||
self.assertEqual(response.template_name, expected_templates)
|
||||
self.assertTrue("error" in str(response.content))
|
||||
self.assertTrue(
|
||||
"Shop does not have enough of the item stocked" in str(response.content)
|
||||
)
|
||||
|
||||
# Should still be asking for confirmation
|
||||
self.assertIn(CONFIRM_ADD, response.rendered_content)
|
||||
|
||||
# Fix the issue by buying only what shop has in stock
|
||||
data["quantity"] = 1
|
||||
# _confirm_add would still be in the POST data
|
||||
response = self.client.post(reverse("admin:market_itemsale_add"), data)
|
||||
|
||||
# Should show confirmation page
|
||||
# Ensure not redirected (confirmation page does not redirect)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected_templates = [
|
||||
@@ -187,26 +197,6 @@ class TestWithValidators(AdminConfirmTestCase):
|
||||
|
||||
self._assertSubmitHtml(rendered_content=response.rendered_content)
|
||||
|
||||
# Confirmation page would not have the _confirm_add sent on submit
|
||||
del data["_confirm_add"]
|
||||
# Selecting to "Yes, I'm sure" on the confirmation page
|
||||
# Would post to the same endpoint
|
||||
response = self.client.post(reverse("admin:market_itemsale_add"), data)
|
||||
|
||||
# Should not have redirected, since there was an error
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected_templates = [
|
||||
"admin/market/itemsale/change_form.html",
|
||||
"admin/market/change_form.html",
|
||||
"admin/change_form.html",
|
||||
]
|
||||
self.assertEqual(response.template_name, expected_templates)
|
||||
self.assertEqual(ItemSale.objects.count(), 0)
|
||||
self.assertTrue("error" in str(response.content))
|
||||
self.assertTrue(
|
||||
"Shop does not have enough of the item stocked" in str(response.content)
|
||||
)
|
||||
|
||||
def test_can_confirm_for_modelform_with_clean_field_and_clean_overridden(self):
|
||||
shop = ShopFactory()
|
||||
data = {
|
||||
@@ -270,27 +260,19 @@ class TestWithValidators(AdminConfirmTestCase):
|
||||
# Should not have been added yet
|
||||
self.assertEqual(Checkout.objects.count(), 0)
|
||||
|
||||
# Ensure not redirected (confirmation page does not redirect)
|
||||
# Should show form with error and not confirmation page
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected_templates = [
|
||||
"admin/market/checkout/change_confirmation.html",
|
||||
"admin/market/change_confirmation.html",
|
||||
"admin/change_confirmation.html",
|
||||
"admin/market/checkout/change_form.html",
|
||||
"admin/market/change_form.html",
|
||||
"admin/change_form.html",
|
||||
]
|
||||
self.assertEqual(response.template_name, expected_templates)
|
||||
|
||||
self._assertSubmitHtml(rendered_content=response.rendered_content)
|
||||
|
||||
# Confirmation page would not have the _confirm_add sent on submit
|
||||
del data["_confirm_add"]
|
||||
# Selecting to "Yes, I'm sure" on the confirmation page
|
||||
# Would post to the same endpoint
|
||||
response = self.client.post(reverse("admin:market_checkout_add"), data)
|
||||
print(response.content)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Checkout.objects.count(), 0)
|
||||
self.assertIn("error", str(response.content))
|
||||
self.assertIn("Invalid Total 111", str(response.content))
|
||||
self.assertIn("error", str(response.rendered_content))
|
||||
self.assertIn("Invalid Total 111", str(response.rendered_content))
|
||||
# Should still ask for confirmation
|
||||
self.assertIn(CONFIRM_ADD, response.rendered_content)
|
||||
|
||||
def test_cannot_confirm_for_modelform_with_clean_overridden_if_validation_fails(
|
||||
self,
|
||||
@@ -311,24 +293,19 @@ class TestWithValidators(AdminConfirmTestCase):
|
||||
# Should not have been added yet
|
||||
self.assertEqual(Checkout.objects.count(), 0)
|
||||
|
||||
# Ensure not redirected (confirmation page does not redirect)
|
||||
# Should not have been added yet
|
||||
self.assertEqual(Checkout.objects.count(), 0)
|
||||
|
||||
# Should show form with error and not confirmation page
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected_templates = [
|
||||
"admin/market/checkout/change_confirmation.html",
|
||||
"admin/market/change_confirmation.html",
|
||||
"admin/change_confirmation.html",
|
||||
"admin/market/checkout/change_form.html",
|
||||
"admin/market/change_form.html",
|
||||
"admin/change_form.html",
|
||||
]
|
||||
self.assertEqual(response.template_name, expected_templates)
|
||||
|
||||
self._assertSubmitHtml(rendered_content=response.rendered_content)
|
||||
|
||||
# Confirmation page would not have the _confirm_add sent on submit
|
||||
del data["_confirm_add"]
|
||||
# Selecting to "Yes, I'm sure" on the confirmation page
|
||||
# Would post to the same endpoint
|
||||
response = self.client.post(reverse("admin:market_checkout_add"), data)
|
||||
print(response.content)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(Checkout.objects.count(), 0)
|
||||
self.assertIn("error", str(response.content))
|
||||
self.assertIn("Invalid Total 222", str(response.content))
|
||||
self.assertIn("error", str(response.rendered_content))
|
||||
self.assertIn("Invalid Total 222", str(response.rendered_content))
|
||||
# Should still ask for confirmation
|
||||
self.assertIn(CONFIRM_ADD, response.rendered_content)
|
||||
|
||||
Reference in New Issue
Block a user