Files
roa2web-service-auto/reports-app/telegram-bot/tests/test_helpers_extended.py
Marius Mutu a7a1bef375 Add missing test files and update .gitignore to allow test files
This commit addresses the overly restrictive .gitignore pattern that
was excluding all test files (test_*.py), including legitimate pytest
and unittest test suites essential for code quality and CI/CD.

Changes to .gitignore:
- Added negation patterns !**/tests/test_*.py and !**/test_*.py
  to allow proper test files while still blocking temporary scripts
- This enables pytest test suites to be tracked by git

Added test files (17 files):

Telegram Bot Tests (15 files):
- reports-app/telegram-bot/tests/test_auth.py
  Tests for authentication and account linking flow
- reports-app/telegram-bot/tests/test_callbacks.py
  Tests for callback query handlers
- reports-app/telegram-bot/tests/test_formatters.py
  Tests for message formatting utilities
- reports-app/telegram-bot/tests/test_formatters_extended.py
  Extended formatter tests
- reports-app/telegram-bot/tests/test_handlers_menu.py
  Tests for menu handlers
- reports-app/telegram-bot/tests/test_helpers.py
  Tests for helper functions
- reports-app/telegram-bot/tests/test_helpers_extended.py
  Extended helper tests
- reports-app/telegram-bot/tests/test_helpers_real.py
  Real integration tests for helpers
- reports-app/telegram-bot/tests/test_helpers_real_simple.py
  Simplified integration tests
- reports-app/telegram-bot/tests/test_login_flow.py
  Complete login flow integration tests
- reports-app/telegram-bot/tests/test_menus.py
  Menu system tests
- reports-app/telegram-bot/tests/test_session_company.py
  Session and company management tests
- reports-app/telegram-bot/test_claude_integration.py
  Manual integration test (Claude AI)
- reports-app/telegram-bot/test_claude_response.py
  Response formatting test
- reports-app/telegram-bot/test_db.py
  Database operations manual test

Shared Module Tests (2 files):
- shared/auth/test_auth.py
  Authentication system tests
- shared/database/test_pool.py
  Oracle connection pool tests

Security verification:
 All test files use mock objects, fixtures, and environment variables
 No hardcoded credentials or secrets found
 Safe for version control

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 15:09:43 +03:00

358 lines
14 KiB
Python

