Skip to content

Commit 97d8b23

Browse files
committed
Refactor redirect tests to use reload_redirects_with_settings instead of override_settings
Replaced @override_settings with a new @reload_redirects_with_settings decorator in test_redirects.py. This change ensures that redirect patterns are reloaded correctly during tests, addressing issues with Django's redirect loading behavior that were affecting redirects for Firefox.com. See #16410
1 parent b0478b6 commit 97d8b23

File tree

3 files changed

+110
-6
lines changed

3 files changed

+110
-6
lines changed

bedrock/firefox/tests/test_redirects.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
from unittest.mock import patch
66

77
from django.conf import settings
8-
from django.test import RequestFactory, override_settings
8+
from django.test import RequestFactory
99

1010
import pytest
1111

1212
from bedrock.firefox.redirects import mobile_app, validate_param_value
13+
from tests.utils import reload_redirects_with_settings
1314

1415

1516
@pytest.mark.parametrize(
@@ -93,7 +94,7 @@ def test_mobile_app():
9394
EXPECTED_REDIRECT_QS = "?redirect_source=mozilla-org"
9495

9596

96-
@override_settings(ENABLE_FIREFOX_COM_REDIRECTS=True)
97+
@reload_redirects_with_settings("bedrock.firefox.redirects", ENABLE_FIREFOX_COM_REDIRECTS=True)
9798
@pytest.mark.django_db
9899
@pytest.mark.parametrize(
99100
"path,expected_location,expected_status,follow_redirects",
@@ -467,7 +468,7 @@ def test_springfield_redirect_patterns(
467468
assert response.headers["Location"] == expected_location
468469

469470

470-
@override_settings(ENABLE_FIREFOX_COM_REDIRECTS=True)
471+
@reload_redirects_with_settings("bedrock.firefox.redirects", ENABLE_FIREFOX_COM_REDIRECTS=True)
471472
@pytest.mark.django_db
472473
@pytest.mark.parametrize(
473474
"path,expected_location,expected_status,follow_redirects",
@@ -504,7 +505,7 @@ def test_springfield_redirects_carry_over_querystrings_and_add_redirect_source(
504505
assert response.headers["Location"] == expected_location
505506

506507

507-
@override_settings(ENABLE_FIREFOX_COM_REDIRECTS=True)
508+
@reload_redirects_with_settings("bedrock.firefox.redirects", ENABLE_FIREFOX_COM_REDIRECTS=True)
508509
@pytest.mark.django_db
509510
@pytest.mark.parametrize(
510511
"path",
@@ -528,6 +529,7 @@ def test_mobile_app_redirector_does_not_go_to_springfield(client):
528529
assert resp.headers["Location"] == "https://apps.apple.com/app/apple-store/id989804926"
529530

530531

532+
@reload_redirects_with_settings("bedrock.firefox.redirects", ENABLE_FIREFOX_COM_REDIRECTS=True)
531533
@pytest.mark.django_db
532534
@pytest.mark.parametrize(
533535
"path",
@@ -541,7 +543,6 @@ def test_mobile_app_redirector_does_not_go_to_springfield(client):
541543
"/firefox/releases/",
542544
),
543545
)
544-
@override_settings(ENABLE_FIREFOX_COM_REDIRECTS=True)
545546
def test_releasenotes_generic_urls_not_rediected_to_springfield(client, path):
546547
resp = client.get(path)
547548
assert resp.status_code == 302
@@ -561,7 +562,7 @@ def test_releasenotes_generic_urls_not_rediected_to_springfield(client, path):
561562
("/en-US/firefox/installer-help/", f"{settings.FXC_BASE_URL}/en-US/download/installer-help/{EXPECTED_REDIRECT_QS}"),
562563
),
563564
)
564-
@override_settings(ENABLE_FIREFOX_COM_REDIRECTS=True)
565+
@reload_redirects_with_settings("bedrock.firefox.redirects", ENABLE_FIREFOX_COM_REDIRECTS=True)
565566
def test_subsequent_redirects_do_not_carry_querystrings_from_earlier_requests(
566567
client,
567568
path,
@@ -584,6 +585,7 @@ def test_subsequent_redirects_do_not_carry_querystrings_from_earlier_requests(
584585
("/firefox/browsers/incognito-browser/", f"{settings.FXC_BASE_URL}/more/incognito-browser/{EXPECTED_REDIRECT_QS}"),
585586
),
586587
)
588+
@reload_redirects_with_settings("bedrock.firefox.redirects", ENABLE_FIREFOX_COM_REDIRECTS=True)
587589
def test_offsite_redirects_still_work_when_locale_not_in_source_path(
588590
client,
589591
path,

tests/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# This Source Code Form is subject to the terms of the Mozilla Public
2+
# License, v. 2.0. If a copy of the MPL was not distributed with this
3+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

tests/utils.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# This Source Code Form is subject to the terms of the Mozilla Public
2+
# License, v. 2.0. If a copy of the MPL was not distributed with this
3+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
import functools
6+
import importlib
7+
from collections.abc import Callable
8+
from typing import Any
9+
10+
from django.test import override_settings
11+
12+
13+
def reload_redirects_with_settings(module_path: str, **settings_kwargs):
14+
"""
15+
Generic decorator that enables dynamic redirect reloading for testing.
16+
17+
This decorator:
18+
1. Overrides specified settings using the same pattern as @override_settings
19+
2. Reloads the specified redirects module to pick up new patterns
20+
3. Patches the redirects middleware to use the reloaded patterns
21+
4. Restores original state after the test
22+
23+
This is needed because Django loads redirects once at startup and doesn't
24+
reload them when @override_settings is used.
25+
26+
Args:
27+
module_path: The module path to reload (e.g., 'bedrock.firefox.redirects')
28+
**settings_kwargs: Settings to override (e.g., ENABLE_FIREFOX_COM_REDIRECTS=True)
29+
30+
Usage:
31+
@reload_redirects_with_settings('bedrock.firefox.redirects', ENABLE_FIREFOX_COM_REDIRECTS=True)
32+
def test_my_redirect():
33+
# Your test code here
34+
pass
35+
"""
36+
37+
def decorator(func: Callable) -> Callable:
38+
@functools.wraps(func)
39+
def wrapper(*args: Any, **kwargs: Any) -> Any:
40+
with override_settings(**settings_kwargs):
41+
original_patterns, original_resolver = _patch_redirects_middleware(module_path)
42+
try:
43+
return func(*args, **kwargs)
44+
finally:
45+
_restore_redirects_middleware(module_path, original_patterns, original_resolver)
46+
47+
return wrapper
48+
49+
return decorator
50+
51+
52+
def _patch_redirects_middleware(module_path: str) -> tuple[Any, Any]:
53+
"""
54+
Patch the redirects middleware to use reloaded patterns from the specified module.
55+
56+
Args:
57+
module_path: The module path to reload
58+
59+
Returns:
60+
Tuple of (original_patterns, original_resolver) for restoration
61+
"""
62+
import bedrock.redirects.middleware
63+
64+
# Import and reload the specified module
65+
module = importlib.import_module(module_path)
66+
importlib.reload(module) # type: ignore
67+
68+
# Store original state
69+
original_patterns = module.redirectpatterns # type: ignore
70+
original_resolver = bedrock.redirects.middleware.get_resolver
71+
72+
# Patch the resolver to use the reloaded patterns
73+
def patched_get_resolver(patterns=None):
74+
if patterns is None:
75+
patterns = module.redirectpatterns # type: ignore
76+
return original_resolver(patterns)
77+
78+
bedrock.redirects.middleware.get_resolver = patched_get_resolver
79+
80+
return original_patterns, original_resolver
81+
82+
83+
def _restore_redirects_middleware(module_path: str, original_patterns: Any, original_resolver: Any) -> None:
84+
"""
85+
Restore the original redirects middleware state.
86+
87+
Args:
88+
module_path: The module path that was reloaded
89+
original_patterns: The original redirectpatterns to restore
90+
original_resolver: The original get_resolver function to restore
91+
"""
92+
import bedrock.redirects.middleware
93+
94+
# Restore original patterns
95+
module = importlib.import_module(module_path)
96+
module.redirectpatterns = original_patterns # type: ignore
97+
98+
# Restore original resolver
99+
bedrock.redirects.middleware.get_resolver = original_resolver

0 commit comments

Comments
 (0)