117 lines
3.8 KiB
Python
117 lines
3.8 KiB
Python
|
|
"""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
|