Skip to content

Commit 0d728cf

Browse files
committed
Add comprehensive integration tests for email registration, password reset, and comment notifications
- Implemented end-to-end tests for user registration, including email verification and login. - Added tests for password reset functionality, ensuring proper email notifications and password updates. - Created tests for comment notifications, verifying that authors receive alerts for new comments and can respond. - Included tests for OAuth configuration and user management, ensuring proper handling of OAuth accounts and bindings.
1 parent 04852cf commit 0d728cf

File tree

8 files changed

+4203
-0
lines changed

8 files changed

+4203
-0
lines changed

accounts/test_user_business_logic.py

Lines changed: 510 additions & 0 deletions
Large diffs are not rendered by default.

blog/test_article_business_logic.py

Lines changed: 607 additions & 0 deletions
Large diffs are not rendered by default.

blog/test_context_processors.py

Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
"""
2+
Test cases for blog context processors
3+
"""
4+
from unittest.mock import patch, Mock
5+
6+
from django.test import TestCase, RequestFactory
7+
from django.utils import timezone
8+
9+
from accounts.models import BlogUser
10+
from blog.context_processors import seo_processor
11+
from blog.models import Category, Article
12+
from djangoblog.utils import cache
13+
14+
15+
class SeoProcessorTest(TestCase):
16+
"""测试SEO上下文处理器"""
17+
18+
def setUp(self):
19+
"""设置测试环境"""
20+
self.factory = RequestFactory()
21+
22+
# 创建测试用户
23+
self.user = BlogUser.objects.create_user(
24+
username='testuser',
25+
26+
password='testpassword'
27+
)
28+
29+
# 创建测试分类
30+
self.category = Category.objects.create(
31+
name='Test Category',
32+
slug='test-category'
33+
)
34+
35+
# 创建测试页面
36+
self.page = Article.objects.create(
37+
title='Test Page',
38+
body='Test page content',
39+
author=self.user,
40+
type='p', # 页面类型
41+
status='p', # 已发布
42+
category=self.category
43+
)
44+
45+
# 清空缓存
46+
cache.clear()
47+
48+
def tearDown(self):
49+
"""清理测试环境"""
50+
cache.clear()
51+
52+
def test_processor_returns_required_variables(self):
53+
"""测试上下文处理器返回必需的变量"""
54+
request = self.factory.get('/')
55+
result = seo_processor(request)
56+
57+
# 验证必需的变量
58+
required_keys = [
59+
'SITE_NAME',
60+
'SHOW_GOOGLE_ADSENSE',
61+
'GOOGLE_ADSENSE_CODES',
62+
'SITE_SEO_DESCRIPTION',
63+
'SITE_DESCRIPTION',
64+
'SITE_KEYWORDS',
65+
'SITE_BASE_URL',
66+
'ARTICLE_SUB_LENGTH',
67+
'nav_category_list',
68+
'nav_pages',
69+
'OPEN_SITE_COMMENT',
70+
'BEIAN_CODE',
71+
'ANALYTICS_CODE',
72+
'BEIAN_CODE_GONGAN',
73+
'SHOW_GONGAN_CODE',
74+
'CURRENT_YEAR',
75+
'GLOBAL_HEADER',
76+
'GLOBAL_FOOTER',
77+
'COMMENT_NEED_REVIEW',
78+
'COLOR_SCHEME',
79+
]
80+
81+
for key in required_keys:
82+
self.assertIn(key, result)
83+
84+
def test_processor_caching(self):
85+
"""测试上下文处理器的缓存机制"""
86+
request = self.factory.get('/')
87+
88+
# 第一次调用 - 应该设置缓存
89+
result1 = seo_processor(request)
90+
91+
# 验证缓存已设置
92+
cached_value = cache.get('seo_processor')
93+
self.assertIsNotNone(cached_value)
94+
95+
# 第二次调用 - 应该从缓存获取
96+
result2 = seo_processor(request)
97+
98+
# 验证两次调用返回相同的基础数据
99+
# 注意:SITE_BASE_URL和CURRENT_YEAR是动态的,可能不同
100+
self.assertEqual(result1['SITE_NAME'], result2['SITE_NAME'])
101+
self.assertEqual(result1['SITE_DESCRIPTION'], result2['SITE_DESCRIPTION'])
102+
103+
def test_processor_with_anonymous_user(self):
104+
"""测试匿名用户访问时的上下文处理器"""
105+
request = self.factory.get('/')
106+
result = seo_processor(request)
107+
108+
# 验证返回结果
109+
self.assertIsNotNone(result)
110+
self.assertIn('SITE_BASE_URL', result)
111+
112+
def test_processor_with_https_request(self):
113+
"""测试HTTPS请求的SITE_BASE_URL"""
114+
request = self.factory.get('/', secure=True)
115+
result = seo_processor(request)
116+
117+
# 验证SITE_BASE_URL包含https
118+
self.assertTrue(result['SITE_BASE_URL'].startswith('https://'))
119+
120+
def test_processor_with_http_request(self):
121+
"""测试HTTP请求的SITE_BASE_URL"""
122+
request = self.factory.get('/')
123+
result = seo_processor(request)
124+
125+
# 验证SITE_BASE_URL包含http
126+
self.assertTrue(result['SITE_BASE_URL'].startswith('http://'))
127+
128+
def test_processor_current_year(self):
129+
"""测试CURRENT_YEAR是当前年份"""
130+
request = self.factory.get('/')
131+
result = seo_processor(request)
132+
133+
# 验证CURRENT_YEAR是当前年份
134+
current_year = timezone.now().year
135+
self.assertEqual(result['CURRENT_YEAR'], current_year)
136+
137+
def test_processor_nav_category_list(self):
138+
"""测试导航分类列表"""
139+
request = self.factory.get('/')
140+
result = seo_processor(request)
141+
142+
# 验证nav_category_list包含创建的分类
143+
nav_categories = list(result['nav_category_list'])
144+
self.assertGreater(len(nav_categories), 0)
145+
146+
# 验证分类在列表中
147+
category_names = [cat.name for cat in nav_categories]
148+
self.assertIn('Test Category', category_names)
149+
150+
def test_processor_nav_pages(self):
151+
"""测试导航页面列表"""
152+
request = self.factory.get('/')
153+
result = seo_processor(request)
154+
155+
# 验证nav_pages包含已发布的页面
156+
nav_pages = list(result['nav_pages'])
157+
self.assertGreater(len(nav_pages), 0)
158+
159+
# 验证创建的页面在列表中
160+
page_titles = [page.title for page in nav_pages]
161+
self.assertIn('Test Page', page_titles)
162+
163+
def test_processor_only_shows_published_pages(self):
164+
"""测试上下文处理器只显示已发布的页面"""
165+
# 创建草稿页面
166+
draft_page = Article.objects.create(
167+
title='Draft Page',
168+
body='Draft content',
169+
author=self.user,
170+
type='p', # 页面类型
171+
status='d', # 草稿状态
172+
category=self.category
173+
)
174+
175+
# 清除缓存以确保重新查询
176+
cache.clear()
177+
178+
request = self.factory.get('/')
179+
result = seo_processor(request)
180+
181+
# 验证草稿页面不在导航页面列表中
182+
nav_pages = list(result['nav_pages'])
183+
page_titles = [page.title for page in nav_pages]
184+
self.assertNotIn('Draft Page', page_titles)
185+
self.assertIn('Test Page', page_titles)
186+
187+
def test_processor_cache_expiration(self):
188+
"""测试缓存过期"""
189+
request = self.factory.get('/')
190+
191+
# 第一次调用
192+
result1 = seo_processor(request)
193+
194+
# 手动删除缓存模拟过期
195+
cache.delete('seo_processor')
196+
197+
# 第二次调用应该重新生成缓存
198+
result2 = seo_processor(request)
199+
200+
# 验证结果仍然正确
201+
self.assertIsNotNone(result2)
202+
self.assertIn('SITE_NAME', result2)
203+
204+
@patch('blog.context_processors.get_blog_setting')
205+
def test_processor_with_custom_blog_settings(self, mock_get_blog_setting):
206+
"""测试使用自定义博客设置"""
207+
# 模拟博客设置
208+
mock_setting = Mock()
209+
mock_setting.site_name = 'Test Blog'
210+
mock_setting.site_description = 'A test blog'
211+
mock_setting.site_seo_description = 'SEO description'
212+
mock_setting.site_keywords = 'test, blog'
213+
mock_setting.article_sub_length = 100
214+
mock_setting.show_google_adsense = False
215+
mock_setting.google_adsense_codes = ''
216+
mock_setting.open_site_comment = True
217+
mock_setting.beian_code = ''
218+
mock_setting.analytics_code = ''
219+
mock_setting.gongan_beiancode = ''
220+
mock_setting.show_gongan_code = False
221+
mock_setting.global_header = ''
222+
mock_setting.global_footer = ''
223+
mock_setting.comment_need_review = False
224+
mock_setting.color_scheme = 'light'
225+
226+
mock_get_blog_setting.return_value = mock_setting
227+
228+
# 清除缓存
229+
cache.clear()
230+
231+
request = self.factory.get('/')
232+
result = seo_processor(request)
233+
234+
# 验证返回的值与模拟的设置匹配
235+
self.assertEqual(result['SITE_NAME'], 'Test Blog')
236+
self.assertEqual(result['SITE_DESCRIPTION'], 'A test blog')
237+
self.assertEqual(result['SITE_SEO_DESCRIPTION'], 'SEO description')
238+
self.assertEqual(result['SITE_KEYWORDS'], 'test, blog')
239+
self.assertEqual(result['ARTICLE_SUB_LENGTH'], 100)
240+
self.assertEqual(result['SHOW_GOOGLE_ADSENSE'], False)
241+
242+
def test_processor_site_base_url_with_different_hosts(self):
243+
"""测试不同主机名的SITE_BASE_URL"""
244+
hosts = ['example.com', 'blog.example.com', 'localhost:8000']
245+
246+
for host in hosts:
247+
request = self.factory.get('/', HTTP_HOST=host)
248+
result = seo_processor(request)
249+
250+
# 验证SITE_BASE_URL包含正确的主机名
251+
self.assertIn(host, result['SITE_BASE_URL'])
252+
253+
def test_processor_dynamic_values_not_cached(self):
254+
"""测试动态值不被缓存(SITE_BASE_URL和CURRENT_YEAR)"""
255+
# 第一次请求 - HTTP
256+
request1 = self.factory.get('/')
257+
result1 = seo_processor(request1)
258+
site_url1 = result1['SITE_BASE_URL']
259+
260+
# 第二次请求 - HTTPS(使用缓存的数据但动态值应该更新)
261+
request2 = self.factory.get('/', secure=True)
262+
result2 = seo_processor(request2)
263+
site_url2 = result2['SITE_BASE_URL']
264+
265+
# 验证SITE_BASE_URL不同(一个是http,一个是https)
266+
self.assertNotEqual(site_url1, site_url2)
267+
self.assertTrue(site_url1.startswith('http://'))
268+
self.assertTrue(site_url2.startswith('https://'))
269+
270+
def test_processor_handles_empty_settings(self):
271+
"""测试处理器处理空设置"""
272+
# 清除缓存
273+
cache.clear()
274+
275+
request = self.factory.get('/')
276+
result = seo_processor(request)
277+
278+
# 即使某些设置为空,处理器也应该正常工作
279+
self.assertIsNotNone(result)
280+
self.assertIn('SITE_NAME', result)
281+
282+
@patch('blog.context_processors.logger')
283+
def test_processor_logs_cache_miss(self, mock_logger):
284+
"""测试缓存未命中时记录日志"""
285+
# 清除缓存
286+
cache.clear()
287+
288+
request = self.factory.get('/')
289+
result = seo_processor(request)
290+
291+
# 验证logger.info被调用
292+
self.assertTrue(mock_logger.info.called)
293+
# 验证日志消息
294+
call_args = str(mock_logger.info.call_args)
295+
self.assertIn('set processor cache', call_args)
296+
297+
def test_processor_multiple_categories(self):
298+
"""测试多个分类的情况"""
299+
# 创建额外的分类
300+
Category.objects.create(name='Category 2', slug='category-2')
301+
Category.objects.create(name='Category 3', slug='category-3')
302+
303+
# 清除缓存
304+
cache.clear()
305+
306+
request = self.factory.get('/')
307+
result = seo_processor(request)
308+
309+
# 验证所有分类都在列表中
310+
nav_categories = list(result['nav_category_list'])
311+
self.assertEqual(len(nav_categories), 3)
312+
313+
def test_processor_multiple_pages(self):
314+
"""测试多个页面的情况"""
315+
# 创建额外的页面
316+
Article.objects.create(
317+
title='Page 2',
318+
body='Content 2',
319+
author=self.user,
320+
type='p',
321+
status='p',
322+
category=self.category
323+
)
324+
Article.objects.create(
325+
title='Page 3',
326+
body='Content 3',
327+
author=self.user,
328+
type='p',
329+
status='p',
330+
category=self.category
331+
)
332+
333+
# 清除缓存
334+
cache.clear()
335+
336+
request = self.factory.get('/')
337+
result = seo_processor(request)
338+
339+
# 验证所有页面都在列表中
340+
nav_pages = list(result['nav_pages'])
341+
self.assertEqual(len(nav_pages), 3)
342+
343+
def test_processor_excludes_articles_from_nav_pages(self):
344+
"""测试nav_pages不包含文章(只包含页面)"""
345+
# 创建一个文章
346+
Article.objects.create(
347+
title='Test Article',
348+
body='Article content',
349+
author=self.user,
350+
type='a', # 文章类型
351+
status='p',
352+
category=self.category
353+
)
354+
355+
# 清除缓存
356+
cache.clear()
357+
358+
request = self.factory.get('/')
359+
result = seo_processor(request)
360+
361+
# 验证文章不在nav_pages中
362+
nav_pages = list(result['nav_pages'])
363+
page_titles = [page.title for page in nav_pages]
364+
self.assertNotIn('Test Article', page_titles)
365+
self.assertIn('Test Page', page_titles) # 但页面应该在
366+
367+
def test_processor_color_scheme_setting(self):
368+
"""测试COLOR_SCHEME设置"""
369+
request = self.factory.get('/')
370+
result = seo_processor(request)
371+
372+
# 验证COLOR_SCHEME存在且有值
373+
self.assertIn('COLOR_SCHEME', result)
374+
self.assertIsNotNone(result['COLOR_SCHEME'])

0 commit comments

Comments
 (0)