kpsul/kfet/tests/utils.py
Aurélien Delobelle d7ca072af3 kfet.tests -- Add factories for many kfet models
- Article
- ArticleCategory
- Checkout
- CheckoutStatement
- Inventory
- InventoryArticle
- Operation
- OperationGroup
2018-10-21 12:50:09 +02:00

378 lines
12 KiB
Python

from datetime import timedelta
from decimal import Decimal
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.utils import timezone
from ..models import (
Account,
Article,
ArticleCategory,
Checkout,
CheckoutStatement,
Inventory,
InventoryArticle,
Operation,
OperationGroup,
)
User = get_user_model()
def _create_user_and_account(user_attrs, account_attrs, perms=None):
"""
Create a user and its account, and assign permissions to this user.
Arguments
user_attrs (dict): User data (first name, last name, password...).
account_attrs (dict): Account data (department, kfet password...).
perms (list of str: 'app.perm'): These permissions will be assigned to
the created user. No permission are assigned by default.
If 'password' is not given in 'user_attrs', username is used as password.
If 'kfet.is_team' is in 'perms' and 'password' is not in 'account_attrs',
the account password is 'kfetpwd_<user pwd>'.
"""
user_pwd = user_attrs.pop("password", user_attrs["username"])
user = User.objects.create(**user_attrs)
user.set_password(user_pwd)
user.save()
account_attrs["cofprofile"] = user.profile
kfet_pwd = account_attrs.pop("password", "kfetpwd_{}".format(user_pwd))
account = Account.objects.create(**account_attrs)
if perms is not None:
user = user_add_perms(user, perms)
if "kfet.is_team" in perms:
account.change_pwd(kfet_pwd)
account.save()
return user
def create_user(username="user", trigramme="000", **kwargs):
"""
Create a user without any permission and its kfet account.
username and trigramme are accepted as arguments (defaults to 'user' and
'000').
user_attrs, account_attrs and perms can be given as keyword arguments to
customize the user and its kfet account.
# Default values
User
* username: user
* password: user
* first_name: first
* last_name: last
* email: mail@user.net
Account
* trigramme: 000
"""
user_attrs = kwargs.setdefault("user_attrs", {})
user_attrs.setdefault("username", username)
user_attrs.setdefault("first_name", "first")
user_attrs.setdefault("last_name", "last")
user_attrs.setdefault("email", "mail@user.net")
account_attrs = kwargs.setdefault("account_attrs", {})
account_attrs.setdefault("trigramme", trigramme)
return _create_user_and_account(**kwargs)
def create_team(username="team", trigramme="100", **kwargs):
"""
Create a user, member of the kfet team, and its kfet account.
username and trigramme are accepted as arguments (defaults to 'team' and
'100').
user_attrs, account_attrs and perms can be given as keyword arguments to
customize the user and its kfet account.
# Default values
User
* username: team
* password: team
* first_name: team
* last_name: member
* email: mail@team.net
Account
* trigramme: 100
* kfet password: kfetpwd_team
"""
user_attrs = kwargs.setdefault("user_attrs", {})
user_attrs.setdefault("username", username)
user_attrs.setdefault("first_name", "team")
user_attrs.setdefault("last_name", "member")
user_attrs.setdefault("email", "mail@team.net")
account_attrs = kwargs.setdefault("account_attrs", {})
account_attrs.setdefault("trigramme", trigramme)
perms = kwargs.setdefault("perms", [])
perms.append("kfet.is_team")
return _create_user_and_account(**kwargs)
def create_root(username="root", trigramme="200", **kwargs):
"""
Create a superuser and its kfet account.
username and trigramme are accepted as arguments (defaults to 'root' and
'200').
user_attrs, account_attrs and perms can be given as keyword arguments to
customize the user and its kfet account.
# Default values
User
* username: root
* password: root
* first_name: super
* last_name: user
* email: mail@root.net
* is_staff, is_superuser: True
Account
* trigramme: 200
* kfet password: kfetpwd_root
"""
user_attrs = kwargs.setdefault("user_attrs", {})
user_attrs.setdefault("username", username)
user_attrs.setdefault("first_name", "super")
user_attrs.setdefault("last_name", "user")
user_attrs.setdefault("email", "mail@root.net")
user_attrs["is_superuser"] = user_attrs["is_staff"] = True
account_attrs = kwargs.setdefault("account_attrs", {})
account_attrs.setdefault("trigramme", trigramme)
return _create_user_and_account(**kwargs)
def get_perms(*labels):
"""Return Permission instances from a list of '<app>.<perm_codename>'."""
perms = {}
for label in set(labels):
app_label, codename = label.split(".", 1)
perms[label] = Permission.objects.get(
content_type__app_label=app_label, codename=codename
)
return perms
def user_add_perms(user, perms_labels):
"""
Add perms to a user.
Args:
user (User instance)
perms (list of str 'app.perm_name')
Returns:
The same user (refetched from DB to avoid missing perms)
"""
perms = get_perms(*perms_labels)
user.user_permissions.add(*perms.values())
# If permissions have already been fetched for this user, we need to reload
# it to avoid using of the previous permissions cache.
# https://docs.djangoproject.com/en/dev/topics/auth/default/#permission-caching
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)