move everything into src/catnab and add more tests
This commit is contained in:
@@ -1,10 +1,102 @@
|
||||
from datetime import date
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from ynab import TransactionFlagColor
|
||||
|
||||
from ai import ItemClassification
|
||||
import main
|
||||
from main import TxCategory
|
||||
from catnab.ai import ItemClassification
|
||||
import catnab
|
||||
from catnab import Context, TxCategory
|
||||
from catnab.amazon import AmazonAccount
|
||||
|
||||
|
||||
def test_is_matchable_ynab(mk_ynab_tx):
|
||||
matchable = mk_ynab_tx()
|
||||
assert catnab.is_matchable_ynab(matchable)
|
||||
|
||||
wrong_payee = mk_ynab_tx(payee_name='Walmart')
|
||||
assert not catnab.is_matchable_ynab(wrong_payee)
|
||||
|
||||
already_matched = mk_ynab_tx(flag_color=TransactionFlagColor.PURPLE)
|
||||
assert not catnab.is_matchable_ynab(already_matched)
|
||||
|
||||
already_approved = mk_ynab_tx(approved=True)
|
||||
assert not catnab.is_matchable_ynab(already_approved)
|
||||
|
||||
# fortunately we only need to check the length of the subtransactions list
|
||||
already_split = mk_ynab_tx(subtransactions=[1, 2, 3])
|
||||
assert not catnab.is_matchable_ynab(already_split)
|
||||
|
||||
|
||||
def test_is_matchable_amz(mk_amz_tx):
|
||||
matchable = mk_amz_tx()
|
||||
assert catnab.is_matchable_amz(matchable)
|
||||
|
||||
refund = mk_amz_tx(is_refund=True)
|
||||
assert not catnab.is_matchable_amz(refund)
|
||||
|
||||
empty_order_number = mk_amz_tx(order_number='')
|
||||
assert not catnab.is_matchable_amz(empty_order_number)
|
||||
|
||||
gift_card_order=mk_amz_tx(payment_method='Amazon Gift Card')
|
||||
assert not catnab.is_matchable_amz(gift_card_order)
|
||||
|
||||
points_order = mk_amz_tx(payment_method='Amazon Visa points')
|
||||
assert not catnab.is_matchable_amz(points_order)
|
||||
|
||||
|
||||
def test_tx_matches(mk_amz_tx, mk_ynab_tx):
|
||||
amz_tx = mk_amz_tx(
|
||||
grand_total=123.45,
|
||||
completed_date=date(2025, 1, 1),
|
||||
)
|
||||
ynab_tx=mk_ynab_tx(
|
||||
amount=123450,
|
||||
var_date=date(2025, 1, 1),
|
||||
)
|
||||
|
||||
matches = catnab.tx_matches([amz_tx], [ynab_tx])
|
||||
assert len(matches) == 1
|
||||
|
||||
|
||||
def test_tx_matches_max_datediff(mk_amz_tx, mk_ynab_tx):
|
||||
# with a 7-day difference, transactions match
|
||||
amz_tx = mk_amz_tx(
|
||||
grand_total=123.45,
|
||||
completed_date=date(2025, 1, 1),
|
||||
)
|
||||
ynab_tx=mk_ynab_tx(
|
||||
amount=123450,
|
||||
var_date=date(2025, 1, 8),
|
||||
)
|
||||
|
||||
matches = catnab.tx_matches([amz_tx], [ynab_tx])
|
||||
assert len(matches) == 1
|
||||
|
||||
# but with an 8-day difference, they don't
|
||||
ynab_tx.var_date = date(2026, 1, 9)
|
||||
matches = catnab.tx_matches([amz_tx], [ynab_tx])
|
||||
assert len(matches) == 0
|
||||
|
||||
|
||||
def test_tx_matches_tiebreak(mk_amz_tx, mk_ynab_tx):
|
||||
amz_tx = mk_amz_tx(
|
||||
grand_total=123.45,
|
||||
completed_date=date(2025, 1, 1),
|
||||
)
|
||||
ynab_1 = mk_ynab_tx(
|
||||
amount=123450,
|
||||
var_date=date(2025, 1, 5),
|
||||
)
|
||||
ynab_2 = mk_ynab_tx(
|
||||
amount=123450,
|
||||
var_date=date(2025, 1, 3),
|
||||
)
|
||||
|
||||
matches = catnab.tx_matches([amz_tx], [ynab_1, ynab_2])
|
||||
assert len(matches) == 1
|
||||
matched_amz, matched_ynab = matches[0]
|
||||
assert matched_ynab is ynab_2
|
||||
|
||||
|
||||
def test_get_tx_categories(monkeypatch):
|
||||
@@ -27,9 +119,9 @@ def test_get_tx_categories(monkeypatch):
|
||||
|
||||
def classify(_names, _categories):
|
||||
return item_details
|
||||
monkeypatch.setattr('ai.classify', classify)
|
||||
monkeypatch.setattr('catnab.ai.classify', classify)
|
||||
|
||||
res = main.get_tx_categories(order, item_details)
|
||||
res = catnab.get_tx_categories(order, item_details)
|
||||
assert len(res) == 1
|
||||
assert res[0].category_name == 'Example category'
|
||||
assert res[0].description == 'example'
|
||||
@@ -65,9 +157,9 @@ def test_get_categories_multi_items(monkeypatch):
|
||||
|
||||
def classify(_names, _categories):
|
||||
return item_details
|
||||
monkeypatch.setattr('ai.classify', classify)
|
||||
monkeypatch.setattr('catnab.ai.classify', classify)
|
||||
|
||||
res = main.get_tx_categories(order, item_details)
|
||||
res = catnab.get_tx_categories(order, item_details)
|
||||
assert len(res) == 1
|
||||
assert res[0].category_name == 'Example category'
|
||||
assert res[0].description == 'example, second example'
|
||||
@@ -103,9 +195,9 @@ def test_get_categories_multi_categories(monkeypatch):
|
||||
|
||||
def classify(_names, _categories):
|
||||
return item_details
|
||||
monkeypatch.setattr('ai.classify', classify)
|
||||
monkeypatch.setattr('catnab.ai.classify', classify)
|
||||
|
||||
res = main.get_tx_categories(order, item_details)
|
||||
res = catnab.get_tx_categories(order, item_details)
|
||||
assert len(res) == 2
|
||||
assert res[0].category_name == 'Example category'
|
||||
assert res[0].description == 'example'
|
||||
@@ -141,8 +233,8 @@ def test_build_tx_update_multi():
|
||||
amount=23760,
|
||||
)
|
||||
|
||||
res = main.build_tx_update(tx_categories, category_ids, tx)
|
||||
assert len(res.subtransactions) == 2
|
||||
res = catnab.build_tx_update(tx_categories, category_ids, tx)
|
||||
assert res.subtransactions is not None and len(res.subtransactions) == 2
|
||||
|
||||
sub0 = res.subtransactions[0]
|
||||
assert sub0.category_id == '123'
|
||||
@@ -153,3 +245,49 @@ def test_build_tx_update_multi():
|
||||
assert sub1.category_id == '456'
|
||||
assert sub1.memo == 'second example'
|
||||
assert sub1.amount == 7603
|
||||
|
||||
|
||||
def test_sync_acct_basic(basic_order, ynab_creds):
|
||||
ctx = Context(sync_days=30)
|
||||
acct = AmazonAccount('test@example.com', 'password123')
|
||||
catnab.sync_account(ctx, acct)
|
||||
|
||||
mock_update = ctx.ynab_client.update_transactions
|
||||
mock_update.assert_called_once()
|
||||
|
||||
tx_update = mock_update.call_args.args[0][0]
|
||||
assert tx_update.id == '1'
|
||||
assert tx_update.category_id == '0'
|
||||
assert tx_update.flag_color == 'purple'
|
||||
|
||||
|
||||
def test_sync_acct_multi_items(multi_item_order, ynab_creds):
|
||||
ctx = Context(sync_days=30)
|
||||
acct = AmazonAccount('test@example.com', 'password123')
|
||||
catnab.sync_account(ctx, acct)
|
||||
|
||||
mock_update = ctx.ynab_client.update_transactions
|
||||
mock_update.assert_called_once()
|
||||
|
||||
tx_update = mock_update.call_args.args[0][0]
|
||||
assert tx_update.id == '1'
|
||||
assert tx_update.category_id == '0'
|
||||
assert tx_update.flag_color == 'purple'
|
||||
|
||||
|
||||
def test_sync_acct_multi_categories(multi_category_order, ynab_creds):
|
||||
ctx = Context(sync_days=30)
|
||||
acct = AmazonAccount('test@example.com', 'password123')
|
||||
catnab.sync_account(ctx, acct)
|
||||
|
||||
mock_update = ctx.ynab_client.update_transactions
|
||||
mock_update.assert_called_once()
|
||||
|
||||
tx_update = mock_update.call_args.args[0][0]
|
||||
assert tx_update.id == '1'
|
||||
assert tx_update.category_id == None
|
||||
assert tx_update.flag_color == 'purple'
|
||||
|
||||
assert len(tx_update.subtransactions) == 2
|
||||
# we don't care about order, but both categories need to be present
|
||||
assert set(st.category_id for st in tx_update.subtransactions) == {'0', '1'}
|
||||
|
||||
Reference in New Issue
Block a user