fix(MR-16): Use cache for FileField and ImageField (#17)
* Remove casting to list in _get_form_data * Use cache for most fields and admin form for m2m files * MR comments/clean up * Cache should obey exclude and fields * Some more tests and docs * Only use cache for image files * Even more tests and handle save as new * fix test * More tests * minor refactor * Improve test coverage * Add no cover for some places * V0.2.3.dev7 * Adding tests for fieldsets * Added cache timeout * Added another test for an edge case * Fix issue with ManagementForm tampered with * Update cache to only set when form is_multipart * Even more testing changes * Update based on comments on MR and clean up a bit * make test names better Co-authored-by: Thu Trang Pham <thu@joinmodernhealth.com>
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
from unittest import mock
|
||||
from django.core.cache import cache
|
||||
|
||||
from admin_confirm.tests.helpers import AdminConfirmTestCase
|
||||
from tests.market.admin import ShoppingMallAdmin
|
||||
from tests.market.models import GeneralManager, ShoppingMall, Town
|
||||
from tests.factories import ShopFactory
|
||||
|
||||
from admin_confirm.constants import CACHE_KEYS, CONFIRMATION_RECEIVED
|
||||
|
||||
|
||||
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
|
||||
class TestAdminOptions(AdminConfirmTestCase):
|
||||
@mock.patch.object(ShoppingMallAdmin, "confirmation_fields", ["name"])
|
||||
@mock.patch.object(ShoppingMallAdmin, "fields", ["name", "town"])
|
||||
def test_change_model_with_m2m_field_without_input_for_m2m_field_should_work(self):
|
||||
gm = GeneralManager.objects.create(name="gm")
|
||||
shops = [ShopFactory() for i in range(3)]
|
||||
town = Town.objects.create(name="town")
|
||||
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
|
||||
mall.shops.set(shops)
|
||||
|
||||
# new values
|
||||
gm2 = GeneralManager.objects.create(name="gm2")
|
||||
shops2 = [ShopFactory() for i in range(3)]
|
||||
town2 = Town.objects.create(name="town2")
|
||||
|
||||
data = {
|
||||
"id": mall.id,
|
||||
"name": "name",
|
||||
"town": town2.id,
|
||||
"_confirm_change": True,
|
||||
"_continue": True,
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
|
||||
)
|
||||
|
||||
# Should be shown confirmation page
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content, save_action="_continue"
|
||||
)
|
||||
|
||||
# Should not have cached the unsaved obj
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNone(cached_item)
|
||||
|
||||
# Should not have saved changes yet
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
mall.refresh_from_db()
|
||||
self.assertEqual(mall.name, "mall")
|
||||
self.assertEqual(mall.general_manager, gm)
|
||||
self.assertEqual(mall.town, town)
|
||||
for shop in mall.shops.all():
|
||||
self.assertIn(shop, shops)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
confirmation_received_data = data
|
||||
del confirmation_received_data["_confirm_change"]
|
||||
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{mall.id}/change/",
|
||||
data=confirmation_received_data,
|
||||
)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, f"/admin/market/shoppingmall/{mall.id}/change/")
|
||||
|
||||
# Should have saved obj
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
saved_item = ShoppingMall.objects.all().first()
|
||||
# should have updated fields that were in form
|
||||
self.assertEqual(saved_item.name, data["name"])
|
||||
self.assertEqual(saved_item.town, town2)
|
||||
# should have presevered the fields that are not in form
|
||||
self.assertEqual(saved_item.general_manager, gm)
|
||||
for shop in saved_item.shops.all():
|
||||
self.assertIn(shop, shops)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
@mock.patch.object(ShoppingMallAdmin, "confirmation_fields", ["name"])
|
||||
@mock.patch.object(ShoppingMallAdmin, "exclude", ["shops"])
|
||||
def test_when_m2m_field_in_exclude_changes_to_field_should_not_be_saved(self):
|
||||
gm = GeneralManager.objects.create(name="gm")
|
||||
shops = [ShopFactory() for i in range(3)]
|
||||
town = Town.objects.create(name="town")
|
||||
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
|
||||
mall.shops.set(shops)
|
||||
|
||||
# new values
|
||||
gm2 = GeneralManager.objects.create(name="gm2")
|
||||
shops2 = [ShopFactory() for i in range(3)]
|
||||
town2 = Town.objects.create(name="town2")
|
||||
|
||||
data = {
|
||||
"id": mall.id,
|
||||
"name": "name",
|
||||
"general_manager": gm2.id,
|
||||
"shops": [1],
|
||||
"town": town2.id,
|
||||
"_confirm_change": True,
|
||||
"_continue": True,
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
|
||||
)
|
||||
# Should be shown confirmation page
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content, save_action="_continue"
|
||||
)
|
||||
|
||||
# Should not have cached the unsaved obj
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNone(cached_item)
|
||||
|
||||
# Should not have saved changes yet
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
mall.refresh_from_db()
|
||||
self.assertEqual(mall.name, "mall")
|
||||
self.assertEqual(mall.general_manager, gm)
|
||||
self.assertEqual(mall.town, town)
|
||||
for shop in mall.shops.all():
|
||||
self.assertIn(shop, shops)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
confirmation_received_data = data
|
||||
del confirmation_received_data["_confirm_change"]
|
||||
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{mall.id}/change/",
|
||||
data=confirmation_received_data,
|
||||
)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, f"/admin/market/shoppingmall/{mall.id}/change/")
|
||||
|
||||
# Should have saved obj
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
saved_item = ShoppingMall.objects.all().first()
|
||||
# should have updated fields that were in form
|
||||
self.assertEqual(saved_item.name, data["name"])
|
||||
self.assertEqual(saved_item.town, town2)
|
||||
self.assertEqual(saved_item.general_manager, gm2)
|
||||
# should have presevered the fields that are not in form (exclude)
|
||||
for shop in saved_item.shops.all():
|
||||
self.assertIn(shop, shops)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
@mock.patch.object(ShoppingMallAdmin, "confirmation_fields", ["name"])
|
||||
@mock.patch.object(ShoppingMallAdmin, "exclude", ["shops", "name"])
|
||||
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
|
||||
def test_if_confirmation_fields_in_exclude_should_not_trigger_confirmation(self):
|
||||
gm = GeneralManager.objects.create(name="gm")
|
||||
shops = [ShopFactory() for i in range(3)]
|
||||
town = Town.objects.create(name="town")
|
||||
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
|
||||
mall.shops.set(shops)
|
||||
|
||||
# new values
|
||||
gm2 = GeneralManager.objects.create(name="gm2")
|
||||
shops2 = [ShopFactory() for i in range(3)]
|
||||
town2 = Town.objects.create(name="town2")
|
||||
|
||||
data = {
|
||||
"id": mall.id,
|
||||
"name": "name",
|
||||
"general_manager": gm2.id,
|
||||
"shops": [1],
|
||||
"town": town2.id,
|
||||
"_confirm_change": True,
|
||||
"_continue": True,
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
|
||||
)
|
||||
# Should not be shown confirmation page
|
||||
# SInce we used "Save and Continue", should show change page
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, f"/admin/market/shoppingmall/{mall.id}/change/")
|
||||
|
||||
# Should have saved the non excluded fields
|
||||
mall.refresh_from_db()
|
||||
for shop in shops:
|
||||
self.assertIn(shop, mall.shops.all())
|
||||
self.assertEqual(mall.name, "mall")
|
||||
# Should have saved other fields
|
||||
self.assertEqual(mall.town, town2)
|
||||
self.assertEqual(mall.general_manager, gm2)
|
||||
|
||||
@mock.patch.object(ShoppingMallAdmin, "confirmation_fields", ["name"])
|
||||
@mock.patch.object(ShoppingMallAdmin, "readonly_fields", ["shops", "name"])
|
||||
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
|
||||
def test_if_confirmation_fields_in_readonly_should_not_trigger_confirmation(self):
|
||||
gm = GeneralManager.objects.create(name="gm")
|
||||
shops = [ShopFactory() for i in range(3)]
|
||||
town = Town.objects.create(name="town")
|
||||
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
|
||||
mall.shops.set(shops)
|
||||
|
||||
# new values
|
||||
gm2 = GeneralManager.objects.create(name="gm2")
|
||||
shops2 = [ShopFactory() for i in range(3)]
|
||||
town2 = Town.objects.create(name="town2")
|
||||
|
||||
data = {
|
||||
"id": mall.id,
|
||||
"name": "name",
|
||||
"general_manager": gm2.id,
|
||||
"shops": [1],
|
||||
"town": town2.id,
|
||||
"_confirm_change": True,
|
||||
"_continue": True,
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
|
||||
)
|
||||
# Should not be shown confirmation page
|
||||
# SInce we used "Save and Continue", should show change page
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, f"/admin/market/shoppingmall/{mall.id}/change/")
|
||||
|
||||
# Should have saved the non excluded fields
|
||||
mall.refresh_from_db()
|
||||
for shop in shops:
|
||||
self.assertIn(shop, mall.shops.all())
|
||||
self.assertEqual(mall.name, "mall")
|
||||
# Should have saved other fields
|
||||
self.assertEqual(mall.town, town2)
|
||||
self.assertEqual(mall.general_manager, gm2)
|
||||
|
||||
@mock.patch.object(ShoppingMallAdmin, "confirmation_fields", ["name"])
|
||||
@mock.patch.object(ShoppingMallAdmin, "readonly_fields", ["shops"])
|
||||
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
|
||||
def test_readonly_fields_should_not_change(self):
|
||||
gm = GeneralManager.objects.create(name="gm")
|
||||
shops = [ShopFactory() for i in range(3)]
|
||||
town = Town.objects.create(name="town")
|
||||
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
|
||||
mall.shops.set(shops)
|
||||
|
||||
# new values
|
||||
gm2 = GeneralManager.objects.create(name="gm2")
|
||||
shops2 = [ShopFactory() for i in range(3)]
|
||||
town2 = Town.objects.create(name="town2")
|
||||
|
||||
data = {
|
||||
"id": mall.id,
|
||||
"name": "name",
|
||||
"general_manager": gm2.id,
|
||||
"shops": [1],
|
||||
"town": town2.id,
|
||||
"_confirm_change": True,
|
||||
"_continue": True,
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
|
||||
)
|
||||
# Should be shown confirmation page
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content, save_action="_continue"
|
||||
)
|
||||
|
||||
# Should not have cached the unsaved obj
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNone(cached_item)
|
||||
|
||||
# Should not have saved changes yet
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
mall.refresh_from_db()
|
||||
self.assertEqual(mall.name, "mall")
|
||||
self.assertEqual(mall.general_manager, gm)
|
||||
self.assertEqual(mall.town, town)
|
||||
for shop in mall.shops.all():
|
||||
self.assertIn(shop, shops)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
confirmation_received_data = data
|
||||
del confirmation_received_data["_confirm_change"]
|
||||
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{mall.id}/change/",
|
||||
data=confirmation_received_data,
|
||||
)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, f"/admin/market/shoppingmall/{mall.id}/change/")
|
||||
|
||||
# Should have saved obj
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
saved_item = ShoppingMall.objects.all().first()
|
||||
# should have updated fields that were in form
|
||||
self.assertEqual(saved_item.name, data["name"])
|
||||
self.assertEqual(saved_item.town, town2)
|
||||
self.assertEqual(saved_item.general_manager, gm2)
|
||||
# should have presevered the fields that are not in form (exclude)
|
||||
for shop in saved_item.shops.all():
|
||||
self.assertIn(shop, shops)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
@@ -0,0 +1,280 @@
|
||||
from django.test import TestCase, RequestFactory
|
||||
from django.contrib.admin.sites import AdminSite
|
||||
from django.contrib.auth.models import Permission, User
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
from tests.market.admin import ShopAdmin
|
||||
from tests.market.models import Shop
|
||||
|
||||
|
||||
class TestConfirmActions(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.superuser = User.objects.create_superuser(
|
||||
username="super", email="super@email.org", password="pass"
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
self.client.force_login(self.superuser)
|
||||
self.factory = RequestFactory()
|
||||
|
||||
def test_get_changelist_should_not_be_affected(self):
|
||||
response = self.client.get(reverse("admin:market_shop_changelist"))
|
||||
self.assertIsNotNone(response)
|
||||
self.assertNotIn("Confirm Action", response.rendered_content)
|
||||
|
||||
def test_action_without_confirmation(self):
|
||||
post_params = {
|
||||
"action": ["show_message_no_confirmation"],
|
||||
"select_across": ["0"],
|
||||
"index": ["0"],
|
||||
"_selected_action": ["3", "2", "1"],
|
||||
}
|
||||
response = self.client.post(
|
||||
reverse("admin:market_shop_changelist"),
|
||||
data=post_params,
|
||||
follow=True, # Follow the redirect to get content
|
||||
)
|
||||
self.assertIsNotNone(response)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
# Should not use confirmaiton page
|
||||
self.assertNotIn("action_confirmation", response.template_name)
|
||||
|
||||
# The action was to show user a message
|
||||
self.assertIn("You selected without confirmation", response.rendered_content)
|
||||
|
||||
def test_action_with_confirmation_should_show_confirmation_page(self):
|
||||
post_params = {
|
||||
"action": ["show_message"],
|
||||
"select_across": ["0"],
|
||||
"index": ["0"],
|
||||
"_selected_action": ["3", "2", "1"],
|
||||
}
|
||||
response = self.client.post(
|
||||
reverse("admin:market_shop_changelist"),
|
||||
data=post_params,
|
||||
follow=True, # Follow the redirect to get content
|
||||
)
|
||||
self.assertIsNotNone(response)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
# Should use confirmaiton page
|
||||
self.assertEqual(
|
||||
response.template_name,
|
||||
[
|
||||
"admin/market/shop/action_confirmation.html",
|
||||
"admin/market/action_confirmation.html",
|
||||
"admin/action_confirmation.html",
|
||||
],
|
||||
)
|
||||
|
||||
# The action was to show user a message, and should not happen yet
|
||||
self.assertNotIn("You selected", response.rendered_content)
|
||||
|
||||
def test_no_permissions_in_database_for_action_with_confirmation(self):
|
||||
"""
|
||||
Django would not show the action in changelist action selector
|
||||
If the user doesn't have permissions, but this doesn't prevent
|
||||
user from calling post with the params to perform the action.
|
||||
|
||||
If the permissions are denied because of Permission in the database,
|
||||
Django would redirect to the changelist.
|
||||
"""
|
||||
# Create a user without permissions for action
|
||||
user = User.objects.create_user(
|
||||
username="user",
|
||||
email="user@email.org",
|
||||
password="pass",
|
||||
is_active=True,
|
||||
is_staff=True,
|
||||
is_superuser=False,
|
||||
)
|
||||
# Give user permissions to ShopAdmin change, add, view but not delete
|
||||
for permission in Permission.objects.filter(
|
||||
codename__in=["change_shop", "view_shop", "add_shop"]
|
||||
):
|
||||
user.user_permissions.add(permission)
|
||||
|
||||
self.client.force_login(user)
|
||||
|
||||
post_params = {
|
||||
"action": ["show_message"],
|
||||
"select_across": ["0"],
|
||||
"index": ["0"],
|
||||
"_selected_action": ["3", "2", "1"],
|
||||
}
|
||||
response = self.client.post(
|
||||
reverse("admin:market_shop_changelist"),
|
||||
data=post_params,
|
||||
follow=True, # Follow the redirect to get content
|
||||
)
|
||||
self.assertIsNotNone(response)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
# Should not use confirmaiton page
|
||||
self.assertEqual(
|
||||
response.template_name,
|
||||
[
|
||||
"admin/market/shop/change_list.html",
|
||||
"admin/market/change_list.html",
|
||||
"admin/change_list.html",
|
||||
],
|
||||
)
|
||||
|
||||
# The action was to show user a message, and should not happen
|
||||
self.assertNotIn("You selected", response.rendered_content)
|
||||
|
||||
# Django won't show the action as an option to you
|
||||
self.assertIn("No action selected", response.rendered_content)
|
||||
|
||||
def test_no_permissions_in_code_non_superuser_for_action_with_confirmation(self):
|
||||
"""
|
||||
Django would not show the action in changelist action selector
|
||||
If the user doesn't have permissions, but this doesn't prevent
|
||||
user from calling post with the params to perform the action.
|
||||
|
||||
If the permissions are denied because of Permission in the database,
|
||||
Django would redirect to the changelist.
|
||||
|
||||
It should also respect the has_xxx_permission methods
|
||||
"""
|
||||
# Create a user without permissions for action
|
||||
user = User.objects.create_user(
|
||||
username="user",
|
||||
email="user@email.org",
|
||||
password="pass",
|
||||
is_active=True,
|
||||
is_staff=True,
|
||||
is_superuser=False,
|
||||
)
|
||||
# Give user permissions to ShopAdmin change, add, view and delete
|
||||
for permission in Permission.objects.filter(
|
||||
codename__in=["change_shop", "view_shop", "add_shop", "delete_shop"]
|
||||
):
|
||||
user.user_permissions.add(permission)
|
||||
|
||||
self.client.force_login(user)
|
||||
|
||||
# ShopAdmin has defined:
|
||||
# def has_delete_permission(self, request, obj=None):
|
||||
# return request.user.is_superuser
|
||||
|
||||
post_params = {
|
||||
"action": ["show_message"],
|
||||
"select_across": ["0"],
|
||||
"index": ["0"],
|
||||
"_selected_action": ["3", "2", "1"],
|
||||
}
|
||||
response = self.client.post(
|
||||
reverse("admin:market_shop_changelist"),
|
||||
data=post_params,
|
||||
follow=True, # Follow the redirect to get content
|
||||
)
|
||||
self.assertIsNotNone(response)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
# Should not use confirmaiton page
|
||||
self.assertEqual(
|
||||
response.template_name,
|
||||
[
|
||||
"admin/market/shop/change_list.html",
|
||||
"admin/market/change_list.html",
|
||||
"admin/change_list.html",
|
||||
],
|
||||
)
|
||||
|
||||
# The action was to show user a message, and should not happen yet
|
||||
self.assertNotIn("You selected", response.rendered_content)
|
||||
|
||||
# Django won't show the action as an option to you
|
||||
self.assertIn("No action selected", response.rendered_content)
|
||||
|
||||
def test_no_permissions_in_code_superuser_for_action_with_confirmation(self):
|
||||
"""
|
||||
Django would not show the action in changelist action selector
|
||||
If the user doesn't have permissions, but this doesn't prevent
|
||||
user from calling post with the params to perform the action.
|
||||
|
||||
When permissions are denied from a change in code
|
||||
(ie has_xxx_permission in ModelAdmin), Django should still
|
||||
redirect to changelist. This should be true even if the user is
|
||||
a superuser.
|
||||
"""
|
||||
# ShopAdmin has defined:
|
||||
# def has_delete_permission(self, request, obj=None):
|
||||
# return request.user.is_superuser
|
||||
|
||||
ShopAdmin.has_delete_permission = lambda self, request, obj=None: False
|
||||
post_params = {
|
||||
"action": ["show_message"],
|
||||
"select_across": ["0"],
|
||||
"index": ["0"],
|
||||
"_selected_action": ["3", "2", "1"],
|
||||
}
|
||||
response = self.client.post(
|
||||
reverse("admin:market_shop_changelist"),
|
||||
data=post_params,
|
||||
follow=True, # Follow the redirect to get content
|
||||
)
|
||||
self.assertIsNotNone(response)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
# Should not use confirmaiton page
|
||||
self.assertEqual(
|
||||
response.template_name,
|
||||
[
|
||||
"admin/market/shop/change_list.html",
|
||||
"admin/market/change_list.html",
|
||||
"admin/change_list.html",
|
||||
],
|
||||
)
|
||||
|
||||
# The action was to show user a message, and should not happen yet
|
||||
self.assertNotIn("You selected", response.rendered_content)
|
||||
|
||||
# Django won't show the action as an option to you
|
||||
self.assertIn("No action selected", response.rendered_content)
|
||||
|
||||
# Remove our modification for ShopAdmin
|
||||
ShopAdmin.has_delete_permission = (
|
||||
lambda self, request, obj=None: request.user.is_superuser
|
||||
)
|
||||
|
||||
def test_confirm_action_submit_button_should_perform_action(self):
|
||||
"""
|
||||
The submit button should have param "_confirm_action"
|
||||
|
||||
Simulate calling the post request that the button would
|
||||
"""
|
||||
post_params = {
|
||||
"_confirm_action": ["Yes, I'm sure"],
|
||||
"action": ["show_message"],
|
||||
"_selected_action": ["3", "2", "1"],
|
||||
}
|
||||
response = self.client.post(
|
||||
reverse("admin:market_shop_changelist"),
|
||||
data=post_params,
|
||||
follow=True, # Follow the redirect to get content
|
||||
)
|
||||
self.assertIsNotNone(response)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
# Should not use confirmaiton page, since we clicked Yes, I'm sure
|
||||
self.assertEqual(
|
||||
response.template_name,
|
||||
[
|
||||
"admin/market/shop/change_list.html",
|
||||
"admin/market/change_list.html",
|
||||
"admin/change_list.html",
|
||||
],
|
||||
)
|
||||
|
||||
# The action was to show user a message, and should happen
|
||||
self.assertIn("You selected", response.rendered_content)
|
||||
|
||||
def test_should_use_action_confirmation_template_if_set(self):
|
||||
expected_template = "market/admin/my_custom_template.html"
|
||||
ShopAdmin.action_confirmation_template = expected_template
|
||||
admin = ShopAdmin(Shop, AdminSite())
|
||||
actual_template = admin.render_action_confirmation(
|
||||
self.factory.request(), context={}
|
||||
).template_name
|
||||
self.assertEqual(expected_template, actual_template)
|
||||
# Clear our setting to not affect other tests
|
||||
ShopAdmin.action_confirmation_template = None
|
||||
@@ -0,0 +1,346 @@
|
||||
from unittest import mock
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.admin.sites import AdminSite
|
||||
from django.contrib.admin.options import TO_FIELD_VAR
|
||||
from django.http import HttpResponseForbidden, HttpResponseBadRequest
|
||||
from django.urls import reverse
|
||||
|
||||
from admin_confirm.tests.helpers import AdminConfirmTestCase
|
||||
from tests.market.admin import ItemAdmin, InventoryAdmin, ShoppingMallAdmin
|
||||
from tests.market.models import Item, Inventory, ShoppingMall
|
||||
from tests.factories import ItemFactory, ShopFactory, InventoryFactory
|
||||
|
||||
|
||||
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
|
||||
class TestConfirmChangeAndAdd(AdminConfirmTestCase):
|
||||
def test_get_add_without_confirm_add(self):
|
||||
ItemAdmin.confirm_add = False
|
||||
response = self.client.get(reverse("admin:market_item_add"))
|
||||
self.assertFalse(response.context_data.get("confirm_add"))
|
||||
self.assertNotIn("_confirm_add", response.rendered_content)
|
||||
|
||||
def test_get_add_with_confirm_add(self):
|
||||
response = self.client.get(reverse("admin:market_inventory_add"))
|
||||
self.assertTrue(response.context_data.get("confirm_add"))
|
||||
self.assertIn("_confirm_add", response.rendered_content)
|
||||
|
||||
def test_get_change_without_confirm_change(self):
|
||||
response = self.client.get(reverse("admin:market_shop_add"))
|
||||
self.assertFalse(response.context_data.get("confirm_change"))
|
||||
self.assertNotIn("_confirm_change", response.rendered_content)
|
||||
|
||||
def test_get_change_with_confirm_change(self):
|
||||
response = self.client.get(reverse("admin:market_inventory_add"))
|
||||
self.assertTrue(response.context_data.get("confirm_change"))
|
||||
self.assertIn("_confirm_change", response.rendered_content)
|
||||
|
||||
def test_post_add_without_confirm_add(self):
|
||||
data = {"name": "name", "price": 2.0, "currency": Item.VALID_CURRENCIES[0]}
|
||||
response = self.client.post(reverse("admin:market_item_add"), data)
|
||||
# Redirects to item changelist and item is added
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
|
||||
def test_post_add_with_confirm_add(self):
|
||||
item = ItemFactory()
|
||||
shop = ShopFactory()
|
||||
data = {
|
||||
"shop": shop.id,
|
||||
"item": item.id,
|
||||
"quantity": 5,
|
||||
"_confirm_add": True,
|
||||
"_continue": True,
|
||||
}
|
||||
response = self.client.post(reverse("admin:market_inventory_add"), data)
|
||||
# Ensure not redirected (confirmation page does not redirect)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected_templates = [
|
||||
"admin/market/inventory/change_confirmation.html",
|
||||
"admin/market/change_confirmation.html",
|
||||
"admin/change_confirmation.html",
|
||||
]
|
||||
self.assertEqual(response.template_name, expected_templates)
|
||||
|
||||
form_data = {"shop": str(shop.id), "item": str(item.id), "quantity": str(5)}
|
||||
self._assertSimpleFieldFormHtml(
|
||||
rendered_content=response.rendered_content, fields=form_data
|
||||
)
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content, save_action="_continue"
|
||||
)
|
||||
|
||||
# Should not have been added yet
|
||||
self.assertEqual(Inventory.objects.count(), 0)
|
||||
|
||||
def test_post_change_with_confirm_change_shoppingmall_name(self):
|
||||
# When testing found that even though name was in confirmation_fields
|
||||
# When only name changed, `form.is_valid` = False, and thus didn't trigger
|
||||
# confirmation page previously, even though it should have
|
||||
|
||||
mall = ShoppingMall.objects.create(name="name")
|
||||
data = {
|
||||
"id": mall.id,
|
||||
"name": "new name",
|
||||
"_confirm_change": True,
|
||||
"csrfmiddlewaretoken": "fake token",
|
||||
"_save": True,
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{mall.id}/change/", data
|
||||
)
|
||||
# Ensure not redirected (confirmation page does not redirect)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected_templates = [
|
||||
"admin/market/shoppingmall/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)
|
||||
|
||||
# Hasn't changed item yet
|
||||
mall.refresh_from_db()
|
||||
self.assertEqual(mall.name, "name")
|
||||
|
||||
def test_post_change_with_confirm_change(self):
|
||||
item = ItemFactory(name="item")
|
||||
data = {
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"currency": Item.VALID_CURRENCIES[0],
|
||||
"id": item.id,
|
||||
"_confirm_change": True,
|
||||
"csrfmiddlewaretoken": "fake token",
|
||||
"_save": True,
|
||||
}
|
||||
response = self.client.post(f"/admin/market/item/{item.id}/change/", data)
|
||||
# Ensure not redirected (confirmation page does not redirect)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected_templates = [
|
||||
"admin/market/item/change_confirmation.html",
|
||||
"admin/market/change_confirmation.html",
|
||||
"admin/change_confirmation.html",
|
||||
]
|
||||
self.assertEqual(response.template_name, expected_templates)
|
||||
form_data = {
|
||||
"name": "name",
|
||||
"price": str(2.0),
|
||||
# "id": str(item.id),
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
}
|
||||
|
||||
self._assertSimpleFieldFormHtml(
|
||||
rendered_content=response.rendered_content, fields=form_data
|
||||
)
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content, multipart_form=True
|
||||
)
|
||||
|
||||
# Hasn't changed item yet
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "item")
|
||||
|
||||
def test_post_change_without_confirm_change(self):
|
||||
shop = ShopFactory(name="bob")
|
||||
data = {"name": "sally"}
|
||||
response = self.client.post(f"/admin/market/shop/{shop.id}/change/", data)
|
||||
# Redirects to changelist
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/market/shop/")
|
||||
# Shop has changed
|
||||
shop.refresh_from_db()
|
||||
self.assertEqual(shop.name, "sally")
|
||||
|
||||
def test_get_confirmation_fields_should_default_if_not_set(self):
|
||||
expected_fields = [f.name for f in Item._meta.fields if f.name != "id"]
|
||||
ItemAdmin.confirmation_fields = None
|
||||
ItemAdmin.fields = expected_fields
|
||||
admin = ItemAdmin(Item, AdminSite())
|
||||
actual_fields = admin.get_confirmation_fields(self.factory.request())
|
||||
for field in expected_fields:
|
||||
self.assertIn(field, actual_fields)
|
||||
|
||||
def test_get_confirmation_fields_default_should_only_include_fields_shown_on_admin(
|
||||
self,
|
||||
):
|
||||
admin_fields = ["name", "price"]
|
||||
ItemAdmin.confirmation_fields = None
|
||||
ItemAdmin.fields = admin_fields
|
||||
admin = ItemAdmin(Item, AdminSite())
|
||||
actual_fields = admin.get_confirmation_fields(self.factory.request())
|
||||
for field in admin_fields:
|
||||
self.assertIn(field, actual_fields)
|
||||
|
||||
def test_get_confirmation_fields_if_set(self):
|
||||
expected_fields = ["name", "currency"]
|
||||
ItemAdmin.confirmation_fields = expected_fields
|
||||
admin = ItemAdmin(Item, AdminSite())
|
||||
actual_fields = admin.get_confirmation_fields(self.factory.request())
|
||||
self.assertEqual(expected_fields, actual_fields)
|
||||
|
||||
def test_custom_template(self):
|
||||
expected_template = "market/admin/my_custom_template.html"
|
||||
ItemAdmin.change_confirmation_template = expected_template
|
||||
admin = ItemAdmin(Item, AdminSite())
|
||||
actual_template = admin.render_change_confirmation(
|
||||
self.factory.request(), context={}
|
||||
).template_name
|
||||
self.assertEqual(expected_template, actual_template)
|
||||
# Clear our setting to not affect other tests
|
||||
ItemAdmin.change_confirmation_template = None
|
||||
|
||||
def test_form_invalid(self):
|
||||
self.assertEqual(InventoryAdmin.confirmation_fields, ["quantity"])
|
||||
|
||||
inventory = InventoryFactory(quantity=1)
|
||||
data = {
|
||||
"quantity": 1,
|
||||
"shop": "Invalid value",
|
||||
"item": "Invalid value",
|
||||
"id": inventory.id,
|
||||
"_confirm_change": True,
|
||||
"csrfmiddlewaretoken": "fake token",
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/inventory/{inventory.id}/change/", data
|
||||
)
|
||||
|
||||
# Form invalid should show errors on form
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIsNotNone(response.context_data.get("errors"))
|
||||
self.assertEqual(
|
||||
response.context_data["errors"][0],
|
||||
["Select a valid choice. That choice is not one of the available choices."],
|
||||
)
|
||||
# Should not have updated inventory
|
||||
inventory.refresh_from_db()
|
||||
self.assertEqual(inventory.quantity, 1)
|
||||
|
||||
def test_confirmation_fields_set_with_confirm_change(self):
|
||||
self.assertEqual(InventoryAdmin.confirmation_fields, ["quantity"])
|
||||
|
||||
inventory = InventoryFactory()
|
||||
another_shop = ShopFactory()
|
||||
data = {
|
||||
"quantity": inventory.quantity,
|
||||
"id": inventory.id,
|
||||
"item": inventory.item.id,
|
||||
"shop": another_shop.id,
|
||||
"_confirm_change": True,
|
||||
"csrfmiddlewaretoken": "fake token",
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/inventory/{inventory.id}/change/", data
|
||||
)
|
||||
|
||||
# Should not have shown confirmation page since shop did not change
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, reverse("admin:market_inventory_changelist"))
|
||||
# Should have updated inventory
|
||||
inventory.refresh_from_db()
|
||||
self.assertEqual(inventory.shop, another_shop)
|
||||
|
||||
def test_confirmation_fields_set_with_confirm_add(self):
|
||||
self.assertEqual(InventoryAdmin.confirmation_fields, ["quantity"])
|
||||
|
||||
item = ItemFactory()
|
||||
shop = ShopFactory()
|
||||
|
||||
# Don't set quantity - let it default
|
||||
data = {"shop": shop.id, "item": item.id, "_confirm_add": True}
|
||||
response = self.client.post(reverse("admin:market_inventory_add"), data)
|
||||
# No confirmation needed
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
# Should have been added
|
||||
self.assertEqual(Inventory.objects.count(), 1)
|
||||
new_inventory = Inventory.objects.all().first()
|
||||
self.assertEqual(new_inventory.shop, shop)
|
||||
self.assertEqual(new_inventory.item, item)
|
||||
self.assertEqual(
|
||||
new_inventory.quantity, Inventory._meta.get_field("quantity").default
|
||||
)
|
||||
|
||||
def test_no_change_permissions(self):
|
||||
user = User.objects.create_user(username="user", is_staff=True)
|
||||
self.client.force_login(user)
|
||||
|
||||
inventory = InventoryFactory()
|
||||
data = {
|
||||
"quantity": 1000,
|
||||
"id": inventory.id,
|
||||
"item": inventory.item.id,
|
||||
"shop": inventory.shop.id,
|
||||
"_confirm_change": True,
|
||||
"csrfmiddlewaretoken": "fake token",
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/inventory/{inventory.id}/change/", data
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 403)
|
||||
self.assertTrue(isinstance(response, HttpResponseForbidden))
|
||||
|
||||
old_quantity = inventory.quantity
|
||||
inventory.refresh_from_db()
|
||||
self.assertEqual(inventory.quantity, old_quantity)
|
||||
|
||||
def test_no_add_permissions(self):
|
||||
user = User.objects.create_user(username="user", is_staff=True)
|
||||
self.client.force_login(user)
|
||||
item = ItemFactory()
|
||||
shop = ShopFactory()
|
||||
data = {"shop": shop.id, "item": item.id, "quantity": 5, "_confirm_add": True}
|
||||
response = self.client.post(reverse("admin:market_inventory_add"), data)
|
||||
# Ensure not redirected (confirmation page does not redirect)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
self.assertTrue(isinstance(response, HttpResponseForbidden))
|
||||
|
||||
# Should not have been added
|
||||
self.assertEqual(Inventory.objects.count(), 0)
|
||||
|
||||
def test_obj_not_found(self):
|
||||
inventory = InventoryFactory()
|
||||
data = {
|
||||
"quantity": 1000,
|
||||
"id": 100,
|
||||
"item": inventory.item.id,
|
||||
"shop": inventory.shop.id,
|
||||
"_confirm_change": True,
|
||||
"csrfmiddlewaretoken": "fake token",
|
||||
}
|
||||
response = self.client.post("/admin/market/inventory/100/change/", data)
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/")
|
||||
self.assertEqual(response.reason_phrase, "Found")
|
||||
|
||||
old_quantity = inventory.quantity
|
||||
inventory.refresh_from_db()
|
||||
self.assertEqual(inventory.quantity, old_quantity)
|
||||
|
||||
self.assertEqual(Inventory.objects.count(), 1)
|
||||
|
||||
def test_handles_to_field_not_allowed(self):
|
||||
item = ItemFactory()
|
||||
shop = ShopFactory()
|
||||
data = {
|
||||
"shop": shop.id,
|
||||
"item": item.id,
|
||||
"quantity": 5,
|
||||
"_confirm_add": True,
|
||||
TO_FIELD_VAR: "shop",
|
||||
}
|
||||
response = self.client.post(reverse("admin:market_inventory_add"), data)
|
||||
# Ensure not redirected (confirmation page does not redirect)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertTrue(isinstance(response, HttpResponseBadRequest))
|
||||
self.assertEqual(response.reason_phrase, "Bad Request")
|
||||
self.assertEqual(
|
||||
response.context.get("exception_value"),
|
||||
"The field shop cannot be referenced.",
|
||||
)
|
||||
|
||||
# Should not have been added
|
||||
self.assertEqual(Inventory.objects.count(), 0)
|
||||
@@ -0,0 +1,195 @@
|
||||
from unittest import mock
|
||||
from admin_confirm.admin import AdminConfirmMixin
|
||||
from django.urls import reverse
|
||||
|
||||
from admin_confirm.tests.helpers import AdminConfirmTestCase
|
||||
from tests.market.admin import ShoppingMallAdmin
|
||||
from tests.market.models import ShoppingMall
|
||||
from tests.factories import ShopFactory
|
||||
|
||||
|
||||
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
|
||||
class TestConfirmChangeAndAddM2MField(AdminConfirmTestCase):
|
||||
def test_post_add_without_confirm_add_m2m(self):
|
||||
shops = [ShopFactory() for i in range(3)]
|
||||
|
||||
data = {"name": "name", "shops": [s.id for s in shops]}
|
||||
response = self.client.post(reverse("admin:market_shoppingmall_add"), data)
|
||||
# Redirects to changelist and is added
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/market/shoppingmall/")
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
self.assertEqual(ShoppingMall.objects.all().first().shops.count(), 3)
|
||||
|
||||
def test_post_add_with_confirm_add_m2m(self):
|
||||
ShoppingMallAdmin.confirmation_fields = ["shops"]
|
||||
shops = [ShopFactory() for i in range(3)]
|
||||
|
||||
data = {
|
||||
"name": "name",
|
||||
"shops": [s.id for s in shops],
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
response = self.client.post(reverse("admin:market_shoppingmall_add"), data)
|
||||
|
||||
# Ensure not redirected (confirmation page does not redirect)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected_templates = [
|
||||
"admin/market/shoppingmall/change_confirmation.html",
|
||||
"admin/market/change_confirmation.html",
|
||||
"admin/change_confirmation.html",
|
||||
]
|
||||
self.assertEqual(response.template_name, expected_templates)
|
||||
|
||||
self._assertManyToManyFormHtml(
|
||||
rendered_content=response.rendered_content,
|
||||
options=shops,
|
||||
selected_ids=data["shops"],
|
||||
)
|
||||
self._assertSubmitHtml(rendered_content=response.rendered_content)
|
||||
|
||||
# Should not have been added yet
|
||||
self.assertEqual(ShoppingMall.objects.count(), 0)
|
||||
|
||||
# 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_shoppingmall_add"), data)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/market/shoppingmall/")
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
self.assertEqual(ShoppingMall.objects.all().first().shops.count(), 3)
|
||||
|
||||
def test_m2m_field_post_change_with_confirm_change(self):
|
||||
shops = [ShopFactory() for i in range(10)]
|
||||
shopping_mall = ShoppingMall.objects.create(name="My Mall")
|
||||
shopping_mall.shops.set(shops)
|
||||
# Currently ShoppingMall configured with confirmation_fields = ['name']
|
||||
data = {
|
||||
"name": "Not My Mall",
|
||||
"shops": [1, 2],
|
||||
"id": shopping_mall.id,
|
||||
"_confirm_change": True,
|
||||
"csrfmiddlewaretoken": "fake token",
|
||||
"_continue": True,
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{shopping_mall.id}/change/", data
|
||||
)
|
||||
# Ensure not redirected (confirmation page does not redirect)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected_templates = [
|
||||
"admin/market/shoppingmall/change_confirmation.html",
|
||||
"admin/market/change_confirmation.html",
|
||||
"admin/change_confirmation.html",
|
||||
]
|
||||
self.assertEqual(response.template_name, expected_templates)
|
||||
|
||||
# Should show two lists for the m2m current and modified values
|
||||
self.assertEqual(response.rendered_content.count("<ul>"), 2)
|
||||
|
||||
self._assertManyToManyFormHtml(
|
||||
rendered_content=response.rendered_content,
|
||||
options=shops,
|
||||
selected_ids=data["shops"],
|
||||
)
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content, save_action="_continue"
|
||||
)
|
||||
|
||||
# Hasn't changed item yet
|
||||
shopping_mall.refresh_from_db()
|
||||
self.assertEqual(shopping_mall.name, "My Mall")
|
||||
|
||||
# Selecting to "Yes, I'm sure" on the confirmation page
|
||||
# Would post to the same endpoint
|
||||
del data["_confirm_change"]
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{shopping_mall.id}/change/", data
|
||||
)
|
||||
# will show the change page for this shopping_mall
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(
|
||||
response.url, f"/admin/market/shoppingmall/{shopping_mall.id}/change/"
|
||||
)
|
||||
# Should not be the confirmation page, we already confirmed change
|
||||
self.assertNotEqual(response.templates, expected_templates)
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
self.assertEqual(ShoppingMall.objects.all().first().shops.count(), 2)
|
||||
|
||||
def test_m2m_field_post_change_with_confirm_change_multiple_selected(self):
|
||||
shops = [ShopFactory() for i in range(10)]
|
||||
shopping_mall = ShoppingMall.objects.create(name="My Mall")
|
||||
shopping_mall.shops.set(shops)
|
||||
# Currently ShoppingMall configured with confirmation_fields = ['name']
|
||||
data = {
|
||||
"name": "Not My Mall",
|
||||
"shops": [1, 2, 3],
|
||||
"id": shopping_mall.id,
|
||||
"_confirm_change": True,
|
||||
"csrfmiddlewaretoken": "fake token",
|
||||
"_save": True,
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{shopping_mall.id}/change/", data
|
||||
)
|
||||
# Ensure not redirected (confirmation page does not redirect)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected_templates = [
|
||||
"admin/market/shoppingmall/change_confirmation.html",
|
||||
"admin/market/change_confirmation.html",
|
||||
"admin/change_confirmation.html",
|
||||
]
|
||||
self.assertEqual(response.template_name, expected_templates)
|
||||
|
||||
# Hasn't changed item yet
|
||||
shopping_mall.refresh_from_db()
|
||||
self.assertEqual(shopping_mall.name, "My Mall")
|
||||
|
||||
def test_post_change_without_confirm_change_m2m_value(self):
|
||||
# make the m2m the confirmation_field
|
||||
ShoppingMallAdmin.confirmation_fields = ["shops"]
|
||||
shops = [ShopFactory() for i in range(3)]
|
||||
shopping_mall = ShoppingMall.objects.create(name="name")
|
||||
shopping_mall.shops.set(shops)
|
||||
assert shopping_mall.shops.count() == 3
|
||||
|
||||
data = {"name": "name", "id": str(shopping_mall.id), "shops": ["1"]}
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{shopping_mall.id}/change/", data
|
||||
)
|
||||
# Redirects to changelist
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/market/shoppingmall/")
|
||||
# Shop has changed
|
||||
shopping_mall.refresh_from_db()
|
||||
self.assertEqual(shopping_mall.shops.count(), 1)
|
||||
|
||||
def test_form_invalid_m2m_value(self):
|
||||
ShoppingMallAdmin.confirmation_fields = ["shops"]
|
||||
shopping_mall = ShoppingMall.objects.create(name="name")
|
||||
|
||||
data = {
|
||||
"id": shopping_mall.id,
|
||||
"name": "name",
|
||||
"shops": [1, 2, 3], # These shops don't exist
|
||||
"_confirm_change": True,
|
||||
"csrfmiddlewaretoken": "fake token",
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{shopping_mall.id}/change/", data
|
||||
)
|
||||
|
||||
# Form invalid should show errors on form
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIsNotNone(response.context_data.get("errors"))
|
||||
self.assertTrue(
|
||||
str(response.context_data["errors"][0][0]).startswith(
|
||||
"Select a valid choice."
|
||||
)
|
||||
)
|
||||
# Should not have updated inventory
|
||||
shopping_mall.refresh_from_db()
|
||||
self.assertEqual(shopping_mall.shops.count(), 0)
|
||||
@@ -0,0 +1,477 @@
|
||||
from unittest import mock
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.core.cache import cache
|
||||
from django.urls import reverse
|
||||
|
||||
from admin_confirm.tests.helpers import AdminConfirmTestCase
|
||||
from tests.market.admin import ItemAdmin, ShoppingMallAdmin
|
||||
from tests.market.models import GeneralManager, Item, ShoppingMall, Town
|
||||
from tests.factories import ItemFactory, ShopFactory
|
||||
|
||||
from admin_confirm.constants import CACHE_KEYS, CONFIRMATION_RECEIVED
|
||||
|
||||
|
||||
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
|
||||
class TestConfirmSaveActions(AdminConfirmTestCase):
|
||||
def test_simple_add_with_save(self):
|
||||
# Load the Add Item Page
|
||||
ItemAdmin.confirm_add = True
|
||||
response = self.client.get(reverse("admin:market_item_add"))
|
||||
|
||||
# Should be asked for confirmation
|
||||
self.assertTrue(response.context_data.get("confirm_add"))
|
||||
self.assertIn("_confirm_add", response.rendered_content)
|
||||
|
||||
# Click "Save"
|
||||
data = {
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
response = self.client.post(reverse("admin:market_item_add"), data=data)
|
||||
|
||||
# Should be shown confirmation page
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content,
|
||||
save_action="_save",
|
||||
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"])
|
||||
|
||||
# Should not have saved the item yet
|
||||
self.assertEqual(Item.objects.count(), 0)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_add"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
response = self.client.post(reverse("admin:market_item_add"), data=data)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# 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"])
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_simple_change_with_continue(self):
|
||||
item = ItemFactory(name="Not name")
|
||||
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.confirm_change = True
|
||||
response = self.client.get(f"/admin/market/item/{item.id}/change/")
|
||||
|
||||
# Should be asked for confirmation
|
||||
self.assertTrue(response.context_data.get("confirm_change"))
|
||||
self.assertIn("_confirm_change", response.rendered_content)
|
||||
|
||||
# Click "Save And Continue"
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"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"])
|
||||
|
||||
# Should not have saved the changes yet
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
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"])
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_file_and_image_add_addanother(self):
|
||||
# Load the Add Item Page
|
||||
ItemAdmin.confirm_add = True
|
||||
response = self.client.get(reverse("admin:market_item_add"))
|
||||
|
||||
# Should be asked for confirmation
|
||||
self.assertTrue(response.context_data.get("confirm_add"))
|
||||
self.assertIn("_confirm_add", response.rendered_content)
|
||||
|
||||
# Select files
|
||||
image_path = "screenshot.png"
|
||||
f = SimpleUploadedFile(
|
||||
name="test_file.jpg",
|
||||
content=open(image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
i = SimpleUploadedFile(
|
||||
name="test_image.jpg",
|
||||
content=open(image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
# Click "Save"
|
||||
data = {
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"file": f,
|
||||
"image": i,
|
||||
"_confirm_add": True,
|
||||
"_addanother": True,
|
||||
}
|
||||
response = self.client.post(reverse("admin:market_item_add"), data=data)
|
||||
|
||||
# Should be shown confirmation page
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content,
|
||||
save_action="_addanother",
|
||||
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)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
confirmation_data = data.copy()
|
||||
del confirmation_data["_confirm_add"]
|
||||
del confirmation_data["image"]
|
||||
del confirmation_data["file"]
|
||||
confirmation_data[CONFIRMATION_RECEIVED] = True
|
||||
response = self.client.post(
|
||||
reverse("admin:market_item_add"), data=confirmation_data
|
||||
)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.status_code, 302)
|
||||
# Should show add page since "add another" was selected
|
||||
self.assertEqual(response.url, "/admin/market/item/add/")
|
||||
|
||||
# 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.assertEqual(saved_item.file, data["file"])
|
||||
self.assertEqual(saved_item.image, data["image"])
|
||||
|
||||
self.assertEqual(saved_item.file.name, "test_file.jpg")
|
||||
self.assertEqual(saved_item.image.name, "test_image.jpg")
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_file_and_image_change_with_saveasnew(self):
|
||||
item = ItemFactory(name="Not name")
|
||||
# Select files
|
||||
image_path = "screenshot.png"
|
||||
f = SimpleUploadedFile(
|
||||
name="test_file.jpg",
|
||||
content=open(image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
i = SimpleUploadedFile(
|
||||
name="test_image.jpg",
|
||||
content=open(image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
item.file = f
|
||||
item.image = i
|
||||
item.save()
|
||||
|
||||
# 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
|
||||
response = self.client.get(f"/admin/market/item/{item.id}/change/")
|
||||
|
||||
# Should be asked for confirmation
|
||||
self.assertTrue(response.context_data.get("confirm_change"))
|
||||
self.assertIn("_confirm_change", response.rendered_content)
|
||||
|
||||
# Upload new image and remove file
|
||||
i2 = SimpleUploadedFile(
|
||||
name="test_image2.jpg",
|
||||
content=open(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,
|
||||
"_saveasnew": 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="_saveasnew",
|
||||
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)
|
||||
|
||||
# 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 + 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, i2)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_relations_add(self):
|
||||
gm = GeneralManager.objects.create(name="gm")
|
||||
shops = [ShopFactory() for i in range(3)]
|
||||
town = Town.objects.create(name="town")
|
||||
|
||||
# Load the Add ShoppingMall Page
|
||||
ShoppingMallAdmin.confirm_add = True
|
||||
response = self.client.get(reverse("admin:market_shoppingmall_add"))
|
||||
|
||||
# Should be asked for confirmation
|
||||
self.assertTrue(response.context_data.get("confirm_add"))
|
||||
self.assertIn("_confirm_add", response.rendered_content)
|
||||
|
||||
# Click "Save"
|
||||
data = {
|
||||
"name": "name",
|
||||
"shops": [s.id for s in shops],
|
||||
"general_manager": gm.id,
|
||||
"town": town.id,
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
response = self.client.post(reverse("admin:market_shoppingmall_add"), data=data)
|
||||
|
||||
# Should be shown confirmation page
|
||||
self._assertManyToManyFormHtml(
|
||||
rendered_content=response.rendered_content,
|
||||
options=shops,
|
||||
selected_ids=data["shops"],
|
||||
)
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content, save_action="_save"
|
||||
)
|
||||
|
||||
# Should not have cached the unsaved object
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNone(cached_item)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
confirmation_received_data = data
|
||||
del confirmation_received_data["_confirm_add"]
|
||||
confirmation_received_data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
response = self.client.post(
|
||||
reverse("admin:market_shoppingmall_add"), data=confirmation_received_data
|
||||
)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/market/shoppingmall/")
|
||||
|
||||
# Should have saved object
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
saved_item = ShoppingMall.objects.all().first()
|
||||
self.assertEqual(saved_item.name, data["name"])
|
||||
self.assertEqual(saved_item.general_manager, gm)
|
||||
self.assertEqual(saved_item.town, town)
|
||||
for shop in saved_item.shops.all():
|
||||
self.assertIn(shop, shops)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_relation_change_with_saveasnew(self):
|
||||
gm = GeneralManager.objects.create(name="gm")
|
||||
shops = [ShopFactory() for i in range(3)]
|
||||
town = Town.objects.create(name="town")
|
||||
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
|
||||
mall.shops.set(shops)
|
||||
|
||||
# new values
|
||||
gm2 = GeneralManager.objects.create(name="gm2")
|
||||
shops2 = [ShopFactory() for i in range(3)]
|
||||
town2 = Town.objects.create(name="town2")
|
||||
|
||||
# Load the Change ShoppingMall Page
|
||||
ShoppingMallAdmin.confirm_change = True
|
||||
response = self.client.get(f"/admin/market/shoppingmall/{mall.id}/change/")
|
||||
|
||||
# Should be asked for confirmation
|
||||
self.assertTrue(response.context_data.get("confirm_change"))
|
||||
self.assertIn("_confirm_change", response.rendered_content)
|
||||
|
||||
# Click "Save"
|
||||
data = {
|
||||
"id": mall.id,
|
||||
"name": "name",
|
||||
"shops": [s.id for s in shops2],
|
||||
"general_manager": gm2.id,
|
||||
"town": town2.id,
|
||||
"_confirm_change": True,
|
||||
"_saveasnew": True,
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
|
||||
)
|
||||
|
||||
# Should be shown confirmation page
|
||||
self._assertManyToManyFormHtml(
|
||||
rendered_content=response.rendered_content,
|
||||
options=shops,
|
||||
selected_ids=data["shops"],
|
||||
)
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content, save_action="_saveasnew"
|
||||
)
|
||||
|
||||
# Should not have cached the unsaved obj
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNone(cached_item)
|
||||
|
||||
# Should not have saved changes yet
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
mall.refresh_from_db()
|
||||
self.assertEqual(mall.name, "mall")
|
||||
self.assertEqual(mall.general_manager, gm)
|
||||
self.assertEqual(mall.town, town)
|
||||
for shop in mall.shops.all():
|
||||
self.assertIn(shop, shops)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
confirmation_received_data = data.copy()
|
||||
del confirmation_received_data["_confirm_change"]
|
||||
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{mall.id}/change/",
|
||||
data=confirmation_received_data,
|
||||
)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(
|
||||
response.url, f"/admin/market/shoppingmall/{mall.id + 1}/change/"
|
||||
)
|
||||
|
||||
# Should have saved obj
|
||||
self.assertEqual(ShoppingMall.objects.count(), 2)
|
||||
# Should not have changed old obj
|
||||
mall.refresh_from_db()
|
||||
self.assertEqual(mall.name, "mall")
|
||||
self.assertEqual(mall.general_manager, gm)
|
||||
self.assertEqual(mall.town, town)
|
||||
for shop in mall.shops.all():
|
||||
self.assertIn(shop, shops)
|
||||
|
||||
# Should have created new obj
|
||||
saved_item = ShoppingMall.objects.filter(id=mall.id + 1).first()
|
||||
self.assertEqual(saved_item.name, data["name"])
|
||||
self.assertEqual(saved_item.general_manager, gm2)
|
||||
self.assertEqual(saved_item.town, town2)
|
||||
|
||||
for shop in saved_item.shops.all():
|
||||
self.assertIn(shop, shops2)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
@@ -0,0 +1,458 @@
|
||||
from unittest import mock
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.core.cache import cache
|
||||
from django.urls import reverse
|
||||
|
||||
from admin_confirm.tests.helpers import AdminConfirmTestCase
|
||||
from tests.market.admin import ItemAdmin, ShoppingMallAdmin
|
||||
from tests.market.models import GeneralManager, Item, ShoppingMall, Town
|
||||
from tests.factories import ItemFactory, ShopFactory
|
||||
|
||||
from admin_confirm.constants import CACHE_KEYS, CONFIRMATION_RECEIVED
|
||||
|
||||
|
||||
@mock.patch.object(ShoppingMallAdmin, "inlines", [])
|
||||
class TestConfirmationCache(AdminConfirmTestCase):
|
||||
def test_simple_add(self):
|
||||
# Load the Add Item Page
|
||||
ItemAdmin.confirm_add = True
|
||||
response = self.client.get(reverse("admin:market_item_add"))
|
||||
|
||||
# Should be asked for confirmation
|
||||
self.assertTrue(response.context_data.get("confirm_add"))
|
||||
self.assertIn("_confirm_add", response.rendered_content)
|
||||
|
||||
# Click "Save"
|
||||
data = {
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
response = self.client.post(reverse("admin:market_item_add"), data=data)
|
||||
|
||||
# Should be shown confirmation page
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content,
|
||||
save_action="_save",
|
||||
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"])
|
||||
|
||||
# Should not have saved the item yet
|
||||
self.assertEqual(Item.objects.count(), 0)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_add"]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
response = self.client.post(reverse("admin:market_item_add"), data=data)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# 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"])
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_simple_change(self):
|
||||
item = ItemFactory(name="Not name")
|
||||
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.confirm_change = True
|
||||
response = self.client.get(f"/admin/market/item/{item.id}/change/")
|
||||
|
||||
# Should be asked for confirmation
|
||||
self.assertTrue(response.context_data.get("confirm_change"))
|
||||
self.assertIn("_confirm_change", response.rendered_content)
|
||||
|
||||
# Click "Save And Continue"
|
||||
data = {
|
||||
"id": item.id,
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"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"])
|
||||
|
||||
# Should not have saved the changes yet
|
||||
self.assertEqual(Item.objects.count(), 1)
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.name, "Not name")
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data["_confirm_change"]
|
||||
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"])
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_file_and_image_add(self):
|
||||
# Load the Add Item Page
|
||||
ItemAdmin.confirm_add = True
|
||||
response = self.client.get(reverse("admin:market_item_add"))
|
||||
|
||||
# Should be asked for confirmation
|
||||
self.assertTrue(response.context_data.get("confirm_add"))
|
||||
self.assertIn("_confirm_add", response.rendered_content)
|
||||
|
||||
# Select files
|
||||
image_path = "screenshot.png"
|
||||
f = SimpleUploadedFile(
|
||||
name="test_file.jpg",
|
||||
content=open(image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
i = SimpleUploadedFile(
|
||||
name="test_image.jpg",
|
||||
content=open(image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
# Click "Save"
|
||||
data = {
|
||||
"name": "name",
|
||||
"price": 2.0,
|
||||
"currency": Item.VALID_CURRENCIES[0][0],
|
||||
"file": f,
|
||||
"image": i,
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
response = self.client.post(reverse("admin:market_item_add"), data=data)
|
||||
|
||||
# Should be shown confirmation page
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content,
|
||||
save_action="_save",
|
||||
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)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
confirmation_data = data.copy()
|
||||
del confirmation_data["_confirm_add"]
|
||||
del confirmation_data["image"]
|
||||
del confirmation_data["file"]
|
||||
confirmation_data[CONFIRMATION_RECEIVED] = True
|
||||
response = self.client.post(
|
||||
reverse("admin:market_item_add"), data=confirmation_data
|
||||
)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/market/item/")
|
||||
|
||||
# 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.assertEqual(saved_item.file, data["file"])
|
||||
self.assertEqual(saved_item.image, data["image"])
|
||||
|
||||
self.assertEqual(saved_item.file.name, "test_file.jpg")
|
||||
self.assertEqual(saved_item.image.name, "test_image.jpg")
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_file_and_image_change(self):
|
||||
item = ItemFactory(name="Not name")
|
||||
# Select files
|
||||
image_path = "screenshot.png"
|
||||
f = SimpleUploadedFile(
|
||||
name="test_file.jpg",
|
||||
content=open(image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
i = SimpleUploadedFile(
|
||||
name="test_image.jpg",
|
||||
content=open(image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
item.file = f
|
||||
item.image = i
|
||||
item.save()
|
||||
|
||||
# Load the Change Item Page
|
||||
ItemAdmin.confirm_change = True
|
||||
ItemAdmin.fields = ["name", "price", "file", "image", "currency"]
|
||||
response = self.client.get(f"/admin/market/item/{item.id}/change/")
|
||||
|
||||
# Should be asked for confirmation
|
||||
self.assertTrue(response.context_data.get("confirm_change"))
|
||||
self.assertIn("_confirm_change", response.rendered_content)
|
||||
|
||||
# Upload new image and remove file
|
||||
i2 = SimpleUploadedFile(
|
||||
name="test_image2.jpg",
|
||||
content=open(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)
|
||||
|
||||
# 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)
|
||||
self.assertEqual(saved_item.image, i2)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_relations_add(self):
|
||||
gm = GeneralManager.objects.create(name="gm")
|
||||
shops = [ShopFactory() for i in range(3)]
|
||||
town = Town.objects.create(name="town")
|
||||
|
||||
# Load the Add ShoppingMall Page
|
||||
ShoppingMallAdmin.confirm_add = True
|
||||
response = self.client.get(reverse("admin:market_shoppingmall_add"))
|
||||
|
||||
# Should be asked for confirmation
|
||||
self.assertTrue(response.context_data.get("confirm_add"))
|
||||
self.assertIn("_confirm_add", response.rendered_content)
|
||||
|
||||
# Click "Save"
|
||||
data = {
|
||||
"name": "name",
|
||||
"shops": [s.id for s in shops],
|
||||
"general_manager": gm.id,
|
||||
"town": town.id,
|
||||
"_confirm_add": True,
|
||||
"_save": True,
|
||||
}
|
||||
response = self.client.post(reverse("admin:market_shoppingmall_add"), data=data)
|
||||
|
||||
# Should be shown confirmation page
|
||||
self._assertManyToManyFormHtml(
|
||||
rendered_content=response.rendered_content,
|
||||
options=shops,
|
||||
selected_ids=data["shops"],
|
||||
)
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content, save_action="_save"
|
||||
)
|
||||
|
||||
# Should not have cached the unsaved object
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNone(cached_item)
|
||||
|
||||
# Should not have saved the object yet
|
||||
self.assertEqual(ShoppingMall.objects.count(), 0)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
confirmation_received_data = data
|
||||
del confirmation_received_data["_confirm_add"]
|
||||
|
||||
response = self.client.post(
|
||||
reverse("admin:market_shoppingmall_add"), data=confirmation_received_data
|
||||
)
|
||||
|
||||
# Should have redirected to changelist
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, "/admin/market/shoppingmall/")
|
||||
|
||||
# Should have saved object
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
saved_item = ShoppingMall.objects.all().first()
|
||||
self.assertEqual(saved_item.name, data["name"])
|
||||
self.assertEqual(saved_item.general_manager, gm)
|
||||
self.assertEqual(saved_item.town, town)
|
||||
for shop in saved_item.shops.all():
|
||||
self.assertIn(shop, shops)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
|
||||
def test_relation_change(self):
|
||||
gm = GeneralManager.objects.create(name="gm")
|
||||
shops = [ShopFactory() for i in range(3)]
|
||||
town = Town.objects.create(name="town")
|
||||
mall = ShoppingMall.objects.create(name="mall", general_manager=gm, town=town)
|
||||
mall.shops.set(shops)
|
||||
|
||||
# new values
|
||||
gm2 = GeneralManager.objects.create(name="gm2")
|
||||
shops2 = [ShopFactory() for i in range(3)]
|
||||
town2 = Town.objects.create(name="town2")
|
||||
|
||||
# Load the Change ShoppingMall Page
|
||||
ShoppingMallAdmin.confirm_change = True
|
||||
response = self.client.get(f"/admin/market/shoppingmall/{mall.id}/change/")
|
||||
|
||||
# Should be asked for confirmation
|
||||
self.assertTrue(response.context_data.get("confirm_change"))
|
||||
self.assertIn("_confirm_change", response.rendered_content)
|
||||
|
||||
# Click "Save"
|
||||
data = {
|
||||
"id": mall.id,
|
||||
"name": "name",
|
||||
"shops": [s.id for s in shops2],
|
||||
"general_manager": gm2.id,
|
||||
"town": town2.id,
|
||||
"_confirm_change": True,
|
||||
"_continue": True,
|
||||
}
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{mall.id}/change/", data=data
|
||||
)
|
||||
|
||||
# Should be shown confirmation page
|
||||
self._assertManyToManyFormHtml(
|
||||
rendered_content=response.rendered_content,
|
||||
options=shops,
|
||||
selected_ids=data["shops"],
|
||||
)
|
||||
self._assertSubmitHtml(
|
||||
rendered_content=response.rendered_content, save_action="_continue"
|
||||
)
|
||||
|
||||
# Should not have cached the unsaved obj
|
||||
cached_item = cache.get(CACHE_KEYS["object"])
|
||||
self.assertIsNone(cached_item)
|
||||
|
||||
# Should not have saved changes yet
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
mall.refresh_from_db()
|
||||
self.assertEqual(mall.name, "mall")
|
||||
self.assertEqual(mall.general_manager, gm)
|
||||
self.assertEqual(mall.town, town)
|
||||
for shop in mall.shops.all():
|
||||
self.assertIn(shop, shops)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
confirmation_received_data = data
|
||||
del confirmation_received_data["_confirm_change"]
|
||||
confirmation_received_data[CONFIRMATION_RECEIVED] = True
|
||||
|
||||
response = self.client.post(
|
||||
f"/admin/market/shoppingmall/{mall.id}/change/",
|
||||
data=confirmation_received_data,
|
||||
)
|
||||
|
||||
# Should not have redirected to changelist
|
||||
self.assertEqual(response.url, f"/admin/market/shoppingmall/{mall.id}/change/")
|
||||
|
||||
# Should have saved obj
|
||||
self.assertEqual(ShoppingMall.objects.count(), 1)
|
||||
saved_item = ShoppingMall.objects.all().first()
|
||||
self.assertEqual(saved_item.name, data["name"])
|
||||
self.assertEqual(saved_item.general_manager, gm2)
|
||||
self.assertEqual(saved_item.town, town2)
|
||||
|
||||
for shop in saved_item.shops.all():
|
||||
self.assertIn(shop, shops2)
|
||||
|
||||
# Should have cleared cache
|
||||
for key in CACHE_KEYS.values():
|
||||
self.assertIsNone(cache.get(key))
|
||||
@@ -0,0 +1,169 @@
|
||||
"""
|
||||
Tests ModelAdmin with fieldsets custom configured through one of the possible methods
|
||||
Ensures that AdminConfirmMixin works correctly when implimenting class alters default fieldsets
|
||||
|
||||
Test Matrix
|
||||
method: `.fieldsets =`, `def get_fieldsets()`
|
||||
action: change, add
|
||||
fieldset: simple, with readonly fields, with custom fields
|
||||
"""
|
||||
import pytest
|
||||
from importlib import reload
|
||||
from tests.market.admin import item_admin
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.admin import AdminSite
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.core.cache import cache
|
||||
|
||||
from admin_confirm.constants import CACHE_KEYS, CONFIRM_CHANGE, CONFIRMATION_RECEIVED
|
||||
|
||||
from tests.market.models import Item
|
||||
from tests.factories import ItemFactory
|
||||
|
||||
|
||||
def fs_simple(admin):
|
||||
return (
|
||||
(None, {"fields": ("name", "price", "image")}),
|
||||
(
|
||||
"Advanced options",
|
||||
{
|
||||
"classes": ("collapse",),
|
||||
"fields": ("currency", "file"),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def fs_w_readonly(admin):
|
||||
admin.readonly_fields = ["description", "image"]
|
||||
return fs_simple(admin)
|
||||
|
||||
|
||||
def fs_w_custom(admin):
|
||||
admin.one = lambda self, obj: "ReadOnly"
|
||||
admin.two = lambda self, obj: "ReadOnly"
|
||||
admin.three = lambda self, obj: "ReadOnly"
|
||||
admin.readonly_fields = ["one", "two", "three"]
|
||||
return (
|
||||
(None, {"fields": ("name", "price", "image", "one")}),
|
||||
(
|
||||
"Advanced options",
|
||||
{
|
||||
"classes": ("collapse",),
|
||||
"fields": ("currency", "two", "file"),
|
||||
},
|
||||
),
|
||||
("More Info", {"fields": ("three", "description")}),
|
||||
)
|
||||
|
||||
|
||||
def set_fieldsets(admin, fieldset):
|
||||
admin.fieldsets = fieldset
|
||||
|
||||
|
||||
def override_get_fieldsets(admin, fieldset):
|
||||
admin.get_fieldsets = lambda self, request, obj=None: fieldset
|
||||
|
||||
|
||||
methods = [set_fieldsets, override_get_fieldsets]
|
||||
actions = ["_confirm_add", "_confirm_change"]
|
||||
fieldsets = [fs_simple, fs_w_readonly, fs_w_custom]
|
||||
|
||||
param_matrix = []
|
||||
for method in methods:
|
||||
for fieldset in fieldsets:
|
||||
for action in actions:
|
||||
param_matrix.append((method, fieldset, action))
|
||||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
@pytest.mark.parametrize("method,get_fieldset,action", param_matrix)
|
||||
def test_fieldsets(client, method, get_fieldset, action):
|
||||
reload(item_admin)
|
||||
|
||||
admin = item_admin.ItemAdmin
|
||||
fs = get_fieldset(admin)
|
||||
# set fieldsets via one of the methods
|
||||
method(admin, fs)
|
||||
|
||||
admin_instance = admin(admin_site=AdminSite(), model=Item)
|
||||
request = RequestFactory().request
|
||||
assert admin_instance.get_fieldsets(request) == fs
|
||||
|
||||
user = User.objects.create_superuser(
|
||||
username="super", email="super@email.org", password="pass"
|
||||
)
|
||||
client.force_login(user)
|
||||
|
||||
url = "/admin/market/item/add/"
|
||||
image_path = "screenshot.png"
|
||||
f2 = SimpleUploadedFile(
|
||||
name="new_file.jpg",
|
||||
content=open(image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
i2 = SimpleUploadedFile(
|
||||
name="new_image.jpg",
|
||||
content=open(image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
data = {
|
||||
"name": "new name",
|
||||
"price": 2,
|
||||
"currency": "USD",
|
||||
"image": i2,
|
||||
"file": f2,
|
||||
action: True,
|
||||
"_save": True,
|
||||
}
|
||||
for f in admin.readonly_fields:
|
||||
if f in data.keys():
|
||||
del data[f]
|
||||
if action == CONFIRM_CHANGE:
|
||||
url = "/admin/market/item/1/change/"
|
||||
f = SimpleUploadedFile(
|
||||
name="old_file.jpg",
|
||||
content=open(image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
i = SimpleUploadedFile(
|
||||
name="old_image.jpg",
|
||||
content=open(image_path, "rb").read(),
|
||||
content_type="image/jpeg",
|
||||
)
|
||||
item = ItemFactory(name="old name", price=1, currency="CAD", file=f, image=i)
|
||||
data["id"] = item.id
|
||||
|
||||
cache_item = Item()
|
||||
for f in ["name", "price", "currency", "image", "file"]:
|
||||
if f not in admin.readonly_fields:
|
||||
setattr(cache_item, f, data[f])
|
||||
|
||||
cache.set(CACHE_KEYS["object"], cache_item)
|
||||
cache.set(CACHE_KEYS["post"], data)
|
||||
|
||||
# Click "Yes, I'm Sure"
|
||||
del data[action]
|
||||
data[CONFIRMATION_RECEIVED] = True
|
||||
response = client.post(url, data=data)
|
||||
|
||||
# Should have redirected to changelist
|
||||
# assert response.status_code == 302
|
||||
assert response.url == "/admin/market/item/"
|
||||
|
||||
# Should have saved item
|
||||
assert Item.objects.count() == 1
|
||||
saved_item = Item.objects.all().first()
|
||||
for f in ["name", "price", "currency"]:
|
||||
if f not in admin.readonly_fields:
|
||||
assert getattr(saved_item, f) == data[f]
|
||||
if "file" not in admin.readonly_fields:
|
||||
assert "new_file" in saved_item.file.name
|
||||
if "image" not in admin.readonly_fields:
|
||||
assert "new_image" in saved_item.image.name
|
||||
|
||||
reload(item_admin)
|
||||
@@ -0,0 +1,967 @@
|
||||
"""
|
||||
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, f"/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(f"/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, f"/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(f"/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, f"/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,
|
||||
}
|
||||
|
||||
# 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
|
||||
)
|
||||
|
||||
# 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(f"/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, f"/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,
|
||||
}
|
||||
|
||||
# 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
|
||||
)
|
||||
|
||||
# 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(f"/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, f"/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, f"/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
|
||||
|
||||
# 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,
|
||||
)
|
||||
|
||||
# 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, f"/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
|
||||
|
||||
# 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,
|
||||
)
|
||||
|
||||
# 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, f"/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, f"/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
|
||||
|
||||
# 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,
|
||||
"_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, f"/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))
|
||||
Reference in New Issue
Block a user