Files
leggen/tests/unit/test_analytics_fix.py
2025-09-14 21:11:01 +01:00

140 lines
5.2 KiB
Python

"""Tests for analytics fixes to ensure all transactions are used in statistics."""
from datetime import datetime, timedelta
from unittest.mock import AsyncMock, Mock, patch
import pytest
from fastapi.testclient import TestClient
from leggen.commands.server import create_app
from leggen.services.database_service import DatabaseService
class TestAnalyticsFix:
"""Test analytics fixes for transaction limits"""
@pytest.fixture
def client(self):
app = create_app()
return TestClient(app)
@pytest.fixture
def mock_database_service(self):
return Mock(spec=DatabaseService)
@pytest.mark.asyncio
async def test_transaction_stats_uses_all_transactions(self, mock_database_service):
"""Test that transaction stats endpoint uses all transactions (not limited to 100)"""
# Mock data for 600 transactions (simulating the issue)
mock_transactions = []
for i in range(600):
mock_transactions.append(
{
"transactionId": f"txn-{i}",
"transactionDate": (
datetime.now() - timedelta(days=i % 365)
).isoformat(),
"description": f"Transaction {i}",
"transactionValue": 10.0 if i % 2 == 0 else -5.0,
"transactionCurrency": "EUR",
"transactionStatus": "booked",
"accountId": f"account-{i % 3}",
}
)
mock_database_service.get_transactions_from_db = AsyncMock(
return_value=mock_transactions
)
# Test that the endpoint calls get_transactions_from_db with limit=None
with patch(
"leggen.api.routes.transactions.database_service", mock_database_service
):
app = create_app()
client = TestClient(app)
response = client.get("/api/v1/transactions/stats?days=365")
assert response.status_code == 200
data = response.json()
# Verify that limit=None was passed to get all transactions
mock_database_service.get_transactions_from_db.assert_called_once()
call_args = mock_database_service.get_transactions_from_db.call_args
assert call_args.kwargs.get("limit") is None, (
"Stats endpoint should pass limit=None to get all transactions"
)
# Verify that the response contains stats for all 600 transactions
assert data["success"] is True
stats = data["data"]
assert stats["total_transactions"] == 600, (
"Should process all 600 transactions, not just 100"
)
# Verify calculations are correct for all transactions
expected_income = sum(
txn["transactionValue"]
for txn in mock_transactions
if txn["transactionValue"] > 0
)
expected_expenses = sum(
abs(txn["transactionValue"])
for txn in mock_transactions
if txn["transactionValue"] < 0
)
assert stats["total_income"] == expected_income
assert stats["total_expenses"] == expected_expenses
@pytest.mark.asyncio
async def test_analytics_endpoint_returns_all_transactions(
self, mock_database_service
):
"""Test that the new analytics endpoint returns all transactions without pagination"""
# Mock data for 600 transactions
mock_transactions = []
for i in range(600):
mock_transactions.append(
{
"transactionId": f"txn-{i}",
"transactionDate": (
datetime.now() - timedelta(days=i % 365)
).isoformat(),
"description": f"Transaction {i}",
"transactionValue": 10.0 if i % 2 == 0 else -5.0,
"transactionCurrency": "EUR",
"transactionStatus": "booked",
"accountId": f"account-{i % 3}",
}
)
mock_database_service.get_transactions_from_db = AsyncMock(
return_value=mock_transactions
)
with patch(
"leggen.api.routes.transactions.database_service", mock_database_service
):
app = create_app()
client = TestClient(app)
response = client.get("/api/v1/transactions/analytics?days=365")
assert response.status_code == 200
data = response.json()
# Verify that limit=None was passed to get all transactions
mock_database_service.get_transactions_from_db.assert_called_once()
call_args = mock_database_service.get_transactions_from_db.call_args
assert call_args.kwargs.get("limit") is None, (
"Analytics endpoint should pass limit=None"
)
# Verify that all 600 transactions are returned
assert data["success"] is True
transactions_data = data["data"]
assert len(transactions_data) == 600, (
"Analytics endpoint should return all 600 transactions"
)