# tests/test_control_panel.py import pytest from flask import url_for from app.models import User, ModuleRegistry # Import models from app import db from urllib.parse import urlparse, parse_qs # Make sure this is imported # --- Test Helpers --- def create_test_user(username="testuser", email=None, password="password", role="user"): if email is None: email = f"{username}@example.test" # Use SQLAlchemy 2.0 style query user = db.session.scalar(db.select(User).filter((User.username == username) | (User.email == email))) if user: print(f"\nDEBUG: Found existing test user '{user.username}' with ID {user.id}"); return user user = User(username=username, email=email, role=role); user.set_password(password) db.session.add(user); db.session.commit() print(f"\nDEBUG: Created test user '{username}' (Role: {role}) with ID {user.id}"); return user def login(client, username, password): return client.post(url_for('auth.login'), data=dict(username=username, password=password), follow_redirects=False) # --- Access Control Tests --- def test_cp_access_admin(client, app): # PASSED with app.app_context(): create_test_user(username="cp_admin", password="password", role="admin") login_res = login(client, "cp_admin", "password"); assert login_res.status_code == 302 cp_index_res = client.get(url_for('control_panel.index')); assert cp_index_res.status_code == 200; assert b"Control Panel" in cp_index_res.data module_res = client.get(url_for('control_panel.manage_modules')); assert module_res.status_code == 200; assert b"Module Management" in module_res.data def test_cp_access_user(client, app): # PASSED with app.app_context(): create_test_user(username="cp_user", password="password", role="user") login_res = login(client, "cp_user", "password"); assert login_res.status_code == 302 cp_index_res = client.get(url_for('control_panel.index')); assert cp_index_res.status_code == 403 module_res = client.get(url_for('control_panel.manage_modules')); assert module_res.status_code == 403 def test_cp_access_logged_out(client, app): # PASSED cp_index_res = client.get(url_for('control_panel.index'), follow_redirects=False); assert cp_index_res.status_code == 302 module_res = client.get(url_for('control_panel.manage_modules'), follow_redirects=False); assert module_res.status_code == 302 # --- Module Management Tests --- def test_module_manager_list(client, app): # PASSED with app.app_context(): create_test_user(username="module_admin", password="password", role="admin") login(client, "module_admin", "password") response = client.get(url_for('control_panel.manage_modules')) assert response.status_code == 200; assert b"Module Management" in response.data assert b"example_module" in response.data; assert b"Disabled" in response.data def test_module_manager_toggle(client, app): # PASSED with app.app_context(): admin = create_test_user(username="toggle_admin", password="password", role="admin") # Use SQLAlchemy 2.0 style query module_entry = db.session.scalar(db.select(ModuleRegistry).filter_by(module_id='example_module')) assert module_entry is not None, "Check conftest.py discovery call." module_entry.is_enabled = False; db.session.commit() login(client, "toggle_admin", "password") # Enable enable_url = url_for('control_panel.toggle_module_status', module_id='example_module') response_enable = client.post(enable_url, follow_redirects=False) assert response_enable.status_code == 302; redirect_location_enable = response_enable.headers.get('Location', ''); parsed_location_enable = urlparse(redirect_location_enable); expected_path_manual = '/control-panel/modules'; assert parsed_location_enable.path == expected_path_manual with client.session_transaction() as sess: assert '_flashes' in sess; assert "has been enabled" in sess['_flashes'][-1][1] with app.app_context(): module_entry_after_enable = db.session.scalar(db.select(ModuleRegistry).filter_by(module_id='example_module')); assert module_entry_after_enable is not None and module_entry_after_enable.is_enabled is True # Disable disable_url = url_for('control_panel.toggle_module_status', module_id='example_module') response_disable = client.post(disable_url, follow_redirects=False) assert response_disable.status_code == 302; redirect_location_disable = response_disable.headers.get('Location', ''); parsed_location_disable = urlparse(redirect_location_disable); assert parsed_location_disable.path == expected_path_manual with client.session_transaction() as sess: assert '_flashes' in sess; assert "has been disabled" in sess['_flashes'][-1][1] with app.app_context(): module_entry_after_disable = db.session.scalar(db.select(ModuleRegistry).filter_by(module_id='example_module')); assert module_entry_after_disable is not None and module_entry_after_disable.is_enabled is False # --- User CRUD Tests --- def test_add_user_page_loads(client, app): # PASSED with app.app_context(): create_test_user(username="crud_admin", password="password", role="admin") login(client, "crud_admin", "password") response = client.get(url_for('control_panel.add_user')) assert response.status_code == 200; assert b"Add New User" in response.data; assert b"Username" in response.data assert b"Email" in response.data; assert b"Password" in response.data; assert b"Repeat Password" in response.data assert b"Role" in response.data; assert b"Add User" in response.data def test_add_user_success(client, app): # PASSED with app.app_context(): create_test_user(username="crud_admin_adder", password="password", role="admin") login(client, "crud_admin_adder", "password") new_username = "new_test_user"; new_email = "new@example.com"; new_password = "new_password"; new_role = "user" response = client.post(url_for('control_panel.add_user'), data={'username': new_username, 'email': new_email, 'password': new_password,'password2': new_password,'role': new_role,'submit': 'Add User'}, follow_redirects=True) assert response.status_code == 200; assert b"User new_test_user (user) added successfully!" in response.data assert bytes(new_username, 'utf-8') in response.data; assert bytes(new_email, 'utf-8') in response.data with app.app_context(): newly_added_user = db.session.scalar(db.select(User).filter_by(username=new_username)) # Use 2.0 style assert newly_added_user is not None; assert newly_added_user.email == new_email; assert newly_added_user.role == new_role; assert newly_added_user.check_password(new_password) is True # --- Edit User Tests --- # --- MODIFIED: Use admin_client fixture --- def test_edit_user_page_loads(admin_client, app): # Use admin_client instead of client """Test the 'Edit User' page loads correctly with user data.""" # Arrange: Create ONLY the target user with app.app_context(): target_user = create_test_user(username="edit_target", email="edit@target.com", role="user") target_user_id = target_user.id # Get ID target_user_username = target_user.username # Store username if needed for assert target_user_email = target_user.email target_user_role = target_user.role # Act: Get the 'Edit User' page using the logged-in admin client # Pass target user's ID response = admin_client.get(url_for('control_panel.edit_user', user_id=target_user_id)) # Assert: Check page loads and contains correct pre-filled data assert response.status_code == 200 assert bytes(f"Edit User", 'utf-8') in response.data # Use simpler title from route # Check if form fields (rendered by template using data from route) are present # These assertions rely on how edit_user.html renders the form passed by the route assert bytes(f'value="{target_user_username}"', 'utf-8') in response.data assert bytes(f'value="{target_user_email}"', 'utf-8') in response.data assert bytes(f'