You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Django/doc/oauth/tests.py

319 lines
13 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import json
from unittest.mock import patch # 用于模拟外部依赖如第三方API调用
from django.conf import settings
from django.contrib import auth # 用于用户认证相关操作
from django.test import Client, RequestFactory, TestCase # Django测试工具
from django.urls import reverse # 用于反向解析URL
from djangoblog.utils import get_sha256 # 导入自定义加密工具函数
from oauth.models import OAuthConfig # 导入OAuth配置模型
from oauth.oauthmanager import BaseOauthManager # 导入OAuth基础管理器
# Create your tests here.
class OAuthConfigTest(TestCase):
"""测试OAuth配置模型及基础登录流程"""
def setUp(self):
"""测试前初始化:创建客户端和请求工厂"""
self.client = Client() # 用于模拟HTTP请求的客户端
self.factory = RequestFactory() # 用于创建请求对象的工厂
def test_oauth_login_test(self):
"""测试OAuth配置创建后登录链接和授权回调的正确性"""
# 创建一个微博的OAuth配置
c = OAuthConfig()
c.type = 'weibo'
c.appkey = 'appkey'
c.appsecret = 'appsecret'
c.save()
# 测试访问微博OAuth登录链接是否重定向到正确的授权页面
response = self.client.get('/oauth/oauthlogin?type=weibo')
self.assertEqual(response.status_code, 302) # 验证重定向状态码
self.assertTrue("api.weibo.com" in response.url) # 验证重定向到微博API
# 测试授权回调接口是否正常重定向
response = self.client.get('/oauth/authorize?type=weibo&code=code')
self.assertEqual(response.status_code, 302) # 验证重定向状态码
self.assertEqual(response.url, '/') # 验证默认重定向到首页
class OauthLoginTest(TestCase):
"""测试各第三方平台的OAuth登录流程"""
def setUp(self) -> None:
"""测试前初始化创建客户端、请求工厂并初始化所有平台的OAuth配置"""
self.client = Client()
self.factory = RequestFactory()
self.apps = self.init_apps() # 初始化所有启用的OAuth应用
def init_apps(self):
"""为所有BaseOauthManager的子类各平台管理器创建对应的OAuth配置"""
# 获取所有第三方平台的OAuth管理器实例
applications = [p() for p in BaseOauthManager.__subclasses__()]
# 为每个平台创建默认配置并保存到数据库
for application in applications:
c = OAuthConfig()
c.type = application.ICON_NAME.lower()
c.appkey = 'appkey'
c.appsecret = 'appsecret'
c.save()
return applications
def get_app_by_type(self, type):
"""根据平台类型获取对应的OAuth管理器实例"""
for app in self.apps:
if app.ICON_NAME.lower() == type:
return app
@patch("oauth.oauthmanager.WBOauthManager.do_post")
@patch("oauth.oauthmanager.WBOauthManager.do_get")
def test_weibo_login(self, mock_do_get, mock_do_post):
"""测试微博登录流程获取授权链接、Token及用户信息"""
# 获取微博OAuth管理器实例
weibo_app = self.get_app_by_type('weibo')
assert weibo_app # 确保实例存在
# 验证授权链接生成无需mock直接调用方法
url = weibo_app.get_authorization_url()
# 模拟获取Token的响应
mock_do_post.return_value = json.dumps({
"access_token": "access_token",
"uid": "uid"
})
# 模拟获取用户信息的响应
mock_do_get.return_value = json.dumps({
"avatar_large": "avatar_large",
"screen_name": "screen_name",
"id": "id",
"email": "email",
})
# 测试通过code获取用户信息
userinfo = weibo_app.get_access_token_by_code('code')
# 验证用户信息是否正确
self.assertEqual(userinfo.token, 'access_token')
self.assertEqual(userinfo.openid, 'id')
@patch("oauth.oauthmanager.GoogleOauthManager.do_post")
@patch("oauth.oauthmanager.GoogleOauthManager.do_get")
def test_google_login(self, mock_do_get, mock_do_post):
"""测试谷歌登录流程获取Token及用户信息"""
google_app = self.get_app_by_type('google')
assert google_app
# 验证授权链接生成
url = google_app.get_authorization_url()
# 模拟获取Token的响应
mock_do_post.return_value = json.dumps({
"access_token": "access_token",
"id_token": "id_token",
})
# 模拟获取用户信息的响应
mock_do_get.return_value = json.dumps({
"picture": "picture",
"name": "name",
"sub": "sub",
"email": "email",
})
# 测试获取Token和用户信息
token = google_app.get_access_token_by_code('code')
userinfo = google_app.get_oauth_userinfo()
# 验证用户信息
self.assertEqual(userinfo.token, 'access_token')
self.assertEqual(userinfo.openid, 'sub')
@patch("oauth.oauthmanager.GitHubOauthManager.do_post")
@patch("oauth.oauthmanager.GitHubOauthManager.do_get")
def test_github_login(self, mock_do_get, mock_do_post):
"""测试GitHub登录流程验证授权链接、Token及用户信息"""
github_app = self.get_app_by_type('github')
assert github_app
# 验证授权链接包含GitHub域名和client_id参数
url = github_app.get_authorization_url()
self.assertTrue("github.com" in url)
self.assertTrue("client_id" in url)
# 模拟GitHub返回的Tokenquery string格式
mock_do_post.return_value = "access_token=gho_16C7e42F292c6912E7710c838347Ae178B4a&scope=repo%2Cgist&token_type=bearer"
# 模拟用户信息响应
mock_do_get.return_value = json.dumps({
"avatar_url": "avatar_url",
"name": "name",
"id": "id",
"email": "email",
})
# 测试获取Token和用户信息
token = github_app.get_access_token_by_code('code')
userinfo = github_app.get_oauth_userinfo()
# 验证Token和openid
self.assertEqual(userinfo.token, 'gho_16C7e42F292c6912E7710c838347Ae178B4a')
self.assertEqual(userinfo.openid, 'id')
@patch("oauth.oauthmanager.FaceBookOauthManager.do_post")
@patch("oauth.oauthmanager.FaceBookOauthManager.do_get")
def test_facebook_login(self, mock_do_get, mock_do_post):
"""测试Facebook登录流程验证授权链接、Token及用户信息"""
facebook_app = self.get_app_by_type('facebook')
assert facebook_app
# 验证授权链接包含Facebook域名
url = facebook_app.get_authorization_url()
self.assertTrue("facebook.com" in url)
# 模拟获取Token的响应
mock_do_post.return_value = json.dumps({
"access_token": "access_token",
})
# 模拟用户信息响应(包含嵌套的头像结构)
mock_do_get.return_value = json.dumps({
"name": "name",
"id": "id",
"email": "email",
"picture": {
"data": {
"url": "url"
}
}
})
# 测试获取Token和用户信息
token = facebook_app.get_access_token_by_code('code')
userinfo = facebook_app.get_oauth_userinfo()
# 验证Token
self.assertEqual(userinfo.token, 'access_token')
@patch("oauth.oauthmanager.QQOauthManager.do_get", side_effect=[
# 模拟三次GET请求的响应1.获取Token 2.获取OpenID 3.获取用户信息
'access_token=access_token&expires_in=3600',
'callback({"client_id":"appid","openid":"openid"} );',
json.dumps({
"nickname": "nickname",
"email": "email",
"figureurl": "figureurl",
"openid": "openid",
})
])
def test_qq_login(self, mock_do_get):
"""测试QQ登录流程QQ需单独获取OpenID"""
qq_app = self.get_app_by_type('qq')
assert qq_app
# 验证授权链接包含QQ域名
url = qq_app.get_authorization_url()
self.assertTrue("qq.com" in url)
# 测试获取Token和用户信息
token = qq_app.get_access_token_by_code('code')
userinfo = qq_app.get_oauth_userinfo()
# 验证Token
self.assertEqual(userinfo.token, 'access_token')
@patch("oauth.oauthmanager.WBOauthManager.do_post")
@patch("oauth.oauthmanager.WBOauthManager.do_get")
def test_weibo_authoriz_login_with_email(self, mock_do_get, mock_do_post):
"""测试微博登录(用户提供邮箱):验证自动注册登录流程"""
# 模拟获取Token的响应
mock_do_post.return_value = json.dumps({
"access_token": "access_token",
"uid": "uid"
})
# 模拟包含邮箱的用户信息
mock_user_info = {
"avatar_large": "avatar_large",
"screen_name": "screen_name1",
"id": "id",
"email": "email",
}
mock_do_get.return_value = json.dumps(mock_user_info)
# 测试访问登录链接是否重定向到微博授权页
response = self.client.get('/oauth/oauthlogin?type=weibo')
self.assertEqual(response.status_code, 302)
self.assertTrue("api.weibo.com" in response.url)
# 测试授权回调后是否重定向到首页
response = self.client.get('/oauth/authorize?type=weibo&code=code')
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/')
# 验证用户已登录,信息正确
user = auth.get_user(self.client)
assert user.is_authenticated
self.assertTrue(user.is_authenticated)
self.assertEqual(user.username, mock_user_info['screen_name'])
self.assertEqual(user.email, mock_user_info['email'])
# 测试退出登录后再次登录是否正常
self.client.logout()
response = self.client.get('/oauth/authorize?type=weibo&code=code')
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/')
# 再次验证用户登录状态和信息
user = auth.get_user(self.client)
assert user.is_authenticated
self.assertTrue(user.is_authenticated)
self.assertEqual(user.username, mock_user_info['screen_name'])
self.assertEqual(user.email, mock_user_info['email'])
@patch("oauth.oauthmanager.WBOauthManager.do_post")
@patch("oauth.oauthmanager.WBOauthManager.do_get")
def test_weibo_authoriz_login_without_email(self, mock_do_get, mock_do_post):
"""测试微博登录(用户未提供邮箱):验证补充邮箱、绑定及确认流程"""
# 模拟获取Token的响应
mock_do_post.return_value = json.dumps({
"access_token": "access_token",
"uid": "uid"
})
# 模拟不包含邮箱的用户信息
mock_user_info = {
"avatar_large": "avatar_large",
"screen_name": "screen_name1",
"id": "id",
}
mock_do_get.return_value = json.dumps(mock_user_info)
# 测试访问登录链接
response = self.client.get('/oauth/oauthlogin?type=weibo')
self.assertEqual(response.status_code, 302)
self.assertTrue("api.weibo.com" in response.url)
# 测试授权后是否跳转到补充邮箱页面
response = self.client.get('/oauth/authorize?type=weibo&code=code')
self.assertEqual(response.status_code, 302)
# 提取补充邮箱页面的OAuth用户ID
oauth_user_id = int(response.url.split('/')[-1].split('.')[0])
self.assertEqual(response.url, f'/oauth/requireemail/{oauth_user_id}.html')
# 测试提交邮箱后是否跳转到绑定成功页
response = self.client.post(response.url, {'email': 'test@gmail.com', 'oauthid': oauth_user_id})
self.assertEqual(response.status_code, 302)
# 生成验证签名(模拟邮箱确认流程)
sign = get_sha256(settings.SECRET_KEY + str(oauth_user_id) + settings.SECRET_KEY)
url = reverse('oauth:bindsuccess', kwargs={'oauthid': oauth_user_id})
self.assertEqual(response.url, f'{url}?type=email')
# 测试邮箱确认后是否跳转到成功页,且用户登录状态正确
path = reverse('oauth:email_confirm', kwargs={'id': oauth_user_id, 'sign': sign})
response = self.client.get(path)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, f'/oauth/bindsuccess/{oauth_user_id}.html?type=success')
# 验证用户信息用户名、邮箱及OAuth用户关联正确
user = auth.get_user(self.client)
from oauth.models import OAuthUser
oauth_user = OAuthUser.objects.get(author=user)
self.assertTrue(user.is_authenticated)
self.assertEqual(user.username, mock_user_info['screen_name'])
self.assertEqual(user.email, 'test@gmail.com')
self.assertEqual(oauth_user.pk, oauth_user_id)