Skip to content

Commit 331fc45

Browse files
fix the tests
1 parent c8e6e3e commit 331fc45

File tree

1 file changed

+41
-87
lines changed

1 file changed

+41
-87
lines changed

tests/test_routing.py

Lines changed: 41 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,27 @@
88
from django_github_app.routing import GitHubRouter
99
from django_github_app.views import BaseWebhookView
1010

11-
pytestmark = pytest.mark.django_db
1211

12+
@pytest.fixture(autouse=True)
13+
def test_router():
14+
import django_github_app.views
15+
from django_github_app.routing import GitHubRouter
1316

14-
class TestView(BaseWebhookView[SyncGitHubAPI]):
17+
old_routers = GitHubRouter._routers.copy()
18+
GitHubRouter._routers = []
19+
20+
old_router = django_github_app.views._router
21+
22+
test_router = GitHubRouter()
23+
django_github_app.views._router = test_router
24+
25+
yield test_router
26+
27+
GitHubRouter._routers = old_routers
28+
django_github_app.views._router = old_router
29+
30+
31+
class FixedTestView(BaseWebhookView[SyncGitHubAPI]):
1532
github_api_class = SyncGitHubAPI
1633

1734
def post(self, request: HttpRequest) -> JsonResponse:
@@ -21,10 +38,9 @@ def post(self, request: HttpRequest) -> JsonResponse:
2138
class BrokenTestView(BaseWebhookView[SyncGitHubAPI]):
2239
github_api_class = SyncGitHubAPI
2340

24-
# Intentionally break the singleton pattern to demonstrate memory issue
2541
@property
2642
def router(self) -> GitHubRouter:
27-
# Always create a new router (simulating the original issue)
43+
# Always create a new router (simulating issue #73)
2844
return GitHubRouter(*GitHubRouter.routers)
2945

3046
def post(self, request: HttpRequest) -> JsonResponse:
@@ -33,117 +49,55 @@ def post(self, request: HttpRequest) -> JsonResponse:
3349

3450
class TestGitHubRouter:
3551
def test_router_single_instance(self):
36-
"""Test that router is instantiated only once per view class."""
37-
# Reset the router
38-
TestView._router = None
39-
40-
# Create multiple views
41-
view1 = TestView()
42-
view2 = TestView()
52+
view1 = FixedTestView()
53+
view2 = FixedTestView()
4354

44-
# Get router from both instances
4555
router1 = view1.router
4656
router2 = view2.router
4757

48-
# Verify they are the same object
4958
assert router1 is router2
50-
51-
# Verify subsequent calls return the same instance
5259
assert view1.router is router1
5360
assert view2.router is router2
5461

55-
def test_router_multiple_instances_without_fix(self):
56-
"""Test demonstrates that without the fix, each view gets a new router instance.
62+
def test_duplicate_routers_without_module_level_router(self):
63+
view_count = 5
5764

58-
This test shows that the problem that was fixed (creating a new router on each access)
59-
would result in multiple router instances. This test validates the behavior without
60-
using memray to measure memory usage.
61-
"""
62-
# Create views with the broken implementation that creates new routers
6365
views = []
6466
routers = []
6567

66-
# Create just a few views - enough to demonstrate the issue
67-
for _ in range(5):
68+
for _ in range(view_count):
6869
view = BrokenTestView()
69-
# Get a router reference from each view
70-
router = view.router
7170
views.append(view)
72-
routers.append(router)
71+
routers.append(view.router)
7372

74-
# Ensure we have the expected number of views
75-
assert len(views) == 5
73+
assert len(views) == view_count
7674

77-
# Verify that without the fix, each view gets a unique router instance
7875
unique_router_count = len(set(id(r) for r in routers))
79-
assert unique_router_count == 5, (
80-
f"Expected 5 unique routers, got {unique_router_count}"
81-
)
76+
assert unique_router_count == view_count
8277

83-
def test_fix_prevents_duplicate_routers(self):
84-
"""Test that the fix prevents duplication of routers even when accessed multiple times.
78+
def test_no_duplicate_routers(self):
79+
router_ids = set()
8580

86-
This test simulates accessing the router multiple times from different views,
87-
which is what happens in a high-traffic application with many requests.
88-
"""
89-
# Reset the router
90-
TestView._router = None
91-
92-
# Create a reference to the first router
93-
view1 = TestView()
94-
first_router = view1.router
95-
96-
# Track the number of unique router instances
97-
router_ids = {id(first_router)}
98-
99-
# Simulate multiple requests by creating views and accessing their router repeatedly
10081
for _ in range(100):
101-
view = TestView()
82+
view = FixedTestView()
10283

103-
# Access the router multiple times (simulating multiple uses per request)
84+
# really goose it up with adding duplicate routers
10485
for _ in range(10):
105-
router = view.router
106-
router_ids.add(id(router))
86+
router_ids.add(id(view.router))
10787

108-
# With the fix, we should have exactly ONE router instance despite
88+
# we should have exactly ONE router instance despite
10989
# creating it 100 views x 10 accesses = 1000 times
110-
assert len(router_ids) == 1, (
111-
f"Expected 1 unique router ID, got {len(router_ids)}"
112-
)
113-
114-
@pytest.mark.limit_memory("10MB")
115-
def test_router_memory_with_fix(self):
116-
"""Test memory usage with the fix in place.
90+
assert len(router_ids) == 1
11791

118-
This test creates a massive number of views with the fixed implementation,
119-
which should use minimal memory since all views share the same router.
120-
"""
121-
# Reset the router
122-
TestView._router = None
92+
@pytest.mark.limit_memory("2.5MB")
93+
def test_router_memory_stress_test(self):
94+
view_count = 50000
12395

124-
# Create an extremely large number of views to really stress test the system
125-
view_count = 5000 # Still a lot of views but not so many that it hangs
126-
127-
# Store all views in memory to prevent garbage collection
128-
# This gives us a more realistic picture of memory usage
12996
views = []
13097

131-
# Create many views and access their routers
132-
for i in range(view_count):
133-
view = TestView()
134-
# Access the router property to trigger creation if not already created
135-
router = view.router
136-
137-
# Store view in list to prevent garbage collection
98+
for _ in range(view_count):
99+
view = FixedTestView()
138100
views.append(view)
139101

140-
# Only store a reference to the first router to compare later
141-
if i == 0:
142-
first_router = router
143-
144-
# Ensure we created the expected number of views
145102
assert len(views) == view_count
146-
147-
# Verify all views share the same router instance
148-
for view in views:
149-
assert view.router is first_router
103+
assert all(view.router is views[0].router for view in views)

0 commit comments

Comments
 (0)