|
|
|
|
@ -1,5 +1,7 @@
|
|
|
|
|
import os
|
|
|
|
|
import requests
|
|
|
|
|
|
|
|
|
|
# 导入Django核心模块:配置、文件上传、命令调用、分页、静态文件、测试工具、URL反转、时区
|
|
|
|
|
from django.conf import settings
|
|
|
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
|
|
|
from django.core.management import call_command
|
|
|
|
|
@ -9,6 +11,7 @@ from django.test import Client, RequestFactory, TestCase
|
|
|
|
|
from django.urls import reverse
|
|
|
|
|
from django.utils import timezone
|
|
|
|
|
|
|
|
|
|
# 导入项目相关模型和工具:用户、博客模型、表单、模板标签、工具函数、OAuth相关
|
|
|
|
|
from accounts.models import BlogUser
|
|
|
|
|
from blog.forms import BlogSearchForm
|
|
|
|
|
from blog.models import Article, Category, Tag, SideBar, Links
|
|
|
|
|
@ -17,61 +20,90 @@ from djangoblog.utils import get_current_site, get_sha256
|
|
|
|
|
from oauth.models import OAuthUser, OAuthConfig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Create your tests here.
|
|
|
|
|
|
|
|
|
|
class ArticleTest(TestCase):
|
|
|
|
|
"""
|
|
|
|
|
博客核心功能测试类
|
|
|
|
|
测试文章、分类、标签、搜索、权限等核心业务逻辑
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
self.client = Client()
|
|
|
|
|
self.factory = RequestFactory()
|
|
|
|
|
"""
|
|
|
|
|
测试前的初始化方法
|
|
|
|
|
创建测试客户端和请求工厂,用于模拟HTTP请求
|
|
|
|
|
"""
|
|
|
|
|
self.client = Client() # 模拟用户浏览器的客户端
|
|
|
|
|
self.factory = RequestFactory() # 用于构造请求对象的工厂
|
|
|
|
|
|
|
|
|
|
def test_validate_article(self):
|
|
|
|
|
"""
|
|
|
|
|
测试文章相关核心功能:
|
|
|
|
|
- 用户模型操作
|
|
|
|
|
- 分类、标签、侧边栏、链接等模型CRUD
|
|
|
|
|
- 文章发布、分页、搜索、评论等流程
|
|
|
|
|
- 页面访问状态码验证
|
|
|
|
|
"""
|
|
|
|
|
# 获取当前站点域名
|
|
|
|
|
site = get_current_site().domain
|
|
|
|
|
|
|
|
|
|
# 创建或获取测试用户(管理员)
|
|
|
|
|
user = BlogUser.objects.get_or_create(
|
|
|
|
|
email="liangliangyy@gmail.com",
|
|
|
|
|
username="liangliangyy")[0]
|
|
|
|
|
user.set_password("liangliangyy")
|
|
|
|
|
user.is_staff = True
|
|
|
|
|
user.is_superuser = True
|
|
|
|
|
user.set_password("liangliangyy") # 设置密码
|
|
|
|
|
user.is_staff = True # 允许登录admin
|
|
|
|
|
user.is_superuser = True # 超级管理员权限
|
|
|
|
|
user.save()
|
|
|
|
|
|
|
|
|
|
# 测试用户个人页面访问
|
|
|
|
|
response = self.client.get(user.get_absolute_url())
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
response = self.client.get('/admin/servermanager/emailsendlog/')
|
|
|
|
|
response = self.client.get('admin/admin/logentry/')
|
|
|
|
|
self.assertEqual(response.status_code, 200) # 验证页面正常访问
|
|
|
|
|
|
|
|
|
|
# 测试admin后台页面访问(未登录状态,实际会跳转登录页)
|
|
|
|
|
self.client.get('/admin/servermanager/emailsendlog/')
|
|
|
|
|
self.client.get('admin/admin/logentry/')
|
|
|
|
|
|
|
|
|
|
# 创建测试侧边栏
|
|
|
|
|
s = SideBar()
|
|
|
|
|
s.sequence = 1
|
|
|
|
|
s.name = 'test'
|
|
|
|
|
s.content = 'test content'
|
|
|
|
|
s.is_enable = True
|
|
|
|
|
s.sequence = 1 # 排序序号
|
|
|
|
|
s.name = 'test' # 名称
|
|
|
|
|
s.content = 'test content' # 内容
|
|
|
|
|
s.is_enable = True # 启用
|
|
|
|
|
s.save()
|
|
|
|
|
|
|
|
|
|
# 创建测试分类
|
|
|
|
|
category = Category()
|
|
|
|
|
category.name = "category"
|
|
|
|
|
category.name = "category" # 分类名称
|
|
|
|
|
category.creation_time = timezone.now()
|
|
|
|
|
category.last_mod_time = timezone.now()
|
|
|
|
|
category.save()
|
|
|
|
|
|
|
|
|
|
# 创建测试标签
|
|
|
|
|
tag = Tag()
|
|
|
|
|
tag.name = "nicetag"
|
|
|
|
|
tag.name = "nicetag" # 标签名称
|
|
|
|
|
tag.save()
|
|
|
|
|
|
|
|
|
|
# 创建测试文章
|
|
|
|
|
article = Article()
|
|
|
|
|
article.title = "nicetitle"
|
|
|
|
|
article.body = "nicecontent"
|
|
|
|
|
article.author = user
|
|
|
|
|
article.category = category
|
|
|
|
|
article.type = 'a'
|
|
|
|
|
article.status = 'p'
|
|
|
|
|
|
|
|
|
|
article.title = "nicetitle" # 标题
|
|
|
|
|
article.body = "nicecontent" # 内容
|
|
|
|
|
article.author = user # 作者
|
|
|
|
|
article.category = category # 所属分类
|
|
|
|
|
article.type = 'a' # 类型为文章
|
|
|
|
|
article.status = 'p' # 状态为已发布
|
|
|
|
|
article.save()
|
|
|
|
|
|
|
|
|
|
# 验证标签关联(初始无标签)
|
|
|
|
|
self.assertEqual(0, article.tags.count())
|
|
|
|
|
# 关联标签并验证
|
|
|
|
|
article.tags.add(tag)
|
|
|
|
|
article.save()
|
|
|
|
|
self.assertEqual(1, article.tags.count())
|
|
|
|
|
|
|
|
|
|
# 批量创建20篇测试文章(用于测试分页)
|
|
|
|
|
for i in range(20):
|
|
|
|
|
article = Article()
|
|
|
|
|
article.title = "nicetitle" + str(i)
|
|
|
|
|
article.body = "nicetitle" + str(i)
|
|
|
|
|
article.title = f"nicetitle{i}"
|
|
|
|
|
article.body = f"nicetitle{i}"
|
|
|
|
|
article.author = user
|
|
|
|
|
article.category = category
|
|
|
|
|
article.type = 'a'
|
|
|
|
|
@ -79,114 +111,175 @@ class ArticleTest(TestCase):
|
|
|
|
|
article.save()
|
|
|
|
|
article.tags.add(tag)
|
|
|
|
|
article.save()
|
|
|
|
|
|
|
|
|
|
# 测试Elasticsearch搜索(如果启用)
|
|
|
|
|
from blog.documents import ELASTICSEARCH_ENABLED
|
|
|
|
|
if ELASTICSEARCH_ENABLED:
|
|
|
|
|
call_command("build_index")
|
|
|
|
|
response = self.client.get('/search', {'q': 'nicetitle'})
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
call_command("build_index") # 构建搜索索引
|
|
|
|
|
response = self.client.get('/search', {'q': 'nicetitle'}) # 执行搜索
|
|
|
|
|
self.assertEqual(response.status_code, 200) # 验证搜索页正常
|
|
|
|
|
|
|
|
|
|
# 测试文章详情页访问
|
|
|
|
|
response = self.client.get(article.get_absolute_url())
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
# 测试搜索引擎推送功能
|
|
|
|
|
from djangoblog.spider_notify import SpiderNotify
|
|
|
|
|
SpiderNotify.notify(article.get_absolute_url())
|
|
|
|
|
SpiderNotify.notify(article.get_absolute_url()) # 推送文章URL到搜索引擎
|
|
|
|
|
|
|
|
|
|
# 测试标签页访问
|
|
|
|
|
response = self.client.get(tag.get_absolute_url())
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
# 测试分类页访问
|
|
|
|
|
response = self.client.get(category.get_absolute_url())
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
# 测试搜索功能(无结果场景)
|
|
|
|
|
response = self.client.get('/search', {'q': 'django'})
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
# 测试文章标签模板标签
|
|
|
|
|
s = load_articletags(article)
|
|
|
|
|
self.assertIsNotNone(s)
|
|
|
|
|
self.assertIsNotNone(s) # 验证模板标签返回结果
|
|
|
|
|
|
|
|
|
|
# 登录测试用户
|
|
|
|
|
self.client.login(username='liangliangyy', password='liangliangyy')
|
|
|
|
|
|
|
|
|
|
# 测试归档页访问
|
|
|
|
|
response = self.client.get(reverse('blog:archives'))
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
# 测试各种场景下的分页功能
|
|
|
|
|
# 1. 所有文章分页
|
|
|
|
|
p = Paginator(Article.objects.all(), settings.PAGINATE_BY)
|
|
|
|
|
self.check_pagination(p, '', '')
|
|
|
|
|
|
|
|
|
|
# 2. 标签筛选分页
|
|
|
|
|
p = Paginator(Article.objects.filter(tags=tag), settings.PAGINATE_BY)
|
|
|
|
|
self.check_pagination(p, '分类标签归档', tag.slug)
|
|
|
|
|
|
|
|
|
|
# 3. 作者筛选分页
|
|
|
|
|
p = Paginator(
|
|
|
|
|
Article.objects.filter(
|
|
|
|
|
author__username='liangliangyy'), settings.PAGINATE_BY)
|
|
|
|
|
Article.objects.filter(author__username='liangliangyy'),
|
|
|
|
|
settings.PAGINATE_BY
|
|
|
|
|
)
|
|
|
|
|
self.check_pagination(p, '作者文章归档', 'liangliangyy')
|
|
|
|
|
|
|
|
|
|
# 4. 分类筛选分页
|
|
|
|
|
p = Paginator(Article.objects.filter(category=category), settings.PAGINATE_BY)
|
|
|
|
|
self.check_pagination(p, '分类目录归档', category.slug)
|
|
|
|
|
|
|
|
|
|
# 测试搜索表单
|
|
|
|
|
f = BlogSearchForm()
|
|
|
|
|
f.search()
|
|
|
|
|
# self.client.login(username='liangliangyy', password='liangliangyy')
|
|
|
|
|
from djangoblog.spider_notify import SpiderNotify
|
|
|
|
|
f.search() # 调用搜索方法
|
|
|
|
|
|
|
|
|
|
# 测试百度搜索引擎推送
|
|
|
|
|
SpiderNotify.baidu_notify([article.get_full_url()])
|
|
|
|
|
|
|
|
|
|
# 测试头像相关模板标签
|
|
|
|
|
from blog.templatetags.blog_tags import gravatar_url, gravatar
|
|
|
|
|
u = gravatar_url('liangliangyy@gmail.com')
|
|
|
|
|
u = gravatar('liangliangyy@gmail.com')
|
|
|
|
|
u = gravatar_url('liangliangyy@gmail.com') # 获取头像URL
|
|
|
|
|
u = gravatar('liangliangyy@gmail.com') # 生成头像HTML
|
|
|
|
|
|
|
|
|
|
# 测试友情链接
|
|
|
|
|
link = Links(
|
|
|
|
|
sequence=1,
|
|
|
|
|
name="lylinux",
|
|
|
|
|
link='https://wwww.lylinux.net')
|
|
|
|
|
link='https://wwww.lylinux.net'
|
|
|
|
|
)
|
|
|
|
|
link.save()
|
|
|
|
|
response = self.client.get('/links.html')
|
|
|
|
|
response = self.client.get('/links.html') # 访问友情链接页
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
# 测试RSS订阅
|
|
|
|
|
response = self.client.get('/feed/')
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
# 测试站点地图
|
|
|
|
|
response = self.client.get('/sitemap.xml')
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
# 测试admin后台操作(删除文章、访问日志)
|
|
|
|
|
self.client.get("/admin/blog/article/1/delete/")
|
|
|
|
|
self.client.get('/admin/servermanager/emailsendlog/')
|
|
|
|
|
self.client.get('/admin/admin/logentry/')
|
|
|
|
|
self.client.get('/admin/admin/logentry/1/change/')
|
|
|
|
|
|
|
|
|
|
def check_pagination(self, p, type, value):
|
|
|
|
|
"""
|
|
|
|
|
测试分页功能的辅助方法
|
|
|
|
|
验证分页控件生成的URL是否可正常访问
|
|
|
|
|
"""
|
|
|
|
|
# 遍历所有分页页面
|
|
|
|
|
for page in range(1, p.num_pages + 1):
|
|
|
|
|
# 获取分页信息(通过模板标签)
|
|
|
|
|
s = load_pagination_info(p.page(page), type, value)
|
|
|
|
|
self.assertIsNotNone(s)
|
|
|
|
|
self.assertIsNotNone(s) # 验证分页信息生成正常
|
|
|
|
|
|
|
|
|
|
# 测试上一页链接
|
|
|
|
|
if s['previous_url']:
|
|
|
|
|
response = self.client.get(s['previous_url'])
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
# 测试下一页链接
|
|
|
|
|
if s['next_url']:
|
|
|
|
|
response = self.client.get(s['next_url'])
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
def test_image(self):
|
|
|
|
|
import requests
|
|
|
|
|
rsp = requests.get(
|
|
|
|
|
'https://www.python.org/static/img/python-logo.png')
|
|
|
|
|
imagepath = os.path.join(settings.BASE_DIR, 'python.png')
|
|
|
|
|
"""
|
|
|
|
|
测试图片上传功能:
|
|
|
|
|
- 未授权上传
|
|
|
|
|
- 授权上传
|
|
|
|
|
- 头像保存工具函数
|
|
|
|
|
- 邮件发送工具函数
|
|
|
|
|
"""
|
|
|
|
|
# 下载测试图片(Python官方logo)
|
|
|
|
|
rsp = requests.get('https://www.python.org/static/img/python-logo.png')
|
|
|
|
|
imagepath = os.path.join(settings.BASE_DIR, 'python.png') # 保存路径
|
|
|
|
|
with open(imagepath, 'wb') as file:
|
|
|
|
|
file.write(rsp.content)
|
|
|
|
|
|
|
|
|
|
# 测试未授权上传(预期403禁止访问)
|
|
|
|
|
rsp = self.client.post('/upload')
|
|
|
|
|
self.assertEqual(rsp.status_code, 403)
|
|
|
|
|
|
|
|
|
|
# 生成上传签名(基于SECRET_KEY的双重SHA256加密)
|
|
|
|
|
sign = get_sha256(get_sha256(settings.SECRET_KEY))
|
|
|
|
|
|
|
|
|
|
# 测试授权上传
|
|
|
|
|
with open(imagepath, 'rb') as file:
|
|
|
|
|
# 构造上传文件对象
|
|
|
|
|
imgfile = SimpleUploadedFile(
|
|
|
|
|
'python.png', file.read(), content_type='image/jpg')
|
|
|
|
|
'python.png', file.read(), content_type='image/jpg'
|
|
|
|
|
)
|
|
|
|
|
form_data = {'python.png': imgfile}
|
|
|
|
|
# 带签名上传
|
|
|
|
|
rsp = self.client.post(
|
|
|
|
|
'/upload?sign=' + sign, form_data, follow=True)
|
|
|
|
|
self.assertEqual(rsp.status_code, 200)
|
|
|
|
|
f'/upload?sign={sign}', form_data, follow=True
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(rsp.status_code, 200) # 验证上传成功
|
|
|
|
|
|
|
|
|
|
# 清理测试文件
|
|
|
|
|
os.remove(imagepath)
|
|
|
|
|
|
|
|
|
|
# 测试用户头像保存和邮件发送工具函数
|
|
|
|
|
from djangoblog.utils import save_user_avatar, send_email
|
|
|
|
|
send_email(['qq@qq.com'], 'testTitle', 'testContent')
|
|
|
|
|
save_user_avatar(
|
|
|
|
|
'https://www.python.org/static/img/python-logo.png')
|
|
|
|
|
send_email(['qq@qq.com'], 'testTitle', 'testContent') # 测试发送邮件
|
|
|
|
|
save_user_avatar('https://www.python.org/static/img/python-logo.png') # 测试保存头像
|
|
|
|
|
|
|
|
|
|
def test_errorpage(self):
|
|
|
|
|
rsp = self.client.get('/eee')
|
|
|
|
|
self.assertEqual(rsp.status_code, 404)
|
|
|
|
|
"""测试错误页面(404页面)"""
|
|
|
|
|
rsp = self.client.get('/eee') # 访问不存在的URL
|
|
|
|
|
self.assertEqual(rsp.status_code, 404) # 验证返回404
|
|
|
|
|
|
|
|
|
|
def test_commands(self):
|
|
|
|
|
"""
|
|
|
|
|
测试Django自定义命令:
|
|
|
|
|
- 索引构建、缓存清理、数据同步等
|
|
|
|
|
"""
|
|
|
|
|
# 创建测试用户
|
|
|
|
|
user = BlogUser.objects.get_or_create(
|
|
|
|
|
email="liangliangyy@gmail.com",
|
|
|
|
|
username="liangliangyy")[0]
|
|
|
|
|
@ -195,23 +288,26 @@ class ArticleTest(TestCase):
|
|
|
|
|
user.is_superuser = True
|
|
|
|
|
user.save()
|
|
|
|
|
|
|
|
|
|
# 创建OAuth配置
|
|
|
|
|
c = OAuthConfig()
|
|
|
|
|
c.type = 'qq'
|
|
|
|
|
c.type = 'qq' # QQ登录
|
|
|
|
|
c.appkey = 'appkey'
|
|
|
|
|
c.appsecret = 'appsecret'
|
|
|
|
|
c.save()
|
|
|
|
|
|
|
|
|
|
# 创建关联用户的OAuth账号
|
|
|
|
|
u = OAuthUser()
|
|
|
|
|
u.type = 'qq'
|
|
|
|
|
u.openid = 'openid'
|
|
|
|
|
u.user = user
|
|
|
|
|
u.picture = static("/blog/img/avatar.png")
|
|
|
|
|
u.user = user # 关联本地用户
|
|
|
|
|
u.picture = static("/blog/img/avatar.png") # 头像
|
|
|
|
|
u.metadata = '''
|
|
|
|
|
{
|
|
|
|
|
"figureurl": "https://qzapp.qlogo.cn/qzapp/101513904/C740E30B4113EAA80E0D9918ABC78E82/30"
|
|
|
|
|
}'''
|
|
|
|
|
}''' # 第三方平台返回的元数据
|
|
|
|
|
u.save()
|
|
|
|
|
|
|
|
|
|
# 创建未关联本地用户的OAuth账号
|
|
|
|
|
u = OAuthUser()
|
|
|
|
|
u.type = 'qq'
|
|
|
|
|
u.openid = 'openid1'
|
|
|
|
|
@ -222,11 +318,14 @@ class ArticleTest(TestCase):
|
|
|
|
|
}'''
|
|
|
|
|
u.save()
|
|
|
|
|
|
|
|
|
|
# 测试Elasticsearch索引构建命令
|
|
|
|
|
from blog.documents import ELASTICSEARCH_ENABLED
|
|
|
|
|
if ELASTICSEARCH_ENABLED:
|
|
|
|
|
call_command("build_index")
|
|
|
|
|
call_command("ping_baidu", "all")
|
|
|
|
|
call_command("create_testdata")
|
|
|
|
|
call_command("clear_cache")
|
|
|
|
|
call_command("sync_user_avatar")
|
|
|
|
|
call_command("build_search_words")
|
|
|
|
|
|
|
|
|
|
# 测试其他自定义命令
|
|
|
|
|
call_command("ping_baidu", "all") # 百度链接推送
|
|
|
|
|
call_command("create_testdata") # 创建测试数据
|
|
|
|
|
call_command("clear_cache") # 清理缓存
|
|
|
|
|
call_command("sync_user_avatar") # 同步用户头像
|
|
|
|
|
call_command("build_search_words") # 构建搜索关键词
|