test_admin.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. """Test admin management API routes."""
  2. import pytest
  3. from flask_bcrypt import Bcrypt
  4. from app import db
  5. from app.models.admin import Admin
  6. @pytest.fixture
  7. def bcrypt_instance(app):
  8. """Create bcrypt instance for testing."""
  9. bcrypt = Bcrypt(app)
  10. return bcrypt
  11. @pytest.fixture
  12. def test_admin(app, db_session, bcrypt_instance):
  13. """Create a test admin for authentication tests."""
  14. with app.app_context():
  15. password_hash = bcrypt_instance.generate_password_hash('testpassword123').decode('utf-8')
  16. admin = Admin(
  17. username='testadmin',
  18. password_hash=password_hash
  19. )
  20. db.session.add(admin)
  21. db.session.commit()
  22. yield admin
  23. @pytest.fixture
  24. def auth_token(client, test_admin):
  25. """Get a valid JWT token for testing protected endpoints."""
  26. response = client.post('/api/auth/login', json={
  27. 'username': 'testadmin',
  28. 'password': 'testpassword123'
  29. })
  30. return response.get_json()['data']['token']
  31. @pytest.fixture
  32. def auth_headers(auth_token):
  33. """Get headers with JWT token for authenticated requests."""
  34. return {'Authorization': f'Bearer {auth_token}'}
  35. def test_list_admins(client, test_admin, auth_headers):
  36. """Test listing all admins."""
  37. response = client.get('/api/admins', headers=auth_headers)
  38. assert response.status_code == 200
  39. data = response.get_json()
  40. assert data['success'] is True
  41. assert len(data['data']) == 1
  42. assert data['data'][0]['username'] == 'testadmin'
  43. # Ensure password_hash is not exposed
  44. assert 'password_hash' not in data['data'][0]
  45. def test_list_admins_unauthorized(client, test_admin):
  46. """Test listing admins without auth returns 401."""
  47. response = client.get('/api/admins')
  48. assert response.status_code == 401
  49. def test_get_admin_by_id(client, test_admin, auth_headers):
  50. """Test getting admin by ID."""
  51. response = client.get(f'/api/admins/{test_admin.id}', headers=auth_headers)
  52. assert response.status_code == 200
  53. data = response.get_json()
  54. assert data['success'] is True
  55. assert data['data']['username'] == 'testadmin'
  56. assert 'password_hash' not in data['data']
  57. def test_get_admin_not_found(client, test_admin, auth_headers):
  58. """Test getting non-existent admin returns 404."""
  59. response = client.get('/api/admins/9999', headers=auth_headers)
  60. assert response.status_code == 404
  61. data = response.get_json()
  62. assert data['success'] is False
  63. assert data['code'] == 'NOT_FOUND'
  64. def test_create_admin(client, test_admin, auth_headers):
  65. """Test creating a new admin."""
  66. response = client.post('/api/admins/create',
  67. headers=auth_headers,
  68. json={
  69. 'username': 'newadmin',
  70. 'password': 'newpassword123'
  71. }
  72. )
  73. assert response.status_code == 201
  74. data = response.get_json()
  75. assert data['success'] is True
  76. assert data['data']['username'] == 'newadmin'
  77. assert 'password_hash' not in data['data']
  78. def test_create_admin_duplicate_username(client, test_admin, auth_headers):
  79. """Test creating admin with duplicate username returns 400."""
  80. response = client.post('/api/admins/create',
  81. headers=auth_headers,
  82. json={
  83. 'username': 'testadmin', # Already exists
  84. 'password': 'somepassword123'
  85. }
  86. )
  87. assert response.status_code == 400
  88. data = response.get_json()
  89. assert data['success'] is False
  90. assert '已存在' in data['error']
  91. def test_create_admin_short_password(client, test_admin, auth_headers):
  92. """Test creating admin with short password returns 400."""
  93. response = client.post('/api/admins/create',
  94. headers=auth_headers,
  95. json={
  96. 'username': 'newadmin',
  97. 'password': '12345' # Less than 6 characters
  98. }
  99. )
  100. assert response.status_code == 400
  101. data = response.get_json()
  102. assert data['success'] is False
  103. assert '至少' in data['error'] or '6' in data['error']
  104. def test_create_admin_empty_username(client, test_admin, auth_headers):
  105. """Test creating admin with empty username returns 400."""
  106. response = client.post('/api/admins/create',
  107. headers=auth_headers,
  108. json={
  109. 'username': '',
  110. 'password': 'validpassword123'
  111. }
  112. )
  113. assert response.status_code == 400
  114. data = response.get_json()
  115. assert data['success'] is False
  116. def test_update_admin(client, test_admin, auth_headers):
  117. """Test updating admin information."""
  118. response = client.post('/api/admins/update',
  119. headers=auth_headers,
  120. json={
  121. 'id': test_admin.id,
  122. 'username': 'updatedadmin'
  123. }
  124. )
  125. assert response.status_code == 200
  126. data = response.get_json()
  127. assert data['success'] is True
  128. assert data['data']['username'] == 'updatedadmin'
  129. def test_update_admin_password(client, test_admin, auth_headers):
  130. """Test updating admin password."""
  131. response = client.post('/api/admins/update',
  132. headers=auth_headers,
  133. json={
  134. 'id': test_admin.id,
  135. 'password': 'newpassword456'
  136. }
  137. )
  138. assert response.status_code == 200
  139. # Verify new password works
  140. login_response = client.post('/api/auth/login', json={
  141. 'username': 'testadmin',
  142. 'password': 'newpassword456'
  143. })
  144. assert login_response.status_code == 200
  145. def test_update_admin_not_found(client, test_admin, auth_headers):
  146. """Test updating non-existent admin returns 404."""
  147. response = client.post('/api/admins/update',
  148. headers=auth_headers,
  149. json={
  150. 'id': 9999,
  151. 'username': 'newname'
  152. }
  153. )
  154. assert response.status_code == 404
  155. def test_delete_admin(client, app, db_session, bcrypt_instance, auth_headers):
  156. """Test deleting an admin (when there are multiple)."""
  157. # Create a second admin first
  158. with app.app_context():
  159. password_hash = bcrypt_instance.generate_password_hash('password123').decode('utf-8')
  160. admin2 = Admin(username='admin2', password_hash=password_hash)
  161. db.session.add(admin2)
  162. db.session.commit()
  163. admin2_id = admin2.id
  164. # Delete the second admin
  165. response = client.post('/api/admins/delete',
  166. headers=auth_headers,
  167. json={'id': admin2_id}
  168. )
  169. assert response.status_code == 200
  170. data = response.get_json()
  171. assert data['success'] is True
  172. def test_delete_last_admin(client, test_admin, auth_headers):
  173. """Test deleting the last admin returns 400."""
  174. response = client.post('/api/admins/delete',
  175. headers=auth_headers,
  176. json={'id': test_admin.id}
  177. )
  178. assert response.status_code == 400
  179. data = response.get_json()
  180. assert data['success'] is False
  181. assert '最后' in data['error'] or '管理员' in data['error']
  182. def test_delete_admin_not_found(client, test_admin, auth_headers):
  183. """Test deleting non-existent admin returns 404."""
  184. response = client.post('/api/admins/delete',
  185. headers=auth_headers,
  186. json={'id': 9999}
  187. )
  188. assert response.status_code == 404
  189. data = response.get_json()
  190. assert data['success'] is False
  191. assert data['code'] == 'NOT_FOUND'