"""
Tests for FAZA 2 extended helper functions.
Tests new helpers for treasury breakdown, clients/suppliers with maturity, and invoices.
"""
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from app.bot.helpers import (
get_treasury_breakdown_split,
get_clients_with_maturity,
get_suppliers_with_maturity,
get_cashflow_evolution_data,
get_client_invoices,
get_supplier_invoices
)
@pytest.mark.asyncio
async def test_get_treasury_breakdown_split():
"""Test split trezorerie în casa/banca"""
mock_response = {
'accounts': [
{'name': 'Casa Ron', 'type': 'Casa', 'balance': 5000},
{'name': 'Casa Valuta', 'type': 'Casa', 'balance': 2000},
{'name': 'BCR', 'type': 'Banca', 'balance': 10000},
{'name': 'BRD', 'type': 'Banca', 'balance': 5000}
]
}
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.get_treasury_breakdown = AsyncMock(return_value=mock_response)
mock_client_getter.return_value = mock_client
result = await get_treasury_breakdown_split(1, "fake_token")
assert result is not None
assert 'casa' in result
assert 'banca' in result
assert result['casa']['total'] == 7000 # 5000 + 2000
assert result['banca']['total'] == 15000 # 10000 + 5000
assert len(result['casa']['accounts']) == 2
assert len(result['banca']['accounts']) == 2
@pytest.mark.asyncio
async def test_get_treasury_breakdown_split_casa_in_name():
"""Test că identifică casa după nume (nu doar după type)"""
mock_response = {
'accounts': [
{'name': 'Casa principala', 'type': 'Gest', 'balance': 3000},
{'name': 'Cont BCR', 'type': 'Gest', 'balance': 10000}
]
}
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.get_treasury_breakdown = AsyncMock(return_value=mock_response)
mock_client_getter.return_value = mock_client
result = await get_treasury_breakdown_split(1, "fake_token")
assert result['casa']['total'] == 3000 # Casa identificată după nume
assert result['banca']['total'] == 10000
@pytest.mark.asyncio
async def test_get_treasury_breakdown_split_api_error():
"""Test comportament când API returnează None"""
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.get_treasury_breakdown = AsyncMock(return_value=None)
mock_client_getter.return_value = mock_client
result = await get_treasury_breakdown_split(1, "fake_token")
assert result is None
@pytest.mark.asyncio
async def test_get_clients_with_maturity():
"""Test obținere clienți cu scadențe"""
mock_clients_data = {
'clients': [
{'id': 1, 'name': 'Client A', 'balance': 15000},
{'id': 2, 'name': 'Client B', 'balance': 8500}
]
}
mock_maturity_data = {
'in_term': 18000,
'overdue': 5500,
'total': 23500
}
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.get_detailed_data = AsyncMock(return_value=mock_clients_data)
mock_client.get_maturity_data = AsyncMock(return_value=mock_maturity_data)
mock_client_getter.return_value = mock_client
result = await get_clients_with_maturity(1, "fake_token")
assert result is not None
assert 'clients' in result
assert 'maturity' in result
assert len(result['clients']) == 2
assert result['maturity']['in_term'] == 18000
assert result['maturity']['overdue'] == 5500
assert result['maturity']['total'] == 23500
@pytest.mark.asyncio
async def test_get_clients_with_maturity_items_key():
"""Test că acceptă atât 'clients' cât și 'items' ca key"""
mock_clients_data = {
'items': [
{'id': 1, 'name': 'Client A', 'balance': 15000}
]
}
mock_maturity_data = {'in_term': 15000, 'overdue': 0, 'total': 15000}
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.get_detailed_data = AsyncMock(return_value=mock_clients_data)
mock_client.get_maturity_data = AsyncMock(return_value=mock_maturity_data)
mock_client_getter.return_value = mock_client
result = await get_clients_with_maturity(1, "fake_token")
assert len(result['clients']) == 1
@pytest.mark.asyncio
async def test_get_clients_with_maturity_no_maturity_data():
"""Test fallback când maturity data e None"""
mock_clients_data = {
'clients': [{'id': 1, 'name': 'Client A', 'balance': 15000}]
}
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.get_detailed_data = AsyncMock(return_value=mock_clients_data)
mock_client.get_maturity_data = AsyncMock(return_value=None)
mock_client_getter.return_value = mock_client
result = await get_clients_with_maturity(1, "fake_token")
assert result['maturity'] == {'in_term': 0, 'overdue': 0, 'total': 0}
@pytest.mark.asyncio
async def test_get_suppliers_with_maturity():
"""Test obținere furnizori cu scadențe"""
mock_suppliers_data = {
'suppliers': [
{'id': 1, 'name': 'Supplier A', 'balance': 5000}
]
}
mock_maturity_data = {
'in_term': 4000,
'overdue': 1000,
'total': 5000
}
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.get_detailed_data = AsyncMock(return_value=mock_suppliers_data)
mock_client.get_maturity_data = AsyncMock(return_value=mock_maturity_data)
mock_client_getter.return_value = mock_client
result = await get_suppliers_with_maturity(1, "fake_token")
assert result is not None
assert 'suppliers' in result
assert 'maturity' in result
assert len(result['suppliers']) == 1
@pytest.mark.asyncio
async def test_get_cashflow_evolution_data():
"""Test obținere date evoluție cash flow"""
mock_performance = {
'incasari_total': 100000,
'plati_total': 80000,
'net': 20000
}
mock_monthly = {
'months': ['Ian', 'Feb', 'Mar'],
'incasari': [30000, 35000, 35000],
'plati': [25000, 27000, 28000]
}
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.get_performance_data = AsyncMock(return_value=mock_performance)
mock_client.get_monthly_flows = AsyncMock(return_value=mock_monthly)
mock_client_getter.return_value = mock_client
result = await get_cashflow_evolution_data(1, "fake_token")
assert result is not None
assert 'performance' in result
assert 'monthly' in result
assert result['performance']['net'] == 20000
assert len(result['monthly']['months']) == 3
@pytest.mark.asyncio
async def test_get_cashflow_evolution_data_no_monthly():
"""Test fallback când monthly data e None"""
mock_performance = {
'incasari_total': 100000,
'plati_total': 80000,
'net': 20000
}
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.get_performance_data = AsyncMock(return_value=mock_performance)
mock_client.get_monthly_flows = AsyncMock(return_value=None)
mock_client_getter.return_value = mock_client
result = await get_cashflow_evolution_data(1, "fake_token")
assert result['monthly'] == {'months': [], 'incasari': [], 'plati': []}
@pytest.mark.asyncio
async def test_get_client_invoices():
"""Test obținere facturi client"""
mock_invoices = [
{'id': 1, 'number': 'FV001', 'amount': 5000, 'status': 'unpaid'},
{'id': 2, 'number': 'FV002', 'amount': 3500, 'status': 'paid'}
]
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.search_invoices = AsyncMock(return_value=mock_invoices)
mock_client_getter.return_value = mock_client
result = await get_client_invoices(1, "Client A", "fake_token")
assert len(result) == 2
assert result[0]['number'] == 'FV001'
# Verifică că a fost apelat cu filtrul corect
mock_client.search_invoices.assert_called_once()
call_args = mock_client.search_invoices.call_args
assert call_args[1]['filters']['partner_type'] == 'CLIENTI'
assert call_args[1]['filters']['partner_name'] == 'Client A'
@pytest.mark.asyncio
async def test_get_supplier_invoices():
"""Test obținere facturi furnizor"""
mock_invoices = [
{'id': 1, 'number': 'FC001', 'amount': 2000, 'status': 'unpaid'}
]
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.search_invoices = AsyncMock(return_value=mock_invoices)
mock_client_getter.return_value = mock_client
result = await get_supplier_invoices(1, "Supplier A", "fake_token")
assert len(result) == 1
assert result[0]['number'] == 'FC001'
# Verifică că a fost apelat cu filtrul corect
mock_client.search_invoices.assert_called_once()
call_args = mock_client.search_invoices.call_args
assert call_args[1]['filters']['partner_type'] == 'FURNIZORI'
assert call_args[1]['filters']['partner_name'] == 'Supplier A'
@pytest.mark.asyncio
async def test_get_client_invoices_empty():
"""Test comportament când nu există facturi"""
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
mock_client.search_invoices = AsyncMock(return_value=[])
mock_client_getter.return_value = mock_client
result = await get_client_invoices(1, "Client A", "fake_token")
assert result == []
@pytest.mark.asyncio
async def test_get_client_invoices_error_handling():
"""Test error handling pentru get_client_invoices"""
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(side_effect=Exception("API Error"))
mock_client_getter.return_value = mock_client
result = await get_client_invoices(1, "Client A", "fake_token")
assert result == [] # Returnează listă goală la eroare
@pytest.mark.asyncio
async def test_helpers_handle_none_responses():
"""Test că helper-ii handle-uiesc răspunsuri None de la API"""
with patch('app.bot.helpers.get_backend_client') as mock_client_getter:
mock_client = MagicMock()
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
mock_client.__aexit__ = AsyncMock(return_value=None)
# Test toate funcțiile cu None responses
mock_client.get_detailed_data = AsyncMock(return_value=None)
mock_client.get_maturity_data = AsyncMock(return_value=None)
mock_client.get_performance_data = AsyncMock(return_value=None)
mock_client.get_monthly_flows = AsyncMock(return_value=None)
mock_client.search_invoices = AsyncMock(return_value=None)
mock_client_getter.return_value = mock_client
# Niciunul nu ar trebui să crapeze
result1 = await get_clients_with_maturity(1, "token")
assert result1 is None
result2 = await get_suppliers_with_maturity(1, "token")
assert result2 is None
result3 = await get_cashflow_evolution_data(1, "token")
assert result3 is None
result4 = await get_client_invoices(1, "Client", "token")
assert result4 == [] # Empty list, not None
result5 = await get_supplier_invoices(1, "Supplier", "token")
assert result5 == [] # Empty list, not None