import unittest from unittest.mock import patch from app import app def _bypass_cache(cache_key, sheet_id, fetch_func, *args, serialize=None, deserialize=None, **kwargs): """Test helper: call fetch_func directly, bypassing the cache layer.""" return fetch_func(*args, **kwargs) class TestWebApp(unittest.TestCase): def setUp(self): app.config['TESTING'] = True self.client = app.test_client() def test_index_page(self): """Test that / returns the refresh meta tag""" response = self.client.get('/') self.assertEqual(response.status_code, 200) self.assertIn(b'url=/fees', response.data) @patch('app.get_cached_data', side_effect=_bypass_cache) @patch('app.get_members_with_fees') @patch('app.fetch_exceptions', return_value={}) def test_fees_route(self, mock_exceptions, mock_get_members, mock_cache): """Test that /fees returns 200 and renders the dashboard""" mock_get_members.return_value = ( [('Test Member', 'A', {'2026-01': (750, 4)})], ['2026-01'] ) response = self.client.get('/fees') self.assertEqual(response.status_code, 200) self.assertIn(b'FUJ Fees Dashboard', response.data) self.assertIn(b'Test Member', response.data) @patch('app.get_cached_data', side_effect=_bypass_cache) @patch('app.get_junior_members_with_fees') @patch('app.fetch_exceptions', return_value={}) def test_fees_juniors_route(self, mock_exceptions, mock_get_junior_members, mock_cache): """Test that /fees-juniors returns 200 and renders the junior dashboard""" mock_get_junior_members.return_value = ( [ ('Test Junior 1', 'J', {'2026-01': ('?', 1, 0, 1)}), ('Test Junior 2', 'J', {'2026-01': (500, 4, 1, 3)}) ], ['2026-01'] ) response = self.client.get('/fees-juniors') self.assertEqual(response.status_code, 200) self.assertIn(b'FUJ Junior Fees Dashboard', response.data) self.assertIn(b'Test Junior 1', response.data) self.assertIn(b'? / 1 (J)', response.data) self.assertIn(b'500 CZK / 4 (1A+3J)', response.data) @patch('app.get_cached_data', side_effect=_bypass_cache) @patch('app.fetch_sheet_data') @patch('app.fetch_exceptions', return_value={}) @patch('app.get_members_with_fees') def test_reconcile_route(self, mock_get_members, mock_exceptions, mock_fetch_sheet, mock_cache): """Test that /reconcile returns 200 and shows matches""" mock_get_members.return_value = ( [('Test Member', 'A', {'2026-01': (750, 4)})], ['2026-01'] ) mock_fetch_sheet.return_value = [{ 'date': '2026-01-01', 'amount': 750, 'person': 'Test Member', 'purpose': '2026-01', 'message': 'test payment', 'sender': 'External Bank User', 'inferred_amount': 750 }] response = self.client.get('/reconcile') self.assertEqual(response.status_code, 200) self.assertIn(b'Payment Reconciliation', response.data) self.assertIn(b'Test Member', response.data) self.assertIn(b'OK', response.data) @patch('app.get_cached_data', side_effect=_bypass_cache) @patch('app.fetch_sheet_data') def test_payments_route(self, mock_fetch_sheet, mock_cache): """Test that /payments returns 200 and groups transactions""" mock_fetch_sheet.return_value = [{ 'date': '2026-01-01', 'amount': 750, 'person': 'Test Member', 'purpose': '2026-01', 'message': 'Direct Member Payment', 'sender': 'External Bank User' }] response = self.client.get('/payments') self.assertEqual(response.status_code, 200) self.assertIn(b'Payments Ledger', response.data) self.assertIn(b'Test Member', response.data) self.assertIn(b'Direct Member Payment', response.data) @patch('app.get_cached_data', side_effect=_bypass_cache) @patch('app.fetch_sheet_data') @patch('app.fetch_exceptions') @patch('app.get_junior_members_with_fees') def test_reconcile_juniors_route(self, mock_get_junior, mock_exceptions, mock_transactions, mock_cache): """Test that /reconcile-juniors correctly computes balances for juniors.""" mock_get_junior.return_value = ( [ ('Junior One', 'J', {'2026-01': (500, 4, 2, 2)}), ('Junior Two', 'X', {'2026-01': ('?', 1, 0, 1)}) ], ['2026-01'] ) mock_exceptions.return_value = {} mock_transactions.return_value = [{ 'date': '2026-01-15', 'amount': 500, 'person': 'Junior One', 'purpose': '2026-01', 'message': '', 'sender': 'Parent', 'inferred_amount': 500 }] response = self.client.get('/reconcile-juniors') self.assertEqual(response.status_code, 200) self.assertIn(b'Junior Payment Reconciliation', response.data) self.assertIn(b'Junior One', response.data) self.assertIn(b'Junior Two', response.data) self.assertIn(b'OK', response.data) self.assertIn(b'?', response.data) if __name__ == '__main__': unittest.main()