"""Integration tests for API route handlers (views.py). Uses Flask test client with mocked database to verify the full request pipeline without needing a live MSSQL connection. """ import base64 from unittest.mock import patch, MagicMock import pytest def _bot_auth(user='testbot', passwd='testpassword'): token = base64.b64encode(f"{user}:{passwd}".encode()).decode() return {'Authorization': f'Basic {token}'} class TestMonitor: @patch('cteward_ng.database.check_backend_okay') def test_monitor_ok(self, mock_check, client): mock_check.return_value = None # no exception resp = client.get('/legacy/monitor') assert resp.status_code == 200 assert resp.json['status'] == 'OK' @patch('cteward_ng.database.check_backend_okay') def test_monitor_broken(self, mock_check, client): mock_check.side_effect = Exception("DB down") resp = client.get('/legacy/monitor') assert resp.status_code == 503 assert resp.json['status'] == 'BROKEN' class TestStatsEndpoints: """Test that stats endpoints reject unauthorized access and pass through correctly.""" @patch('cteward_ng.database.run_query') def test_stats_members_unauthorized(self, mock_run, client): resp = client.get('/legacy/stats/members') assert resp.status_code == 401 @patch('cteward_ng.database.run_query') def test_stats_members_authorized(self, mock_run, client): mock_run.return_value = [ {'Year': 2023, 'Month': 1, 'Members': 10}, ] resp = client.get( '/legacy/stats/members', headers=_bot_auth(), ) assert resp.status_code == 200 data = resp.json assert len(data) == 1 assert data[0]['Year'] == 2023 class TestMemberEndpoint: @patch('cteward_ng.database.run_query') def test_member_unauthorized(self, mock_run, client): resp = client.get('/legacy/member/alice') assert resp.status_code == 401 @patch('cteward_ng.database.run_query') def test_member_raw_invalid_type(self, mock_run, client): resp = client.get( '/legacy/member/alice/invalid/raw/', headers=_bot_auth(), ) assert resp.status_code == 400 class TestMemberMemo: @patch('cteward_ng.database.run_query') def test_memo_empty_crewname(self, mock_run, client): # Empty crewname should be caught by Flask as a routing issue or handled # The bot doesn't have _board_ flag, so this should 403 resp = client.get( '/legacy/member/alice/memo', headers=_bot_auth(), ) # Bot has no board access → should fail auth assert resp.status_code in (401, 403) class TestContributions: @patch('cteward_ng.database.run_query') def test_contributions_board_only(self, mock_run, client): resp = client.get( '/legacy/member/alice/contributions', headers=_bot_auth(), ) # Bot has no board access → 403 or 401 assert resp.status_code in (401, 403) class TestMemberDetailRaw: @patch('cteward_ng.database.run_query') def test_contract_detail(self, mock_run, client): mock_run.return_value = [{ 'VertragNr': '1', 'ArtName': 'Ehren', 'Sollstellung': 'monatlich', 'Betrag': 25, }] resp = client.get( '/legacy/member/alice/contract/raw/', headers=_bot_auth(), ) # Bot has no board access → should fail assert resp.status_code in (401, 403) @patch('cteward_ng.database.run_query') def test_invalid_detail_type(self, mock_run, client): resp = client.get( '/legacy/member/alice/unknown/raw/', headers=_bot_auth(), ) assert resp.status_code == 400