From 2cfce1c921d3a75185a6131061ce5edc797d7c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Delobelle?= Date: Wed, 16 Aug 2017 17:45:59 +0200 Subject: [PATCH] Add tests for kfet views. kfet.tests.testcases embed mixins for TestCase: - TestCaseMixin provides assertion helpers, - ViewTestCaseMixin provides a few basic tests, which are common to every view. kfet.tests.utils provides helpers for users and permissions management. Each kfet view get a testcase (at least very basic) in kfet.tests.test_views. --- kfet/tests/test_tests_utils.py | 95 ++ kfet/tests/test_views.py | 2121 ++++++++++++++++++++++++++++++-- kfet/tests/testcases.py | 180 ++- kfet/tests/utils.py | 75 +- 4 files changed, 2265 insertions(+), 206 deletions(-) create mode 100644 kfet/tests/test_tests_utils.py diff --git a/kfet/tests/test_tests_utils.py b/kfet/tests/test_tests_utils.py new file mode 100644 index 00000000..8308bd5b --- /dev/null +++ b/kfet/tests/test_tests_utils.py @@ -0,0 +1,95 @@ +from django.contrib.auth import get_user_model +from django.contrib.auth.models import Permission +from django.contrib.contenttypes.models import ContentType +from django.test import TestCase + +from gestioncof.models import CofProfile + +from ..models import Account +from .testcases import TestCaseMixin +from .utils import ( + create_user, create_team, create_root, get_perms, user_add_perms, +) + + +User = get_user_model() + + +class UserHelpersTests(TestCaseMixin, TestCase): + + def test_create_user(self): + """create_user creates a basic user and its account.""" + u = create_user() + a = u.profile.account_kfet + + self.assertInstanceExpected(u, { + 'get_full_name': 'first last', + 'username': 'user', + }) + self.assertFalse(u.user_permissions.exists()) + + self.assertEqual('000', a.trigramme) + + def test_create_team(self): + u = create_team() + a = u.profile.account_kfet + + self.assertInstanceExpected(u, { + 'get_full_name': 'team member', + 'username': 'team', + }) + self.assertTrue(u.has_perm('kfet.is_team')) + + self.assertEqual('100', a.trigramme) + + def test_create_root(self): + u = create_root() + a = u.profile.account_kfet + + self.assertInstanceExpected(u, { + 'get_full_name': 'super user', + 'username': 'root', + 'is_superuser': True, + 'is_staff': True, + }) + + self.assertEqual('200', a.trigramme) + + +class PermHelpersTest(TestCaseMixin, TestCase): + + def setUp(self): + cts = ContentType.objects.get_for_models(Account, CofProfile) + self.perm1 = Permission.objects.create( + content_type=cts[Account], + codename='test_perm', + name='Perm for test', + ) + self.perm2 = Permission.objects.create( + content_type=cts[CofProfile], + codename='another_test_perm', + name='Another one', + ) + self.perm_team = Permission.objects.get( + content_type__app_label='kfet', + codename='is_team', + ) + + def test_get_perms(self): + perms = get_perms('kfet.test_perm', 'gestioncof.another_test_perm') + self.assertDictEqual(perms, { + 'kfet.test_perm': self.perm1, + 'gestioncof.another_test_perm': self.perm2, + }) + + def test_user_add_perms(self): + user = User.objects.create_user(username='user', password='user') + user.user_permissions.add(self.perm1) + + user_add_perms(user, ['kfet.is_team', 'gestioncof.another_test_perm']) + + self.assertQuerysetEqual( + user.user_permissions.all(), + map(repr, [self.perm1, self.perm2, self.perm_team]), + ordered=False, + ) diff --git a/kfet/tests/test_views.py b/kfet/tests/test_views.py index f7457786..5ac82f33 100644 --- a/kfet/tests/test_views.py +++ b/kfet/tests/test_views.py @@ -1,15 +1,21 @@ import json +from datetime import datetime, timedelta from decimal import Decimal +from unittest import mock -from django.contrib.auth.models import Group, Permission +from django.contrib.auth.models import Group from django.core.urlresolvers import reverse from django.test import Client, TestCase from django.utils import timezone -from ..models import Account, Checkout, Operation, OperationGroup - +from ..config import kfet_config +from ..models import ( + Account, Article, ArticleCategory, Checkout, CheckoutStatement, Inventory, + InventoryArticle, Operation, OperationGroup, Order, OrderArticle, Supplier, + SupplierArticle, Transfer, TransferGroup, +) from .testcases import ViewTestCaseMixin -from .utils import create_team, create_user +from .utils import create_team, create_user, get_perms class LoginGenericTeamViewTests(ViewTestCaseMixin, TestCase): @@ -22,8 +28,8 @@ class LoginGenericTeamViewTests(ViewTestCaseMixin, TestCase): def test_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - logged_in_username = r.wsgi_request.user.username - self.assertEqual(logged_in_username, 'kfet_genericteam') + logged_in = r.wsgi_request.user + self.assertEqual(logged_in.username, 'kfet_genericteam') class AccountListViewTests(ViewTestCaseMixin, TestCase): @@ -74,16 +80,23 @@ class AccountCreateViewTests(ViewTestCaseMixin, TestCase): url_name = 'kfet.account.create' url_expected = '/k-fet/accounts/new' + http_methods = ['GET', 'POST'] + auth_user = 'team' auth_forbidden = [None, 'user'] + post_data = { + 'trigramme': 'AAA', + 'username': 'plopplopplop', + 'first_name': 'first', + 'last_name': 'last', + 'email': 'email@domain.net', + } + @property def users_extra(self): return { - 'team__add_account': create_team( - 'team__add_account', '101', - perms=['kfet.add_account'], - ), + 'team1': create_team('team1', '101', perms=['kfet.add_account']), } def test_get_ok(self): @@ -91,91 +104,69 @@ class AccountCreateViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(r.status_code, 200) def test_post_ok(self): - post_data = { - 'trigramme': 'AAA', + client = Client() + client.login(username='team1', password='team1') + + r = client.post(self.url, self.post_data) + self.assertRedirects(r, reverse('kfet.account.create')) + + account = Account.objects.get(trigramme='AAA') + + self.assertInstanceExpected(account, { 'username': 'plopplopplop', 'first_name': 'first', 'last_name': 'last', - 'email': 'email@domain.net', - } - - client = Client() - client.login( - username='team__add_account', - password='team__add_account', - ) - r = client.post(self.url, post_data) - - self.assertRedirects(r, self.url) - a = Account.objects.get(trigramme='AAA') - self.assertEqual(a.username, 'plopplopplop') + }) def test_post_forbidden(self): - post_data = { - 'trigramme': 'AAA', - 'username': 'plopplopplop', - 'first_name': 'first', - 'last_name': 'last', - 'email': 'email@domain.net', - } - - # A team member (without kfet.add_account) is authenticated with - # self.client. - r = self.client.post(self.url, post_data) - - self.assertEqual(r.status_code, 200) - with self.assertRaises(Account.DoesNotExist): - Account.objects.get(trigramme='AAA') + r = self.client.post(self.url, self.post_data) + self.assertForbiddenKfet(r) class AccountCreateAjaxViewTests(ViewTestCaseMixin, TestCase): - urls_conf = [ - { - 'name': 'kfet.account.create.fromuser', - 'kwargs': {'username': 'user'}, - 'expected': '/k-fet/accounts/new/user/user', + urls_conf = [{ + 'name': 'kfet.account.create.fromuser', + 'kwargs': {'username': 'user'}, + 'expected': '/k-fet/accounts/new/user/user', + }, { + 'name': 'kfet.account.create.fromclipper', + 'kwargs': { + 'login_clipper': 'myclipper', + 'fullname': 'first last1 last2', }, - { - 'name': 'kfet.account.create.fromclipper', - 'kwargs': { - 'login_clipper': 'myclipper', - 'fullname': 'first last1 last2', - }, - 'expected': ( - '/k-fet/accounts/new/clipper/myclipper/first%20last1%20last2' - ), - }, - { - 'name': 'kfet.account.create.empty', - 'expected': '/k-fet/accounts/new/empty', - }, - ] + 'expected': ( + '/k-fet/accounts/new/clipper/myclipper/first%20last1%20last2' + ), + }, { + 'name': 'kfet.account.create.empty', + 'expected': '/k-fet/accounts/new/empty', + }] auth_user = 'team' auth_forbidden = [None, 'user'] def test_fromuser(self): r = self.client.get(self.t_urls[0]) + self.assertEqual(r.status_code, 200) user = self.users['user'] - self.assertEqual(r.status_code, 200) self.assertEqual(r.context['user_form'].instance, user) self.assertEqual(r.context['cof_form'].instance, user.profile) self.assertIn('account_form', r.context) def test_fromclipper(self): r = self.client.get(self.t_urls[1]) - self.assertEqual(r.status_code, 200) + self.assertIn('user_form', r.context) self.assertIn('cof_form', r.context) self.assertIn('account_form', r.context) def test_empty(self): - r = self.client.get(self.t_urls[0]) - + r = self.client.get(self.t_urls[2]) self.assertEqual(r.status_code, 200) + self.assertIn('user_form', r.context) self.assertIn('cof_form', r.context) self.assertIn('account_form', r.context) @@ -191,12 +182,11 @@ class AccountCreateAutocompleteViewTests(ViewTestCaseMixin, TestCase): def test_ok(self): r = self.client.get(self.url, {'q': 'first'}) self.assertEqual(r.status_code, 200) - self.assertListEqual(list(r.context['users_notcof']), []) - self.assertListEqual(list(r.context['users_cof']), []) - self.assertListEqual( - list(r.context['kfet']), - [(self.accounts['user'], self.users['user'])], - ) + self.assertEqual(len(r.context['users_notcof']), 0) + self.assertEqual(len(r.context['users_cof']), 0) + self.assertSetEqual(set(r.context['kfet']), set([ + (self.accounts['user'], self.users['user']), + ])) class AccountSearchViewTests(ViewTestCaseMixin, TestCase): @@ -209,10 +199,9 @@ class AccountSearchViewTests(ViewTestCaseMixin, TestCase): def test_ok(self): r = self.client.get(self.url, {'q': 'first'}) self.assertEqual(r.status_code, 200) - self.assertListEqual( - list(r.context['accounts']), - [('000', 'first last')], - ) + self.assertSetEqual(set(r.context['accounts']), set([ + ('000', 'first last'), + ])) class AccountReadViewTests(ViewTestCaseMixin, TestCase): @@ -281,43 +270,105 @@ class AccountUpdateViewTests(ViewTestCaseMixin, TestCase): url_kwargs = {'trigramme': '001'} url_expected = '/k-fet/accounts/001/edit' + http_methods = ['GET', 'POST'] + auth_user = 'team' auth_forbidden = [None, 'user'] + post_data = { + # User + 'first_name': 'The first', + 'last_name': 'The last', + 'email': '', + # Group + 'groups[]': [], + # Account + 'trigramme': '051', + 'nickname': '', + 'promo': '', + # 'is_frozen': not checked + # Account password + 'pwd1': '', + 'pwd2': '', + } + @property def users_extra(self): return { 'user1': create_user('user1', '001'), + 'team1': create_team('team1', '101', perms=[ + 'kfet.change_account', + ]), } - def test_ok(self): + def test_get_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - def test_ok_self(self): + def test_get_ok_self(self): client = Client() client.login(username='user1', password='user1') r = client.get(self.url) self.assertEqual(r.status_code, 200) + def test_post_ok(self): + client = Client() + client.login(username='team1', password='team1') -class BaseAccountGroupViewTests(ViewTestCaseMixin): - auth_user = 'team__manage_perms' + r = client.post(self.url, self.post_data) + self.assertRedirects(r, reverse('kfet.account.read', args=['051'])) + + self.accounts['user1'].refresh_from_db() + self.users['user1'].refresh_from_db() + + self.assertInstanceExpected(self.accounts['user1'], { + 'first_name': 'The first', + 'last_name': 'The last', + 'trigramme': '051', + }) + + def test_post_ok_self(self): + client = Client() + client.login(username='user1', password='user1') + + post_data = { + 'first_name': 'The first', + 'last_name': 'The last', + } + + r = client.post(self.url, post_data) + self.assertRedirects(r, reverse('kfet.account.read', args=['001'])) + + self.accounts['user1'].refresh_from_db() + self.users['user1'].refresh_from_db() + + self.assertInstanceExpected(self.accounts['user1'], { + 'first_name': 'The first', + 'last_name': 'The last', + }) + + def test_post_forbidden(self): + r = self.client.post(self.url, self.post_data) + self.assertForbiddenKfet(r) + + +class AccountGroupListViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.account.group' + url_expected = '/k-fet/accounts/groups' + + auth_user = 'team1' auth_forbidden = [None, 'user', 'team'] @property def users_extra(self): return { - 'team__manage_perms': create_team( - 'team__manage_perms', '101', - perms=['kfet.manage_perms'], - ), + 'team1': create_team('team1', '101', perms=['kfet.manage_perms']), } - -class AccountGroupListViewTests(BaseAccountGroupViewTests, TestCase): - url_name = 'kfet.account.group' - url_expected = '/k-fet/accounts/groups' + def setUp(self): + super().setUp() + self.group1 = Group.objects.create(name='K-Fêt - Group1') + self.group2 = Group.objects.create(name='K-Fêt - Group2') def test_ok(self): r = self.client.get(self.url) @@ -325,51 +376,1893 @@ class AccountGroupListViewTests(BaseAccountGroupViewTests, TestCase): self.assertQuerysetEqual( r.context['groups'], - Group.objects.filter(name__icontains='K-Fêt'), + map(repr, [self.group1, self.group2]), ordered=False, ) -class AccountGroupCreateViewTests(BaseAccountGroupViewTests, TestCase): +class AccountGroupCreateViewTests(ViewTestCaseMixin, TestCase): url_name = 'kfet.account.group.create' url_expected = '/k-fet/accounts/groups/new' - def test_ok(self): - r = self.client.get(self.url) - self.assertEqual(r.status_code, 200) + http_methods = ['GET', 'POST'] + auth_user = 'team1' + auth_forbidden = [None, 'user', 'team'] -class AccountGroupUpdateViewTests(BaseAccountGroupViewTests, TestCase): - url_name = 'kfet.account.group.update' - url_kwargs = {'pk': 42} - url_expected = '/k-fet/accounts/groups/42/edit' + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=['kfet.manage_perms']), + } + + @property + def post_data(self): + return { + 'name': 'The Group', + 'permissions': [ + str(self.perms['kfet.is_team'].pk), + str(self.perms['kfet.manage_perms'].pk), + ], + } def setUp(self): super().setUp() - self.group1 = Group.objects.create(pk=42, name='K-Fêt - Group') - self.group1.permissions = [ - Permission.objects.get( - content_type__app_label='kfet', - codename='is_team', - ) - ] + self.perms = get_perms( + 'kfet.is_team', + 'kfet.manage_perms', + ) def test_get_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) def test_post_ok(self): - post_data = { - 'name': 'Group42', - 'permissions': ( - self.group1.permissions - .values_list('pk', flat=True) - ), - } - - r = self.client.post(self.url, post_data) - + r = self.client.post(self.url, self.post_data) self.assertRedirects(r, reverse('kfet.account.group')) - self.group1.refresh_from_db() - self.assertEqual(self.group1.name, 'K-Fêt Group42') + group = Group.objects.get(name='K-Fêt The Group') + + self.assertQuerysetEqual( + group.permissions.all(), + map(repr, [ + self.perms['kfet.is_team'], + self.perms['kfet.manage_perms'], + ]), + ordered=False, + ) + + +class AccountGroupUpdateViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.account.group.update' + + http_methods = ['GET', 'POST'] + + auth_user = 'team1' + auth_forbidden = [None, 'user', 'team'] + + @property + def url_kwargs(self): + return {'pk': self.group.pk} + + @property + def url_expected(self): + return '/k-fet/accounts/groups/{}/edit'.format(self.group.pk) + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=['kfet.manage_perms']), + } + + @property + def post_data(self): + return { + 'name': 'The Group', + 'permissions': [ + str(self.perms['kfet.is_team'].pk), + str(self.perms['kfet.manage_perms'].pk), + ], + } + + def setUp(self): + super().setUp() + self.perms = get_perms( + 'kfet.is_team', + 'kfet.manage_perms', + ) + self.group = Group.objects.create(name='K-Fêt - Group') + self.group.permissions = self.perms.values() + + def test_get_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + def test_post_ok(self): + r = self.client.post(self.url, self.post_data) + self.assertRedirects(r, reverse('kfet.account.group')) + + self.group.refresh_from_db() + + self.assertEqual(self.group.name, 'K-Fêt The Group') + self.assertQuerysetEqual( + self.group.permissions.all(), + map(repr, [ + self.perms['kfet.is_team'], + self.perms['kfet.manage_perms'], + ]), + ordered=False, + ) + + +class AccountNegativeListViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.account.negative' + url_expected = '/k-fet/accounts/negatives' + + auth_user = 'team1' + auth_forbidden = [None, 'user', 'team'] + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=['kfet.view_negs']), + } + + def setUp(self): + super().setUp() + account = self.accounts['user'] + account.balance = -5 + account.save() + account.update_negative() + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + self.assertQuerysetEqual( + r.context['negatives'], + map(repr, [self.accounts['user'].negative]), + ordered=False, + ) + + +class AccountStatOperationListViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.account.stat.operation.list' + url_kwargs = {'trigramme': '001'} + url_expected = '/k-fet/accounts/001/stat/operations/list' + + auth_user = 'user1' + auth_forbidden = [None, 'user', 'team'] + + @property + def users_extra(self): + return {'user1': create_user('user1', '001')} + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + content = json.loads(r.content.decode('utf-8')) + + base_url = reverse('kfet.account.stat.operation', args=['001']) + + expected_stats = [{ + 'label': 'Derniers mois', + 'url': { + 'path': base_url, + 'query': { + 'scale_n_steps': ['7'], + 'scale_name': ['month'], + 'types': ["['purchase']"], + 'scale_last': ['True'], + }, + }, + }, { + 'label': 'Dernières semaines', + 'url': { + 'path': base_url, + 'query': { + 'scale_n_steps': ['7'], + 'scale_name': ['week'], + 'types': ["['purchase']"], + 'scale_last': ['True'], + }, + }, + }, { + 'label': 'Derniers jours', + 'url': { + 'path': base_url, + 'query': { + 'scale_n_steps': ['7'], + 'scale_name': ['day'], + 'types': ["['purchase']"], + 'scale_last': ['True'], + }, + }, + }] + + for stat, expected in zip(content['stats'], expected_stats): + expected_url = expected.pop('url') + self.assertUrlsEqual(stat['url'], expected_url) + self.assertDictContainsSubset(expected, stat) + + +class AccountStatOperationViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.account.stat.operation' + url_kwargs = {'trigramme': '001'} + url_expected = '/k-fet/accounts/001/stat/operations' + + auth_user = 'user1' + auth_forbidden = [None, 'user', 'team'] + + @property + def users_extra(self): + return {'user1': create_user('user1', '001')} + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + +class AccountStatBalanceListViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.account.stat.balance.list' + url_kwargs = {'trigramme': '001'} + url_expected = '/k-fet/accounts/001/stat/balance/list' + + auth_user = 'user1' + auth_forbidden = [None, 'user', 'team'] + + @property + def users_extra(self): + return {'user1': create_user('user1', '001')} + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + content = json.loads(r.content.decode('utf-8')) + + base_url = reverse('kfet.account.stat.balance', args=['001']) + + expected_stats = [{ + 'label': 'Tout le temps', + 'url': base_url, + }, { + 'label': '1 an', + 'url': { + 'path': base_url, + 'query': {'last_days': ['365']}, + }, + }, { + 'label': '6 mois', + 'url': { + 'path': base_url, + 'query': {'last_days': ['183']}, + }, + }, { + 'label': '3 mois', + 'url': { + 'path': base_url, + 'query': {'last_days': ['90']}, + }, + }, { + 'label': '30 jours', + 'url': { + 'path': base_url, + 'query': {'last_days': ['30']}, + }, + }] + + for stat, expected in zip(content['stats'], expected_stats): + expected_url = expected.pop('url') + self.assertUrlsEqual(stat['url'], expected_url) + self.assertDictContainsSubset(expected, stat) + + +class AccountStatBalanceViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.account.stat.balance' + url_kwargs = {'trigramme': '001'} + url_expected = '/k-fet/accounts/001/stat/balance' + + auth_user = 'user1' + auth_forbidden = [None, 'user', 'team'] + + @property + def users_extra(self): + return {'user1': create_user('user1', '001')} + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + +class CheckoutListViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.checkout' + url_expected = '/k-fet/checkouts/' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def setUp(self): + super().setUp() + self.checkout1 = Checkout.objects.create( + name='Checkout 1', + created_by=self.accounts['team'], + valid_from=self.now, + valid_to=self.now + timedelta(days=5), + ) + self.checkout2 = Checkout.objects.create( + name='Checkout 2', + created_by=self.accounts['team'], + valid_from=self.now + timedelta(days=10), + valid_to=self.now + timedelta(days=15), + ) + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + self.assertQuerysetEqual( + r.context['checkouts'], + map(repr, [self.checkout1, self.checkout2]), + ordered=False, + ) + + +class CheckoutCreateViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.checkout.create' + url_expected = '/k-fet/checkouts/new' + + http_methods = ['GET', 'POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + post_data = { + 'name': 'Checkout', + 'valid_from': '2017-10-08 17:45:00', + 'valid_to': '2017-11-08 16:00:00', + 'balance': '3.14', + # 'is_protected': not checked + } + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=['kfet.add_checkout']), + } + + def test_get_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + def test_post_ok(self): + client = Client() + client.login(username='team1', password='team1') + + r = client.post(self.url, self.post_data) + + checkout = Checkout.objects.get(name='Checkout') + self.assertRedirects(r, checkout.get_absolute_url()) + + self.assertInstanceExpected(checkout, { + 'name': 'Checkout', + 'valid_from': timezone.make_aware(datetime(2017, 10, 8, 17, 45)), + 'valid_to': timezone.make_aware(datetime(2017, 11, 8, 16, 00)), + 'balance': Decimal('3.14'), + 'is_protected': False, + }) + + def test_post_forbidden(self): + r = self.client.post(self.url, self.post_data) + self.assertForbiddenKfet(r) + + +class CheckoutReadViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.checkout.read' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + @property + def url_kwargs(self): + return {'pk': self.checkout.pk} + + @property + def url_expected(self): + return '/k-fet/checkouts/{}'.format(self.checkout.pk) + + def setUp(self): + super().setUp() + self.checkout = Checkout.objects.create( + name='Checkout', + created_by=self.accounts['team'], + valid_from=self.now, + valid_to=self.now + timedelta(days=5), + ) + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + self.assertEqual(r.context['checkout'], self.checkout) + + +class CheckoutUpdateViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.checkout.update' + + http_methods = ['GET', 'POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + post_data = { + 'name': 'Checkout updated', + 'valid_from': '2018-01-01 08:00:00', + 'valid_to': '2018-07-01 16:00:00', + } + + @property + def url_kwargs(self): + return {'pk': self.checkout.pk} + + @property + def url_expected(self): + return '/k-fet/checkouts/{}/edit'.format(self.checkout.pk) + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=[ + 'kfet.change_checkout', + ]), + } + + def setUp(self): + super().setUp() + self.checkout = Checkout.objects.create( + name='Checkout', + valid_from=self.now, + valid_to=self.now + timedelta(days=5), + balance='3.14', + is_protected=False, + created_by=self.accounts['team'], + ) + + def test_get_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + def test_post_ok(self): + client = Client() + client.login(username='team1', password='team1') + + r = client.post(self.url, self.post_data) + self.assertRedirects(r, self.checkout.get_absolute_url()) + + self.checkout.refresh_from_db() + + self.assertInstanceExpected(self.checkout, { + 'name': 'Checkout updated', + 'valid_from': timezone.make_aware(datetime(2018, 1, 1, 8, 0, 0)), + 'valid_to': timezone.make_aware(datetime(2018, 7, 1, 16, 0, 0)), + }) + + def test_post_forbidden(self): + r = self.client.post(self.url, self.post_data) + self.assertForbiddenKfet(r) + + +class CheckoutStatementListViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.checkoutstatement' + url_expected = '/k-fet/checkouts/statements/' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def setUp(self): + super().setUp() + self.checkout1 = Checkout.objects.create( + created_by=self.accounts['team'], + name='Checkout 1', + valid_from=self.now, + valid_to=self.now + timedelta(days=5), + ) + self.checkout2 = Checkout.objects.create( + created_by=self.accounts['team'], + name='Checkout 2', + valid_from=self.now + timedelta(days=10), + valid_to=self.now + timedelta(days=15), + ) + self.statement1 = CheckoutStatement.objects.create( + checkout=self.checkout1, + by=self.accounts['team'], + balance_old=5, + balance_new=0, + amount_taken=5, + ) + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + expected_statements = ( + list(self.checkout1.statements.all()) + + list(self.checkout2.statements.all()) + ) + + self.assertQuerysetEqual( + r.context['checkoutstatements'], + map(repr, expected_statements), + ) + + +class CheckoutStatementCreateViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.checkoutstatement.create' + + http_methods = ['GET', 'POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + post_data = { + # Let + 'balance_001': 0, 'balance_002': 0, 'balance_005': 0, + 'balance_01': 0, 'balance_02': 0, 'balance_05': 0, + 'balance_1': 1, 'balance_2': 0, 'balance_5': 0, + 'balance_10': 1, 'balance_20': 0, 'balance_50': 0, + 'balance_100': 1, 'balance_200': 0, 'balance_500': 0, + # Taken + 'taken_001': 0, 'taken_002': 0, 'taken_005': 0, + 'taken_01': 0, 'taken_02': 0, 'taken_05': 0, + 'taken_1': 2, 'taken_2': 0, 'taken_5': 0, + 'taken_10': 2, 'taken_20': 0, 'taken_50': 0, + 'taken_100': 2, 'taken_200': 0, 'taken_500': 0, + 'taken_cheque': 0, + # 'not_count': not checked + } + + @property + def url_kwargs(self): + return {'pk_checkout': self.checkout.pk} + + @property + def url_expected(self): + return '/k-fet/checkouts/{}/statements/add'.format(self.checkout.pk) + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '001', perms=[ + 'kfet.add_checkoutstatement', + ]), + } + + def setUp(self): + super().setUp() + self.checkout = Checkout.objects.create( + name='Checkout', + created_by=self.accounts['team'], + balance=5, + valid_from=self.now, + valid_to=self.now + timedelta(days=5), + ) + + def test_get_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + @mock.patch('django.utils.timezone.now') + def test_post_ok(self, mock_now): + self.now += timedelta(days=2) + mock_now.return_value = self.now + + client = Client() + client.login(username='team1', password='team1') + + r = client.post(self.url, self.post_data) + self.assertRedirects(r, self.checkout.get_absolute_url()) + + statement = CheckoutStatement.objects.get(at=self.now) + + self.assertInstanceExpected(statement, { + 'by': self.accounts['team1'], + 'checkout': self.checkout, + 'balance_old': Decimal('5'), + 'balance_new': Decimal('111'), + 'amount_taken': Decimal('222'), + 'amount_error': Decimal('328'), + 'at': self.now, + 'not_count': False, + }) + + def test_post_forbidden(self): + r = self.client.post(self.url, self.post_data) + self.assertForbiddenKfet(r) + + +class CheckoutStatementUpdateViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.checkoutstatement.update' + + http_methods = ['GET', 'POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + post_data = { + 'amount_taken': 3, + 'amount_error': 2, + 'balance_old': 8, + 'balance_new': 5, + # Taken + 'taken_001': 0, 'taken_002': 0, 'taken_005': 0, + 'taken_01': 0, 'taken_02': 0, 'taken_05': 0, + 'taken_1': 1, 'taken_2': 1, 'taken_5': 0, + 'taken_10': 0, 'taken_20': 0, 'taken_50': 0, + 'taken_100': 0, 'taken_200': 0, 'taken_500': 0, + 'taken_cheque': 0, + } + + @property + def url_kwargs(self): + return { + 'pk_checkout': self.checkout.pk, + 'pk': self.statement.pk, + } + + @property + def url_expected(self): + return '/k-fet/checkouts/{pk_checkout}/statements/{pk}/edit'.format( + pk_checkout=self.checkout.pk, + pk=self.statement.pk, + ) + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=[ + 'kfet.change_checkoutstatement', + ]), + } + + def setUp(self): + super().setUp() + self.checkout = Checkout.objects.create( + name='Checkout', + created_by=self.accounts['team'], + balance=5, + valid_from=self.now, + valid_to=self.now + timedelta(days=5), + ) + self.statement = CheckoutStatement.objects.create( + by=self.accounts['team'], + checkout=self.checkout, + balance_new=5, + balance_old=8, + amount_error=2, + amount_taken=5, + ) + + def test_get_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + @mock.patch('django.utils.timezone.now') + def test_post_ok(self, mock_now): + self.now += timedelta(days=2) + mock_now.return_value = self.now + + client = Client() + client.login(username='team1', password='team1') + + r = client.post(self.url, self.post_data) + self.assertRedirects(r, self.checkout.get_absolute_url()) + + self.statement.refresh_from_db() + + self.assertInstanceExpected(self.statement, { + 'taken_1': 1, + 'taken_2': 1, + 'balance_new': 5, + 'balance_old': 8, + 'amount_error': 0, + 'amount_taken': 3, + }) + + def test_post_forbidden(self): + r = self.client.post(self.url, self.post_data) + self.assertForbiddenKfet(r) + + +class ArticleCategoryListViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.category' + url_expected = '/k-fet/categories/' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def setUp(self): + super().setUp() + self.category1 = ArticleCategory.objects.create(name='Category 1') + self.category2 = ArticleCategory.objects.create(name='Category 2') + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + self.assertQuerysetEqual( + r.context['categories'], + map(repr, [self.category1, self.category2]), + ) + + +class ArticleCategoryUpdateViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.category.update' + + http_methods = ['GET', 'POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + @property + def url_kwargs(self): + return {'pk': self.category.pk} + + @property + def url_expected(self): + return '/k-fet/categories/{}/edit'.format(self.category.pk) + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=[ + 'kfet.change_articlecategory', + ]), + } + + @property + def post_data(self): + return { + 'name': 'The Category', + # 'has_addcost': not checked + } + + def setUp(self): + super().setUp() + self.category = ArticleCategory.objects.create(name='Category') + + def test_get_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + def test_post_ok(self): + client = Client() + client.login(username='team1', password='team1') + + r = client.post(self.url, self.post_data) + self.assertRedirects(r, reverse('kfet.category')) + + self.category.refresh_from_db() + + self.assertInstanceExpected(self.category, { + 'name': 'The Category', + 'has_addcost': False, + }) + + def test_post_forbidden(self): + r = self.client.post(self.url, self.post_data) + self.assertForbiddenKfet(r) + + +class ArticleListViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.article' + url_expected = '/k-fet/articles/' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def setUp(self): + super().setUp() + category = ArticleCategory.objects.create(name='Category') + self.article1 = Article.objects.create( + name='Article 1', + category=category, + ) + self.article2 = Article.objects.create( + name='Article 2', + category=category, + ) + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + self.assertQuerysetEqual( + r.context['articles'], + map(repr, [self.article1, self.article2]), + ) + + +class ArticleCreateViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.article.create' + url_expected = '/k-fet/articles/new' + + http_methods = ['GET', 'POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=['kfet.add_article']), + } + + @property + def post_data(self): + return { + 'name': 'Article', + 'category': self.category.pk, + 'stock': 5, + 'price': '2.5', + } + + def setUp(self): + super().setUp() + self.category = ArticleCategory.objects.create(name='Category') + + def test_get_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + def test_post_ok(self): + client = Client() + client.login(username='team1', password='team1') + + r = client.post(self.url, self.post_data) + + article = Article.objects.get(name='Article') + + self.assertRedirects(r, article.get_absolute_url()) + + self.assertInstanceExpected(article, { + 'name': 'Article', + 'category': self.category, + }) + + def test_post_forbidden(self): + r = self.client.post(self.url, self.post_data) + self.assertForbiddenKfet(r) + + +class ArticleReadViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.article.read' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + @property + def url_kwargs(self): + return {'pk': self.article.pk} + + @property + def url_expected(self): + return '/k-fet/articles/{}'.format(self.article.pk) + + def setUp(self): + super().setUp() + self.article = Article.objects.create( + name='Article', + category=ArticleCategory.objects.create(name='Category'), + stock=5, + price=Decimal('2.5'), + ) + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + self.assertEqual(r.context['article'], self.article) + + +class ArticleUpdateViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.article.update' + + http_methods = ['GET', 'POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + @property + def url_kwargs(self): + return {'pk': self.article.pk} + + @property + def url_expected(self): + return '/k-fet/articles/{}/edit'.format(self.article.pk) + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=[ + 'kfet.change_article', + ]), + } + + @property + def post_data(self): + return { + 'name': 'The Article', + 'category': self.article.category.pk, + 'is_sold': '1', + 'price': '3.5', + 'box_type': 'carton', + # 'hidden': not checked + } + + def setUp(self): + super().setUp() + self.category = ArticleCategory.objects.create(name='Category') + self.article = Article.objects.create( + name='Article', + category=self.category, + stock=5, + price=Decimal('2.5'), + ) + + def test_get_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + def test_post_ok(self): + client = Client() + client.login(username='team1', password='team1') + + r = client.post(self.url, self.post_data) + + self.assertRedirects(r, self.article.get_absolute_url()) + + self.article.refresh_from_db() + + self.assertInstanceExpected(self.article, { + 'name': 'The Article', + 'price': Decimal('3.5'), + }) + + def test_post_forbidden(self): + r = self.client.post(self.url, self.post_data) + self.assertForbiddenKfet(r) + + +class ArticleStatSalesListViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.article.stat.sales.list' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + @property + def url_kwargs(self): + return {'pk': self.article.pk} + + @property + def url_expected(self): + return '/k-fet/articles/{}/stat/sales/list'.format(self.article.pk) + + def setUp(self): + super().setUp() + self.article = Article.objects.create( + name='Article', + category=ArticleCategory.objects.create(name='Category'), + ) + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + content = json.loads(r.content.decode('utf-8')) + + base_url = reverse('kfet.article.stat.sales', args=[self.article.pk]) + + expected_stats = [ + { + 'label': 'Derniers mois', + 'url': { + 'path': base_url, + 'query': { + 'scale_n_steps': ['7'], + 'scale_name': ['month'], + 'scale_last': ['True'], + }, + }, + }, + { + 'label': 'Dernières semaines', + 'url': { + 'path': base_url, + 'query': { + 'scale_n_steps': ['7'], + 'scale_name': ['week'], + 'scale_last': ['True'], + }, + }, + }, + { + 'label': 'Derniers jours', + 'url': { + 'path': base_url, + 'query': { + 'scale_n_steps': ['7'], + 'scale_name': ['day'], + 'scale_last': ['True'], + }, + }, + }, + ] + + for stat, expected in zip(content['stats'], expected_stats): + expected_url = expected.pop('url') + self.assertUrlsEqual(stat['url'], expected_url) + self.assertDictContainsSubset(expected, stat) + + +class ArticleStatSalesViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.article.stat.sales' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + @property + def url_kwargs(self): + return {'pk': self.article.pk} + + @property + def url_expected(self): + return '/k-fet/articles/{}/stat/sales'.format(self.article.pk) + + def setUp(self): + super().setUp() + self.article = Article.objects.create( + name='Article', + category=ArticleCategory.objects.create(name='Category'), + ) + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + +class KPsulViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.kpsul' + url_expected = '/k-fet/k-psul/' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + +class KPsulCheckoutDataViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.kpsul.checkout_data' + url_expected = '/k-fet/k-psul/checkout_data' + + http_methods = ['POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def setUp(self): + super().setUp() + self.checkout = Checkout.objects.create( + name='Checkout', + balance=Decimal('10'), + created_by=self.accounts['team'], + valid_from=self.now, + valid_to=self.now + timedelta(days=5), + ) + + def test_ok(self): + r = self.client.post(self.url, {'pk': self.checkout.pk}) + self.assertEqual(r.status_code, 200) + + content = json.loads(r.content.decode('utf-8')) + + expected = { + 'name': 'Checkout', + 'balance': '10.00', + } + + self.assertDictContainsSubset(expected, content) + + self.assertSetEqual(set(content.keys()), set([ + 'balance', 'id', 'name', 'valid_from', 'valid_to', + 'last_statement_at', 'last_statement_balance', + 'last_statement_by_first_name', 'last_statement_by_last_name', + 'last_statement_by_trigramme', + ])) + + +class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.kpsul.perform_operations' + url_expected = '/k-fet/k-psul/perform_operations' + + http_methods = ['POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def test_ok(self): + pass + + +class KPsulCancelOperationsViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.kpsul.cancel_operations' + url_expected = '/k-fet/k-psul/cancel_operations' + + http_methods = ['POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def test_ok(self): + pass + + +class KPsulArticlesData(ViewTestCaseMixin, TestCase): + url_name = 'kfet.kpsul.articles_data' + url_expected = '/k-fet/k-psul/articles_data' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def setUp(self): + super().setUp() + category = ArticleCategory.objects.create(name='Catégorie') + self.article1 = Article.objects.create( + category=category, + name='Article 1', + ) + self.article2 = Article.objects.create( + category=category, + name='Article 2', + price=Decimal('2.5'), + ) + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + content = json.loads(r.content.decode('utf-8')) + + articles = content['articles'] + + expected_list = [{ + 'category__name': 'Catégorie', + 'name': 'Article 1', + 'price': '0.00', + }, { + 'category__name': 'Catégorie', + 'name': 'Article 2', + 'price': '2.50', + }] + + for expected, article in zip(expected_list, articles): + self.assertDictContainsSubset(expected, article) + self.assertSetEqual(set(article.keys()), set([ + 'id', 'name', 'price', 'stock', + 'category_id', 'category__name', 'category__has_addcost', + ])) + + +class KPsulUpdateAddcost(ViewTestCaseMixin, TestCase): + url_name = 'kfet.kpsul.update_addcost' + url_expected = '/k-fet/k-psul/update_addcost' + + http_methods = ['POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + post_data = { + 'trigramme': '000', + 'amount': '0.5', + } + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=[ + 'kfet.manage_addcosts', + ]), + } + + def test_ok(self): + client = Client() + client.login(username='team1', password='team1') + + r = client.post(self.url, self.post_data) + self.assertEqual(r.status_code, 200) + + self.assertEqual( + kfet_config.addcost_for, + Account.objects.get(trigramme='000'), + ) + self.assertEqual(kfet_config.addcost_amount, Decimal('0.5')) + + def test_post_forbidden(self): + r = self.client.post(self.url, self.post_data) + self.assertForbidden(r) + + +class KPsulGetSettings(ViewTestCaseMixin, TestCase): + url_name = 'kfet.kpsul.get_settings' + url_expected = '/k-fet/k-psul/get_settings' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + +class HistoryJSONViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.history.json' + url_expected = '/k-fet/history.json' + + auth_user = 'user' + auth_forbidden = [None] + + def test_ok(self): + r = self.client.post(self.url) + self.assertEqual(r.status_code, 200) + + +class AccountReadJSONViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.account.read.json' + url_expected = '/k-fet/accounts/read.json' + + http_methods = ['POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def test_ok(self): + r = self.client.post(self.url, {'trigramme': '000'}) + self.assertEqual(r.status_code, 200) + + content = json.loads(r.content.decode('utf-8')) + + expected = { + 'name': 'first last', + 'trigramme': '000', + 'balance': '0.00', + } + self.assertDictContainsSubset(expected, content) + + self.assertSetEqual(set(content.keys()), set([ + 'balance', 'departement', 'email', 'id', 'is_cof', 'is_frozen', + 'name', 'nickname', 'promo', 'trigramme', + ])) + + +class SettingsListViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.settings' + url_expected = '/k-fet/settings/' + + auth_user = 'team1' + auth_forbidden = [None, 'user', 'team'] + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=[ + 'kfet.change_settings', + ]), + } + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + +class SettingsUpdateViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.settings.update' + url_expected = '/k-fet/settings/edit' + + http_methods = ['GET', 'POST'] + + auth_user = 'team1' + auth_forbidden = [None, 'user', 'team'] + + @property + def post_data(self): + return { + 'kfet_reduction_cof': '25', + 'kfet_addcost_amount': '0.5', + 'kfet_addcost_for': self.accounts['user'].pk, + 'kfet_overdraft_duration': '2 00:00:00', + 'kfet_overdraft_amount': '25', + 'kfet_cancel_duration': '00:20:00', + } + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=[ + 'kfet.change_settings', + ]), + } + + def test_get_ok(self): + r = self.client.get(self.url) + self.assertequal(r.status_code, 200) + + def test_post_ok(self): + r = self.client.post(self.url, self.post_data) + self.assertRedirects(r, reverse('kfet.settings')) + + self.assertDictEqual(dict(kfet_config.list()), { + 'reduction_cof': Decimal('25'), + 'addcost_amount': Decimal('0.5'), + 'addcost_for': self.accounts['user'], + 'overdraft_duration': timedelta(day=2), + 'overdraft_amount': Decimal('25'), + 'kfet_cancel_duration': timedelta(minutes=20), + }) + + +class TransferListViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.transfers' + url_expected = '/k-fet/transfers/' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + +class TransferCreateViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.transfers.create' + url_expected = '/k-fet/transfers/new' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + +class TransferPerformViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.transfers.perform' + url_expected = '/k-fet/transfers/perform' + + http_methods = ['POST'] + + auth_user = 'team1' + auth_forbidden = [None, 'user', 'team'] + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=[ + # Required + 'kfet.add_transfer', + # Convenience + 'kfet.perform_negative_operations', + ]), + } + + @property + def post_data(self): + return { + # General + 'comment': '', + # Formset management + 'form-TOTAL_FORMS': '10', + 'form-INITIAL_FORMS': '0', + 'form-MIN_NUM_FORMS': '1', + 'form-MAX_NUM_FORMS': '1000', + # Transfer 1 + 'form-0-from_acc': str(self.accounts['user'].pk), + 'form-0-to_acc': str(self.accounts['team'].pk), + 'form-0-amount': '3.5', + # Transfer 2 + 'form-1-from_acc': str(self.accounts['team'].pk), + 'form-1-to_acc': str(self.accounts['team1'].pk), + 'form-1-amount': '2.4', + } + + def test_ok(self): + r = self.client.post(self.url, self.post_data) + self.assertEqual(r.status_code, 200) + + user = self.accounts['user'] + user.refresh_from_db() + self.assertEqual(user.balance, Decimal('-3.5')) + + team = self.accounts['team'] + team.refresh_from_db() + self.assertEqual(team.balance, Decimal('1.1')) + + team1 = self.accounts['team1'] + team1.refresh_from_db() + self.assertEqual(team1.balance, Decimal('2.4')) + + +class TransferCancelViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.transfers.cancel' + url_expected = '/k-fet/transfers/cancel' + + http_methods = ['POST'] + + auth_user = 'team1' + auth_forbidden = [None, 'user', 'team'] + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=[ + # Convenience + 'kfet.perform_negative_operations', + ]), + } + + @property + def post_data(self): + return { + 'transfers[]': [self.transfer1.pk, self.transfer2.pk], + } + + def setUp(self): + super().setUp() + group = TransferGroup.objects.create() + self.transfer1 = Transfer.objects.create( + group=group, + from_acc=self.accounts['user'], + to_acc=self.accounts['team'], + amount='3.5', + ) + self.transfer2 = Transfer.objects.create( + group=group, + from_acc=self.accounts['team'], + to_acc=self.accounts['root'], + amount='2.4', + ) + + def test_ok(self): + r = self.client.post(self.url, self.post_data) + self.assertEqual(r.status_code, 200) + + user = self.accounts['user'] + user.refresh_from_db() + self.assertEqual(user.balance, Decimal('3.5')) + + team = self.accounts['team'] + team.refresh_from_db() + self.assertEqual(team.balance, Decimal('-1.1')) + + root = self.accounts['root'] + root.refresh_from_db() + self.assertEqual(root.balance, Decimal('-2.4')) + + +class InventoryListViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.inventory' + url_expected = '/k-fet/inventaires/' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def setUp(self): + super().setUp() + self.inventory = Inventory.objects.create( + by=self.accounts['team'], + ) + category = ArticleCategory.objects.create(name='Category') + article = Article.objects.create( + name='Article', + category=category, + ) + InventoryArticle.objects.create( + inventory=self.inventory, + article=article, + stock_old=5, + stock_new=0, + ) + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + inventories = r.context['inventories'] + self.assertQuerysetEqual( + inventories, + map(repr, [self.inventory]), + ) + + +class InventoryCreateViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.inventory.create' + url_expected = '/k-fet/inventaires/new' + + http_methods = ['GET', 'POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=[ + 'kfet.add_inventory', + ]), + } + + @property + def post_data(self): + return { + # Formset management + 'form-TOTAL_FORMS': '2', + 'form-INITIAL_FORMS': '2', + 'form-MIN_NUM_FORMS': '0', + 'form-MAX_NUM_FORMS': '1000', + # Article 1 + 'form-0-article': str(self.article1.pk), + 'form-0-stock_new': '5', + # Article 2 + 'form-1-article': str(self.article2.pk), + 'form-1-stock_new': '10', + } + + def setUp(self): + super().setUp() + category = ArticleCategory.objects.create(name='Category') + self.article1 = Article.objects.create( + category=category, + name='Article 1', + ) + self.article2 = Article.objects.create( + category=category, + name='Article 2', + ) + + def test_get_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + def test_post_ok(self): + client = Client() + client.login(username='team1', password='team1') + + r = client.post(self.url, self.post_data) + self.assertRedirects(r, reverse('kfet.inventory')) + + def test_post_forbidden(self): + r = self.client.post(self.url, self.post_data) + self.assertForbiddenKfet(r) + + +class InventoryReadViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.inventory.read' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + @property + def url_kwargs(self): + return {'pk': self.inventory.pk} + + @property + def url_expected(self): + return '/k-fet/inventaires/{}'.format(self.inventory.pk) + + def setUp(self): + super().setUp() + self.inventory = Inventory.objects.create( + by=self.accounts['team'], + ) + category = ArticleCategory.objects.create(name='Category') + article = Article.objects.create( + name='Article', + category=category, + ) + InventoryArticle.objects.create( + inventory=self.inventory, + article=article, + stock_old=5, + stock_new=0, + ) + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + +class OrderListViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.order' + url_expected = '/k-fet/orders/' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + def setUp(self): + super().setUp() + category = ArticleCategory.objects.create(name='Category') + article = Article.objects.create(name='Article', category=category) + + supplier = Supplier.objects.create(name='Supplier') + SupplierArticle.objects.create(supplier=supplier, article=article) + + self.order = Order.objects.create(supplier=supplier) + OrderArticle.objects.create( + order=self.order, + article=article, + quantity_ordered=24, + ) + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + orders = r.context['orders'] + self.assertQuerysetEqual( + orders, + map(repr, [self.order]), + ) + + +class OrderReadViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.order.read' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + @property + def url_kwargs(self): + return {'pk': self.order.pk} + + @property + def url_expected(self): + return '/k-fet/orders/{}'.format(self.order.pk) + + def setUp(self): + super().setUp() + category = ArticleCategory.objects.create(name='Category') + article = Article.objects.create(name='Article', category=category) + + supplier = Supplier.objects.create(name='Supplier') + SupplierArticle.objects.create(supplier=supplier, article=article) + + self.order = Order.objects.create(supplier=supplier) + OrderArticle.objects.create( + order=self.order, + article=article, + quantity_ordered=24, + ) + + def test_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + +class SupplierUpdateViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.order.supplier.update' + + http_methods = ['GET', 'POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + @property + def url_kwargs(self): + return {'pk': self.supplier.pk} + + @property + def url_expected(self): + return '/k-fet/orders/suppliers/{}/edit'.format(self.supplier.pk) + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=[ + 'kfet.change_supplier', + ]), + } + + @property + def post_data(self): + return { + 'name': 'The Supplier', + 'phone': '', + 'comment': '', + 'address': '', + 'email': '', + } + + def setUp(self): + super().setUp() + self.supplier = Supplier.objects.create(name='Supplier') + + def test_get_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + def test_post_ok(self): + client = Client() + client.login(username='team1', password='team1') + + r = client.post(self.url, self.post_data) + self.assertRedirects(r, reverse('kfet.order')) + + self.supplier.refresh_from_db() + self.assertEqual(self.supplier.name, 'The Supplier') + + def test_post_forbidden(self): + r = self.client.post(self.url, self.post_data) + self.assertForbiddenKfet(r) + + +class OrderCreateViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.order.new' + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + @property + def url_kwargs(self): + return {'pk': self.supplier.pk} + + @property + def url_expected(self): + return '/k-fet/orders/suppliers/{}/new-order'.format(self.supplier.pk) + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=['kfet.add_order']), + } + + @property + def post_data(self): + return { + # Formset management + 'form-TOTAL_FORMS': '1', + 'form-INITIAL_FORMS': '1', + 'form-MIN_NUM_FORMS': '0', + 'form-MAX_NUM_FORMS': '1000', + # Article + 'form-0-article': self.article.pk, + 'form-0-quantity_ordered': '20', + } + + def setUp(self): + super().setUp() + category = ArticleCategory.objects.create(name='Category') + self.article = Article.objects.create( + name='Article', + category=category, + ) + + self.supplier = Supplier.objects.create(name='Supplier') + SupplierArticle.objects.create( + supplier=self.supplier, + article=self.article, + ) + + def test_get_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + @mock.patch('django.utils.timezone.now') + def test_post_ok(self, mock_now): + mock_now.return_value = self.now + + client = Client() + client.login(username='team1', password='team1') + + r = client.post(self.url, self.post_data) + + order = Order.objects.get(at=self.now) + + self.assertRedirects(r, reverse('kfet.order.read', args=[order.pk])) + + def test_post_forbidden(self): + r = self.client.post(self.url, self.post_data) + self.assertForbiddenKfet(r) + + +class OrderToInventoryViewTests(ViewTestCaseMixin, TestCase): + url_name = 'kfet.order.to_inventory' + + http_methods = ['GET', 'POST'] + + auth_user = 'team' + auth_forbidden = [None, 'user'] + + @property + def url_kwargs(self): + return {'pk': self.order.pk} + + @property + def url_expected(self): + return '/k-fet/orders/{}/to_inventory'.format(self.order.pk) + + @property + def users_extra(self): + return { + 'team1': create_team('team1', '101', perms=[ + 'kfet.order_to_inventory', + ]), + } + + @property + def post_data(self): + return { + # Formset mangaement + 'form-TOTAL_FORMS': '1', + 'form-INITIAL_FORMS': '1', + 'form-MIN_NUM_FORMS': '0', + 'form-MAX_NUM_FORMS': '1000', + # Article 1 + 'form-0-article': self.article.pk, + 'form-0-quantity_received': '20', + 'form-0-price_HT': '', + 'form-0-TVA': '', + 'form-0-rights': '', + } + + def setUp(self): + super().setUp() + category = ArticleCategory.objects.create(name='Category') + self.article = Article.objects.create( + name='Article', + category=category, + ) + + supplier = Supplier.objects.create(name='Supplier') + SupplierArticle.objects.create(supplier=supplier, article=self.article) + + self.order = Order.objects.create(supplier=supplier) + OrderArticle.objects.create( + order=self.order, + article=self.article, + quantity_ordered=24, + ) + + def test_get_ok(self): + r = self.client.get(self.url) + self.assertEqual(r.status_code, 200) + + @mock.patch('django.utils.timezone.now') + def test_post_ok(self, mock_now): + mock_now.return_value = self.now + + client = Client() + client.login(username='team1', password='team1') + + r = client.post(self.url, self.post_data) + self.assertRedirects(r, reverse('kfet.order')) + + inventory = Inventory.objects.first() + + self.assertInstanceExpected(inventory, { + 'by': self.accounts['team1'], + 'at': self.now, + 'order': self.order, + }) + self.assertQuerysetEqual( + inventory.articles.all(), + map(repr, [self.article]), + ) + + compte = InventoryArticle.objects.get(article=self.article) + + self.assertInstanceExpected(compte, { + 'stock_old': 0, + 'stock_new': 20, + 'stock_error': 0, + }) + + def test_post_forbidden(self): + r = self.client.post(self.url, self.post_data) + self.assertForbiddenKfet(r) diff --git a/kfet/tests/testcases.py b/kfet/tests/testcases.py index c2e1b848..977345e7 100644 --- a/kfet/tests/testcases.py +++ b/kfet/tests/testcases.py @@ -1,16 +1,114 @@ from unittest import mock +from urllib.parse import parse_qs, urlparse from django.core.urlresolvers import reverse from django.http import QueryDict from django.test import Client +from django.utils import timezone from .utils import create_root, create_team, create_user -class ViewTestCaseMixin: +class TestCaseMixin: + def assertForbidden(self, response): + request = response.wsgi_request + + try: + try: + # Is this an HTTP Forbidden response ? + self.assertEqual(response.status_code, 403) + except AssertionError: + # A redirection to the login view is fine too. + + # Let's build the login url with the 'next' param on current + # page. + full_path = request.get_full_path() + + querystring = QueryDict(mutable=True) + querystring['next'] = full_path + + login_url = '/login?' + querystring.urlencode(safe='/') + + # We don't focus on what the login view does. + # So don't fetch the redirect. + self.assertRedirects( + response, login_url, + fetch_redirect_response=False, + ) + except AssertionError: + raise AssertionError( + "%(http_method)s request at %(path)s should be forbidden for " + "%(username)s user.\n" + "Response isn't 403, nor a redirect to login view. Instead, " + "response code is %(code)d." % { + 'http_method': request.method, + 'path': request.get_full_path(), + 'username': ( + "'{}'".format(request.user) + if request.user.is_authenticated() + else 'anonymous' + ), + 'code': response.status_code, + } + ) + + def assertForbiddenKfet(self, response, form_ctx='form'): + try: + self.assertEqual(response.status_code, 200) + try: + form = response.context[form_ctx] + self.assertIn("Permission refusée", form.non_field_errors()) + except (AssertionError, AttributeError, KeyError): + messages = [str(msg) for msg in response.context['messages']] + self.assertIn("Permission refusée", messages) + except AssertionError: + request = response.wsgi_request + raise AssertionError( + "%(http_method)s request at %(path)s should raise an error " + "for %(username)s user.\n" + "Cannot find any errors in non-field errors of form " + "'%(form_ctx)s', nor in messages." % { + 'http_method': request.method, + 'path': request.get_full_path(), + 'username': ( + "'%s'" % request.user + if request.user.is_authenticated() + else 'anonymous' + ), + 'form_ctx': form_ctx, + } + ) + + def assertInstanceExpected(self, instance, expected): + for attr, expected_value in expected.items(): + value = getattr(instance, attr) + if callable(value): + value = value() + self.assertEqual(value, expected_value) + + def assertUrlsEqual(self, actual, expected): + if type(expected) == dict: + parsed = urlparse(actual) + checks = ['scheme', 'netloc', 'path', 'params'] + for check in checks: + self.assertEqual( + getattr(parsed, check), + expected.get(check, ''), + ) + self.assertDictEqual( + parse_qs(parsed.query), + expected.get('query', {}), + ) + else: + self.assertEqual(actual, expected) + + +class ViewTestCaseMixin(TestCaseMixin): url_name = None url_expected = None + http_methods = ['GET'] + auth_user = None auth_forbidden = [] @@ -22,6 +120,11 @@ class ViewTestCaseMixin: patcher_messages.start() self.addCleanup(patcher_messages.stop) + # A test can mock 'django.utils.timezone.now' and give this as return + # value. E.g. it is useful if the test checks values of 'auto_now' or + # 'auto_now_add' fields. + self.now = timezone.now() + self.users = {} self.accounts = {} @@ -58,6 +161,11 @@ class ViewTestCaseMixin: if hasattr(user.profile, 'account_kfet'): self.accounts[label] = user.profile.account_kfet + def get_user(self, label): + if self.auth_user is not None: + return self.auth_user + return self.auth_user_mapping.get(label) + @property def urls_conf(self): return [{ @@ -81,62 +189,24 @@ class ViewTestCaseMixin: def url(self): return self.t_urls[0] - def assertForbidden(self, response): - request = response.wsgi_request - - try: - try: - # Is this an HTTP Forbidden response ? - self.assertEqual(response.status_code, 403) - except AssertionError: - # A redirection to the login view is fine too. - - # Let's build the login url with the 'next' param on current - # page. - full_path = request.get_full_path() - - querystring = QueryDict(mutable=True) - querystring['next'] = full_path - - login_url = '/login?' + querystring.urlencode(safe='/') - - # We don't focus on what the login view does. - # So don't fetch the redirect. - self.assertRedirects( - response, login_url, - fetch_redirect_response=False, - ) - except AssertionError: - raise AssertionError( - "%(http_method)s request at %(path)s should be forbidden for " - "%(username)s user.\n" - "Response isn't 403, nor a redirect to login view. Instead, " - "response code is %(code)d." % { - 'http_method': request.method, - 'path': request.get_full_path(), - 'username': ( - "'{}'".format(request.user.username) - if request.user.username - else 'anonymous' - ), - 'code': response.status_code, - } - ) - - def assertForbiddenKfet(self, response): - self.assertEqual(response.status_code, 200) - form = response.context['form'] - self.assertIn("Permission refusée", form.non_field_errors) - def test_urls(self): for url, conf in zip(self.t_urls, self.urls_conf): self.assertEqual(url, conf['expected']) def test_forbidden(self): - for creds in self.auth_forbidden: - for url in self.t_urls: - client = Client() - if creds is not None: - client.login(username=creds, password=creds) - r = client.get(url) - self.assertForbidden(r) + for method in self.http_methods: + for user in self.auth_forbidden: + for url in self.t_urls: + self.check_forbidden(method, url, user) + + def check_forbidden(self, method, url, user=None): + method = method.lower() + client = Client() + if user is not None: + client.login(username=user, password=user) + + send_request = getattr(client, method) + data = getattr(self, '{}_data'.format(method), {}) + + r = send_request(url, data) + self.assertForbidden(r) diff --git a/kfet/tests/utils.py b/kfet/tests/utils.py index 4b739003..4681da67 100644 --- a/kfet/tests/utils.py +++ b/kfet/tests/utils.py @@ -7,44 +7,14 @@ from ..models import Account User = get_user_model() -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) - - """ - u_labels = set(perms_labels) - - perms = [] - for label in u_labels: - app_label, codename = label.split('.', 1) - perms.append( - Permission.objects.get( - content_type__app_label=app_label, - codename=codename, - ) - ) - - user.user_permissions.add(*perms) - - # 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/1.11/topics/auth/default/#permission-caching - return User.objects.get(pk=user.pk) - - def _create_user_and_account(user_attrs, account_attrs, perms=None): - user_attrs.setdefault('password', user_attrs['username']) - user = User.objects.create_user(**user_attrs) + 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', None) + kfet_pwd = account_attrs.pop('password', 'kfetpwd_{}'.format(user_pwd)) account = Account.objects.create(**account_attrs) @@ -52,8 +22,6 @@ def _create_user_and_account(user_attrs, account_attrs, perms=None): user = user_add_perms(user, perms) if 'kfet.is_team' in perms: - if kfet_pwd is None: - kfet_pwd = 'kfetpwd_{}'.format(user_attrs['password']) account.change_pwd(kfet_pwd) account.save() @@ -98,8 +66,41 @@ def create_root(username='root', trigramme='200', **kwargs): 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): + 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/1.11/topics/auth/default/#permission-caching + return User.objects.get(pk=user.pk)