2 Commits

Author SHA1 Message Date
6d21c82e63 Advanced to version 1.6.1. 2024-12-03 07:13:01 +08:00
271d98aa8a Fixed to work with httpx 0.28.0. 2024-12-03 07:12:34 +08:00
16 changed files with 66 additions and 194 deletions

View File

@@ -7,7 +7,7 @@ Version 1.6.1
Released 2024/12/3
Fix test cases for compatibility with httpx 0.28.0.
Fixed to work with httpx 0.28.0.
Version 1.6.0

View File

@@ -1,7 +1,7 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2022/8/21
# Copyright (c) 2022-2026 imacat.
# Copyright (c) 2022-2024 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -43,7 +43,7 @@ dependencies = [
[project.optional-dependencies]
devel = [
"httpx >= 0.28.0",
"httpx >= 0.20.0",
"OpenCC",
]

View File

@@ -54,20 +54,13 @@ def __validate_username(ctx: click.core.Context, param: click.core.Option,
@click.option("-u", "--username", metavar="USERNAME", prompt=True,
help="The username.", callback=__validate_username,
default=lambda: os.getlogin())
@click.option("--skip-accounts", is_flag=True, default=False,
help="Skip initializing accounts.")
@click.option("--skip-currencies", is_flag=True, default=False,
help="Skip initializing currencies.")
@with_appcontext
def init_db_command(username: str, skip_accounts: bool,
skip_currencies: bool) -> None:
def init_db_command(username: str) -> None:
"""Initializes the accounting database."""
db.create_all()
init_base_accounts_command()
if not skip_accounts:
init_accounts_command(username)
if not skip_currencies:
init_currencies_command(username)
init_accounts_command(username)
init_currencies_command(username)
db.session.commit()
click.echo("Accounting database initialized.")

View File

@@ -1,7 +1,7 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/2/1
# Copyright (c) 2023-2026 imacat.
# Copyright (c) 2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -72,10 +72,14 @@ class AccountTestCase(unittest.TestCase):
:return: None.
"""
self.__app: Flask = create_test_app(is_skip_accounts=True)
self.__app: Flask = create_test_app()
"""The Flask application."""
with self.__app.app_context():
from accounting.models import Account, AccountL10n
AccountL10n.query.delete()
Account.query.delete()
db.session.commit()
self.__encoded_next_uri: str = encode_next(NEXT_URI)
"""The encoded next URI."""
@@ -101,15 +105,6 @@ class AccountTestCase(unittest.TestCase):
self.assertEqual(response.headers["Location"],
f"{PREFIX}/{BANK.code}")
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_nobody(self) -> None:
"""Test the permission as nobody.

View File

@@ -1,7 +1,7 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/1/26
# Copyright (c) 2023-2026 imacat.
# Copyright (c) 2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ import unittest
import httpx
from flask import Flask
from test_site import db
from testlib import create_test_app, get_client
LIST_URI: str = "/accounting/base-accounts"
@@ -43,15 +42,6 @@ class BaseAccountTestCase(unittest.TestCase):
self.__app: Flask = create_test_app()
"""The Flask application."""
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_nobody(self) -> None:
"""Test the permission as nobody.

View File

@@ -1,7 +1,7 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/4/10
# Copyright (c) 2023-2026 imacat.
# Copyright (c) 2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -45,15 +45,6 @@ class ConsoleCommandTestCase(unittest.TestCase):
self.__app: Flask = create_test_app()
"""The Flask application."""
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_init_db(self) -> None:
"""Tests the "accounting-init-db" console command.
@@ -93,7 +84,7 @@ class ConsoleCommandTestCase(unittest.TestCase):
with open(data_dir / "base_accounts.csv") as fp:
rows: list[dict[str, str]] = list(csv.DictReader(fp))
data: dict[str, dict[str, Any]] \
data: dict[dict[str, Any]] \
= {x["code"]: {"code": x["code"],
"title": x["title"],
"l10n": {y[5:]: x[y]
@@ -167,7 +158,7 @@ class ConsoleCommandTestCase(unittest.TestCase):
from accounting.models import Currency
with open(data_dir / "currencies.csv") as fp:
data: dict[str, dict[str, Any]] \
data: dict[dict[str, Any]] \
= {x["code"]: {"code": x["code"],
"name": x["name"],
"l10n": {y[5:]: x[y]

View File

@@ -1,7 +1,7 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/2/1
# Copyright (c) 2023-2026 imacat.
# Copyright (c) 2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -65,9 +65,15 @@ class CurrencyTestCase(unittest.TestCase):
:return: None.
"""
self.__app: Flask = create_test_app(is_skip_currencies=True)
self.__app: Flask = create_test_app()
"""The Flask application."""
with self.__app.app_context():
from accounting.models import Currency, CurrencyL10n
CurrencyL10n.query.delete()
Currency.query.delete()
db.session.commit()
self.__client: httpx.Client = get_client(self.__app, "editor")
"""The user client."""
self.__csrf_token: str = get_csrf_token(self.__client)
@@ -88,15 +94,6 @@ class CurrencyTestCase(unittest.TestCase):
self.assertEqual(response.status_code, 302)
self.assertEqual(response.headers["Location"], f"{PREFIX}/{EUR.code}")
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_nobody(self) -> None:
"""Test the permission as nobody.

View File

@@ -1,7 +1,7 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/2/28
# Copyright (c) 2023-2026 imacat.
# Copyright (c) 2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -24,7 +24,6 @@ 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, \
get_csrf_token, add_journal_entry
@@ -42,6 +41,9 @@ class DescriptionEditorTestCase(unittest.TestCase):
"""The Flask application."""
with self.__app.app_context():
from accounting.models import JournalEntry, JournalEntryLineItem
JournalEntry.query.delete()
JournalEntryLineItem.query.delete()
self.__encoded_next_uri: str = encode_next(NEXT_URI)
"""The encoded next URI."""
@@ -50,15 +52,6 @@ class DescriptionEditorTestCase(unittest.TestCase):
self.__csrf_token: str = get_csrf_token(self.__client)
"""The CSRF token."""
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_description_editor(self) -> None:
"""Test the description editor.

View File

@@ -1,7 +1,7 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/2/24
# Copyright (c) 2023-2026 imacat.
# Copyright (c) 2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -52,6 +52,9 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase):
"""The Flask application."""
with self.__app.app_context():
from accounting.models import JournalEntry, JournalEntryLineItem
JournalEntry.query.delete()
JournalEntryLineItem.query.delete()
self.__encoded_next_uri: str = encode_next(NEXT_URI)
"""The encoded next URI."""
@@ -60,15 +63,6 @@ class CashReceiptJournalEntryTestCase(unittest.TestCase):
self.__csrf_token: str = get_csrf_token(self.__client)
"""The CSRF token."""
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_nobody(self) -> None:
"""Test the permission as nobody.
@@ -683,6 +677,9 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase):
"""The Flask application."""
with self.__app.app_context():
from accounting.models import JournalEntry, JournalEntryLineItem
JournalEntry.query.delete()
JournalEntryLineItem.query.delete()
self.__encoded_next_uri: str = encode_next(NEXT_URI)
"""The encoded next URI."""
@@ -691,15 +688,6 @@ class CashDisbursementJournalEntryTestCase(unittest.TestCase):
self.__csrf_token: str = get_csrf_token(self.__client)
"""The CSRF token."""
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_nobody(self) -> None:
"""Test the permission as nobody.
@@ -1289,6 +1277,10 @@ class TransferJournalEntryTestCase(unittest.TestCase):
"""The Flask application."""
with self.__app.app_context():
from accounting.models import JournalEntry, \
JournalEntryLineItem
JournalEntry.query.delete()
JournalEntryLineItem.query.delete()
self.__encoded_next_uri: str = encode_next(NEXT_URI)
"""The encoded next URI."""
@@ -1297,15 +1289,6 @@ class TransferJournalEntryTestCase(unittest.TestCase):
self.__csrf_token: str = get_csrf_token(self.__client)
"""The CSRF token."""
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_nobody(self) -> None:
"""Test the permission as nobody.
@@ -2175,6 +2158,9 @@ class JournalEntryReorderTestCase(unittest.TestCase):
"""The Flask application."""
with self.__app.app_context():
from accounting.models import JournalEntry, JournalEntryLineItem
JournalEntry.query.delete()
JournalEntryLineItem.query.delete()
self.__encoded_next_uri: str = encode_next(NEXT_URI)
"""The encoded next URI."""
@@ -2183,15 +2169,6 @@ class JournalEntryReorderTestCase(unittest.TestCase):
self.__csrf_token: str = get_csrf_token(self.__client)
"""The CSRF token."""
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_change_date(self) -> None:
"""Tests to change the date of a journal entry.

View File

@@ -1,7 +1,7 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/3/11
# Copyright (c) 2023-2026 imacat.
# Copyright (c) 2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -49,6 +49,9 @@ class OffsetTestCase(unittest.TestCase):
"""The Flask application."""
with self.__app.app_context():
from accounting.models import JournalEntry, JournalEntryLineItem
JournalEntry.query.delete()
JournalEntryLineItem.query.delete()
self.__encoded_next_uri: str = encode_next(NEXT_URI)
"""The encoded next URI."""
@@ -60,15 +63,6 @@ class OffsetTestCase(unittest.TestCase):
"""The offset test data."""
self.__data.populate()
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_add_receivable_offset(self) -> None:
"""Tests to add the receivable offset.

View File

@@ -1,7 +1,7 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/3/22
# Copyright (c) 2023-2026 imacat.
# Copyright (c) 2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -45,6 +45,8 @@ class OptionTestCase(unittest.TestCase):
"""The Flask application."""
with self.__app.app_context():
from accounting.models import Option
Option.query.delete()
self.__encoded_next_uri: str = encode_next(NEXT_URI)
"""The encoded next URI."""
@@ -53,15 +55,6 @@ class OptionTestCase(unittest.TestCase):
self.__csrf_token: str = get_csrf_token(self.__client)
"""The CSRF token."""
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_nobody(self) -> None:
"""Test the permission as nobody.

View File

@@ -1,7 +1,7 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/4/9
# Copyright (c) 2023-2026 imacat.
# Copyright (c) 2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@ import unittest
import httpx
from flask import Flask
from test_site import db
from test_site.lib import BaseTestData
from testlib import create_test_app, get_client, get_csrf_token, Accounts
@@ -45,20 +44,16 @@ class ReportTestCase(unittest.TestCase):
self.__app: Flask = create_test_app()
"""The Flask application."""
with self.__app.app_context():
from accounting.models import JournalEntry, JournalEntryLineItem
JournalEntry.query.delete()
JournalEntryLineItem.query.delete()
self.__client: httpx.Client = get_client(self.__app, "editor")
"""The user client."""
self.__csrf_token: str = get_csrf_token(self.__client)
"""The CSRF token."""
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_nobody(self) -> None:
"""Test the permission as nobody.

View File

@@ -40,15 +40,10 @@ db: SQLAlchemy = SQLAlchemy()
"""The database instance."""
def create_app(is_testing: bool = False, is_skip_accounts: bool = False,
is_skip_currencies: bool = False) -> Flask:
def create_app(is_testing: bool = False) -> Flask:
"""Create and configure the application.
:param is_testing: True if we are running for testing, or False otherwise.
:param is_skip_accounts: True to skip account initialization, or False
otherwise.
:param is_skip_currencies: True to skip currency initialization, or False
otherwise.
:return: The application.
"""
import accounting
@@ -122,20 +117,15 @@ def create_app(is_testing: bool = False, is_skip_accounts: bool = False,
accounting.init_app(app, user_utils=UserUtilities())
with app.app_context():
init_db(app, is_skip_accounts, is_skip_currencies)
init_db(app)
return app
def init_db(app: Flask, is_skip_accounts: bool,
is_skip_currencies: bool) -> None:
def init_db(app: Flask) -> None:
"""Initializes the database.
:param app: The Flask application.
:param is_skip_accounts: True to skip account initialization, or False
otherwise.
:param is_skip_currencies: True to skip currency initialization, or False
otherwise.
:return: None.
"""
db.create_all()
@@ -145,12 +135,7 @@ def init_db(app: Flask, is_skip_accounts: bool,
db.session.add(User(username=username))
db.session.commit()
runner: FlaskCliRunner = app.test_cli_runner()
args: list[str] = ["accounting-init-db", "-u", "editor"]
if is_skip_accounts:
args += ["--skip-accounts"]
if is_skip_currencies:
args += ["--skip-currencies"]
result: Result = runner.invoke(args=args)
result: Result = runner.invoke(args=["accounting-init-db", "-u", "editor"])
assert result.exit_code == 0, result.output + str(result.exception)

View File

@@ -1,7 +1,7 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/4/8
# Copyright (c) 2023-2026 imacat.
# Copyright (c) 2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -46,6 +46,9 @@ class UnmatchedOffsetTestCase(unittest.TestCase):
"""The Flask application."""
with self.__app.app_context():
from accounting.models import JournalEntry, JournalEntryLineItem
JournalEntry.query.delete()
JournalEntryLineItem.query.delete()
self.__encoded_next_uri: str = encode_next(NEXT_URI)
"""The encoded next URI."""
@@ -54,15 +57,6 @@ class UnmatchedOffsetTestCase(unittest.TestCase):
self.__csrf_token: str = get_csrf_token(self.__client)
"""The CSRF token."""
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_nobody(self) -> None:
"""Test the permission as nobody.

View File

@@ -1,7 +1,7 @@
# The Mia! Accounting Project.
# Author: imacat@mail.imacat.idv.tw (imacat), 2023/2/3
# Copyright (c) 2023-2026 imacat.
# Copyright (c) 2023 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -27,7 +27,6 @@ from accounting.utils.next_uri import append_next, inherit_next, or_next, \
encode_next, decode_next
from accounting.utils.pagination import Pagination, DEFAULT_PAGE_SIZE
from accounting.utils.query import parse_query_keywords
from test_site import db
from testlib import TEST_SERVER, create_test_app, get_csrf_token, NEXT_URI
@@ -44,15 +43,6 @@ class NextUriTestCase(unittest.TestCase):
self.__app: Flask = create_test_app()
"""The Flask application."""
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def test_next_uri(self) -> None:
"""Tests the next URI utilities with the next URI.
@@ -246,15 +236,6 @@ class PaginationTestCase(unittest.TestCase):
"""The user client."""
self.__client.headers["Referer"] = TEST_SERVER
def tearDown(self) -> None:
"""Tears down the test.
This is run once per test.
:return: None.
"""
with self.__app.app_context():
db.engine.dispose()
def __test_success(self, query: str, items: range,
result: range, is_paged: bool = True,
is_reversed: bool | None = None) -> None:

View File

@@ -60,18 +60,12 @@ class Accounts:
RENT_INCOME: str = "7482-001"
def create_test_app(is_skip_accounts: bool = False,
is_skip_currencies: bool = False) -> Flask:
def create_test_app() -> Flask:
"""Creates and returns the testing Flask application.
:param is_skip_accounts: True to skip account initialization, or False
otherwise.
:param is_skip_currencies: True to skip currency initialization, or False
otherwise.
:return: The testing Flask application.
"""
app: Flask = create_app(is_testing=True, is_skip_accounts=is_skip_accounts,
is_skip_currencies=is_skip_currencies)
app: Flask = create_app(is_testing=True)
@app.get("/.csrf-token")
def get_csrf_token_view() -> str: