From 3fca8190f7272be75b754bd73c75305af11c641e Mon Sep 17 00:00:00 2001 From: Marcin Date: Mon, 18 May 2026 09:50:07 +0100 Subject: [PATCH] test: drive middleware via django.test.Client instead of RequestFactory RequestFactory bypasses MIDDLEWARE ordering and URL resolution, so the prior tests could pass even if the middleware was missing from settings or registered in the wrong position. Register PrerenderMiddleware in tests/settings.MIDDLEWARE, add a tests/urls.py with a catch-all view, and switch to Client().get/post so each request runs the full Django pipeline. Also covers the X-Bufferbot and POST-pass-through cases the previous test suite was missing. Co-Authored-By: Claude Opus 4.7 (1M context) --- tests/settings.py | 8 +++++ tests/test_middleware.py | 73 ++++++++++++++++++++++------------------ tests/urls.py | 11 ++++++ 3 files changed, 59 insertions(+), 33 deletions(-) create mode 100644 tests/urls.py diff --git a/tests/settings.py b/tests/settings.py index 07608ba..e344a52 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -1,3 +1,11 @@ SECRET_KEY = 'test-secret-key' DATABASES = {} INSTALLED_APPS = [] + +ROOT_URLCONF = 'tests.urls' + +MIDDLEWARE = [ + 'prerender_django.middleware.PrerenderMiddleware', +] + +ALLOWED_HOSTS = ['*'] diff --git a/tests/test_middleware.py b/tests/test_middleware.py index d78b9f5..5162ce9 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -1,23 +1,22 @@ +"""Tests against the real Django request pipeline. + +Uses django.test.Client (not RequestFactory) so MIDDLEWARE ordering, URL +resolution, and full request/response wiring are exercised. The upstream +Prerender service is faked by patching urllib.request.urlopen — replacing +that with a real mock server is the contract-test layer. +""" import urllib.error from unittest.mock import MagicMock, patch -from django.http import HttpResponse -from django.test import RequestFactory - -from prerender_django.middleware import PrerenderMiddleware +import pytest +from django.test import Client BOT_UA = 'Mozilla/5.0 (compatible; Googlebot/2.1)' BROWSER_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' PRERENDERED_HTML = 'prerendered' -factory = RequestFactory() - - -def normal_response(_request): - return HttpResponse('original') - -def mock_urlopen(status=200, body=PRERENDERED_HTML): +def _fake_urlopen(status=200, body=PRERENDERED_HTML): cm = MagicMock() cm.__enter__ = MagicMock(return_value=cm) cm.__exit__ = MagicMock(return_value=False) @@ -26,44 +25,52 @@ def mock_urlopen(status=200, body=PRERENDERED_HTML): return cm -def test_browser_passes_through(): - middleware = PrerenderMiddleware(normal_response) - request = factory.get('/', HTTP_USER_AGENT=BROWSER_UA) - response = middleware(request) +@pytest.fixture +def client(): + return Client() + + +def test_browser_passes_through(client): + response = client.get('/', HTTP_USER_AGENT=BROWSER_UA) assert response.status_code == 200 assert response.content == b'original' -def test_bot_receives_prerendered_response(): - middleware = PrerenderMiddleware(normal_response) - request = factory.get('/', HTTP_USER_AGENT=BOT_UA) - with patch('urllib.request.urlopen', return_value=mock_urlopen()): - response = middleware(request) +def test_bot_receives_prerendered_response(client): + with patch('urllib.request.urlopen', return_value=_fake_urlopen()): + response = client.get('/about', HTTP_USER_AGENT=BOT_UA) assert response.status_code == 200 assert PRERENDERED_HTML in response.content.decode() -def test_static_asset_with_bot_ua_passes_through(): - middleware = PrerenderMiddleware(normal_response) - request = factory.get('/style.css', HTTP_USER_AGENT=BOT_UA) - response = middleware(request) +def test_static_asset_with_bot_ua_passes_through(client): + response = client.get('/style.css', HTTP_USER_AGENT=BOT_UA) assert response.status_code == 200 assert response.content == b'original' -def test_escaped_fragment_triggers_prerender(): - middleware = PrerenderMiddleware(normal_response) - request = factory.get('/', {'_escaped_fragment_': ''}, HTTP_USER_AGENT=BROWSER_UA) - with patch('urllib.request.urlopen', return_value=mock_urlopen()): - response = middleware(request) +def test_escaped_fragment_triggers_prerender(client): + with patch('urllib.request.urlopen', return_value=_fake_urlopen()): + response = client.get('/?_escaped_fragment_=', HTTP_USER_AGENT=BROWSER_UA) assert response.status_code == 200 assert PRERENDERED_HTML in response.content.decode() -def test_network_error_falls_back_to_normal_response(): - middleware = PrerenderMiddleware(normal_response) - request = factory.get('/', HTTP_USER_AGENT=BOT_UA) +def test_x_bufferbot_header_triggers_prerender(client): + with patch('urllib.request.urlopen', return_value=_fake_urlopen()): + response = client.get('/', HTTP_USER_AGENT=BROWSER_UA, HTTP_X_BUFFERBOT='true') + assert response.status_code == 200 + assert PRERENDERED_HTML in response.content.decode() + + +def test_post_request_passes_through(client): + response = client.post('/', HTTP_USER_AGENT=BOT_UA) + assert response.status_code == 200 + assert response.content == b'original' + + +def test_network_error_falls_back_to_normal_response(client): with patch('urllib.request.urlopen', side_effect=urllib.error.URLError('network error')): - response = middleware(request) + response = client.get('/', HTTP_USER_AGENT=BOT_UA) assert response.status_code == 200 assert response.content == b'original' diff --git a/tests/urls.py b/tests/urls.py new file mode 100644 index 0000000..b7e02ff --- /dev/null +++ b/tests/urls.py @@ -0,0 +1,11 @@ +from django.http import HttpResponse +from django.urls import re_path + + +def _original(request): + return HttpResponse('original') + + +urlpatterns = [ + re_path(r'^.*$', _original), +]