diff --git a/tests/conftest.py b/tests/conftest.py index 5399d8a..e9c8857 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,7 @@ import os +from unittest.mock import MagicMock, patch + +import pytest os.environ["SOCKETIO_ASYNC_MODE"] = "threading" os.environ["SECRET_KEY"] = "test-secret" @@ -7,10 +10,19 @@ os.environ["TESTING"] = "true" os.environ["LIMITER_STORAGE_URI"] = "memory://" -import pytest - @pytest.fixture(autouse=True, scope="session") def setup_test_env(): # Already set at top level, but kept for clarity pass + + +@pytest.fixture(autouse=True) +def mock_redis(): + mock = MagicMock() + with ( + patch("hookwise.extensions.redis_client", mock), + patch("hookwise.tasks.redis_client", mock), + patch("hookwise.api.redis_client", mock), + ): + yield mock diff --git a/tests/test_utils.py b/tests/test_utils.py index b948bb7..c0f3476 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -4,13 +4,16 @@ from unittest.mock import patch import pytest +from flask import session from hookwise import create_app from hookwise.extensions import db +from hookwise.models import AuditLog from hookwise.utils import ( check_auth, decrypt_string, encrypt_string, + log_audit, mask_secrets, resolve_jsonpath, ) @@ -193,3 +196,57 @@ def test_check_auth_disabled_empty_username(): def test_check_auth_disabled_empty_password(): """Auth should be disabled if GUI_PASSWORD is empty.""" assert check_auth("any", "any") is True + + +# --- Audit Logging --- + + +def test_log_audit_no_context(app): + """When no request context exists, user should be 'System'.""" + with app.app_context(): + log_audit(action="test_action", details="test details") + + entry = AuditLog.query.filter_by(action="test_action").first() + assert entry is not None + assert entry.user == "System" + assert entry.details == "test details" + + +def test_log_audit_session_user(app): + """When a session username exists, it should be used as the user.""" + with app.test_request_context(): + session["username"] = "session_user" + log_audit(action="session_action", config_id="cfg1") + + entry = AuditLog.query.filter_by(action="session_action").first() + assert entry is not None + assert entry.user == "session_user" + assert entry.config_id == "cfg1" + + +def test_log_audit_auth_user(app): + """When Basic Auth is provided, the username should be used.""" + import base64 + + auth_header = f"Basic {base64.b64encode(b'auth_user:pass').decode()}" + with app.test_request_context(headers={"Authorization": auth_header}): + log_audit(action="auth_action") + + entry = AuditLog.query.filter_by(action="auth_action").first() + assert entry is not None + assert entry.user == "auth_user" + + +def test_log_audit_custom_session_and_no_commit(app): + """Verify custom DB session is used and commit=False works.""" + with app.app_context(): + log_audit(action="no_commit", commit=False, db_session=db.session) + + # Entry should be in session but not yet committed + entry = AuditLog.query.filter_by(action="no_commit").first() + assert entry is not None + + # Rollback should remove it + db.session.rollback() + entry = AuditLog.query.filter_by(action="no_commit").first() + assert entry is None