kfet.tests -- Add factories for many kfet models

- Article
- ArticleCategory
- Checkout
- CheckoutStatement
- Inventory
- InventoryArticle
- Operation
- OperationGroup
This commit is contained in:
Aurélien Delobelle 2018-10-04 14:18:00 +02:00
parent 928abc5a06
commit d7ca072af3
2 changed files with 281 additions and 3 deletions

View file

@ -1,3 +1,6 @@
from decimal import Decimal
from unittest import mock
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -5,9 +8,16 @@ from django.test import TestCase
from gestioncof.models import CofProfile from gestioncof.models import CofProfile
from ..models import Account from ..models import Account, Article, ArticleCategory, Checkout, Operation
from .testcases import TestCaseMixin from .testcases import TestCaseMixin
from .utils import create_root, create_team, create_user, get_perms, user_add_perms from .utils import (
create_operation_group,
create_root,
create_team,
create_user,
get_perms,
user_add_perms,
)
User = get_user_model() User = get_user_model()
@ -86,3 +96,80 @@ class PermHelpersTest(TestCaseMixin, TestCase):
map(repr, [self.perm1, self.perm2, self.perm_team]), map(repr, [self.perm1, self.perm2, self.perm_team]),
ordered=False, ordered=False,
) )
class OperationHelpersTest(TestCase):
def test_create_operation_group(self):
operation_group = create_operation_group()
on_acc = Account.objects.get(cofprofile__user__username="user")
checkout = Checkout.objects.get(name="Checkout")
self.assertDictEqual(
operation_group.__dict__,
{
"_checkout_cache": checkout,
"_on_acc_cache": on_acc,
"_state": mock.ANY,
"amount": 0,
"at": mock.ANY,
"checkout_id": checkout.pk,
"comment": "",
"id": mock.ANY,
"is_cof": False,
"on_acc_id": on_acc.pk,
"valid_by_id": None,
},
)
self.assertFalse(operation_group.opes.all())
def test_create_operation_group_with_content(self):
article_category = ArticleCategory.objects.create(name="Category")
article1 = Article.objects.create(
category=article_category, name="Article 1", price=Decimal("2.50")
)
article2 = Article.objects.create(
category=article_category, name="Article 2", price=Decimal("4.00")
)
operation_group = create_operation_group(
content=[
{
"type": Operation.PURCHASE,
"amount": Decimal("-3.50"),
"article": article1,
"article_nb": 2,
},
{"type": Operation.PURCHASE, "article": article2, "article_nb": 2},
{"type": Operation.PURCHASE, "article": article2},
{"type": Operation.DEPOSIT, "amount": Decimal("10.00")},
{"type": Operation.WITHDRAW, "amount": Decimal("-1.00")},
{"type": Operation.EDIT, "amount": Decimal("7.00")},
]
)
self.assertEqual(operation_group.amount, Decimal("0.50"))
operation_list = list(operation_group.opes.all())
# Passed args: with purchase, article, article_nb, amount
self.assertEqual(operation_list[0].type, Operation.PURCHASE)
self.assertEqual(operation_list[0].article, article1)
self.assertEqual(operation_list[0].article_nb, 2)
self.assertEqual(operation_list[0].amount, Decimal("-3.50"))
# Passed args: with purchase, article, article_nb; without amount
self.assertEqual(operation_list[1].type, Operation.PURCHASE)
self.assertEqual(operation_list[1].article, article2)
self.assertEqual(operation_list[1].article_nb, 2)
self.assertEqual(operation_list[1].amount, Decimal("-8.00"))
# Passed args: with purchase, article; without article_nb, amount
self.assertEqual(operation_list[2].type, Operation.PURCHASE)
self.assertEqual(operation_list[2].article, article2)
self.assertEqual(operation_list[2].article_nb, 1)
self.assertEqual(operation_list[2].amount, Decimal("-4.00"))
# Passed args: with deposit, amount
self.assertEqual(operation_list[3].type, Operation.DEPOSIT)
self.assertEqual(operation_list[3].amount, Decimal("10.00"))
# Passed args: with withdraw, amount
self.assertEqual(operation_list[4].type, Operation.WITHDRAW)
self.assertEqual(operation_list[4].amount, Decimal("-1.00"))
# Passed args: with edit, amount
self.assertEqual(operation_list[5].type, Operation.EDIT)
self.assertEqual(operation_list[5].amount, Decimal("7.00"))

View file

