"""Admin tryout toggle — open/close gate and enforcement on tryout routes. ADMIN-006. The tryouts_open setting gates every tryout mutation route for non-admins. Admins always bypass the lock. These tests verify the toggle itself and the gate on each affected route. """ import pytest from app.models import AppSettings, Tryout NON_ADMIN_ROLES = ['manager', 'coach'] def _redirected(response): return response.status_code in (301, 302) # --------------------------------------------------------------------------- # Toggle logic # --------------------------------------------------------------------------- class TestTryoutToggle: def test_toggle_from_open_to_closed(self, client, as_role): as_role('admin') # Default is open client.post('/admin/toggle-tryouts', data={}, follow_redirects=False) # Now should be closed with client.application.app_context(): assert not AppSettings.get_bool('tryouts_open', default=True) def test_toggle_from_closed_to_open(self, client, as_role): as_role('admin') # Close first client.post('/admin/toggle-tryouts', data={}, follow_redirects=False) # Open again client.post('/admin/toggle-tryouts', data={}, follow_redirects=False) with client.application.app_context(): assert AppSettings.get_bool('tryouts_open', default=True) def test_toggle_creates_audit_log(self, client, as_role): from app.models import AuditLog as_role('admin') client.post('/admin/toggle-tryouts', data={}, follow_redirects=False) with client.application.app_context(): entry = AuditLog.query.filter_by(action='tryouts_toggled').first() assert entry is not None def test_default_is_open(self, app): with app.app_context(): assert AppSettings.get_bool('tryouts_open', default=True) # --------------------------------------------------------------------------- # Gate enforcement on tryout routes # --------------------------------------------------------------------------- class TestTryoutGateEnforcement: @pytest.mark.parametrize('role', NON_ADMIN_ROLES) def test_non_admin_cannot_create_tryout_when_closed(self, client, as_role, role): as_role('admin') client.post('/admin/toggle-tryouts', data={}) # close as_role(role) response = client.post( '/tryouts/create', data={'title': 'Test', 'game': 'Valorant', 'date': '2026-12-01'}, follow_redirects=True, ) html = response.data.decode() assert 'closed' in html.lower() or 'open tryouts' in html.lower() @pytest.mark.parametrize('role', NON_ADMIN_ROLES) def test_non_admin_cannot_edit_tryout_when_closed(self, app, client, as_role, make_user, role): # Create a tryout as admin first as_role('admin') client.post( '/tryouts/create', data={'title': 'Edit Test', 'game': 'Valorant', 'date': '2026-12-01'}, follow_redirects=False, ) with app.app_context(): tryout_id = Tryout.query.filter_by(title='Edit Test').first().id # Close tryouts client.post('/admin/toggle-tryouts', data={}) # Try to edit as non-admin as_role(role) response = client.post( f'/tryouts/{tryout_id}/edit', data={'title': 'Hacked', 'game': 'Valorant', 'date': '2026-12-01'}, follow_redirects=True, ) html = response.data.decode() assert 'closed' in html.lower() or 'open tryouts' in html.lower() @pytest.mark.parametrize('role', NON_ADMIN_ROLES) def test_non_admin_cannot_delete_tryout_when_closed( self, app, client, as_role, make_user, role ): as_role('admin') client.post( '/tryouts/create', data={'title': 'Delete Test', 'game': 'Valorant', 'date': '2026-12-01'}, follow_redirects=False, ) with app.app_context(): tryout_id = Tryout.query.filter_by(title='Delete Test').first().id client.post('/admin/toggle-tryouts', data={}) # close as_role(role) response = client.post( f'/tryouts/{tryout_id}/delete', data={}, follow_redirects=True, ) html = response.data.decode() assert 'closed' in html.lower() or 'open tryouts' in html.lower() def test_admin_can_always_create_tryout(self, client, as_role): as_role('admin') # Close tryouts client.post('/admin/toggle-tryouts', data={}) # Admin should still be able to create response = client.post( '/tryouts/create', data={'title': 'Admin Test', 'game': 'Valorant', 'date': '2026-12-01'}, follow_redirects=False, ) assert _redirected(response) # success redirect, not blocked def test_admin_can_always_edit_tryout(self, app, client, as_role): as_role('admin') client.post( '/tryouts/create', data={'title': 'Admin Edit', 'game': 'Valorant', 'date': '2026-12-01'}, follow_redirects=False, ) with app.app_context(): tryout_id = Tryout.query.filter_by(title='Admin Edit').first().id client.post('/admin/toggle-tryouts', data={}) # close response = client.post( f'/tryouts/{tryout_id}/edit', data={'title': 'Admin Edited', 'game': 'Valorant', 'date': '2026-12-01'}, follow_redirects=False, ) assert _redirected(response) # success, not blocked