Files
fuj-management/tests/test_app.py
Jan Novak 394da2e6b8
Some checks failed
Deploy to K8s / deploy (push) Successful in 11s
Build and Push / build (push) Successful in 6s
Build and Push / build-go (push) Failing after 6s
fix: Tolerate diacritic/case/whitespace mismatches in Person column matching
- Add canonical_member_key() in match_payments.py to normalize names via
  NFKD + lowercase + whitespace-collapse before ledger lookup; resolves
  payments attributed to e.g. "Maria Maco" to canonical "Mária Maco".
  Emits logger.info when a non-canonical cell is rescued so sheet typos
  are visible in logs without losing the payment allocation.
- Extend group_payments_by_person() in app.py to accept member_names and
  re-key raw-payment groups under the canonical attendance-sheet name so
  the modal's Raw Payments debug section also finds the row correctly.
- Add raw payments collapsible section to member detail modal in adults.html
  and juniors.html for debugging payment attribution issues.
- Remove 4 obsolete tests targeting routes /fees, /fees-juniors, /reconcile,
  /reconcile-juniors that no longer exist; add test_match_payments.py
  covering canonical key equivalence and reconcile() tolerance end-to-end.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 17:22:54 +02:00

102 lines
3.9 KiB
Python

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=/adults', 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', return_value={})
@patch('app.get_members_with_fees')
def test_adults_route(self, mock_get_members, mock_exceptions, mock_fetch_sheet, mock_cache):
"""Test that /adults returns 200 and shows combined 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('/adults')
self.assertEqual(response.status_code, 200)
self.assertIn(b'Adults Dashboard', response.data)
self.assertIn(b'Test Member', response.data)
self.assertNotIn(b'OK', response.data)
self.assertIn(b'750/750 CZK (4)', 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_junior_members_with_fees')
def test_juniors_route(self, mock_get_junior_members, mock_exceptions, mock_fetch_sheet, mock_cache):
"""Test that /juniors returns 200, uses single line format, and displays '?' properly"""
mock_get_junior_members.return_value = (
[
('Junior One', 'J', {'2026-01': (500, 3, 0, 3)}),
('Junior Two', 'X', {'2026-01': ('?', 1, 0, 1)})
],
['2026-01']
)
mock_exceptions.return_value = {}
mock_fetch_sheet.return_value = [{
'date': '2026-01-15',
'amount': 500,
'person': 'Junior One',
'purpose': '2026-01',
'message': '',
'sender': 'Parent',
'inferred_amount': 500
}]
response = self.client.get('/juniors')
self.assertEqual(response.status_code, 200)
self.assertIn(b'Juniors Dashboard', response.data)
self.assertIn(b'Junior One', response.data)
self.assertIn(b'Junior Two', response.data)
self.assertNotIn(b'OK', response.data)
self.assertIn(b'500/500 CZK', response.data)
self.assertIn(b'?', response.data)
if __name__ == '__main__':
unittest.main()