# 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'