@ -1,7 +1,21 @@
from datetime import timedelta
from decimal import Decimal
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.utils import timezone
from ..models import Account from ..models import (
Account,
Article,
ArticleCategory,
Checkout,
CheckoutStatement,
Inventory,
InventoryArticle,
Operation,
OperationGroup,
)
User = get_user_model() User = get_user_model()
@ -184,3 +198,180 @@ def user_add_perms(user, perms_labels):
# it to avoid using of the previous permissions cache. # it to avoid using of the previous permissions cache.
# https://docs.djangoproject.com/en/dev/topics/auth/default/#permission-caching # https://docs.djangoproject.com/en/dev/topics/auth/default/#permission-caching
return User.objects.get(pk=user.pk) return User.objects.get(pk=user.pk)
def create_checkout(**kwargs):
"""
Factory to create a checkout.
See defaults for unpassed arguments in code below.
"""
if "created_by" not in kwargs or "created_by_id" not in kwargs:
try:
team_account = Account.objects.get(cofprofile__user__username="team")
except Account.DoesNotExist:
team_account = create_team().profile.account_kfet
kwargs["created_by"] = team_account
kwargs.setdefault("name", "Checkout")
kwargs.setdefault("valid_from", timezone.now() - timedelta(days=14))
kwargs.setdefault("valid_to", timezone.now() - timedelta(days=14))
return Checkout.objects.create(**kwargs)
def create_operation_group(content=None, **kwargs):
"""
Factory to create an OperationGroup and a set of related Operation.
It aims to get objects for testing purposes with minimal setup, and
preserving consistency.
For this, it uses, and creates if necessary, default objects for unpassed
arguments.
Args:
content: list of dict
Describe set of Operation to create along the OperationGroup.
Each item is passed to the Operation factory.
kwargs:
Used to control OperationGroup creation.
"""
if content is None:
content = []
# Prepare OperationGroup creation.
# Set 'checkout' for OperationGroup if unpassed.
if "checkout" not in kwargs and "checkout_id" not in kwargs:
try:
checkout = Checkout.objects.get(name="Checkout")
except Checkout.DoesNotExist:
checkout = create_checkout()
kwargs["checkout"] = checkout
# Set 'on_acc' for OperationGroup if unpassed.
if "on_acc" not in kwargs and "on_acc_id" not in kwargs:
try:
on_acc = Account.objects.get(cofprofile__user__username="user")
except Account.DoesNotExist:
on_acc = create_user().profile.account_kfet
kwargs["on_acc"] = on_acc
# Set 'is_cof' for OperationGroup if unpassed.
if "is_cof" not in kwargs:
# Use current is_cof status of 'on_acc'.
kwargs["is_cof"] = kwargs["on_acc"].cofprofile.is_cof
# Create OperationGroup.
group = OperationGroup.objects.create(**kwargs)
# We can now create objects referencing this OperationGroup.
# Process set of related Operation.
if content:
# Create them.
operation_list = []
for operation_kwargs in content:
operation = create_operation(group=group, **operation_kwargs)
operation_list.append(operation)
# Update OperationGroup accordingly, for consistency.
for operation in operation_list:
if not operation.canceled_at:
group.amount += operation.amount
group.save()
return group
def create_operation(**kwargs):
"""
Factory to create an Operation for testing purposes.
If you give a 'group' (OperationGroup), it won't update it, you have do
this "manually". Prefer using OperationGroup factory to get a consistent
group with operations.
"""
if "group" not in kwargs and "group_id" not in kwargs:
# To get a consistent OperationGroup (amount...) for the operation
# in-creation, prefer using create_operation_group factory with
# 'content'.
kwargs["group"] = create_operation_group()
if "type" not in kwargs:
raise RuntimeError("Can't create an Operation without 'type'.")
# Apply defaults for purchase
if kwargs["type"] == Operation.PURCHASE:
if "article" not in kwargs:
raise NotImplementedError(
"One could write a create_article factory. Right now, you must"
"pass an 'article'."
)
# Unpassed 'article_nb' defaults to 1.
kwargs.setdefault("article_nb", 1)
# Unpassed 'amount' will use current article price and quantity.
if "amount" not in kwargs:
if "addcost_for" in kwargs or "addcost_amount" in kwargs:
raise NotImplementedError(
"One could handle the case where 'amount' is missing and "
"addcost applies. Right now, please pass an 'amount'."
)
kwargs["amount"] = -kwargs["article"].price * kwargs["article_nb"]
return Operation.objects.create(**kwargs)
def create_checkout_statement(**kwargs):
if "checkout" not in kwargs:
kwargs["checkout"] = create_checkout()
if "by" not in kwargs:
try:
team_account = Account.objects.get(cofprofile__user__username="team")
except Account.DoesNotExist:
team_account = create_team().profile.account_kfet
kwargs["by"] = team_account
kwargs.setdefault("balance_new", kwargs["checkout"].balance)
kwargs.setdefault("balance_old", kwargs["checkout"].balance)
kwargs.setdefault("amount_taken", Decimal(0))
return CheckoutStatement.objects.create(**kwargs)
def create_article(**kwargs):
kwargs.setdefault("name", "Article")
kwargs.setdefault("price", Decimal("2.50"))
kwargs.setdefault("stock", 20)
if "category" not in kwargs:
kwargs["category"] = create_article_category()
return Article.objects.create(**kwargs)
def create_article_category(**kwargs):
kwargs.setdefault("name", "Category")
return ArticleCategory.objects.create(**kwargs)
def create_inventory(**kwargs):
if "by" not in kwargs:
try:
team_account = Account.objects.get(cofprofile__user__username="team")
except Account.DoesNotExist:
team_account = create_team().profile.account_kfet
kwargs["by"] = team_account
return Inventory.objects.create(**kwargs)
def create_inventory_article(**kwargs):
if "inventory" not in kwargs:
kwargs["inventory"] = create_inventory()
if "article" not in kwargs:
kwargs["article"] = create_article()
kwargs.setdefault("stock_old", kwargs["article"].stock)
kwargs.setdefault("stock_new", kwargs["article"].stock)
return InventoryArticle.objects.create(**kwargs)