"""Test admin management API routes.""" import pytest from flask_bcrypt import Bcrypt from app import db from app.models.admin import Admin @pytest.fixture def bcrypt_instance(app): """Create bcrypt instance for testing.""" bcrypt = Bcrypt(app) return bcrypt @pytest.fixture def test_admin(app, db_session, bcrypt_instance): """Create a test admin for authentication tests.""" with app.app_context(): password_hash = bcrypt_instance.generate_password_hash('testpassword123').decode('utf-8') admin = Admin( username='testadmin', password_hash=password_hash ) db.session.add(admin) db.session.commit() yield admin @pytest.fixture def auth_token(client, test_admin): """Get a valid JWT token for testing protected endpoints.""" response = client.post('/api/auth/login', json={ 'username': 'testadmin', 'password': 'testpassword123' }) return response.get_json()['data']['token'] @pytest.fixture def auth_headers(auth_token): """Get headers with JWT token for authenticated requests.""" return {'Authorization': f'Bearer {auth_token}'} def test_list_admins(client, test_admin, auth_headers): """Test listing all admins.""" response = client.get('/api/admins', headers=auth_headers) assert response.status_code == 200 data = response.get_json() assert data['success'] is True assert len(data['data']) == 1 assert data['data'][0]['username'] == 'testadmin' # Ensure password_hash is not exposed assert 'password_hash' not in data['data'][0] def test_list_admins_unauthorized(client, test_admin): """Test listing admins without auth returns 401.""" response = client.get('/api/admins') assert response.status_code == 401 def test_get_admin_by_id(client, test_admin, auth_headers): """Test getting admin by ID.""" response = client.get(f'/api/admins/{test_admin.id}', headers=auth_headers) assert response.status_code == 200 data = response.get_json() assert data['success'] is True assert data['data']['username'] == 'testadmin' assert 'password_hash' not in data['data'] def test_get_admin_not_found(client, test_admin, auth_headers): """Test getting non-existent admin returns 404.""" response = client.get('/api/admins/9999', headers=auth_headers) assert response.status_code == 404 data = response.get_json() assert data['success'] is False assert data['code'] == 'NOT_FOUND' def test_create_admin(client, test_admin, auth_headers): """Test creating a new admin.""" response = client.post('/api/admins/create', headers=auth_headers, json={ 'username': 'newadmin', 'password': 'newpassword123' } ) assert response.status_code == 201 data = response.get_json() assert data['success'] is True assert data['data']['username'] == 'newadmin' assert 'password_hash' not in data['data'] def test_create_admin_duplicate_username(client, test_admin, auth_headers): """Test creating admin with duplicate username returns 400.""" response = client.post('/api/admins/create', headers=auth_headers, json={ 'username': 'testadmin', # Already exists 'password': 'somepassword123' } ) assert response.status_code == 400 data = response.get_json() assert data['success'] is False assert '已存在' in data['error'] def test_create_admin_short_password(client, test_admin, auth_headers): """Test creating admin with short password returns 400.""" response = client.post('/api/admins/create', headers=auth_headers, json={ 'username': 'newadmin', 'password': '12345' # Less than 6 characters } ) assert response.status_code == 400 data = response.get_json() assert data['success'] is False assert '至少' in data['error'] or '6' in data['error'] def test_create_admin_empty_username(client, test_admin, auth_headers): """Test creating admin with empty username returns 400.""" response = client.post('/api/admins/create', headers=auth_headers, json={ 'username': '', 'password': 'validpassword123' } ) assert response.status_code == 400 data = response.get_json() assert data['success'] is False def test_update_admin(client, test_admin, auth_headers): """Test updating admin information.""" response = client.post('/api/admins/update', headers=auth_headers, json={ 'id': test_admin.id, 'username': 'updatedadmin' } ) assert response.status_code == 200 data = response.get_json() assert data['success'] is True assert data['data']['username'] == 'updatedadmin' def test_update_admin_password(client, test_admin, auth_headers): """Test updating admin password.""" response = client.post('/api/admins/update', headers=auth_headers, json={ 'id': test_admin.id, 'password': 'newpassword456' } ) assert response.status_code == 200 # Verify new password works login_response = client.post('/api/auth/login', json={ 'username': 'testadmin', 'password': 'newpassword456' }) assert login_response.status_code == 200 def test_update_admin_not_found(client, test_admin, auth_headers): """Test updating non-existent admin returns 404.""" response = client.post('/api/admins/update', headers=auth_headers, json={ 'id': 9999, 'username': 'newname' } ) assert response.status_code == 404 def test_delete_admin(client, app, db_session, bcrypt_instance, auth_headers): """Test deleting an admin (when there are multiple).""" # Create a second admin first with app.app_context(): password_hash = bcrypt_instance.generate_password_hash('password123').decode('utf-8') admin2 = Admin(username='admin2', password_hash=password_hash) db.session.add(admin2) db.session.commit() admin2_id = admin2.id # Delete the second admin response = client.post('/api/admins/delete', headers=auth_headers, json={'id': admin2_id} ) assert response.status_code == 200 data = response.get_json() assert data['success'] is True def test_delete_last_admin(client, test_admin, auth_headers): """Test deleting the last admin returns 400.""" response = client.post('/api/admins/delete', headers=auth_headers, json={'id': test_admin.id} ) assert response.status_code == 400 data = response.get_json() assert data['success'] is False assert '最后' in data['error'] or '管理员' in data['error'] def test_delete_admin_not_found(client, test_admin, auth_headers): """Test deleting non-existent admin returns 404.""" response = client.post('/api/admins/delete', headers=auth_headers, json={'id': 9999} ) assert response.status_code == 404 data = response.get_json() assert data['success'] is False assert data['code'] == 'NOT_FOUND'