diff --git a/tests/conftest.py b/tests/conftest.py index 5399d8a..709c11c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ import os +from unittest.mock import patch os.environ["SOCKETIO_ASYNC_MODE"] = "threading" os.environ["SECRET_KEY"] = "test-secret" @@ -6,6 +7,7 @@ os.environ["DATABASE_URL"] = "sqlite:///:memory:" os.environ["TESTING"] = "true" os.environ["LIMITER_STORAGE_URI"] = "memory://" +os.environ["GUI_PASSWORD"] = "test-password" import pytest @@ -14,3 +16,18 @@ def setup_test_env(): # Already set at top level, but kept for clarity pass + + +@pytest.fixture(autouse=True) +def mock_redis(): + with ( + patch("hookwise.extensions.redis_client") as mock_ext, + patch("hookwise.tasks.redis_client") as mock_tasks, + patch("hookwise.api.redis_client") as mock_api, + ): + # Mock common redis methods + for m in [mock_ext, mock_tasks, mock_api]: + m.get.return_value = None + m.set.return_value = True + m.ping.return_value = True + yield (mock_ext, mock_tasks, mock_api) diff --git a/tests/test_webhook_rejection.py b/tests/test_webhook_rejection.py new file mode 100644 index 0000000..a72c097 --- /dev/null +++ b/tests/test_webhook_rejection.py @@ -0,0 +1,72 @@ +from unittest.mock import MagicMock, patch + +import pytest + +from hookwise import create_app +from hookwise.extensions import db +from hookwise.models import WebhookConfig + + +@pytest.fixture +def app(): + app = create_app() + app.config["TESTING"] = True + app.config["WTF_CSRF_ENABLED"] = False + app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///:memory:" + return app + + +@pytest.fixture +def client(app): + with app.app_context(): + db.create_all() + yield app.test_client() + db.session.remove() + db.drop_all() + + +@pytest.fixture +def disabled_config(app, client): + with app.app_context(): + config = WebhookConfig( + name="Disabled Config", + is_enabled=False, + bearer_token="test-token", + customer_id_default="TESTCO", + board="Test Board", + ) + db.session.add(config) + db.session.commit() + return config.id + + +@patch("hookwise.webhook.log_webhook_received") +@patch("hookwise.webhook.db.session.commit") +@patch("hookwise.webhook.db.session.rollback") +@patch("logging.getLogger") +def test_webhook_rejection_log_failure( + mock_get_logger, mock_rollback, mock_commit, mock_log_received, client, disabled_config +): + """Test that failure to log a webhook rejection is handled gracefully.""" + # Setup mock logger + mock_logger = MagicMock() + mock_get_logger.return_value = mock_logger + + # Make commit fail + mock_commit.side_effect = Exception("Database error") + + payload = {"test": "data"} + response = client.post(f"/w/{disabled_config}", json=payload) + + # Verify response is still 403 (disabled) + assert response.status_code == 403 + assert response.json["status"] == "error" + assert response.json["message"] == "Endpoint is disabled" + + # Verify rollback was called + mock_rollback.assert_called_once() + + # Verify error was logged + # hookwise/webhook.py uses logging.getLogger(__name__) + mock_get_logger.assert_called_with("hookwise.webhook") + mock_logger.error.assert_called_with("Failed to log webhook rejection: Database error")