aboutsummaryrefslogtreecommitdiff
path: root/budget/tests
diff options
context:
space:
mode:
authorAlexis Metaireau <alexis@notmyidea.org>2017-07-07 00:06:56 +0200
committerGitHub <noreply@github.com>2017-07-07 00:06:56 +0200
commit3a4282fd75e3b3317b2b08b4aa2e6ac154310e73 (patch)
tree9470c907ba1f884246af87d26d55c3aaac6d6dc5 /budget/tests
parent0e374cd5e0ef5a9be67084365f91de2ab84f636c (diff)
downloadihatemoney-mirror-3a4282fd75e3b3317b2b08b4aa2e6ac154310e73.zip
ihatemoney-mirror-3a4282fd75e3b3317b2b08b4aa2e6ac154310e73.tar.gz
ihatemoney-mirror-3a4282fd75e3b3317b2b08b4aa2e6ac154310e73.tar.bz2
Absolute imports & some other improvements (#243)
* Use absolute imports and rename package to ihatemoney * Add a ihatemoney command * Factorize application creation logic * Refactor the tests * Update the wsgi.py module with the new create_app() function * Fix some styling thanks to Flake8. * Automate Flake8 check in the CI.
Diffstat (limited to 'budget/tests')
-rw-r--r--budget/tests/__init__.py0
-rw-r--r--budget/tests/ihatemoney.cfg7
-rw-r--r--budget/tests/ihatemoney_envvar.cfg7
-rw-r--r--budget/tests/tests.py1181
4 files changed, 0 insertions, 1195 deletions
diff --git a/budget/tests/__init__.py b/budget/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/budget/tests/__init__.py
+++ /dev/null
diff --git a/budget/tests/ihatemoney.cfg b/budget/tests/ihatemoney.cfg
deleted file mode 100644
index 6345fcf..0000000
--- a/budget/tests/ihatemoney.cfg
+++ /dev/null
@@ -1,7 +0,0 @@
-DEBUG = False
-SQLALCHEMY_DATABASE_URI = 'sqlite:///budget.db'
-SQLACHEMY_ECHO = DEBUG
-
-SECRET_KEY = "supersecret"
-
-MAIL_DEFAULT_SENDER = ("Budget manager", "budget@notmyidea.org")
diff --git a/budget/tests/ihatemoney_envvar.cfg b/budget/tests/ihatemoney_envvar.cfg
deleted file mode 100644
index dbc078e..0000000
--- a/budget/tests/ihatemoney_envvar.cfg
+++ /dev/null
@@ -1,7 +0,0 @@
-DEBUG = False
-SQLALCHEMY_DATABASE_URI = 'sqlite:///budget.db'
-SQLACHEMY_ECHO = DEBUG
-
-SECRET_KEY = "lalatra"
-
-MAIL_DEFAULT_SENDER = ("Budget manager", "budget@notmyidea.org")
diff --git a/budget/tests/tests.py b/budget/tests/tests.py
deleted file mode 100644
index 386920f..0000000
--- a/budget/tests/tests.py
+++ /dev/null
@@ -1,1181 +0,0 @@
- # -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-try:
- import unittest2 as unittest
-except ImportError:
- import unittest # NOQA
-
-import os
-import json
-from collections import defaultdict
-import six
-
-from werkzeug.security import generate_password_hash
-from flask import session
-
-# Unset configuration file env var if previously set
-if 'IHATEMONEY_SETTINGS_FILE_PATH' in os.environ:
- del os.environ['IHATEMONEY_SETTINGS_FILE_PATH']
-
-from .. import run
-from .. import models
-from .. import utils
-
-__HERE__ = os.path.dirname(os.path.abspath(__file__))
-
-
-class TestCase(unittest.TestCase):
-
- def setUp(self):
- run.app.config['TESTING'] = True
-
- run.app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///memory"
- run.app.config['WTF_CSRF_ENABLED'] = False # simplify the tests
- self.app = run.app.test_client()
- try:
- models.db.init_app(run.app)
- run.mail.init_app(run.app)
- except:
- pass
-
- models.db.app = run.app
- models.db.create_all()
-
- def tearDown(self):
- # clean after testing
- models.db.session.remove()
- models.db.drop_all()
- # reconfigure app with default settings
- run.configure()
-
- def login(self, project, password=None, test_client=None):
- password = password or project
-
- return self.app.post('/authenticate', data=dict(
- id=project, password=password), follow_redirects=True)
-
- def post_project(self, name):
- """Create a fake project"""
- # create the project
- self.app.post("/create", data={
- 'name': name,
- 'id': name,
- 'password': name,
- 'contact_email': '%s@notmyidea.org' % name
- })
-
- def create_project(self, name):
- models.db.session.add(models.Project(id=name, name=six.text_type(name),
- password=name, contact_email="%s@notmyidea.org" % name))
- models.db.session.commit()
-
-
-class BudgetTestCase(TestCase):
-
- def test_default_configuration(self):
- """Test that default settings are loaded when no other configuration file is specified"""
- run.configure()
- self.assertFalse(run.app.config['DEBUG'])
- self.assertEqual(run.app.config['SQLALCHEMY_DATABASE_URI'], 'sqlite:///budget.db')
- self.assertFalse(run.app.config['SQLALCHEMY_TRACK_MODIFICATIONS'])
- self.assertEqual(run.app.config['SECRET_KEY'], 'tralala')
- self.assertEqual(run.app.config['MAIL_DEFAULT_SENDER'],
- ("Budget manager", "budget@notmyidea.org"))
-
- def test_env_var_configuration_file(self):
- """Test that settings are loaded from the specified configuration file"""
- os.environ['IHATEMONEY_SETTINGS_FILE_PATH'] = os.path.join(__HERE__,
- "ihatemoney_envvar.cfg")
- run.configure()
- self.assertEqual(run.app.config['SECRET_KEY'], 'lalatra')
-
- # Test that the specified configuration file is loaded
- # even if the default configuration file ihatemoney.cfg exists
- os.environ['IHATEMONEY_SETTINGS_FILE_PATH'] = os.path.join(__HERE__,
- "ihatemoney_envvar.cfg")
- run.app.config.root_path = __HERE__
- run.configure()
- self.assertEqual(run.app.config['SECRET_KEY'], 'lalatra')
-
- if 'IHATEMONEY_SETTINGS_FILE_PATH' in os.environ:
- del os.environ['IHATEMONEY_SETTINGS_FILE_PATH']
-
- def test_default_configuration_file(self):
- """Test that settings are loaded from the default configuration file"""
- run.app.config.root_path = __HERE__
- run.configure()
- self.assertEqual(run.app.config['SECRET_KEY'], 'supersecret')
-
- def test_notifications(self):
- """Test that the notifications are sent, and that email adresses
- are checked properly.
- """
- # sending a message to one person
- with run.mail.record_messages() as outbox:
-
- # create a project
- self.login("raclette")
-
- self.post_project("raclette")
- self.app.post("/raclette/invite",
- data={"emails": 'alexis@notmyidea.org'})
-
- self.assertEqual(len(outbox), 2)
- self.assertEqual(outbox[0].recipients, ["raclette@notmyidea.org"])
- self.assertEqual(outbox[1].recipients, ["alexis@notmyidea.org"])
-
- # sending a message to multiple persons
- with run.mail.record_messages() as outbox:
- self.app.post("/raclette/invite",
- data={"emails": 'alexis@notmyidea.org, toto@notmyidea.org'})
-
- # only one message is sent to multiple persons
- self.assertEqual(len(outbox), 1)
- self.assertEqual(outbox[0].recipients,
- ["alexis@notmyidea.org", "toto@notmyidea.org"])
-
- # mail address checking
- with run.mail.record_messages() as outbox:
- response = self.app.post("/raclette/invite",
- data={"emails": "toto"})
- self.assertEqual(len(outbox), 0) # no message sent
- self.assertIn("The email toto is not valid", response.data.decode('utf-8'))
-
- # mixing good and wrong adresses shouldn't send any messages
- with run.mail.record_messages() as outbox:
- self.app.post("/raclette/invite",
- data={"emails": 'alexis@notmyidea.org, alexis'}) # not valid
-
- # only one message is sent to multiple persons
- self.assertEqual(len(outbox), 0)
-
- def test_password_reminder(self):
- # test that it is possible to have an email cotaining the password of a
- # project in case people forget it (and it happens!)
-
- self.create_project("raclette")
-
- with run.mail.record_messages() as outbox:
- # a nonexisting project should not send an email
- self.app.post("/password-reminder", data={"id": "unexisting"})
- self.assertEqual(len(outbox), 0)
-
- # a mail should be sent when a project exists
- self.app.post("/password-reminder", data={"id": "raclette"})
- self.assertEqual(len(outbox), 1)
- self.assertIn("raclette", outbox[0].body)
- self.assertIn("raclette@notmyidea.org", outbox[0].recipients)
-
- def test_project_creation(self):
- with run.app.test_client() as c:
-
- # add a valid project
- c.post("/create", data={
- 'name': 'The fabulous raclette party',
- 'id': 'raclette',
- 'password': 'party',
- 'contact_email': 'raclette@notmyidea.org'
- })
-
- # session is updated
- self.assertEqual(session['raclette'], 'party')
-
- # project is created
- self.assertEqual(len(models.Project.query.all()), 1)
-
- # Add a second project with the same id
- models.Project.query.get('raclette')
-
- c.post("/create", data={
- 'name': 'Another raclette party',
- 'id': 'raclette', # already used !
- 'password': 'party',
- 'contact_email': 'raclette@notmyidea.org'
- })
-
- # no new project added
- self.assertEqual(len(models.Project.query.all()), 1)
-
- def test_project_deletion(self):
-
- with run.app.test_client() as c:
- c.post("/create", data={
- 'name': 'raclette party',
- 'id': 'raclette',
- 'password': 'party',
- 'contact_email': 'raclette@notmyidea.org'
- })
-
- # project added
- self.assertEqual(len(models.Project.query.all()), 1)
-
- c.get('/raclette/delete')
-
- # project removed
- self.assertEqual(len(models.Project.query.all()), 0)
-
- def test_membership(self):
- self.post_project("raclette")
- self.login("raclette")
-
- # adds a member to this project
- self.app.post("/raclette/members/add", data={'name': 'alexis'})
- self.assertEqual(len(models.Project.query.get("raclette").members), 1)
-
- # adds him twice
- result = self.app.post("/raclette/members/add",
- data={'name': 'alexis'})
-
- # should not accept him
- self.assertEqual(len(models.Project.query.get("raclette").members), 1)
-
- # add fred
- self.app.post("/raclette/members/add", data={'name': 'fred'})
- self.assertEqual(len(models.Project.query.get("raclette").members), 2)
-
- # check fred is present in the bills page
- result = self.app.get("/raclette/")
- self.assertIn("fred", result.data.decode('utf-8'))
-
- # remove fred
- self.app.post("/raclette/members/%s/delete" %
- models.Project.query.get("raclette").members[-1].id)
-
- # as fred is not bound to any bill, he is removed
- self.assertEqual(len(models.Project.query.get("raclette").members), 1)
-
- # add fred again
- self.app.post("/raclette/members/add", data={'name': 'fred'})
- fred_id = models.Project.query.get("raclette").members[-1].id
-
- # bound him to a bill
- result = self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'fromage à raclette',
- 'payer': fred_id,
- 'payed_for': [fred_id, ],
- 'amount': '25',
- })
-
- # remove fred
- self.app.post("/raclette/members/%s/delete" % fred_id)
-
- # he is still in the database, but is deactivated
- self.assertEqual(len(models.Project.query.get("raclette").members), 2)
- self.assertEqual(
- len(models.Project.query.get("raclette").active_members), 1)
-
- # as fred is now deactivated, check that he is not listed when adding
- # a bill or displaying the balance
- result = self.app.get("/raclette/")
- self.assertNotIn(("/raclette/members/%s/delete" % fred_id), result.data.decode('utf-8'))
-
- result = self.app.get("/raclette/add")
- self.assertNotIn("fred", result.data.decode('utf-8'))
-
- # adding him again should reactivate him
- self.app.post("/raclette/members/add", data={'name': 'fred'})
- self.assertEqual(
- len(models.Project.query.get("raclette").active_members), 2)
-
- # adding an user with the same name as another user from a different
- # project should not cause any troubles
- self.post_project("randomid")
- self.login("randomid")
- self.app.post("/randomid/members/add", data={'name': 'fred'})
- self.assertEqual(
- len(models.Project.query.get("randomid").active_members), 1)
-
- def test_person_model(self):
- self.post_project("raclette")
- self.login("raclette")
-
- # adds a member to this project
- self.app.post("/raclette/members/add", data={'name': 'alexis'})
- alexis = models.Project.query.get("raclette").members[-1]
-
- # should not have any bills
- self.assertFalse(alexis.has_bills())
-
- # bound him to a bill
- self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'fromage à raclette',
- 'payer': alexis.id,
- 'payed_for': [alexis.id, ],
- 'amount': '25',
- })
-
- # should have a bill now
- alexis = models.Project.query.get("raclette").members[-1]
- self.assertTrue(alexis.has_bills())
-
- def test_member_delete_method(self):
- self.post_project("raclette")
- self.login("raclette")
-
- # adds a member to this project
- self.app.post("/raclette/members/add", data={'name': 'alexis'})
-
- # try to remove the member using GET method
- response = self.app.get("/raclette/members/1/delete")
- self.assertEqual(response.status_code, 405)
-
- #delete user using POST method
- self.app.post("/raclette/members/1/delete")
- self.assertEqual(
- len(models.Project.query.get("raclette").active_members), 0)
- #try to delete an user already deleted
- self.app.post("/raclette/members/1/delete")
-
- def test_demo(self):
- # test that a demo project is created if none is defined
- self.assertEqual([], models.Project.query.all())
- self.app.get("/demo")
- self.assertTrue(models.Project.query.get("demo") is not None)
-
- def test_deactivated_demo(self):
- run.app.config['ACTIVATE_DEMO_PROJECT'] = False
-
- # test redirection to the create project form when demo is deactivated
- resp = self.app.get("/demo")
- self.assertIn('<a href="/create?project_id=demo">', resp.data.decode('utf-8'))
-
- def test_authentication(self):
- # try to authenticate without credentials should redirect
- # to the authentication page
- resp = self.app.post("/authenticate")
- self.assertIn("Authentication", resp.data.decode('utf-8'))
-
- # raclette that the login / logout process works
- self.create_project("raclette")
-
- # try to see the project while not being authenticated should redirect
- # to the authentication page
- resp = self.app.get("/raclette", follow_redirects=True)
- self.assertIn("Authentication", resp.data.decode('utf-8'))
-
- # try to connect with wrong credentials should not work
- with run.app.test_client() as c:
- resp = c.post("/authenticate",
- data={'id': 'raclette', 'password': 'nope'})
-
- self.assertIn("Authentication", resp.data.decode('utf-8'))
- self.assertNotIn('raclette', session)
-
- # try to connect with the right credentials should work
- with run.app.test_client() as c:
- resp = c.post("/authenticate",
- data={'id': 'raclette', 'password': 'raclette'})
-
- self.assertNotIn("Authentication", resp.data.decode('utf-8'))
- self.assertIn('raclette', session)
- self.assertEqual(session['raclette'], 'raclette')
-
- # logout should wipe the session out
- c.get("/exit")
- self.assertNotIn('raclette', session)
-
- def test_admin_authentication(self):
- run.app.config['ADMIN_PASSWORD'] = generate_password_hash("pass")
-
- # test the redirection to the authentication page when trying to access admin endpoints
- resp = self.app.get("/create")
- self.assertIn('<a href="/admin?goto=%2Fcreate">', resp.data.decode('utf-8'))
-
- # test right password
- resp = self.app.post("/admin?goto=%2Fcreate", data={'admin_password': 'pass'})
- self.assertIn('<a href="/create">/create</a>', resp.data.decode('utf-8'))
-
- # test wrong password
- resp = self.app.post("/admin?goto=%2Fcreate", data={'admin_password': 'wrong'})
- self.assertNotIn('<a href="/create">/create</a>', resp.data.decode('utf-8'))
-
- # test empty password
- resp = self.app.post("/admin?goto=%2Fcreate", data={'admin_password': ''})
- self.assertNotIn('<a href="/create">/create</a>', resp.data.decode('utf-8'))
-
- def test_manage_bills(self):
- self.post_project("raclette")
-
- # add two persons
- self.app.post("/raclette/members/add", data={'name': 'alexis'})
- self.app.post("/raclette/members/add", data={'name': 'fred'})
-
- members_ids = [m.id for m in
- models.Project.query.get("raclette").members]
-
- # create a bill
- self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'fromage à raclette',
- 'payer': members_ids[0],
- 'payed_for': members_ids,
- 'amount': '25',
- })
- models.Project.query.get("raclette")
- bill = models.Bill.query.one()
- self.assertEqual(bill.amount, 25)
-
- # edit the bill
- self.app.post("/raclette/edit/%s" % bill.id, data={
- 'date': '2011-08-10',
- 'what': 'fromage à raclette',
- 'payer': members_ids[0],
- 'payed_for': members_ids,
- 'amount': '10',
- })
-
- bill = models.Bill.query.one()
- self.assertEqual(bill.amount, 10, "bill edition")
-
- # delete the bill
- self.app.get("/raclette/delete/%s" % bill.id)
- self.assertEqual(0, len(models.Bill.query.all()), "bill deletion")
-
- # test balance
- self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'fromage à raclette',
- 'payer': members_ids[0],
- 'payed_for': members_ids,
- 'amount': '19',
- })
-
- self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'fromage à raclette',
- 'payer': members_ids[1],
- 'payed_for': members_ids[0],
- 'amount': '20',
- })
-
- self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'fromage à raclette',
- 'payer': members_ids[1],
- 'payed_for': members_ids,
- 'amount': '17',
- })
-
- balance = models.Project.query.get("raclette").balance
- self.assertEqual(set(balance.values()), set([19.0, -19.0]))
-
- #Bill with negative amount
- self.app.post("/raclette/add", data={
- 'date': '2011-08-12',
- 'what': 'fromage à raclette',
- 'payer': members_ids[0],
- 'payed_for': members_ids,
- 'amount': '-25'
- })
- bill = models.Bill.query.filter(models.Bill.date == '2011-08-12')[0]
- self.assertEqual(bill.amount, -25)
-
- #add a bill with a comma
- self.app.post("/raclette/add", data={
- 'date': '2011-08-01',
- 'what': 'fromage à raclette',
- 'payer': members_ids[0],
- 'payed_for': members_ids,
- 'amount': '25,02',
- })
- bill = models.Bill.query.filter(models.Bill.date == '2011-08-01')[0]
- self.assertEqual(bill.amount, 25.02)
-
- def test_weighted_balance(self):
- self.post_project("raclette")
-
- # add two persons
- self.app.post("/raclette/members/add", data={'name': 'alexis'})
- self.app.post("/raclette/members/add", data={'name': 'freddy familly', 'weight': 4})
-
- members_ids = [m.id for m in
- models.Project.query.get("raclette").members]
-
- # test balance
- self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'fromage à raclette',
- 'payer': members_ids[0],
- 'payed_for': members_ids,
- 'amount': '10',
- })
-
- self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'pommes de terre',
- 'payer': members_ids[1],
- 'payed_for': members_ids,
- 'amount': '10',
- })
-
- balance = models.Project.query.get("raclette").balance
- self.assertEqual(set(balance.values()), set([6, -6]))
-
- def test_weighted_members_list(self):
- self.post_project("raclette")
-
- # add two persons
- self.app.post("/raclette/members/add", data={'name': 'alexis'})
- self.app.post("/raclette/members/add", data={'name': 'tata', 'weight': 1})
-
- resp = self.app.get("/raclette/")
- self.assertIn('extra-info', resp.data.decode('utf-8'))
-
- self.app.post("/raclette/members/add", data={'name': 'freddy familly', 'weight': 4})
-
- resp = self.app.get("/raclette/")
- self.assertNotIn('extra-info', resp.data.decode('utf-8'))
-
-
- def test_rounding(self):
- self.post_project("raclette")
-
- # add members
- self.app.post("/raclette/members/add", data={'name': 'alexis'})
- self.app.post("/raclette/members/add", data={'name': 'fred'})
- self.app.post("/raclette/members/add", data={'name': 'tata'})
-
- # create bills
- self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'fromage à raclette',
- 'payer': 1,
- 'payed_for': [1, 2, 3],
- 'amount': '24.36',
- })
-
- self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'red wine',
- 'payer': 2,
- 'payed_for': [1],
- 'amount': '19.12',
- })
-
- self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'delicatessen',
- 'payer': 1,
- 'payed_for': [1, 2],
- 'amount': '22',
- })
-
- balance = models.Project.query.get("raclette").balance
- result = {}
- result[models.Project.query.get("raclette").members[0].id] = 8.12
- result[models.Project.query.get("raclette").members[1].id] = 0.0
- result[models.Project.query.get("raclette").members[2].id] = -8.12
- # Since we're using floating point to store currency, we can have some rounding issues that prevent test from working.
- # However, we should obtain the same values as the theorical ones if we round to 2 decimals, like in the UI.
- for key, value in six.iteritems(balance):
- self.assertEqual(round(value, 2), result[key])
-
- def test_edit_project(self):
- # A project should be editable
-
- self.post_project("raclette")
- new_data = {
- 'name': 'Super raclette party!',
- 'contact_email': 'alexis@notmyidea.org',
- 'password': 'didoudida'
- }
-
- resp = self.app.post("/raclette/edit", data=new_data,
- follow_redirects=True)
- self.assertEqual(resp.status_code, 200)
- project = models.Project.query.get("raclette")
-
- for key, value in new_data.items():
- self.assertEqual(getattr(project, key), value, key)
-
- # Editing a project with a wrong email address should fail
- new_data['contact_email'] = 'wrong_email'
-
- resp = self.app.post("/raclette/edit", data=new_data,
- follow_redirects=True)
- self.assertIn("Invalid email address", resp.data.decode('utf-8'))
-
- def test_dashboard(self):
- response = self.app.get("/dashboard")
- self.assertEqual(response.status_code, 200)
-
- def test_settle_page(self):
- self.post_project("raclette")
- response = self.app.get("/raclette/settle_bills")
- self.assertEqual(response.status_code, 200)
-
- def test_settle(self):
- self.post_project("raclette")
-
- # add members
- self.app.post("/raclette/members/add", data={'name': 'alexis'})
- self.app.post("/raclette/members/add", data={'name': 'fred'})
- self.app.post("/raclette/members/add", data={'name': 'tata'})
- #Add a member with a balance=0 :
- self.app.post("/raclette/members/add", data={'name': 'toto'})
-
- # create bills
- self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'fromage à raclette',
- 'payer': 1,
- 'payed_for': [1, 2, 3],
- 'amount': '10.0',
- })
-
- self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'red wine',
- 'payer': 2,
- 'payed_for': [1],
- 'amount': '20',
- })
-
- self.app.post("/raclette/add", data={
- 'date': '2011-08-10',
- 'what': 'delicatessen',
- 'payer': 1,
- 'payed_for': [1, 2],
- 'amount': '10',
- })
- project = models.Project.query.get('raclette')
- transactions = project.get_transactions_to_settle_bill()
- members = defaultdict(int)
- #We should have the same values between transactions and project balances
- for t in transactions:
- members[t['ower']]-=t['amount']
- members[t['receiver']]+=t['amount']
- balance = models.Project.query.get("raclette").balance
- for m, a in members.items():
- self.assertEqual(a, balance[m.id])
- return
-
- def test_settle_zero(self):
- self.post_project("raclette")
-
- # add members
- self.app.post("/raclette/members/add", data={'name': 'alexis'})
- self.app.post("/raclette/members/add", data={'name': 'fred'})
- self.app.post("/raclette/members/add", data={'name': 'tata'})
-
- # create bills
- self.app.post("/raclette/add", data={
- 'date': '2016-12-31',
- 'what': 'fromage à raclette',
- 'payer': 1,
- 'payed_for': [1, 2, 3],
- 'amount': '10.0',
- })
-
- self.app.post("/raclette/add", data={
- 'date': '2016-12-31',
- 'what': 'red wine',
- 'payer': 2,
- 'payed_for': [1, 3],
- 'amount': '20',
- })
-
- self.app.post("/raclette/add", data={
- 'date': '2017-01-01',
- 'what': 'refund',
- 'payer': 3,
- 'payed_for': [2],
- 'amount': '13.33',
- })
- project = models.Project.query.get('raclette')
- transactions = project.get_transactions_to_settle_bill()
- members = defaultdict(int)
- # There should not be any zero-amount transfer after rounding
- for t in transactions:
- rounded_amount = round(t['amount'], 2)
- self.assertNotEqual(0.0, rounded_amount,
- msg='%f is equal to zero after rounding' % t['amount'])
-
- def test_export(self):
- self.post_project("raclette")
-
- # add members
- self.app.post("/raclette/members/add", data={'name': 'alexis', 'weight': 2})
- self.app.post("/raclette/members/add", data={'name': 'fred'})
- self.app.post("/raclette/members/add", data={'name': 'tata'})
- self.app.post("/raclette/members/add", data={'name': 'pépé'})
-
- # create bills
- self.app.post("/raclette/add", data={
- 'date': '2016-12-31',
- 'what': 'fromage à raclette',
- 'payer': 1,
- 'payed_for': [1, 2, 3, 4],
- 'amount': '10.0',
- })
-
- self.app.post("/raclette/add", data={
- 'date': '2016-12-31',
- 'what': 'red wine',
- 'payer': 2,
- 'payed_for': [1, 3],
- 'amount': '200',
- })
-
- self.app.post("/raclette/add", data={
- 'date': '2017-01-01',
- 'what': 'refund',
- 'payer': 3,
- 'payed_for': [2],
- 'amount': '13.33',
- })
-
- # generate json export of bills
- resp = self.app.post("/raclette/edit", data={
- 'export_format': 'json',
- 'export_type': 'bills'
- })
- expected = [{'date': '2017-01-01', 'what': 'refund',
- 'amount': 13.33, 'payer_name': 'tata', 'payer_weight': 1.0, 'owers': ['fred']},
- {'date': '2016-12-31', 'what': 'red wine',
- 'amount': 200.0, 'payer_name': 'fred', 'payer_weight': 1.0, 'owers': ['alexis', 'tata']},
- {'date': '2016-12-31', 'what': 'fromage \xe0 raclette',
- 'amount': 10.0, 'payer_name': 'alexis', 'payer_weight': 2.0, 'owers': ['alexis', 'fred', 'tata', 'p\xe9p\xe9']}]
- self.assertEqual(json.loads(resp.data.decode('utf-8')), expected)
-
- # generate csv export of bills
- resp = self.app.post("/raclette/edit", data={
- 'export_format': 'csv',
- 'export_type': 'bills'
- })
- expected = ["date,what,amount,payer_name,payer_weight,owers",
- "2017-01-01,refund,13.33,tata,1.0,fred",
- "2016-12-31,red wine,200.0,fred,1.0,\"alexis, tata\"",
- "2016-12-31,fromage à raclette,10.0,alexis,2.0,\"alexis, fred, tata, pépé\""]
- received_lines = resp.data.decode('utf-8').split("\n")
-
- for i, line in enumerate(expected):
- self.assertEqual(
- set(line.split(",")),
- set(received_lines[i].strip("\r").split(","))
- )
-
- # generate json export of transactions
- resp = self.app.post("/raclette/edit", data={
- 'export_format': 'json',
- 'export_type': 'transactions'
- })
- expected = [{"amount": 127.33, "receiver": "fred", "ower": "alexis"},
- {"amount": 55.34, "receiver": "fred", "ower": "tata"},
- {"amount": 2.00, "receiver": "fred", "ower": "p\xe9p\xe9"}]
- self.assertEqual(json.loads(resp.data.decode('utf-8')), expected)
-
- # generate csv export of transactions
- resp = self.app.post("/raclette/edit", data={
- 'export_format': 'csv',
- 'export_type': 'transactions'
- })
-
- expected = ["amount,receiver,ower",
- "127.33,fred,alexis",
- "55.34,fred,tata",
- "2.0,fred,pépé"]
- received_lines = resp.data.decode('utf-8').split("\n")
-
- for i, line in enumerate(expected):
- self.assertEqual(
- set(line.split(",")),
- set(received_lines[i].strip("\r").split(","))
- )
-
- # wrong export_format should return a 200 and export form
- resp = self.app.post("/raclette/edit", data={
- 'export_format': 'wrong_export_format',
- 'export_type': 'transactions'
- })
-
- self.assertEqual(resp.status_code, 200)
- self.assertIn('id="export_format" name="export_format"', resp.data.decode('utf-8'))
-
- # wrong export_type should return a 200 and export form
- resp = self.app.post("/raclette/edit", data={
- 'export_format': 'json',
- 'export_type': 'wrong_export_type'
- })
-
- self.assertEqual(resp.status_code, 200)
- self.assertIn('id="export_format" name="export_format"', resp.data.decode('utf-8'))
-
-
-class APITestCase(TestCase):
- """Tests the API"""
-
- def api_create(self, name, id=None, password=None, contact=None):
- id = id or name
- password = password or name
- contact = contact or "%s@notmyidea.org" % name
-
- return self.app.post("/api/projects", data={
- 'name': name,
- 'id': id,
- 'password': password,
- 'contact_email': contact
- })
-
- def api_add_member(self, project, name, weight=1):
- self.app.post("/api/projects/%s/members" % project,
- data={"name": name, "weight": weight},
- headers=self.get_auth(project))
-
- def get_auth(self, username, password=None):
- password = password or username
- base64string = utils.base64_encode(
- ('%s:%s' % (username, password)).encode('utf-8')).decode('utf-8').replace('\n', '')
- return {"Authorization": "Basic %s" % base64string}
-
- def assertStatus(self, expected, resp, url=""):
-
- return self.assertEqual(expected, resp.status_code,
- "%s expected %s, got %s" % (url, expected, resp.status_code))
-
- def test_basic_auth(self):
- # create a project
- resp = self.api_create("raclette")
- self.assertStatus(201, resp)
-
- # try to do something on it being unauth should return a 401
- resp = self.app.get("/api/projects/raclette")
- self.assertStatus(401, resp)
-
- # PUT / POST / DELETE / GET on the different resources
- # should also return a 401
- for verb in ('post',):
- for resource in ("/raclette/members", "/raclette/bills"):
- url = "/api/projects" + resource
- self.assertStatus(401, getattr(self.app, verb)(url),
- verb + resource)
-
- for verb in ('get', 'delete', 'put'):
- for resource in ("/raclette", "/raclette/members/1",
- "/raclette/bills/1"):
- url = "/api/projects" + resource
-
- self.assertStatus(401, getattr(self.app, verb)(url),
- verb + resource)
-
- def test_project(self):
- # wrong email should return an error
- resp = self.app.post("/api/projects", data={
- 'name': "raclette",
- 'id': "raclette",
- 'password': "raclette",
- 'contact_email': "not-an-email"
- })
-
- self.assertTrue(400, resp.status_code)
- self.assertEqual('{"contact_email": ["Invalid email address."]}',
- resp.data.decode('utf-8'))
-
- # create it
- resp = self.api_create("raclette")
- self.assertTrue(201, resp.status_code)
-
- # create it twice should return a 400
- resp = self.api_create("raclette")
-
- self.assertTrue(400, resp.status_code)
- self.assertIn('id', json.loads(resp.data.decode('utf-8')))
-
- # get information about it
- resp = self.app.get("/api/projects/raclette",
- headers=self.get_auth("raclette"))
-
- self.assertTrue(200, resp.status_code)
- expected = {
- "active_members": [],
- "name": "raclette",
- "contact_email": "raclette@notmyidea.org",
- "members": [],
- "password": "raclette",
- "id": "raclette",
- "balance": {},
- }
- self.assertDictEqual(json.loads(resp.data.decode('utf-8')), expected)
-
- # edit should work
- resp = self.app.put("/api/projects/raclette", data={
- "contact_email": "yeah@notmyidea.org",
- "password": "raclette",
- "name": "The raclette party",
- }, headers=self.get_auth("raclette"))
-
- self.assertEqual(200, resp.status_code)
-
- resp = self.app.get("/api/projects/raclette",
- headers=self.get_auth("raclette"))
-
- self.assertEqual(200, resp.status_code)
- expected = {
- "active_members": [],
- "name": "The raclette party",
- "contact_email": "yeah@notmyidea.org",
- "members": [],
- "password": "raclette",
- "id": "raclette",
- "balance": {},
- }
- self.assertDictEqual(json.loads(resp.data.decode('utf-8')), expected)
-
- # delete should work
- resp = self.app.delete("/api/projects/raclette",
- headers=self.get_auth("raclette"))
-
- self.assertEqual(200, resp.status_code)
-
- # get should return a 401 on an unknown resource
- resp = self.app.get("/api/projects/raclette",
- headers=self.get_auth("raclette"))
- self.assertEqual(401, resp.status_code)
-
- def test_member(self):
- # create a project
- self.api_create("raclette")
-
- # get the list of members (should be empty)
- req = self.app.get("/api/projects/raclette/members",
- headers=self.get_auth("raclette"))
-
- self.assertStatus(200, req)
- self.assertEqual('[]', req.data.decode('utf-8'))
-
- # add a member
- req = self.app.post("/api/projects/raclette/members", data={
- "name": "Alexis"
- }, headers=self.get_auth("raclette"))
-
- # the id of the new member should be returned
- self.assertStatus(201, req)
- self.assertEqual("1", req.data.decode('utf-8'))
-
- # the list of members should contain one member
- req = self.app.get("/api/projects/raclette/members",
- headers=self.get_auth("raclette"))
-
- self.assertStatus(200, req)
- self.assertEqual(len(json.loads(req.data.decode('utf-8'))), 1)
-
- # edit this member
- req = self.app.put("/api/projects/raclette/members/1", data={
- "name": "Fred"
- }, headers=self.get_auth("raclette"))
-
- self.assertStatus(200, req)
-
- # get should return the new name
- req = self.app.get("/api/projects/raclette/members/1",
- headers=self.get_auth("raclette"))
-
- self.assertStatus(200, req)
- self.assertEqual("Fred", json.loads(req.data.decode('utf-8'))["name"])
-
- # delete a member
-
- req = self.app.delete("/api/projects/raclette/members/1",
- headers=self.get_auth("raclette"))
-
- self.assertStatus(200, req)
-
- # the list of members should be empty
- # get the list of members (should be empty)
- req = self.app.get("/api/projects/raclette/members",
- headers=self.get_auth("raclette"))
-
- self.assertStatus(200, req)
- self.assertEqual('[]', req.data.decode('utf-8'))
-
- def test_bills(self):
- # create a project
- self.api_create("raclette")
-
- # add members
- self.api_add_member("raclette", "alexis")
- self.api_add_member("raclette", "fred")
- self.api_add_member("raclette", "arnaud")
-
- # get the list of bills (should be empty)
- req = self.app.get("/api/projects/raclette/bills",
- headers=self.get_auth("raclette"))
- self.assertStatus(200, req)
-
- self.assertEqual("[]", req.data.decode('utf-8'))
-
- # add a bill
- req = self.app.post("/api/projects/raclette/bills", data={
- 'date': '2011-08-10',
- 'what': 'fromage',
- 'payer': "1",
- 'payed_for': ["1", "2"],
- 'amount': '25',
- }, headers=self.get_auth("raclette"))
-
- # should return the id
- self.assertStatus(201, req)
- self.assertEqual(req.data.decode('utf-8'), "1")
-
- # get this bill details
- req = self.app.get("/api/projects/raclette/bills/1",
- headers=self.get_auth("raclette"))
-
- # compare with the added info
- self.assertStatus(200, req)
- expected = {
- "what": "fromage",
- "payer_id": 1,
- "owers": [
- {"activated": True, "id": 1, "name": "alexis", "weight": 1},
- {"activated": True, "id": 2, "name": "fred", "weight": 1}],
- "amount": 25.0,
- "date": "2011-08-10",
- "id": 1}
-
- self.assertDictEqual(expected, json.loads(req.data.decode('utf-8')))
-
- # the list of bills should lenght 1
- req = self.app.get("/api/projects/raclette/bills",
- headers=self.get_auth("raclette"))
- self.assertStatus(200, req)
- self.assertEqual(1, len(json.loads(req.data.decode('utf-8'))))
-
- # edit with errors should return an error
- req = self.app.put("/api/projects/raclette/bills/1", data={
- 'date': '201111111-08-10', # not a date
- 'what': 'fromage',
- 'payer': "1",
- 'payed_for': ["1", "2"],
- 'amount': '25',
- }, headers=self.get_auth("raclette"))
-
- self.assertStatus(400, req)
- self.assertEqual('{"date": ["This field is required."]}', req.data.decode('utf-8'))
-
- # edit a bill
- req = self.app.put("/api/projects/raclette/bills/1", data={
- 'date': '2011-09-10',
- 'what': 'beer',
- 'payer': "2",
- 'payed_for': ["1", "2"],
- 'amount': '25',
- }, headers=self.get_auth("raclette"))
-
- # check its fields
- req = self.app.get("/api/projects/raclette/bills/1",
- headers=self.get_auth("raclette"))
-
- expected = {
- "what": "beer",
- "payer_id": 2,
- "owers": [
- {"activated": True, "id": 1, "name": "alexis", "weight": 1},
- {"activated": True, "id": 2, "name": "fred", "weight": 1}],
- "amount": 25.0,
- "date": "2011-09-10",
- "id": 1}
-
- self.assertDictEqual(expected, json.loads(req.data.decode('utf-8')))
-
- # delete a bill
- req = self.app.delete("/api/projects/raclette/bills/1",
- headers=self.get_auth("raclette"))
- self.assertStatus(200, req)
-
- # getting it should return a 404
- req = self.app.get("/api/projects/raclette/bills/1",
- headers=self.get_auth("raclette"))
- self.assertStatus(404, req)
-
- def test_username_xss(self):
- # create a project
- #self.api_create("raclette")
- self.post_project("raclette")
- self.login("raclette")
-
- # add members
- self.api_add_member("raclette", "<script>")
-
- result = self.app.get('/raclette/')
- self.assertNotIn("<script>", result.data.decode('utf-8'))
-
- def test_weighted_bills(self):
- # create a project
- self.api_create("raclette")
-
- # add members
- self.api_add_member("raclette", "alexis")
- self.api_add_member("raclette", "freddy familly", 4)
- self.api_add_member("raclette", "arnaud")
-
- # add a bill
- req = self.app.post("/api/projects/raclette/bills", data={
- 'date': '2011-08-10',
- 'what': "fromage",
- 'payer': "1",
- 'payed_for': ["1", "2"],
- 'amount': '25',
- }, headers=self.get_auth("raclette"))
-
- # get this bill details
- req = self.app.get("/api/projects/raclette/bills/1",
- headers=self.get_auth("raclette"))
-
- # compare with the added info
- self.assertStatus(200, req)
- expected = {
- "what": "fromage",
- "payer_id": 1,
- "owers": [
- {"activated": True, "id": 1, "name": "alexis", "weight": 1},
- {"activated": True, "id": 2, "name": "freddy familly", "weight": 4}],
- "amount": 25.0,
- "date": "2011-08-10",
- "id": 1}
- self.assertDictEqual(expected, json.loads(req.data.decode('utf-8')))
-
- # getting it should return a 404
- req = self.app.get("/api/projects/raclette",
- headers=self.get_auth("raclette"))
-
- expected = {
- "active_members": [
- {"activated": True, "id": 1, "name": "alexis", "weight": 1.0},
- {"activated": True, "id": 2, "name": "freddy familly", "weight": 4.0},
- {"activated": True, "id": 3, "name": "arnaud", "weight": 1.0}
- ],
- "balance": {"1": 20.0, "2": -20.0, "3": 0},
- "contact_email": "raclette@notmyidea.org",
- "id": "raclette",
-
- "members": [
- {"activated": True, "id": 1, "name": "alexis", "weight": 1.0},
- {"activated": True, "id": 2, "name": "freddy familly", "weight": 4.0},
- {"activated": True, "id": 3, "name": "arnaud", "weight": 1.0}
- ],
- "name": "raclette",
- "password": "raclette"}
-
- self.assertStatus(200, req)
- self.assertEqual(expected, json.loads(req.data.decode('utf-8')))
-
-class ServerTestCase(APITestCase):
- def setUp(self):
- run.configure()
- super(ServerTestCase, self).setUp()
-
- def test_unprefixed(self):
- run.app.config['APPLICATION_ROOT'] = '/'
- req = self.app.get("/foo/")
- self.assertStatus(303, req)
-
- def test_prefixed(self):
- run.app.config['APPLICATION_ROOT'] = '/foo'
- req = self.app.get("/foo/")
- self.assertStatus(200, req)
-
-if __name__ == "__main__":
- unittest.main()