Skip to content

Commit

Permalink
Merge pull request #14 from Sha-yol/foreign-currency-update-tests
Browse files Browse the repository at this point in the history
Update tests for foreign currency check
  • Loading branch information
adyanth authored Oct 18, 2024
2 parents 783a2df + 8ffb812 commit 58f07d4
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 14 deletions.
5 changes: 5 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,11 @@ def getExpenseTransactionBody(exp: Expense, myshare: ExpenseUser, data: list[str
return newTxn

def getAccounts(account_type: str="asset") -> list:
"""Get accounts from Firefly.
:param account_type: The type of account
:return: A list of accounts
"""
return callApi("accounts/", method="GET", params={"type": account_type}).json()['data']

def cache_account_currency(function):
Expand Down
78 changes: 78 additions & 0 deletions tests/test_account_currency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import pytest
from unittest.mock import patch, Mock
import importlib
import requests

# Mock the entire requests library
@pytest.fixture(autouse=True)
def mock_requests():
with patch('requests.request') as mock:
mock.return_value.json.return_value = {'data': []}
yield mock

# Reload the main module in each test to ensure a clean slate
def reload_main():
with patch('main.callApi') as mock_call_api:
mock_call_api.return_value.json.return_value = {'data': []}
import main
importlib.reload(main)
return main

def test_getAccounts(mock_requests):
mock_requests.return_value.json.return_value = {
'data': [
{'attributes': {'name': 'Account1', 'currency_code': 'USD'}},
{'attributes': {'name': 'Account2', 'currency_code': 'EUR'}}
]
}

main = reload_main()
result = main.getAccounts()

assert len(result) == 2
assert result[0]['attributes']['name'] == 'Account1'
assert result[1]['attributes']['currency_code'] == 'EUR'

def test_getAccountCurrencyCode(mock_requests):
mock_requests.return_value.json.return_value = {
'data': [
{'attributes': {'name': 'Account1', 'currency_code': 'USD'}},
{'attributes': {'name': 'Account2', 'currency_code': 'EUR'}}
]
}

main = reload_main()

assert main.getAccountCurrencyCode('Account1') == 'USD'
assert main.getAccountCurrencyCode('Account2') == 'EUR'

with pytest.raises(ValueError):
main.getAccountCurrencyCode('NonexistentAccount')

def test_cache_behavior(mock_requests):
call_count = 0
def mock_request(*args, **kwargs):
nonlocal call_count
call_count += 1
mock_response = Mock()
mock_response.json.return_value = {
'data': [
{'attributes': {'name': 'Account1', 'currency_code': 'USD'}},
{'attributes': {'name': 'Account2', 'currency_code': 'EUR'}}
]
}
return mock_response

mock_requests.side_effect = mock_request

main = reload_main()

assert main.getAccountCurrencyCode('Account1') == 'USD'
assert main.getAccountCurrencyCode('Account2') == 'EUR'
assert main.getAccountCurrencyCode('Account1') == 'USD'

# The request should only be made once due to caching
assert call_count == 1

if __name__ == "__main__":
pytest.main([__file__])
65 changes: 51 additions & 14 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@
from splitwise import Splitwise, Expense, User, Comment
from splitwise.user import ExpenseUser
from unittest.mock import MagicMock, patch
import requests
import importlib

@pytest.fixture(autouse=True)
def mock_requests():
with patch('requests.request') as mock:
mock.return_value.json.return_value = {'data': []}
yield mock


def load_main():
import main
return main

from main import (
formatExpense, getSWUrlForExpense, getDate, getExpensesAfter,
processText, callApi, searchTransactions, getTransactionsAfter,
processExpense, getExpenseTransactionBody
)

@pytest.fixture
def mock_splitwise():
Expand Down Expand Up @@ -47,6 +55,7 @@ def mock_expense_user():
return expense_user

def test_formatExpense(mock_expense, mock_expense_user):
formatExpense = load_main().formatExpense
result = formatExpense(mock_expense, mock_expense_user)
assert "Test Expense" in result
assert "USD" in result
Expand All @@ -55,10 +64,12 @@ def test_formatExpense(mock_expense, mock_expense_user):
assert result == "Expense Test Expense for USD 10.00 on 2023-09-10T12:00:00Z"

def test_getSWUrlForExpense(mock_expense):
getSWUrlForExpense = load_main().getSWUrlForExpense
result = getSWUrlForExpense(mock_expense)
assert result == "https://secure.splitwise.com/expenses/67890"

def test_getDate():
getDate = load_main().getDate
date_str = "2023-09-10T12:00:00Z"
result = getDate(date_str)
assert isinstance(result, datetime)
Expand All @@ -72,10 +83,12 @@ def test_getDate():
("normal text", []),
])
def test_processText(text, expected):
processText = load_main().processText
assert processText(text) == expected

