|
|
|
|
@ -5,203 +5,292 @@ from django.utils.translation import gettext_lazy as _
|
|
|
|
|
|
|
|
|
|
from accounts.models import BlogUser
|
|
|
|
|
from blog.models import Article, Category
|
|
|
|
|
from djangoblog.utils import *
|
|
|
|
|
from . import utils
|
|
|
|
|
from djangoblog.utils import * # 导入项目公共工具函数(如加密、缓存操作等)
|
|
|
|
|
from . import utils # 导入accounts应用自定义工具函数(如验证码设置/验证、邮件发送)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 模块级注释——accounts应用的单元测试文件,
|
|
|
|
|
# 覆盖用户核心业务流程的测试场景:用户登录、注册、邮箱验证码验证、忘记密码重置等,
|
|
|
|
|
# 基于Django TestCase框架,通过模拟HTTP请求和数据库操作,验证业务逻辑的正确性,
|
|
|
|
|
# 确保用户相关功能稳定可用(如权限控制、数据一致性、错误处理)
|
|
|
|
|
# Create your tests here.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AccountTest(TestCase):
|
|
|
|
|
"""
|
|
|
|
|
用户相关核心功能测试类,继承Django TestCase
|
|
|
|
|
集中测试用户登录、注册、验证码验证、忘记密码等关键流程,
|
|
|
|
|
每个测试方法对应一个独立的业务场景,确保测试隔离性
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
self.client = Client()
|
|
|
|
|
self.factory = RequestFactory()
|
|
|
|
|
"""
|
|
|
|
|
测试初始化方法(每个测试方法执行前自动调用)
|
|
|
|
|
初始化测试所需的核心对象和测试数据,避免重复代码
|
|
|
|
|
"""
|
|
|
|
|
self.client = Client() # 模拟HTTP客户端,用于发送GET/POST请求
|
|
|
|
|
self.factory = RequestFactory() # 请求工厂,用于构建自定义请求对象(本测试未直接使用)
|
|
|
|
|
# 创建普通测试用户(用于后续登录、密码重置等测试)
|
|
|
|
|
self.blog_user = BlogUser.objects.create_user(
|
|
|
|
|
username="test",
|
|
|
|
|
email="admin@admin.com",
|
|
|
|
|
password="12345678"
|
|
|
|
|
username="test", # 用户名
|
|
|
|
|
email="admin@admin.com", # 邮箱
|
|
|
|
|
password="12345678" # 密码(明文,create_user会自动哈希存储)
|
|
|
|
|
)
|
|
|
|
|
self.new_test = "xxx123--="
|
|
|
|
|
self.new_test = "xxx123--=" # 测试用新密码(用于忘记密码重置场景)
|
|
|
|
|
|
|
|
|
|
def test_validate_account(self):
|
|
|
|
|
"""
|
|
|
|
|
测试用户登录、管理员权限、文章管理访问流程
|
|
|
|
|
验证点:超级用户登录成功、管理员页面访问权限、文章创建后管理页访问权限
|
|
|
|
|
"""
|
|
|
|
|
# 获取当前站点域名(用于URL生成,本测试未直接使用)
|
|
|
|
|
site = get_current_site().domain
|
|
|
|
|
# 创建超级用户(用于测试管理员权限)
|
|
|
|
|
user = BlogUser.objects.create_superuser(
|
|
|
|
|
email="liangliangyy1@gmail.com",
|
|
|
|
|
username="liangliangyy1",
|
|
|
|
|
password="qwer!@#$ggg")
|
|
|
|
|
password="qwer!@#$ggg") # 超级用户密码
|
|
|
|
|
# 根据用户名查询创建的超级用户(验证用户创建成功)
|
|
|
|
|
testuser = BlogUser.objects.get(username='liangliangyy1')
|
|
|
|
|
|
|
|
|
|
# 模拟超级用户登录,验证登录结果是否成功
|
|
|
|
|
loginresult = self.client.login(
|
|
|
|
|
username='liangliangyy1',
|
|
|
|
|
password='qwer!@#$ggg')
|
|
|
|
|
self.assertEqual(loginresult, True)
|
|
|
|
|
self.assertEqual(loginresult, True) # 断言登录成功
|
|
|
|
|
|
|
|
|
|
# 访问管理员首页,验证是否有权限(状态码200表示访问成功)
|
|
|
|
|
response = self.client.get('/admin/')
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
# 创建测试分类(用于后续文章关联)
|
|
|
|
|
category = Category()
|
|
|
|
|
category.name = "categoryaaa"
|
|
|
|
|
category.creation_time = timezone.now()
|
|
|
|
|
category.last_modify_time = timezone.now()
|
|
|
|
|
category.save()
|
|
|
|
|
category.name = "categoryaaa" # 分类名称
|
|
|
|
|
category.creation_time = timezone.now() # 创建时间
|
|
|
|
|
category.last_modify_time = timezone.now() # 最后修改时间
|
|
|
|
|
category.save() # 保存到数据库
|
|
|
|
|
|
|
|
|
|
# 创建测试文章(关联超级用户和分类)
|
|
|
|
|
article = Article()
|
|
|
|
|
article.title = "nicetitleaaa"
|
|
|
|
|
article.body = "nicecontentaaa"
|
|
|
|
|
article.author = user
|
|
|
|
|
article.category = category
|
|
|
|
|
article.type = 'a'
|
|
|
|
|
article.status = 'p'
|
|
|
|
|
article.save()
|
|
|
|
|
|
|
|
|
|
article.title = "nicetitleaaa" # 文章标题
|
|
|
|
|
article.body = "nicecontentaaa" # 文章内容
|
|
|
|
|
article.author = user # 文章作者(超级用户)
|
|
|
|
|
article.category = category # 文章分类
|
|
|
|
|
article.type = 'a' # 文章类型(推测为"article"普通文章)
|
|
|
|
|
article.status = 'p' # 文章状态(推测为"published"已发布)
|
|
|
|
|
article.save() # 保存到数据库
|
|
|
|
|
|
|
|
|
|
# 访问文章的管理员编辑页,验证超级用户是否有权限
|
|
|
|
|
response = self.client.get(article.get_admin_url())
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
self.assertEqual(response.status_code, 200) # 断言访问成功
|
|
|
|
|
|
|
|
|
|
def test_validate_register(self):
|
|
|
|
|
"""
|
|
|
|
|
测试用户注册完整流程
|
|
|
|
|
验证点:注册提交、用户创建、验证邮件链接访问、登录后权限升级、文章创建、退出登录、错误密码登录
|
|
|
|
|
"""
|
|
|
|
|
# 注册前检查目标邮箱是否存在(预期不存在,计数为0)
|
|
|
|
|
self.assertEquals(
|
|
|
|
|
0, len(
|
|
|
|
|
BlogUser.objects.filter(
|
|
|
|
|
email='user123@user.com')))
|
|
|
|
|
|
|
|
|
|
# 模拟POST请求提交注册表单(调用注册路由)
|
|
|
|
|
response = self.client.post(reverse('account:register'), {
|
|
|
|
|
'username': 'user1233',
|
|
|
|
|
'email': 'user123@user.com',
|
|
|
|
|
'password1': 'password123!q@wE#R$T',
|
|
|
|
|
'password2': 'password123!q@wE#R$T',
|
|
|
|
|
'username': 'user1233', # 注册用户名
|
|
|
|
|
'email': 'user123@user.com', # 注册邮箱
|
|
|
|
|
'password1': 'password123!q@wE#R$T', # 注册密码(符合强度要求)
|
|
|
|
|
'password2': 'password123!q@wE#R$T', # 密码确认(与密码一致)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# 注册后检查目标邮箱是否创建成功(预期计数为1)
|
|
|
|
|
self.assertEquals(
|
|
|
|
|
1, len(
|
|
|
|
|
BlogUser.objects.filter(
|
|
|
|
|
email='user123@user.com')))
|
|
|
|
|
|
|
|
|
|
# 获取刚注册的用户,生成邮箱验证链接(基于用户ID和加密签名)
|
|
|
|
|
user = BlogUser.objects.filter(email='user123@user.com')[0]
|
|
|
|
|
# 生成验证签名(双重SHA256加密,结合SECRET_KEY和用户ID,确保链接安全性)
|
|
|
|
|
sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
|
|
|
|
|
path = reverse('accounts:result')
|
|
|
|
|
path = reverse('accounts:result') # 验证结果页路由
|
|
|
|
|
# 拼接完整的验证链接(包含用户ID和签名)
|
|
|
|
|
url = '{path}?type=validation&id={id}&sign={sign}'.format(
|
|
|
|
|
path=path, id=user.id, sign=sign)
|
|
|
|
|
# 访问验证链接,验证页面是否正常响应(状态码200)
|
|
|
|
|
response = self.client.get(url)
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
# 使用注册的用户名和密码登录
|
|
|
|
|
self.client.login(username='user1233', password='password123!q@wE#R$T')
|
|
|
|
|
# 将该用户升级为超级用户(用于测试管理员权限)
|
|
|
|
|
user = BlogUser.objects.filter(email='user123@user.com')[0]
|
|
|
|
|
user.is_superuser = True
|
|
|
|
|
user.is_staff = True
|
|
|
|
|
user.save()
|
|
|
|
|
delete_sidebar_cache()
|
|
|
|
|
user.is_superuser = True # 设为超级用户
|
|
|
|
|
user.is_staff = True # 允许访问admin后台
|
|
|
|
|
user.save() # 保存权限变更
|
|
|
|
|
|
|
|
|
|
delete_sidebar_cache() # 清除侧边栏缓存(项目自定义缓存操作,避免缓存影响测试)
|
|
|
|
|
|
|
|
|
|
# 创建测试分类(用于关联文章)
|
|
|
|
|
category = Category()
|
|
|
|
|
category.name = "categoryaaa"
|
|
|
|
|
category.creation_time = timezone.now()
|
|
|
|
|
category.last_modify_time = timezone.now()
|
|
|
|
|
category.save()
|
|
|
|
|
|
|
|
|
|
# 创建测试文章(关联升级后的超级用户)
|
|
|
|
|
article = Article()
|
|
|
|
|
article.category = category
|
|
|
|
|
article.title = "nicetitle333"
|
|
|
|
|
article.body = "nicecontentttt"
|
|
|
|
|
article.author = user
|
|
|
|
|
|
|
|
|
|
article.type = 'a'
|
|
|
|
|
article.status = 'p'
|
|
|
|
|
article.save()
|
|
|
|
|
|
|
|
|
|
# 访问文章管理页,验证升级后的用户是否有权限
|
|
|
|
|
response = self.client.get(article.get_admin_url())
|
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
# 模拟退出登录
|
|
|
|
|
response = self.client.get(reverse('account:logout'))
|
|
|
|
|
# 断言退出登录响应状态码(301/302为跳转,200为成功响应,均符合预期)
|
|
|
|
|
self.assertIn(response.status_code, [301, 302, 200])
|
|
|
|
|
|
|
|
|
|
# 退出后访问文章管理页(预期无权限,可能跳转登录页)
|
|
|
|
|
response = self.client.get(article.get_admin_url())
|
|
|
|
|
self.assertIn(response.status_code, [301, 302, 200])
|
|
|
|
|
|
|
|
|
|
# 使用错误密码尝试登录
|
|
|
|
|
response = self.client.post(reverse('account:login'), {
|
|
|
|
|
'username': 'user1233',
|
|
|
|
|
'password': 'password123'
|
|
|
|
|
'password': 'password123' # 错误密码(原密码为password123!q@wE#R$T)
|
|
|
|
|
})
|
|
|
|
|
# 断言登录失败后的响应状态码(跳转或重新显示登录页)
|
|
|
|
|
self.assertIn(response.status_code, [301, 302, 200])
|
|
|
|
|
|
|
|
|
|
# 错误密码登录后访问文章管理页(预期无权限)
|
|
|
|
|
response = self.client.get(article.get_admin_url())
|
|
|
|
|
self.assertIn(response.status_code, [301, 302, 200])
|
|
|
|
|
|
|
|
|
|
def test_verify_email_code(self):
|
|
|
|
|
to_email = "admin@admin.com"
|
|
|
|
|
code = generate_code()
|
|
|
|
|
utils.set_code(to_email, code)
|
|
|
|
|
utils.send_verify_email(to_email, code)
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
测试邮箱验证码的验证逻辑
|
|
|
|
|
验证点:正确邮箱+正确验证码验证成功、错误邮箱+正确验证码验证失败
|
|
|
|
|
"""
|
|
|
|
|
to_email = "admin@admin.com" # 测试邮箱(已在setUp中创建关联用户)
|
|
|
|
|
code = generate_code() # 生成随机验证码(项目工具函数)
|
|
|
|
|
utils.set_code(to_email, code) # 存储验证码(推测基于缓存或数据库存储)
|
|
|
|
|
utils.send_verify_email(to_email, code) # 发送验证邮件(模拟邮件发送流程)
|
|
|
|
|
|
|
|
|
|
# 验证:正确邮箱+正确验证码 → 预期无错误(返回None)
|
|
|
|
|
err = utils.verify("admin@admin.com", code)
|
|
|
|
|
self.assertEqual(err, None)
|
|
|
|
|
|
|
|
|
|
# 验证:错误邮箱+正确验证码 → 预期返回错误信息(字符串类型)
|
|
|
|
|
err = utils.verify("admin@123.com", code)
|
|
|
|
|
self.assertEqual(type(err), str)
|
|
|
|
|
|
|
|
|
|
def test_forget_password_email_code_success(self):
|
|
|
|
|
"""
|
|
|
|
|
测试获取忘记密码验证码的成功场景
|
|
|
|
|
验证点:输入已注册邮箱,成功获取验证码(响应"ok")
|
|
|
|
|
"""
|
|
|
|
|
# 模拟POST请求提交邮箱,获取忘记密码验证码
|
|
|
|
|
resp = self.client.post(
|
|
|
|
|
path=reverse("account:forget_password_code"),
|
|
|
|
|
data=dict(email="admin@admin.com")
|
|
|
|
|
path=reverse("account:forget_password_code"), # 忘记密码验证码路由
|
|
|
|
|
data=dict(email="admin@admin.com") # 已注册的测试邮箱
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
|
self.assertEqual(resp.content.decode("utf-8"), "ok")
|
|
|
|
|
self.assertEqual(resp.status_code, 200) # 断言请求成功
|
|
|
|
|
self.assertEqual(resp.content.decode("utf-8"), "ok") # 断言响应内容为"ok"(表示验证码发送成功)
|
|
|
|
|
|
|
|
|
|
def test_forget_password_email_code_fail(self):
|
|
|
|
|
"""
|
|
|
|
|
测试获取忘记密码验证码的失败场景
|
|
|
|
|
验证点:无邮箱输入、邮箱格式错误,均返回"错误的邮箱"
|
|
|
|
|
"""
|
|
|
|
|
# 场景1:不传入邮箱参数 → 预期返回"错误的邮箱"
|
|
|
|
|
resp = self.client.post(
|
|
|
|
|
path=reverse("account:forget_password_code"),
|
|
|
|
|
data=dict()
|
|
|
|
|
data=dict() # 空数据(无邮箱)
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
|
|
|
|
|
|
|
|
|
|
# 场景2:传入格式错误的邮箱(无@后的域名后缀)→ 预期返回"错误的邮箱"
|
|
|
|
|
resp = self.client.post(
|
|
|
|
|
path=reverse("account:forget_password_code"),
|
|
|
|
|
data=dict(email="admin@com")
|
|
|
|
|
data=dict(email="admin@com") # 格式错误的邮箱
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
|
|
|
|
|
|
|
|
|
|
def test_forget_password_email_success(self):
|
|
|
|
|
code = generate_code()
|
|
|
|
|
utils.set_code(self.blog_user.email, code)
|
|
|
|
|
"""
|
|
|
|
|
测试忘记密码重置成功的场景
|
|
|
|
|
验证点:输入正确邮箱、验证码、新密码,密码成功更新
|
|
|
|
|
"""
|
|
|
|
|
code = generate_code() # 生成验证码
|
|
|
|
|
utils.set_code(self.blog_user.email, code) # 绑定验证码到测试用户邮箱
|
|
|
|
|
# 构建重置密码表单数据
|
|
|
|
|
data = dict(
|
|
|
|
|
new_password1=self.new_test,
|
|
|
|
|
new_password2=self.new_test,
|
|
|
|
|
email=self.blog_user.email,
|
|
|
|
|
code=code,
|
|
|
|
|
new_password1=self.new_test, # 新密码
|
|
|
|
|
new_password2=self.new_test, # 新密码确认(与新密码一致)
|
|
|
|
|
email=self.blog_user.email, # 测试用户邮箱
|
|
|
|
|
code=code, # 正确的验证码
|
|
|
|
|
)
|
|
|
|
|
# 模拟POST请求提交重置密码表单
|
|
|
|
|
resp = self.client.post(
|
|
|
|
|
path=reverse("account:forget_password"),
|
|
|
|
|
path=reverse("account:forget_password"), # 忘记密码重置路由
|
|
|
|
|
data=data
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(resp.status_code, 302)
|
|
|
|
|
self.assertEqual(resp.status_code, 302) # 断言重置成功后跳转(302为重定向)
|
|
|
|
|
|
|
|
|
|
# 验证用户密码是否修改成功
|
|
|
|
|
# 验证密码是否真正修改成功
|
|
|
|
|
blog_user = BlogUser.objects.filter(
|
|
|
|
|
email=self.blog_user.email,
|
|
|
|
|
).first() # type: BlogUser
|
|
|
|
|
self.assertNotEqual(blog_user, None)
|
|
|
|
|
).first() # 获取测试用户
|
|
|
|
|
self.assertNotEqual(blog_user, None) # 断言用户存在
|
|
|
|
|
# 验证新密码是否匹配(check_password会自动哈希比对)
|
|
|
|
|
self.assertEqual(blog_user.check_password(data["new_password1"]), True)
|
|
|
|
|
|
|
|
|
|
def test_forget_password_email_not_user(self):
|
|
|
|
|
"""
|
|
|
|
|
测试忘记密码重置的失败场景:输入未注册的邮箱
|
|
|
|
|
验证点:未注册邮箱提交重置请求,返回200状态码(表单重新显示错误)
|
|
|
|
|
"""
|
|
|
|
|
# 构建重置密码表单数据(邮箱未注册)
|
|
|
|
|
data = dict(
|
|
|
|
|
new_password1=self.new_test,
|
|
|
|
|
new_password2=self.new_test,
|
|
|
|
|
email="123@123.com",
|
|
|
|
|
code="123456",
|
|
|
|
|
email="123@123.com", # 未注册的邮箱
|
|
|
|
|
code="123456", # 任意验证码
|
|
|
|
|
)
|
|
|
|
|
# 模拟POST请求提交重置密码表单
|
|
|
|
|
resp = self.client.post(
|
|
|
|
|
path=reverse("account:forget_password"),
|
|
|
|
|
data=data
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
|
|
|
|
|
|
self.assertEqual(resp.status_code, 200) # 断言返回表单页面(显示邮箱不存在错误)
|
|
|
|
|
|
|
|
|
|
def test_forget_password_email_code_error(self):
|
|
|
|
|
code = generate_code()
|
|
|
|
|
"""
|
|
|
|
|
测试忘记密码重置的失败场景:验证码错误
|
|
|
|
|
验证点:输入正确邮箱、错误验证码,返回200状态码(表单重新显示错误)
|
|
|
|
|
"""
|
|
|
|
|
code = generate_code() # 生成正确验证码并绑定到测试用户邮箱
|
|
|
|
|
utils.set_code(self.blog_user.email, code)
|
|
|
|
|
# 构建重置密码表单数据(验证码错误)
|
|
|
|
|
data = dict(
|
|
|
|
|
new_password1=self.new_test,
|
|
|
|
|
new_password2=self.new_test,
|
|
|
|
|
email=self.blog_user.email,
|
|
|
|
|
code="111111",
|
|
|
|
|
email=self.blog_user.email, # 正确邮箱
|
|
|
|
|
code="111111", # 错误验证码(与绑定的code不一致)
|
|
|
|
|
)
|
|
|
|
|
# 模拟POST请求提交重置密码表单
|
|
|
|
|
resp = self.client.post(
|
|
|
|
|
path=reverse("account:forget_password"),
|
|
|
|
|
data=data
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
|
|
|
|
|
|
self.assertEqual(resp.status_code, 200) # 断言返回表单页面(显示验证码错误)
|