From 818c357613329056a2863c2ddfc7e97a2e80880a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=9D=E7=91=AA=E8=B2=93?= Date: Tue, 23 May 2023 08:24:12 +0800 Subject: [PATCH] Revised the next URI utilities to apply URLSafeSerializer for encoding and decoding the next URI, in order to prevent tampering with the next URI. --- src/accounting/utils/next_uri.py | 23 +++- tests/test_account.py | 20 +-- tests/test_currency.py | 11 +- tests/test_description_editor.py | 24 ++-- tests/test_journal_entry.py | 134 ++++++++++++------- tests/test_offset.py | 191 +++++++++++++++++++--------- tests/test_option.py | 131 ++++++++++--------- tests/test_site/__init__.py | 14 +- tests/test_site/lib.py | 22 ++-- tests/test_site/templates/base.html | 2 +- tests/test_unmatched_offset.py | 18 +-- tests/test_utils.py | 52 ++++---- tests/testlib.py | 18 ++- tests/testlib_journal_entry.py | 17 ++- 14 files changed, 426 insertions(+), 251 deletions(-) diff --git a/src/accounting/utils/next_uri.py b/src/accounting/utils/next_uri.py index 921f2d4..bbc5813 100644 --- a/src/accounting/utils/next_uri.py +++ b/src/accounting/utils/next_uri.py @@ -22,7 +22,8 @@ This module should not import any other module from the application. from urllib.parse import urlparse, parse_qsl, ParseResult, urlencode, \ urlunparse -from flask import request, Blueprint +from flask import request, Blueprint, current_app +from itsdangerous import URLSafeSerializer, BadData def append_next(uri: str) -> str: @@ -62,9 +63,13 @@ def __get_next() -> str | None: """ next_uri: str | None = request.form.get("next") \ if request.method == "POST" else request.args.get("next") - if next_uri is None or not next_uri.startswith("/"): + if next_uri is None: + return None + try: + return URLSafeSerializer(current_app.config["SECRET_KEY"])\ + .loads(next_uri, "next") + except BadData: return None - return next_uri def __set_next(uri: str, next_uri: str) -> str: @@ -77,12 +82,22 @@ def __set_next(uri: str, next_uri: str) -> str: uri_p: ParseResult = urlparse(uri) params: list[tuple[str, str]] = parse_qsl(uri_p.query) params = [x for x in params if x[0] != "next"] - params.append(("next", next_uri)) + params.append(("next", encode_next(next_uri))) parts: list[str] = list(uri_p) parts[4] = urlencode(params) return urlunparse(parts) +def encode_next(uri: str) -> str: + """Encodes the next URI. + + :param uri: The next URI. + :return: The encoded next URI. + """ + return URLSafeSerializer(current_app.config["SECRET_KEY"])\ + .dumps(uri, "next") + + def init_app(bp: Blueprint) -> None: """Initializes the application. diff --git a/tests/test_account.py b/tests/test_account.py index 4c4b6a6..a8304ff 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -23,6 +23,7 @@ import unittest import httpx from flask import Flask +from accounting.utils.next_uri import encode_next from test_site import db from testlib import NEXT_URI, create_test_app, get_client, set_locale, \ add_journal_entry @@ -78,6 +79,7 @@ class AccountTestCase(unittest.TestCase): AccountL10n.query.delete() Account.query.delete() db.session.commit() + self.encoded_next_uri: str = encode_next(NEXT_URI) self.client, self.csrf_token = get_client(self.app, "editor") response: httpx.Response @@ -143,7 +145,7 @@ class AccountTestCase(unittest.TestCase): response = client.post(f"{PREFIX}/bases/{CASH.base_code}", data={"csrf_token": csrf_token, - "next": NEXT_URI, + "next": self.encoded_next_uri, f"{cash_id}-no": "5"}) self.assertEqual(response.status_code, 403) @@ -192,7 +194,7 @@ class AccountTestCase(unittest.TestCase): response = client.post(f"{PREFIX}/bases/{CASH.base_code}", data={"csrf_token": csrf_token, - "next": NEXT_URI, + "next": self.encoded_next_uri, f"{cash_id}-no": "5"}) self.assertEqual(response.status_code, 403) @@ -244,7 +246,7 @@ class AccountTestCase(unittest.TestCase): response = self.client.post(f"{PREFIX}/bases/{CASH.base_code}", data={"csrf_token": self.csrf_token, - "next": NEXT_URI, + "next": self.encoded_next_uri, f"{cash_id}-no": "5"}) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], NEXT_URI) @@ -526,7 +528,7 @@ class AccountTestCase(unittest.TestCase): self.assertEqual(account.title_l10n, CASH.title) self.assertEqual(account.l10n, []) - set_locale(self.client, self.csrf_token, "zh_Hant") + set_locale(self.app, self.client, self.csrf_token, "zh_Hant") response = self.client.post(update_uri, data={"csrf_token": self.csrf_token, @@ -541,7 +543,7 @@ class AccountTestCase(unittest.TestCase): self.assertEqual({(x.locale, x.title) for x in account.l10n}, {("zh_Hant", f"{CASH.title}-zh_Hant")}) - set_locale(self.client, self.csrf_token, "en") + set_locale(self.app, self.client, self.csrf_token, "en") response = self.client.post(update_uri, data={"csrf_token": self.csrf_token, @@ -556,7 +558,7 @@ class AccountTestCase(unittest.TestCase): self.assertEqual({(x.locale, x.title) for x in account.l10n}, {("zh_Hant", f"{CASH.title}-zh_Hant")}) - set_locale(self.client, self.csrf_token, "zh_Hant") + set_locale(self.app, self.client, self.csrf_token, "zh_Hant") response = self.client.post(update_uri, data={"csrf_token": self.csrf_token, @@ -591,7 +593,7 @@ class AccountTestCase(unittest.TestCase): add_journal_entry(self.client, form={"csrf_token": self.csrf_token, - "next": NEXT_URI, + "next": self.encoded_next_uri, "date": dt.date.today().isoformat(), "currency-1-code": "USD", "currency-1-credit-1-account_code": BANK.code, @@ -709,7 +711,7 @@ class AccountTestCase(unittest.TestCase): response = self.client.post(f"{PREFIX}/bases/1111", data={"csrf_token": self.csrf_token, - "next": NEXT_URI, + "next": self.encoded_next_uri, f"{id_1}-no": "4", f"{id_2}-no": "1", f"{id_3}-no": "5", @@ -736,7 +738,7 @@ class AccountTestCase(unittest.TestCase): response = self.client.post(f"{PREFIX}/bases/1111", data={"csrf_token": self.csrf_token, - "next": NEXT_URI, + "next": self.encoded_next_uri, f"{id_2}-no": "3a", f"{id_3}-no": "5", f"{id_4}-no": "2"}) diff --git a/tests/test_currency.py b/tests/test_currency.py index 9c243fe..c47e44e 100644 --- a/tests/test_currency.py +++ b/tests/test_currency.py @@ -23,6 +23,7 @@ import unittest import httpx from flask import Flask +from accounting.utils.next_uri import encode_next from test_site import db from testlib import NEXT_URI, create_test_app, get_client, set_locale, \ add_journal_entry @@ -468,7 +469,7 @@ class CurrencyTestCase(unittest.TestCase): self.assertEqual(currency.name_l10n, USD.name) self.assertEqual(currency.l10n, []) - set_locale(self.client, self.csrf_token, "zh_Hant") + set_locale(self.app, self.client, self.csrf_token, "zh_Hant") response = self.client.post(update_uri, data={"csrf_token": self.csrf_token, @@ -483,7 +484,7 @@ class CurrencyTestCase(unittest.TestCase): self.assertEqual({(x.locale, x.name) for x in currency.l10n}, {("zh_Hant", f"{USD.name}-zh_Hant")}) - set_locale(self.client, self.csrf_token, "en") + set_locale(self.app, self.client, self.csrf_token, "en") response = self.client.post(update_uri, data={"csrf_token": self.csrf_token, @@ -498,7 +499,7 @@ class CurrencyTestCase(unittest.TestCase): self.assertEqual({(x.locale, x.name) for x in currency.l10n}, {("zh_Hant", f"{USD.name}-zh_Hant")}) - set_locale(self.client, self.csrf_token, "zh_Hant") + set_locale(self.app, self.client, self.csrf_token, "zh_Hant") response = self.client.post(update_uri, data={"csrf_token": self.csrf_token, @@ -521,6 +522,8 @@ class CurrencyTestCase(unittest.TestCase): from accounting.models import Currency detail_uri: str = f"{PREFIX}/{JPY.code}" delete_uri: str = f"{PREFIX}/{JPY.code}/delete" + with self.app.app_context(): + encoded_next_uri: str = encode_next(NEXT_URI) list_uri: str = PREFIX response: httpx.Response @@ -533,7 +536,7 @@ class CurrencyTestCase(unittest.TestCase): add_journal_entry(self.client, form={"csrf_token": self.csrf_token, - "next": NEXT_URI, + "next": encoded_next_uri, "date": dt.date.today().isoformat(), "currency-1-code": EUR.code, "currency-1-credit-1-account_code": "1111-001", diff --git a/tests/test_description_editor.py b/tests/test_description_editor.py index 06ad83b..ed34769 100644 --- a/tests/test_description_editor.py +++ b/tests/test_description_editor.py @@ -22,6 +22,7 @@ import unittest from flask import Flask +from accounting.utils.next_uri import encode_next from testlib import NEXT_URI, Accounts, create_test_app, get_client, \ add_journal_entry @@ -41,6 +42,7 @@ class DescriptionEditorTestCase(unittest.TestCase): from accounting.models import JournalEntry, JournalEntryLineItem JournalEntry.query.delete() JournalEntryLineItem.query.delete() + self.encoded_next_uri: str = encode_next(NEXT_URI) self.client, self.csrf_token = get_client(self.app, "editor") @@ -51,7 +53,7 @@ class DescriptionEditorTestCase(unittest.TestCase): """ from accounting.journal_entry.utils.description_editor import \ DescriptionEditor - for form in get_form_data(self.csrf_token): + for form in get_form_data(self.csrf_token, self.encoded_next_uri): add_journal_entry(self.client, form) with self.app.app_context(): editor: DescriptionEditor = DescriptionEditor() @@ -143,22 +145,24 @@ class DescriptionEditorTestCase(unittest.TestCase): Accounts.PREPAID) -def get_form_data(csrf_token: str) -> list[dict[str, str]]: +def get_form_data(csrf_token: str, encoded_next_uri: str) \ + -> list[dict[str, str]]: """Returns the form data for multiple journal entry forms. :param csrf_token: The CSRF token. + :param encoded_next_uri: The encoded next URI. :return: A list of the form data. """ journal_entry_date: str = dt.date.today().isoformat() return [{"csrf_token": csrf_token, - "next": NEXT_URI, + "next": encoded_next_uri, "date": journal_entry_date, "currency-0-code": "USD", "currency-0-credit-0-account_code": Accounts.SERVICE, "currency-0-credit-0-description": " Salary ", "currency-0-credit-0-amount": "2500"}, {"csrf_token": csrf_token, - "next": NEXT_URI, + "next": encoded_next_uri, "date": journal_entry_date, "currency-0-code": "USD", "currency-0-debit-0-account_code": Accounts.MEAL, @@ -180,7 +184,7 @@ def get_form_data(csrf_token: str) -> list[dict[str, str]]: "currency-0-credit-2-description": " Dinner—Hamburger ", "currency-0-credit-2-amount": "4.25"}, {"csrf_token": csrf_token, - "next": NEXT_URI, + "next": encoded_next_uri, "date": journal_entry_date, "currency-0-code": "USD", "currency-0-debit-0-account_code": Accounts.MEAL, @@ -196,7 +200,7 @@ def get_form_data(csrf_token: str) -> list[dict[str, str]]: "currency-0-credit-1-description": " Dinner—Steak ", "currency-0-credit-1-amount": "8.28"}, {"csrf_token": csrf_token, - "next": NEXT_URI, + "next": encoded_next_uri, "date": journal_entry_date, "currency-0-code": "USD", "currency-0-debit-0-account_code": Accounts.MEAL, @@ -212,14 +216,14 @@ def get_form_data(csrf_token: str) -> list[dict[str, str]]: "currency-0-credit-1-description": " Lunch—Noodles ", "currency-0-credit-1-amount": "7.47"}, {"csrf_token": csrf_token, - "next": NEXT_URI, + "next": encoded_next_uri, "date": journal_entry_date, "currency-0-code": "USD", "currency-0-debit-0-account_code": Accounts.TRAVEL, "currency-0-debit-0-description": " Airplane—Lake City↔Hill Town", "currency-0-debit-0-amount": "800"}, {"csrf_token": csrf_token, - "next": NEXT_URI, + "next": encoded_next_uri, "date": journal_entry_date, "currency-0-code": "USD", "currency-0-debit-0-account_code": Accounts.TRAVEL, @@ -247,7 +251,7 @@ def get_form_data(csrf_token: str) -> list[dict[str, str]]: "currency-0-credit-3-description": " Train—Red—Mall→Museum ", "currency-0-credit-3-amount": "4.4"}, {"csrf_token": csrf_token, - "next": NEXT_URI, + "next": encoded_next_uri, "date": journal_entry_date, "currency-0-code": "USD", "currency-0-debit-0-account_code": Accounts.TRAVEL, @@ -293,7 +297,7 @@ def get_form_data(csrf_token: str) -> list[dict[str, str]]: "currency-0-credit-6-description": " Bike—Theatre→Home ", "currency-0-credit-6-amount": "5.5"}, {"csrf_token": csrf_token, - "next": NEXT_URI, + "next": encoded_next_uri, "date": journal_entry_date, "currency-0-code": "USD", "currency-0-debit-0-account_code": Accounts.PETTY_CASH, diff --git a/tests/test_journal_entry.py b/tests/test_journal_entry.py index a76189c..002069b 100644 --- a/tests/test_journal_entry.py +++ b/tests/test_journal_entry.py @@ -24,6 +24,7 @@ from decimal import Decimal import httpx from flask import Flask +from accounting.utils.next_uri import encode_next from test_site import db from testlib import NEXT_URI, Accounts, create_test_app, get_client, \ add_journal_entry, match_journal_entry_detail @@ -53,6 +54,7 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase): from accounting.models import JournalEntry, JournalEntryLineItem JournalEntry.query.delete() JournalEntryLineItem.query.delete() + self.encoded_next_uri: str = encode_next(NEXT_URI) self.client, self.csrf_token = get_client(self.app, "editor") @@ -153,7 +155,8 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase): data=update_form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], - f"{PREFIX}/{journal_entry_id}?next=%2F_next") + f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") response = self.client.post(f"{PREFIX}/{journal_entry_id}/delete", data={"csrf_token": self.csrf_token}) @@ -166,7 +169,8 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase): :return: None. """ from accounting.models import JournalEntry, JournalEntryCurrency - create_uri: str = f"{PREFIX}/create/receipt?next=%2F_next" + create_uri: str = (f"{PREFIX}/create/receipt?" + f"next={self.encoded_next_uri}") store_uri: str = f"{PREFIX}/store/receipt" response: httpx.Response form: dict[str, str] @@ -322,8 +326,10 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase): from accounting.models import JournalEntry, JournalEntryCurrency journal_entry_id: int \ = add_journal_entry(self.client, self.__get_add_form()) - detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next" - edit_uri: str = f"{PREFIX}/{journal_entry_id}/edit?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") + edit_uri: str = (f"{PREFIX}/{journal_entry_id}/edit?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_id}/update" form_0: dict[str, str] = self.__get_update_form(journal_entry_id) @@ -485,7 +491,8 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase): from accounting.models import JournalEntry journal_entry_id: int \ = add_journal_entry(self.client, self.__get_add_form()) - detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_id}/update" journal_entry: JournalEntry response: httpx.Response @@ -524,7 +531,8 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase): = add_journal_entry(self.client, self.__get_add_form()) editor_username, admin_username = "editor", "admin" client, csrf_token = get_client(self.app, admin_username) - detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_id}/update" journal_entry: JournalEntry response: httpx.Response @@ -557,7 +565,8 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase): from accounting.models import JournalEntry, JournalEntryLineItem journal_entry_id_1: int \ = add_journal_entry(self.client, self.__get_add_form()) - detail_uri: str = f"{PREFIX}/{journal_entry_id_1}?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id_1}?" + f"next={self.encoded_next_uri}") delete_uri: str = f"{PREFIX}/{journal_entry_id_1}/delete" response: httpx.Response @@ -575,7 +584,7 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase): add_journal_entry( self.client, form={"csrf_token": self.csrf_token, - "next": NEXT_URI, + "next": self.encoded_next_uri, "date": dt.date.today().isoformat(), "currency-1-code": line_item.currency_code, "currency-1-debit-1-original_line_item_id": line_item.id, @@ -585,17 +594,18 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase): # Cannot delete the journal entry that is in use response = self.client.post(f"{PREFIX}/{journal_entry_id_2}/delete", data={"csrf_token": self.csrf_token, - "next": NEXT_URI}) + "next": self.encoded_next_uri}) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], - f"{PREFIX}/{journal_entry_id_2}?next=%2F_next") + f"{PREFIX}/{journal_entry_id_2}?" + f"next={self.encoded_next_uri}") # Success response = self.client.get(detail_uri) self.assertEqual(response.status_code, 200) response = self.client.post(delete_uri, data={"csrf_token": self.csrf_token, - "next": NEXT_URI}) + "next": self.encoded_next_uri}) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], NEXT_URI) @@ -603,7 +613,7 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase): self.assertEqual(response.status_code, 404) response = self.client.post(delete_uri, data={"csrf_token": self.csrf_token, - "next": NEXT_URI}) + "next": self.encoded_next_uri}) self.assertEqual(response.status_code, 404) def __get_add_form(self) -> dict[str, str]: @@ -611,7 +621,8 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase): :return: The form data to add a new journal entry. """ - form: dict[str, str] = get_add_form(self.csrf_token) + form: dict[str, str] = get_add_form(self.csrf_token, + self.encoded_next_uri) form = {x: form[x] for x in form if "-debit-" not in x} return form @@ -625,7 +636,7 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase): not changed. """ form: dict[str, str] = get_unchanged_update_form( - journal_entry_id, self.app, self.csrf_token) + journal_entry_id, self.app, self.csrf_token, self.encoded_next_uri) form = {x: form[x] for x in form if "-debit-" not in x} return form @@ -638,7 +649,8 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase): changed. """ form: dict[str, str] = get_update_form( - journal_entry_id, self.app, self.csrf_token, False) + journal_entry_id, self.app, self.csrf_token, self.encoded_next_uri, + False) form = {x: form[x] for x in form if "-debit-" not in x} return form @@ -658,6 +670,7 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase): from accounting.models import JournalEntry, JournalEntryLineItem JournalEntry.query.delete() JournalEntryLineItem.query.delete() + self.encoded_next_uri: str = encode_next(NEXT_URI) self.client, self.csrf_token = get_client(self.app, "editor") @@ -758,7 +771,8 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase): data=update_form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], - f"{PREFIX}/{journal_entry_id}?next=%2F_next") + f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") response = self.client.post(f"{PREFIX}/{journal_entry_id}/delete", data={"csrf_token": self.csrf_token}) @@ -771,7 +785,8 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase): :return: None. """ from accounting.models import JournalEntry, JournalEntryCurrency - create_uri: str = f"{PREFIX}/create/disbursement?next=%2F_next" + create_uri: str = (f"{PREFIX}/create/disbursement?" + f"next={self.encoded_next_uri}") store_uri: str = f"{PREFIX}/store/disbursement" response: httpx.Response form: dict[str, str] @@ -930,8 +945,10 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase): from accounting.models import JournalEntry, JournalEntryCurrency journal_entry_id: int \ = add_journal_entry(self.client, self.__get_add_form()) - detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next" - edit_uri: str = f"{PREFIX}/{journal_entry_id}/edit?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") + edit_uri: str = (f"{PREFIX}/{journal_entry_id}/edit?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_id}/update" form_0: dict[str, str] = self.__get_update_form(journal_entry_id) @@ -1097,7 +1114,8 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase): from accounting.models import JournalEntry journal_entry_id: int \ = add_journal_entry(self.client, self.__get_add_form()) - detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_id}/update" journal_entry: JournalEntry response: httpx.Response @@ -1136,7 +1154,8 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase): = add_journal_entry(self.client, self.__get_add_form()) editor_username, admin_username = "editor", "admin" client, csrf_token = get_client(self.app, admin_username) - detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_id}/update" journal_entry: JournalEntry response: httpx.Response @@ -1168,7 +1187,8 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase): """ journal_entry_id: int \ = add_journal_entry(self.client, self.__get_add_form()) - detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") delete_uri: str = f"{PREFIX}/{journal_entry_id}/delete" response: httpx.Response @@ -1176,7 +1196,7 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase): self.assertEqual(response.status_code, 200) response = self.client.post(delete_uri, data={"csrf_token": self.csrf_token, - "next": NEXT_URI}) + "next": self.encoded_next_uri}) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], NEXT_URI) @@ -1184,7 +1204,7 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase): self.assertEqual(response.status_code, 404) response = self.client.post(delete_uri, data={"csrf_token": self.csrf_token, - "next": NEXT_URI}) + "next": self.encoded_next_uri}) self.assertEqual(response.status_code, 404) def __get_add_form(self) -> dict[str, str]: @@ -1192,7 +1212,8 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase): :return: The form data to add a new journal entry. """ - form: dict[str, str] = get_add_form(self.csrf_token) + form: dict[str, str] = get_add_form(self.csrf_token, + self.encoded_next_uri) form = {x: form[x] for x in form if "-credit-" not in x} return form @@ -1206,7 +1227,7 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase): not changed. """ form: dict[str, str] = get_unchanged_update_form( - journal_entry_id, self.app, self.csrf_token) + journal_entry_id, self.app, self.csrf_token, self.encoded_next_uri) form = {x: form[x] for x in form if "-credit-" not in x} return form @@ -1219,7 +1240,8 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase): changed. """ form: dict[str, str] = get_update_form( - journal_entry_id, self.app, self.csrf_token, True) + journal_entry_id, self.app, self.csrf_token, self.encoded_next_uri, + True) form = {x: form[x] for x in form if "-credit-" not in x} return form @@ -1240,6 +1262,7 @@ class TransferJournalEntryTestCase(unittest.TestCase): JournalEntryLineItem JournalEntry.query.delete() JournalEntryLineItem.query.delete() + self.encoded_next_uri: str = encode_next(NEXT_URI) self.client, self.csrf_token = get_client(self.app, "editor") @@ -1340,7 +1363,8 @@ class TransferJournalEntryTestCase(unittest.TestCase): data=update_form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], - f"{PREFIX}/{journal_entry_id}?next=%2F_next") + f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") response = self.client.post(f"{PREFIX}/{journal_entry_id}/delete", data={"csrf_token": self.csrf_token}) @@ -1353,7 +1377,8 @@ class TransferJournalEntryTestCase(unittest.TestCase): :return: None. """ from accounting.models import JournalEntry, JournalEntryCurrency - create_uri: str = f"{PREFIX}/create/transfer?next=%2F_next" + create_uri: str = (f"{PREFIX}/create/transfer?" + f"next={self.encoded_next_uri}") store_uri: str = f"{PREFIX}/store/transfer" response: httpx.Response form: dict[str, str] @@ -1548,8 +1573,10 @@ class TransferJournalEntryTestCase(unittest.TestCase): from accounting.models import JournalEntry, JournalEntryCurrency journal_entry_id: int \ = add_journal_entry(self.client, self.__get_add_form()) - detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next" - edit_uri: str = f"{PREFIX}/{journal_entry_id}/edit?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") + edit_uri: str = (f"{PREFIX}/{journal_entry_id}/edit?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_id}/update" form_0: dict[str, str] = self.__get_update_form(journal_entry_id) @@ -1758,7 +1785,8 @@ class TransferJournalEntryTestCase(unittest.TestCase): from accounting.models import JournalEntry journal_entry_id: int \ = add_journal_entry(self.client, self.__get_add_form()) - detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_id}/update" journal_entry: JournalEntry response: httpx.Response @@ -1797,7 +1825,8 @@ class TransferJournalEntryTestCase(unittest.TestCase): = add_journal_entry(self.client, self.__get_add_form()) editor_username, admin_username = "editor", "admin" client, csrf_token = get_client(self.app, admin_username) - detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_id}/update" journal_entry: JournalEntry response: httpx.Response @@ -1831,7 +1860,8 @@ class TransferJournalEntryTestCase(unittest.TestCase): from accounting.models import JournalEntry, JournalEntryCurrency journal_entry_id: int \ = add_journal_entry(self.client, self.__get_add_form()) - detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_id}/update?as=receipt" form_0: dict[str, str] = self.__get_update_form(journal_entry_id) form_0 = {x: form_0[x] for x in form_0 if "-debit-" not in x} @@ -1932,7 +1962,8 @@ class TransferJournalEntryTestCase(unittest.TestCase): from accounting.models import JournalEntry, JournalEntryCurrency journal_entry_id: int \ = add_journal_entry(self.client, self.__get_add_form()) - detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_id}/update?as=disbursement" form_0: dict[str, str] = self.__get_update_form(journal_entry_id) form_0 = {x: form_0[x] for x in form_0 if "-credit-" not in x} @@ -2035,7 +2066,8 @@ class TransferJournalEntryTestCase(unittest.TestCase): """ journal_entry_id: int \ = add_journal_entry(self.client, self.__get_add_form()) - detail_uri: str = f"{PREFIX}/{journal_entry_id}?next=%2F_next" + detail_uri: str = (f"{PREFIX}/{journal_entry_id}?" + f"next={self.encoded_next_uri}") delete_uri: str = f"{PREFIX}/{journal_entry_id}/delete" response: httpx.Response @@ -2043,7 +2075,7 @@ class TransferJournalEntryTestCase(unittest.TestCase): self.assertEqual(response.status_code, 200) response = self.client.post(delete_uri, data={"csrf_token": self.csrf_token, - "next": NEXT_URI}) + "next": self.encoded_next_uri}) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], NEXT_URI) @@ -2051,7 +2083,7 @@ class TransferJournalEntryTestCase(unittest.TestCase): self.assertEqual(response.status_code, 404) response = self.client.post(delete_uri, data={"csrf_token": self.csrf_token, - "next": NEXT_URI}) + "next": self.encoded_next_uri}) self.assertEqual(response.status_code, 404) def __get_add_form(self) -> dict[str, str]: @@ -2059,7 +2091,7 @@ class TransferJournalEntryTestCase(unittest.TestCase): :return: The form data to add a new journal entry. """ - return get_add_form(self.csrf_token) + return get_add_form(self.csrf_token, self.encoded_next_uri) def __get_unchanged_update_form(self, journal_entry_id: int) \ -> dict[str, str]: @@ -2071,7 +2103,7 @@ class TransferJournalEntryTestCase(unittest.TestCase): not changed. """ return get_unchanged_update_form( - journal_entry_id, self.app, self.csrf_token) + journal_entry_id, self.app, self.csrf_token, self.encoded_next_uri) def __get_update_form(self, journal_entry_id: int) -> dict[str, str]: """Returns the form data to update a journal entry, where the data are @@ -2081,8 +2113,9 @@ class TransferJournalEntryTestCase(unittest.TestCase): :return: The form data to update the journal entry, where the data are changed. """ - return get_update_form(journal_entry_id, - self.app, self.csrf_token, None) + return get_update_form( + journal_entry_id, self.app, self.csrf_token, self.encoded_next_uri, + None) class JournalEntryReorderTestCase(unittest.TestCase): @@ -2100,6 +2133,7 @@ class JournalEntryReorderTestCase(unittest.TestCase): from accounting.models import JournalEntry, JournalEntryLineItem JournalEntry.query.delete() JournalEntryLineItem.query.delete() + self.encoded_next_uri: str = encode_next(NEXT_URI) self.client, self.csrf_token = get_client(self.app, "editor") @@ -2147,7 +2181,7 @@ class JournalEntryReorderTestCase(unittest.TestCase): response = self.client.post(f"{PREFIX}/{id_2}/update", data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], - f"{PREFIX}/{id_2}?next=%2F_next") + f"{PREFIX}/{id_2}?next={self.encoded_next_uri}") with self.app.app_context(): self.assertEqual(db.session.get(JournalEntry, id_1).no, 1) @@ -2181,7 +2215,7 @@ class JournalEntryReorderTestCase(unittest.TestCase): response = self.client.post( f"{PREFIX}/dates/{date.isoformat()}", data={"csrf_token": self.csrf_token, - "next": NEXT_URI, + "next": self.encoded_next_uri, f"{id_1}-no": "4", f"{id_2}-no": "1", f"{id_3}-no": "5", @@ -2209,7 +2243,7 @@ class JournalEntryReorderTestCase(unittest.TestCase): response = self.client.post( f"{PREFIX}/dates/{date.isoformat()}", data={"csrf_token": self.csrf_token, - "next": NEXT_URI, + "next": self.encoded_next_uri, f"{id_2}-no": "3a", f"{id_3}-no": "5", f"{id_4}-no": "2"}) @@ -2228,7 +2262,8 @@ class JournalEntryReorderTestCase(unittest.TestCase): :return: The form data to add a new cash receipt journal entry. """ - form: dict[str, str] = get_add_form(self.csrf_token) + form: dict[str, str] = get_add_form(self.csrf_token, + self.encoded_next_uri) form = {x: form[x] for x in form if "-debit-" not in x} return form @@ -2237,7 +2272,8 @@ class JournalEntryReorderTestCase(unittest.TestCase): :return: The form data to add a new cash disbursement journal entry. """ - form: dict[str, str] = get_add_form(self.csrf_token) + form: dict[str, str] = get_add_form(self.csrf_token, + self.encoded_next_uri) form = {x: form[x] for x in form if "-credit-" not in x} return form @@ -2251,7 +2287,7 @@ class JournalEntryReorderTestCase(unittest.TestCase): where the data are not changed. """ form: dict[str, str] = get_unchanged_update_form( - journal_entry_id, self.app, self.csrf_token) + journal_entry_id, self.app, self.csrf_token, self.encoded_next_uri) form = {x: form[x] for x in form if "-credit-" not in x} return form @@ -2260,4 +2296,4 @@ class JournalEntryReorderTestCase(unittest.TestCase): :return: The form data to add a new journal entry. """ - return get_add_form(self.csrf_token) + return get_add_form(self.csrf_token, self.encoded_next_uri) diff --git a/tests/test_offset.py b/tests/test_offset.py index 6069443..81ee9d7 100644 --- a/tests/test_offset.py +++ b/tests/test_offset.py @@ -25,6 +25,7 @@ from decimal import Decimal import httpx from flask import Flask +from accounting.utils.next_uri import encode_next from test_site import db from test_site.lib import JournalEntryLineItemData, JournalEntryCurrencyData, \ JournalEntryData, BaseTestData @@ -50,6 +51,7 @@ class OffsetTestCase(unittest.TestCase): from accounting.models import JournalEntry, JournalEntryLineItem JournalEntry.query.delete() JournalEntryLineItem.query.delete() + self.encoded_next_uri: str = encode_next(NEXT_URI) self.client, self.csrf_token = get_client(self.app, "editor") self.data: OffsetTestData = OffsetTestData(self.app, "editor") @@ -61,7 +63,8 @@ class OffsetTestCase(unittest.TestCase): :return: None. """ from accounting.models import Account, JournalEntry - create_uri: str = f"{PREFIX}/create/receipt?next=%2F_next" + create_uri: str = (f"{PREFIX}/create/receipt?" + f"next={self.encoded_next_uri}") store_uri: str = f"{PREFIX}/store/receipt" form: dict[str, str] old_amount: Decimal @@ -85,14 +88,16 @@ class OffsetTestCase(unittest.TestCase): original_line_item=self.data.l_r_or3d)])]) # Non-existing original line item ID - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-credit-1-original_line_item_id"] = "9999" response = self.client.post(store_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], create_uri) # The same debit or credit - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-credit-1-original_line_item_id"] \ = str(self.data.l_p_or1c.id) form["currency-1-credit-1-account_code"] = self.data.l_p_or1c.account @@ -108,7 +113,8 @@ class OffsetTestCase(unittest.TestCase): db.session.commit() response = self.client.post( store_uri, - data=journal_entry_data.new_form(self.csrf_token, NEXT_URI)) + data=journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri)) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], create_uri) with self.app.app_context(): @@ -117,7 +123,8 @@ class OffsetTestCase(unittest.TestCase): db.session.commit() # The original line item is also an offset - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-credit-1-original_line_item_id"] \ = str(self.data.l_p_of1d.id) form["currency-1-credit-1-account_code"] = self.data.l_p_of1d.account @@ -126,21 +133,24 @@ class OffsetTestCase(unittest.TestCase): self.assertEqual(response.headers["Location"], create_uri) # Not the same currency - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-code"] = "EUR" response = self.client.post(store_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], create_uri) # Not the same account - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-credit-1-account_code"] = Accounts.NOTES_RECEIVABLE response = self.client.post(store_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], create_uri) # Not exceeding net balance - partially offset - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-credit-1-amount"] \ = str(journal_entry_data.currencies[0].credit[0].amount + Decimal("0.01")) @@ -149,7 +159,8 @@ class OffsetTestCase(unittest.TestCase): self.assertEqual(response.headers["Location"], create_uri) # Not exceeding net balance - unmatched - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-credit-3-amount"] \ = str(journal_entry_data.currencies[0].credit[2].amount + Decimal("0.01")) @@ -160,14 +171,16 @@ class OffsetTestCase(unittest.TestCase): # Not before the original line items old_days = journal_entry_data.days journal_entry_data.days = old_days + 1 - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) response = self.client.post(store_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], create_uri) journal_entry_data.days = old_days # Success - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) response = self.client.post(store_uri, data=form) self.assertEqual(response.status_code, 302) journal_entry_id: int \ @@ -184,7 +197,8 @@ class OffsetTestCase(unittest.TestCase): """ from accounting.models import Account journal_entry_data: JournalEntryData = self.data.j_r_of2 - edit_uri: str = f"{PREFIX}/{journal_entry_data.id}/edit?next=%2F_next" + edit_uri: str = (f"{PREFIX}/{journal_entry_data.id}/edit?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_data.id}/update" form: dict[str, str] response: httpx.Response @@ -196,14 +210,16 @@ class OffsetTestCase(unittest.TestCase): journal_entry_data.currencies[0].credit[2].amount = Decimal("600") # Non-existing original line item ID - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-credit-1-original_line_item_id"] = "9999" response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) # The same debit or credit - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-credit-1-original_line_item_id"] \ = str(self.data.l_p_or1c.id) form["currency-1-credit-1-account_code"] = self.data.l_p_or1c.account @@ -220,7 +236,8 @@ class OffsetTestCase(unittest.TestCase): db.session.commit() response = self.client.post( update_uri, - data=journal_entry_data.update_form(self.csrf_token, NEXT_URI)) + data=journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri)) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) with self.app.app_context(): @@ -229,7 +246,8 @@ class OffsetTestCase(unittest.TestCase): db.session.commit() # The original line item is also an offset - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-credit-1-original_line_item_id"] \ = str(self.data.l_p_of1d.id) form["currency-1-credit-1-account_code"] = self.data.l_p_of1d.account @@ -238,21 +256,24 @@ class OffsetTestCase(unittest.TestCase): self.assertEqual(response.headers["Location"], edit_uri) # Not the same currency - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-code"] = "EUR" response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) # Not the same account - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-credit-1-account_code"] = Accounts.NOTES_RECEIVABLE response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) # Not exceeding net balance - partially offset - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-amount"] \ = str(journal_entry_data.currencies[0].debit[0].amount + Decimal("0.01")) @@ -264,7 +285,8 @@ class OffsetTestCase(unittest.TestCase): self.assertEqual(response.headers["Location"], edit_uri) # Not exceeding net balance - unmatched - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-3-amount"] \ = str(journal_entry_data.currencies[0].debit[2].amount + Decimal("0.01")) @@ -278,18 +300,21 @@ class OffsetTestCase(unittest.TestCase): # Not before the original line items old_days: int = journal_entry_data.days journal_entry_data.days = old_days + 1 - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) journal_entry_data.days = old_days # Success - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], - f"{PREFIX}/{journal_entry_data.id}?next=%2F_next") + f"{PREFIX}/{journal_entry_data.id}?" + f"next={self.encoded_next_uri}") def test_edit_receivable_original_line_item(self) -> None: """Tests to edit the receivable original line item. @@ -298,7 +323,8 @@ class OffsetTestCase(unittest.TestCase): """ from accounting.models import JournalEntry journal_entry_data: JournalEntryData = self.data.j_r_or1 - edit_uri: str = f"{PREFIX}/{journal_entry_data.id}/edit?next=%2F_next" + edit_uri: str = (f"{PREFIX}/{journal_entry_data.id}/edit?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_data.id}/update" form: dict[str, str] response: httpx.Response @@ -310,21 +336,24 @@ class OffsetTestCase(unittest.TestCase): journal_entry_data.currencies[0].credit[1].amount = Decimal("3.4") # Not the same currency - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-code"] = "EUR" response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) # Not the same account - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-account_code"] = Accounts.NOTES_RECEIVABLE response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) # Not less than offset total - partially offset - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-amount"] \ = str(journal_entry_data.currencies[0].debit[0].amount - Decimal("0.01")) @@ -336,7 +365,8 @@ class OffsetTestCase(unittest.TestCase): self.assertEqual(response.headers["Location"], edit_uri) # Not less than offset total - fully offset - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-2-amount"] \ = str(journal_entry_data.currencies[0].debit[1].amount - Decimal("0.01")) @@ -350,25 +380,29 @@ class OffsetTestCase(unittest.TestCase): # Not after the offset items old_days: int = journal_entry_data.days journal_entry_data.days = old_days - 1 - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) journal_entry_data.days = old_days # Not deleting matched original line items - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) del form["currency-1-debit-1-id"] response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) # Success - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], - f"{PREFIX}/{journal_entry_data.id}?next=%2F_next") + f"{PREFIX}/{journal_entry_data.id}?" + f"next={self.encoded_next_uri}") # The original line item is always before the offset item, even when # they happen in the same day. @@ -388,7 +422,8 @@ class OffsetTestCase(unittest.TestCase): :return: None. """ from accounting.models import Account, JournalEntry - create_uri: str = f"{PREFIX}/create/disbursement?next=%2F_next" + create_uri: str = (f"{PREFIX}/create/disbursement?" + f"next={self.encoded_next_uri}") store_uri: str = f"{PREFIX}/store/disbursement" form: dict[str, str] response: httpx.Response @@ -411,14 +446,16 @@ class OffsetTestCase(unittest.TestCase): [])]) # Non-existing original line item ID - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-original_line_item_id"] = "9999" response = self.client.post(store_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], create_uri) # The same debit or credit - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-original_line_item_id"] \ = str(self.data.l_r_or1d.id) form["currency-1-debit-1-account_code"] = self.data.l_r_or1d.account @@ -434,7 +471,8 @@ class OffsetTestCase(unittest.TestCase): db.session.commit() response = self.client.post( store_uri, - data=journal_entry_data.new_form(self.csrf_token, NEXT_URI)) + data=journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri)) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], create_uri) with self.app.app_context(): @@ -443,7 +481,8 @@ class OffsetTestCase(unittest.TestCase): db.session.commit() # The original line item is also an offset - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-original_line_item_id"] \ = str(self.data.l_r_of1c.id) form["currency-1-debit-1-account_code"] = self.data.l_r_of1c.account @@ -452,21 +491,24 @@ class OffsetTestCase(unittest.TestCase): self.assertEqual(response.headers["Location"], create_uri) # Not the same currency - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-code"] = "EUR" response = self.client.post(store_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], create_uri) # Not the same account - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-account_code"] = Accounts.NOTES_PAYABLE response = self.client.post(store_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], create_uri) # Not exceeding net balance - partially offset - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-amount"] \ = str(journal_entry_data.currencies[0].debit[0].amount + Decimal("0.01")) @@ -475,7 +517,8 @@ class OffsetTestCase(unittest.TestCase): self.assertEqual(response.headers["Location"], create_uri) # Not exceeding net balance - unmatched - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-3-amount"] \ = str(journal_entry_data.currencies[0].debit[2].amount + Decimal("0.01")) @@ -486,14 +529,16 @@ class OffsetTestCase(unittest.TestCase): # Not before the original line items old_days: int = journal_entry_data.days journal_entry_data.days = old_days + 1 - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) response = self.client.post(store_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], create_uri) journal_entry_data.days = old_days # Success - form = journal_entry_data.new_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.new_form(self.csrf_token, + self.encoded_next_uri) response = self.client.post(store_uri, data=form) self.assertEqual(response.status_code, 302) journal_entry_id: int \ @@ -510,7 +555,8 @@ class OffsetTestCase(unittest.TestCase): """ from accounting.models import Account, JournalEntry journal_entry_data: JournalEntryData = self.data.j_p_of2 - edit_uri: str = f"{PREFIX}/{journal_entry_data.id}/edit?next=%2F_next" + edit_uri: str = (f"{PREFIX}/{journal_entry_data.id}/edit?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_data.id}/update" form: dict[str, str] response: httpx.Response @@ -522,14 +568,16 @@ class OffsetTestCase(unittest.TestCase): journal_entry_data.currencies[0].credit[2].amount = Decimal("900") # Non-existing original line item ID - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-original_line_item_id"] = "9999" response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) # The same debit or credit - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-original_line_item_id"] \ = str(self.data.l_r_or1d.id) form["currency-1-debit-1-account_code"] = self.data.l_r_or1d.account @@ -546,7 +594,8 @@ class OffsetTestCase(unittest.TestCase): db.session.commit() response = self.client.post( update_uri, - data=journal_entry_data.update_form(self.csrf_token, NEXT_URI)) + data=journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri)) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) with self.app.app_context(): @@ -555,7 +604,8 @@ class OffsetTestCase(unittest.TestCase): db.session.commit() # The original line item is also an offset - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-original_line_item_id"] \ = str(self.data.l_r_of1c.id) form["currency-1-debit-1-account_code"] = self.data.l_r_of1c.account @@ -564,21 +614,24 @@ class OffsetTestCase(unittest.TestCase): self.assertEqual(response.headers["Location"], edit_uri) # Not the same currency - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-code"] = "EUR" response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) # Not the same account - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-account_code"] = Accounts.NOTES_PAYABLE response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) # Not exceeding net balance - partially offset - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-amount"] \ = str(journal_entry_data.currencies[0].debit[0].amount + Decimal("0.01")) @@ -590,7 +643,8 @@ class OffsetTestCase(unittest.TestCase): self.assertEqual(response.headers["Location"], edit_uri) # Not exceeding net balance - unmatched - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-3-amount"] \ = str(journal_entry_data.currencies[0].debit[2].amount + Decimal("0.01")) @@ -604,14 +658,16 @@ class OffsetTestCase(unittest.TestCase): # Not before the original line items old_days: int = journal_entry_data.days journal_entry_data.days = old_days + 1 - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) journal_entry_data.days = old_days # Success - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) journal_entry_id: int \ @@ -628,7 +684,8 @@ class OffsetTestCase(unittest.TestCase): """ from accounting.models import JournalEntry journal_entry_data: JournalEntryData = self.data.j_p_or1 - edit_uri: str = f"{PREFIX}/{journal_entry_data.id}/edit?next=%2F_next" + edit_uri: str = (f"{PREFIX}/{journal_entry_data.id}/edit?" + f"next={self.encoded_next_uri}") update_uri: str = f"{PREFIX}/{journal_entry_data.id}/update" form: dict[str, str] response: httpx.Response @@ -640,21 +697,24 @@ class OffsetTestCase(unittest.TestCase): journal_entry_data.currencies[0].credit[1].amount = Decimal("0.9") # Not the same currency - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-code"] = "EUR" response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) # Not the same account - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-credit-1-account_code"] = Accounts.NOTES_PAYABLE response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) # Not less than offset total - partially offset - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-1-amount"] \ = str(journal_entry_data.currencies[0].debit[0].amount - Decimal("0.01")) @@ -666,7 +726,8 @@ class OffsetTestCase(unittest.TestCase): self.assertEqual(response.headers["Location"], edit_uri) # Not less than offset total - fully offset - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) form["currency-1-debit-2-amount"] \ = str(journal_entry_data.currencies[0].debit[1].amount - Decimal("0.01")) @@ -680,25 +741,29 @@ class OffsetTestCase(unittest.TestCase): # Not after the offset items old_days: int = journal_entry_data.days journal_entry_data.days = old_days - 1 - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) journal_entry_data.days = old_days # Not deleting matched original line items - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) del form["currency-1-credit-1-id"] response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], edit_uri) # Success - form = journal_entry_data.update_form(self.csrf_token, NEXT_URI) + form = journal_entry_data.update_form(self.csrf_token, + self.encoded_next_uri) response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) self.assertEqual(response.headers["Location"], - f"{PREFIX}/{journal_entry_data.id}?next=%2F_next") + f"{PREFIX}/{journal_entry_data.id}?" + f"next={self.encoded_next_uri}") # The original line item is always before the offset item, even when # they happen in the same day diff --git a/tests/test_option.py b/tests/test_option.py index f6dcefd..9946ad9 100644 --- a/tests/test_option.py +++ b/tests/test_option.py @@ -23,17 +23,12 @@ import unittest import httpx from flask import Flask +from accounting.utils.next_uri import encode_next from test_site import db from testlib import NEXT_URI, Accounts, create_test_app, get_client PREFIX: str = "/accounting/options" """The URL prefix for the option management.""" -DETAIL_URI: str = f"{PREFIX}?next=%2F_next" -"""THE URI for the option detail.""" -EDIT_URI: str = f"{PREFIX}/edit?next=%2F_next" -"""THE URI for the form to edit the options.""" -UPDATE_URI: str = f"{PREFIX}/update" -"""THE URI to update the options.""" class OptionTestCase(unittest.TestCase): @@ -50,6 +45,7 @@ class OptionTestCase(unittest.TestCase): with self.app.app_context(): from accounting.models import Option Option.query.delete() + self.encoded_next_uri: str = encode_next(NEXT_URI) self.client, self.csrf_token = get_client(self.app, "admin") @@ -59,15 +55,18 @@ class OptionTestCase(unittest.TestCase): :return: None. """ client, csrf_token = get_client(self.app, "nobody") + detail_uri: str = f"{PREFIX}?next={self.encoded_next_uri}" + edit_uri: str = f"{PREFIX}/edit?next={self.encoded_next_uri}" + update_uri: str = f"{PREFIX}/update" response: httpx.Response - response = client.get(DETAIL_URI) + response = client.get(detail_uri) self.assertEqual(response.status_code, 403) - response = client.get(EDIT_URI) + response = client.get(edit_uri) self.assertEqual(response.status_code, 403) - response = client.post(UPDATE_URI, data=self.__get_form(csrf_token)) + response = client.post(update_uri, data=self.__get_form(csrf_token)) self.assertEqual(response.status_code, 403) def test_viewer(self) -> None: @@ -76,15 +75,18 @@ class OptionTestCase(unittest.TestCase): :return: None. """ client, csrf_token = get_client(self.app, "viewer") + detail_uri: str = f"{PREFIX}?next={self.encoded_next_uri}" + edit_uri: str = f"{PREFIX}/edit?next={self.encoded_next_uri}" + update_uri: str = f"{PREFIX}/update" response: httpx.Response - response = client.get(DETAIL_URI) + response = client.get(detail_uri) self.assertEqual(response.status_code, 403) - response = client.get(EDIT_URI) + response = client.get(edit_uri) self.assertEqual(response.status_code, 403) - response = client.post(UPDATE_URI, data=self.__get_form(csrf_token)) + response = client.post(update_uri, data=self.__get_form(csrf_token)) self.assertEqual(response.status_code, 403) def test_editor(self) -> None: @@ -93,15 +95,18 @@ class OptionTestCase(unittest.TestCase): :return: None. """ client, csrf_token = get_client(self.app, "editor") + detail_uri: str = f"{PREFIX}?next={self.encoded_next_uri}" + edit_uri: str = f"{PREFIX}/edit?next={self.encoded_next_uri}" + update_uri: str = f"{PREFIX}/update" response: httpx.Response - response = client.get(DETAIL_URI) + response = client.get(detail_uri) self.assertEqual(response.status_code, 403) - response = client.get(EDIT_URI) + response = client.get(edit_uri) self.assertEqual(response.status_code, 403) - response = client.post(UPDATE_URI, data=self.__get_form(csrf_token)) + response = client.post(update_uri, data=self.__get_form(csrf_token)) self.assertEqual(response.status_code, 403) def test_admin(self) -> None: @@ -109,17 +114,20 @@ class OptionTestCase(unittest.TestCase): :return: None. """ + detail_uri: str = f"{PREFIX}?next={self.encoded_next_uri}" + edit_uri: str = f"{PREFIX}/edit?next={self.encoded_next_uri}" + update_uri: str = f"{PREFIX}/update" response: httpx.Response - response = self.client.get(DETAIL_URI) + response = self.client.get(detail_uri) self.assertEqual(response.status_code, 200) - response = self.client.get(EDIT_URI) + response = self.client.get(edit_uri) self.assertEqual(response.status_code, 200) - response = self.client.post(UPDATE_URI, data=self.__get_form()) + response = self.client.post(update_uri, data=self.__get_form()) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], DETAIL_URI) + self.assertEqual(response.headers["Location"], detail_uri) def test_set(self) -> None: """Test to set the options. @@ -127,59 +135,62 @@ class OptionTestCase(unittest.TestCase): :return: None. """ from accounting.utils.options import options + detail_uri: str = f"{PREFIX}?next={self.encoded_next_uri}" + edit_uri: str = f"{PREFIX}/edit?next={self.encoded_next_uri}" + update_uri: str = f"{PREFIX}/update" form: dict[str, str] response: httpx.Response # Empty currency code form = self.__get_form() form["default_currency_code"] = " " - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], EDIT_URI) + self.assertEqual(response.headers["Location"], edit_uri) # Non-existing currency code form = self.__get_form() form["default_currency_code"] = "ZZZ" - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], EDIT_URI) + self.assertEqual(response.headers["Location"], edit_uri) # Empty current account form = self.__get_form() form["default_ie_account_code"] = " " - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], EDIT_URI) + self.assertEqual(response.headers["Location"], edit_uri) # Non-existing current account form = self.__get_form() form["default_ie_account_code"] = "9999-999" - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], EDIT_URI) + self.assertEqual(response.headers["Location"], edit_uri) # Not a current account form = self.__get_form() form["default_ie_account_code"] = Accounts.MEAL - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], EDIT_URI) + self.assertEqual(response.headers["Location"], edit_uri) # Recurring item name empty form = self.__get_form() key = [x for x in form if x.endswith("-name")][0] form[key] = " " - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], EDIT_URI) + self.assertEqual(response.headers["Location"], edit_uri) # Recurring item account empty form = self.__get_form() key = [x for x in form if x.endswith("-account_code")][0] form[key] = " " - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], EDIT_URI) + self.assertEqual(response.headers["Location"], edit_uri) # Recurring item non-expense account form = self.__get_form() @@ -187,9 +198,9 @@ class OptionTestCase(unittest.TestCase): if x.startswith("recurring-expense-") and x.endswith("-account_code")][0] form[key] = Accounts.SERVICE - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], EDIT_URI) + self.assertEqual(response.headers["Location"], edit_uri) # Recurring item non-income account form = self.__get_form() @@ -197,9 +208,9 @@ class OptionTestCase(unittest.TestCase): if x.startswith("recurring-income-") and x.endswith("-account_code")][0] form[key] = Accounts.UTILITIES - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], EDIT_URI) + self.assertEqual(response.headers["Location"], edit_uri) # Recurring item payable expense form = self.__get_form() @@ -207,9 +218,9 @@ class OptionTestCase(unittest.TestCase): if x.startswith("recurring-expense-") and x.endswith("-account_code")][0] form[key] = Accounts.PAYABLE - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], EDIT_URI) + self.assertEqual(response.headers["Location"], edit_uri) # Recurring item receivable income form = self.__get_form() @@ -217,17 +228,17 @@ class OptionTestCase(unittest.TestCase): if x.startswith("recurring-income-") and x.endswith("-account_code")][0] form[key] = Accounts.RECEIVABLE - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], EDIT_URI) + self.assertEqual(response.headers["Location"], edit_uri) # Recurring item description template empty form = self.__get_form() key = [x for x in form if x.endswith("-description_template")][0] form[key] = " " - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], EDIT_URI) + self.assertEqual(response.headers["Location"], edit_uri) # Success, with malformed order with self.app.app_context(): @@ -236,9 +247,9 @@ class OptionTestCase(unittest.TestCase): self.assertEqual(len(options.recurring.expenses), 0) self.assertEqual(len(options.recurring.incomes), 0) - response = self.client.post(UPDATE_URI, data=self.__get_form()) + response = self.client.post(update_uri, data=self.__get_form()) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], DETAIL_URI) + self.assertEqual(response.headers["Location"], detail_uri) with self.app.app_context(): self.assertEqual(options.default_currency_code, "EUR") @@ -261,9 +272,9 @@ class OptionTestCase(unittest.TestCase): # Success, with no recurring data form = self.__get_form() form = {x: form[x] for x in form if not x.startswith("recurring-")} - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], DETAIL_URI) + self.assertEqual(response.headers["Location"], detail_uri) with self.app.app_context(): self.assertEqual(len(options.recurring.expenses), 0) @@ -275,13 +286,15 @@ class OptionTestCase(unittest.TestCase): :return: None. """ from accounting.models import Option + detail_uri: str = f"{PREFIX}?next={self.encoded_next_uri}" + update_uri: str = f"{PREFIX}/update" form: dict[str, str] option: Option | None resource: httpx.Response - response = self.client.post(UPDATE_URI, data=self.__get_form()) + response = self.client.post(update_uri, data=self.__get_form()) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], DETAIL_URI) + self.assertEqual(response.headers["Location"], detail_uri) with self.app.app_context(): option = db.session.get(Option, "recurring") @@ -295,9 +308,9 @@ class OptionTestCase(unittest.TestCase): # The recurring setting was not modified form = self.__get_form() form["default_currency_code"] = "JPY" - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], DETAIL_URI) + self.assertEqual(response.headers["Location"], detail_uri) with self.app.app_context(): option = db.session.get(Option, "recurring") @@ -311,9 +324,9 @@ class OptionTestCase(unittest.TestCase): if x.startswith("recurring-expense-") and x.endswith("-account_code")][0] form[key] = Accounts.MEAL - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], DETAIL_URI) + self.assertEqual(response.headers["Location"], detail_uri) with self.app.app_context(): option = db.session.get(Option, "recurring") @@ -328,12 +341,14 @@ class OptionTestCase(unittest.TestCase): from accounting.models import Option from accounting.utils.user import get_user_pk admin_username, editor_username = "admin", "editor" + detail_uri: str = f"{PREFIX}?next={self.encoded_next_uri}" + update_uri: str = f"{PREFIX}/update" option: Option | None response: httpx.Response - response = self.client.post(UPDATE_URI, data=self.__get_form()) + response = self.client.post(update_uri, data=self.__get_form()) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], DETAIL_URI) + self.assertEqual(response.headers["Location"], detail_uri) with self.app.app_context(): editor_pk: int = get_user_pk(editor_username) @@ -348,9 +363,9 @@ class OptionTestCase(unittest.TestCase): if x.startswith("recurring-expense-") and x.endswith("-account_code")][0] form[key] = Accounts.MEAL - response = self.client.post(UPDATE_URI, data=form) + response = self.client.post(update_uri, data=form) self.assertEqual(response.status_code, 302) - self.assertEqual(response.headers["Location"], DETAIL_URI) + self.assertEqual(response.headers["Location"], detail_uri) with self.app.app_context(): option = db.session.get(Option, "recurring") @@ -367,7 +382,7 @@ class OptionTestCase(unittest.TestCase): if csrf_token is None: csrf_token = self.csrf_token return {"csrf_token": csrf_token, - "next": NEXT_URI, + "next": self.encoded_next_uri, "default_currency_code": "EUR", "default_ie_account_code": "0000-000", "recurring-expense-1-name": "Water bill", diff --git a/tests/test_site/__init__.py b/tests/test_site/__init__.py index d7f8e07..9e24dc5 100644 --- a/tests/test_site/__init__.py +++ b/tests/test_site/__init__.py @@ -23,13 +23,15 @@ from typing import Type from click.testing import Result from flask import Flask, Blueprint, render_template, redirect, Response, \ - url_for + url_for, request from flask.testing import FlaskCliRunner from flask_babel_js import BabelJS from flask_sqlalchemy import SQLAlchemy from flask_wtf import CSRFProtect from sqlalchemy import Column +from accounting.utils.next_uri import encode_next + bp: Blueprint = Blueprint("home", __name__) """The global blueprint.""" babel_js: BabelJS = BabelJS() @@ -66,6 +68,7 @@ def create_app(is_testing: bool = False) -> Flask: db.init_app(app) app.register_blueprint(bp, url_prefix="/") + app.add_template_global(__as_next, "accounting_as_next") from . import locale locale.init_app(app) @@ -146,3 +149,12 @@ def get_home() -> str: :return: The home page. """ return render_template("home.html") + + +def __as_next() -> str: + """Encodes the current request URI as value for the next URI. + + :return: The current request URI as value for the next URI. + """ + return encode_next( + request.full_path if request.query_string else request.path) diff --git a/tests/test_site/lib.py b/tests/test_site/lib.py index 7e27aba..2f925f2 100644 --- a/tests/test_site/lib.py +++ b/tests/test_site/lib.py @@ -140,36 +140,38 @@ class JournalEntryData: for line_item in currency.credit: line_item.journal_entry = self - def new_form(self, csrf_token: str, next_uri: str) -> dict[str, str]: + def new_form(self, csrf_token: str, encoded_next_uri: str) \ + -> dict[str, str]: """Returns the journal entry as a creation form. :param csrf_token: The CSRF token. - :param next_uri: The next URI. + :param encoded_next_uri: The encoded next URI. :return: The journal entry as a creation form. """ - return self.__form(csrf_token, next_uri, is_update=False) + return self.__form(csrf_token, encoded_next_uri, is_update=False) - def update_form(self, csrf_token: str, next_uri: str) -> dict[str, str]: + def update_form(self, csrf_token: str, encoded_next_uri: str) \ + -> dict[str, str]: """Returns the journal entry as an update form. :param csrf_token: The CSRF token. - :param next_uri: The next URI. + :param encoded_next_uri: The encoded next URI. :return: The journal entry as an update form. """ - return self.__form(csrf_token, next_uri, is_update=True) + return self.__form(csrf_token, encoded_next_uri, is_update=True) - def __form(self, csrf_token: str, next_uri: str, is_update: bool = False) \ - -> dict[str, str]: + def __form(self, csrf_token: str, encoded_next_uri: str, + is_update: bool = False) -> dict[str, str]: """Returns the journal entry as a form. :param csrf_token: The CSRF token. - :param next_uri: The next URI. + :param encoded_next_uri: The encoded next URI. :param is_update: True for an update operation, or False otherwise :return: The journal entry as a form. """ date: dt.date = dt.date.today() - dt.timedelta(days=self.days) form: dict[str, str] = {"csrf_token": csrf_token, - "next": next_uri, + "next": encoded_next_uri, "date": date.isoformat()} for i in range(len(self.currencies)): form.update(self.currencies[i].form(i + 1, is_update)) diff --git a/tests/test_site/templates/base.html b/tests/test_site/templates/base.html index 46b4708..0ba4800 100644 --- a/tests/test_site/templates/base.html +++ b/tests/test_site/templates/base.html @@ -96,7 +96,7 @@ First written: 2023/1/27
- +