@patch('requests.request')
def test_callApi(mock_request):
callApi = load_main().callApi
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {"data": "test"}
Expand All @@ -87,6 +100,7 @@ def test_callApi(mock_request):

@patch('main.callApi')
def test_searchTransactions(mock_callApi):
searchTransactions = load_main().searchTransactions
# Simulate pagination
mock_responses = [
MagicMock(json=lambda: {"data": [{"id": "1"}, {"id": "2"}]}),
Expand All @@ -102,47 +116,69 @@ def test_searchTransactions(mock_callApi):

@patch('main.searchTransactions')
def test_getTransactionsAfter(mock_searchTransactions):
getTransactionsAfter = load_main().getTransactionsAfter
mock_searchTransactions.return_value = [
{"attributes": {"transactions": [{"external_url": "url1"}]}},
{"attributes": {"transactions": [{"external_url": "url2"}]}}
]

result = getTransactionsAfter(datetime.now() - timedelta(days=1))
result = getTransactionsAfter(datetime.now().astimezone() - timedelta(days=1))
assert len(result) == 2
assert "url1" in result
assert "url2" in result

def test_getExpenseTransactionBody(mock_expense, mock_expense_user):
result = getExpenseTransactionBody(mock_expense, mock_expense_user, ["Dest", "Category", "Desc"])
@patch('main.getAccountCurrencyCode')
def test_getExpenseTransactionBody(mock_getAccountCurrencyCode, mock_expense, mock_expense_user):
getExpenseTransactionBody = load_main().getExpenseTransactionBody
mock_getAccountCurrencyCode.return_value = "USD"
result = getExpenseTransactionBody(mock_expense, mock_expense_user, ["Dest", "Category", "Desc", "Amex"])

assert result["source_name"] == "Amex"
assert result["destination_name"] == "Dest"
assert result["category_name"] == "Category"
assert result["amount"] == "10.00"
assert result["currency_code"] == "USD"
assert result["description"] == "Desc"

@patch('main.callApi')
@patch('main.updateTransaction')
@patch('main.addTransaction')
@patch('main.searchTransactions')
def test_processExpense_update(mock_searchTransactions, mock_addTransaction, mock_updateTransaction, mock_callApi, mock_expense, mock_expense_user):
@patch('main.getAccountCurrencyCode')
def test_processExpense_update(mock_getAccountCurrencyCode,
mock_searchTransactions,
mock_addTransaction,
mock_updateTransaction,
mock_callApi,
mock_expense,
mock_expense_user):
processExpense = load_main().processExpense
getSWUrlForExpense = load_main().getSWUrlForExpense

mock_getAccountCurrencyCode.return_value = "USD"
mock_callApi.return_value = MagicMock(json=lambda: {})
mock_searchTransactions.return_value = []

ff_txns = {getSWUrlForExpense(mock_expense): {"id": "123", "attributes": {}}}
processExpense(datetime.now().astimezone() - timedelta(days=1), ff_txns, mock_expense, mock_expense_user, [])
mock_updateTransaction.assert_called_once()
mock_addTransaction.assert_not_called()
mock_searchTransactions.assert_not_called()
mock_searchTransactions.assert_called_once()

@patch('main.callApi')
@patch('main.updateTransaction')
@patch('main.addTransaction')
@patch('main.searchTransactions')
def test_processExpense_add_new(mock_searchTransactions, mock_addTransaction, mock_updateTransaction, mock_callApi, mock_expense, mock_expense_user):
@patch('main.getAccountCurrencyCode')
def test_processExpense_add_new(mock_getAccountCurrencyCode,
mock_searchTransactions,
mock_addTransaction,
mock_updateTransaction,
mock_callApi,
mock_expense,
mock_expense_user):
processExpense = load_main().processExpense
mock_callApi.return_value = MagicMock(json=lambda: {})
mock_searchTransactions.return_value = []
mock_getAccountCurrencyCode.return_value = "USD"

ff_txns = {}
processExpense(datetime.now().astimezone() - timedelta(days=1), ff_txns, mock_expense, mock_expense_user, ["Dest", "Category", "Desc"])
Expand All @@ -155,6 +191,7 @@ def mock_splitwise():
return MagicMock()

def test_getExpensesAfter(mock_splitwise, mock_user):
getExpensesAfter = load_main().getExpensesAfter
# Setup
mock_expense1 = MagicMock(spec=Expense)
mock_expense1.getId.return_value = "1"
Expand Down Expand Up @@ -246,4 +283,4 @@ def test_getExpensesAfter(mock_splitwise, mock_user):
assert all(r[0].getId() != "3" for r in result), "Expense without Firefly data should not be returned"

if __name__ == "__main__":
pytest.main()
pytest.main([__file__])

0 comments on commit 58f07d4

Please sign in to comment.