From bb17b4ba831d99c115896e165d7c0b35a988c06d Mon Sep 17 00:00:00 2001 From: Herculino Trotta Date: Fri, 1 Nov 2024 17:23:57 -0300 Subject: [PATCH] test: add starting tests --- app/apps/accounts/tests.py | 40 +++++++- app/apps/currencies/tests.py | 93 ++++++++++++++++- app/apps/transactions/tests.py | 181 ++++++++++++++++++++++++++++++++- 3 files changed, 309 insertions(+), 5 deletions(-) diff --git a/app/apps/accounts/tests.py b/app/apps/accounts/tests.py index 7ce503c..3d81d0d 100644 --- a/app/apps/accounts/tests.py +++ b/app/apps/accounts/tests.py @@ -1,3 +1,41 @@ from django.test import TestCase -# Create your tests here. +from apps.accounts.models import Account, AccountGroup +from apps.currencies.models import Currency + + +class AccountTests(TestCase): + def setUp(self): + """Set up test data""" + self.currency = Currency.objects.create( + code="USD", name="US Dollar", decimal_places=2, prefix="$ " + ) + self.exchange_currency = Currency.objects.create( + code="EUR", name="Euro", decimal_places=2, prefix="€ " + ) + self.account_group = AccountGroup.objects.create(name="Test Group") + + def test_account_creation(self): + """Test basic account creation""" + account = Account.objects.create( + name="Test Account", + group=self.account_group, + currency=self.currency, + is_asset=False, + is_archived=False, + ) + self.assertEqual(str(account), "Test Account") + self.assertEqual(account.name, "Test Account") + self.assertEqual(account.group, self.account_group) + self.assertEqual(account.currency, self.currency) + self.assertFalse(account.is_asset) + self.assertFalse(account.is_archived) + + def test_account_with_exchange_currency(self): + """Test account creation with exchange currency""" + account = Account.objects.create( + name="Exchange Account", + currency=self.currency, + exchange_currency=self.exchange_currency, + ) + self.assertEqual(account.exchange_currency, self.exchange_currency) diff --git a/app/apps/currencies/tests.py b/app/apps/currencies/tests.py index 7ce503c..9f5f5de 100644 --- a/app/apps/currencies/tests.py +++ b/app/apps/currencies/tests.py @@ -1,3 +1,92 @@ -from django.test import TestCase +from decimal import Decimal -# Create your tests here. +from django.core.exceptions import ValidationError +from django.db import IntegrityError +from django.test import TestCase +from django.utils import timezone + +from apps.currencies.models import Currency, ExchangeRate + + +class CurrencyTests(TestCase): + def test_currency_creation(self): + """Test basic currency creation""" + currency = Currency.objects.create( + code="USD", name="US Dollar", decimal_places=2, prefix="$ ", suffix=" END " + ) + self.assertEqual(str(currency), "US Dollar") + self.assertEqual(currency.code, "USD") + self.assertEqual(currency.decimal_places, 2) + self.assertEqual(currency.prefix, "$ ") + self.assertEqual(currency.suffix, " END ") + + def test_currency_decimal_places_validation(self): + """Test decimal places validation for maximum value""" + currency = Currency( + code="TEST", + name="Test Currency", + decimal_places=31, # Should fail as max is 30 + ) + with self.assertRaises(ValidationError): + currency.full_clean() + + def test_currency_decimal_places_negative(self): + """Test decimal places validation for negative value""" + currency = Currency( + code="TEST", + name="Test Currency", + decimal_places=-1, # Should fail as min is 0 + ) + with self.assertRaises(ValidationError): + currency.full_clean() + + def test_currency_unique_code(self): + """Test that currency codes must be unique""" + Currency.objects.create(code="USD", name="US Dollar", decimal_places=2) + with self.assertRaises(IntegrityError): + Currency.objects.create(code="USD", name="Another Dollar", decimal_places=2) + + def test_currency_unique_name(self): + """Test that currency names must be unique""" + Currency.objects.create(code="USD", name="US Dollar", decimal_places=2) + with self.assertRaises(IntegrityError): + Currency.objects.create(code="USD2", name="US Dollar", decimal_places=2) + + +class ExchangeRateTests(TestCase): + def setUp(self): + """Set up test data""" + self.usd = Currency.objects.create( + code="USD", name="US Dollar", decimal_places=2, prefix="$ " + ) + self.eur = Currency.objects.create( + code="EUR", name="Euro", decimal_places=2, prefix="€ " + ) + + def test_exchange_rate_creation(self): + """Test basic exchange rate creation""" + rate = ExchangeRate.objects.create( + from_currency=self.usd, + to_currency=self.eur, + rate=Decimal("0.85"), + date=timezone.now(), + ) + self.assertEqual(rate.rate, Decimal("0.85")) + self.assertIn("USD to EUR", str(rate)) + + def test_unique_exchange_rate_constraint(self): + """Test that duplicate exchange rates for same currency pair and date are prevented""" + date = timezone.now() + ExchangeRate.objects.create( + from_currency=self.usd, + to_currency=self.eur, + rate=Decimal("0.85"), + date=date, + ) + with self.assertRaises(Exception): # Could be IntegrityError + ExchangeRate.objects.create( + from_currency=self.usd, + to_currency=self.eur, + rate=Decimal("0.86"), + date=date, + ) diff --git a/app/apps/transactions/tests.py b/app/apps/transactions/tests.py index 7ce503c..4cc8fab 100644 --- a/app/apps/transactions/tests.py +++ b/app/apps/transactions/tests.py @@ -1,3 +1,180 @@ -from django.test import TestCase +import datetime +from decimal import Decimal +from datetime import date, timedelta -# Create your tests here. +from django.test import TestCase +from django.core.exceptions import ValidationError +from django.utils import timezone + +from apps.transactions.models import ( + TransactionCategory, + TransactionTag, + Transaction, + InstallmentPlan, + RecurringTransaction, +) +from apps.accounts.models import Account, AccountGroup +from apps.currencies.models import Currency, ExchangeRate + + +class TransactionCategoryTests(TestCase): + def test_category_creation(self): + """Test basic category creation""" + category = TransactionCategory.objects.create(name="Groceries") + self.assertEqual(str(category), "Groceries") + self.assertFalse(category.mute) + + +class TransactionTagTests(TestCase): + def test_tag_creation(self): + """Test basic tag creation""" + tag = TransactionTag.objects.create(name="Essential") + self.assertEqual(str(tag), "Essential") + + +class TransactionTests(TestCase): + def setUp(self): + """Set up test data""" + self.currency = Currency.objects.create( + code="USD", name="US Dollar", decimal_places=2, prefix="$ " + ) + self.account_group = AccountGroup.objects.create(name="Test Group") + self.account = Account.objects.create( + name="Test Account", group=self.account_group, currency=self.currency + ) + self.category = TransactionCategory.objects.create(name="Test Category") + + def test_transaction_creation(self): + """Test basic transaction creation with required fields""" + transaction = Transaction.objects.create( + account=self.account, + type=Transaction.Type.EXPENSE, + date=timezone.now().date(), + amount=Decimal("100.00"), + description="Test transaction", + ) + self.assertTrue(transaction.is_paid) + self.assertEqual(transaction.type, Transaction.Type.EXPENSE) + self.assertEqual(transaction.account.currency.code, "USD") + + def test_transaction_with_exchange_currency(self): + """Test transaction with exchange currency""" + eur = Currency.objects.create( + code="EUR", name="Euro", decimal_places=2, prefix="€" + ) + self.account.exchange_currency = eur + self.account.save() + + # Create exchange rate + ExchangeRate.objects.create( + from_currency=self.currency, + to_currency=eur, + rate=Decimal("0.85"), + date=timezone.now(), + ) + + transaction = Transaction.objects.create( + account=self.account, + type=Transaction.Type.EXPENSE, + date=timezone.now().date(), + amount=Decimal("100.00"), + description="Test transaction", + ) + + exchanged = transaction.exchanged_amount() + self.assertIsNotNone(exchanged) + self.assertEqual(exchanged["prefix"], "€") + + def test_truncating_amount(self): + """Test amount truncating based on account.currency decimal places""" + transaction = Transaction.objects.create( + account=self.account, + type=Transaction.Type.EXPENSE, + date=timezone.now().date(), + amount=Decimal( + "100.0100001" + ), # account currency has two decimal places, the last 1 should be removed + description="Test transaction", + ) + self.assertEqual(transaction.amount, Decimal("100.0100000")) + + def test_automatic_reference_date(self): + """Test reference_date from date""" + transaction = Transaction.objects.create( + account=self.account, + type=Transaction.Type.EXPENSE, + date=datetime.datetime(day=20, month=1, year=2000).date(), + amount=Decimal("100"), + description="Test transaction", + ) + self.assertEqual( + transaction.reference_date, + datetime.datetime(day=1, month=1, year=2000).date(), + ) + + def test_reference_date_is_always_on_first_day(self): + """Test reference_date is always on the first day""" + transaction = Transaction.objects.create( + account=self.account, + type=Transaction.Type.EXPENSE, + date=datetime.datetime(day=20, month=1, year=2000).date(), + reference_date=datetime.datetime(day=20, month=2, year=2000).date(), + amount=Decimal("100"), + description="Test transaction", + ) + self.assertEqual( + transaction.reference_date, + datetime.datetime(day=1, month=2, year=2000).date(), + ) + + +class InstallmentPlanTests(TestCase): + def setUp(self): + """Set up test data""" + self.currency = Currency.objects.create( + code="USD", name="US Dollar", decimal_places=2, prefix="$ " + ) + self.account = Account.objects.create( + name="Test Account", currency=self.currency + ) + + def test_installment_plan_creation(self): + """Test basic installment plan creation""" + plan = InstallmentPlan.objects.create( + account=self.account, + type=Transaction.Type.EXPENSE, + description="Test Plan", + number_of_installments=12, + start_date=timezone.now().date(), + installment_amount=Decimal("100.00"), + recurrence=InstallmentPlan.Recurrence.MONTHLY, + ) + self.assertEqual(plan.number_of_installments, 12) + self.assertEqual(plan.installment_start, 1) + self.assertEqual(plan.account.currency.code, "USD") + + +class RecurringTransactionTests(TestCase): + def setUp(self): + """Set up test data""" + self.currency = Currency.objects.create( + code="USD", name="US Dollar", decimal_places=2, prefix="$ " + ) + self.account = Account.objects.create( + name="Test Account", currency=self.currency + ) + + def test_recurring_transaction_creation(self): + """Test basic recurring transaction creation""" + recurring = RecurringTransaction.objects.create( + account=self.account, + type=Transaction.Type.EXPENSE, + amount=Decimal("100.00"), + description="Monthly Payment", + start_date=timezone.now().date(), + recurrence_type=RecurringTransaction.RecurrenceType.MONTH, + recurrence_interval=1, + ) + self.assertFalse(recurring.paused) + self.assertEqual(recurring.recurrence_interval, 1) + self.assertEqual(recurring.account.currency.code, "USD")