"""lrj: OAuth 认证测试模块 包含OAuth配置、登录流程和各平台OAuth功能的单元测试 """ import json from unittest.mock import patch from django.conf import settings from django.contrib import auth from django.test import Client, RequestFactory, TestCase from django.urls import reverse from djangoblog.utils import get_sha256 from oauth.models import OAuthConfig from oauth.oauthmanager import BaseOauthManager # Create your tests here. class OAuthConfigTest(TestCase): """lrj: OAuth配置模型测试类 测试OAuth配置相关的功能 """ def setUp(self): """lrj:测试初始化设置""" self.client = Client() # lrj:Django测试客户端 self.factory = RequestFactory() # lrj:请求工厂 def test_oauth_login_test(self): """lrj:测试OAuth登录流程""" # lrj:创建微博OAuth配置 c = OAuthConfig() c.type = 'weibo' c.appkey = 'appkey' c.appsecret = 'appsecret' c.save() # lrj:测试OAuth登录重定向 response = self.client.get('/oauth/oauthlogin?type=weibo') self.assertEqual(response.status_code, 302) # lrj:验证重定向状态码 self.assertTrue("api.weibo.com" in response.url) # lrj:验证重定向到微博授权页面 # lrj:测试授权回调处理 response = self.client.get('/oauth/authorize?type=weibo&code=code') self.assertEqual(response.status_code, 302) # lrj:验证重定向状态码 self.assertEqual(response.url, '/') # lrj:验证重定向到首页 class OauthLoginTest(TestCase): """lrj: OAuth登录流程测试类 测试各平台OAuth登录的完整流程 """ def setUp(self) -> None: """lrj:测试初始化设置""" self.client = Client() # lrj:Django测试客户端 self.factory = RequestFactory() # lrj:请求工厂 self.apps = self.init_apps() # lrj:初始化所有OAuth应用配置 def init_apps(self): """lrj:初始化所有OAuth平台配置""" # lrj:获取所有OAuth管理器子类并实例化 applications = [p() for p in BaseOauthManager.__subclasses__()] for application in applications: # lrj:为每个平台创建配置 c = OAuthConfig() c.type = application.ICON_NAME.lower() # lrj:平台类型 c.appkey = 'appkey' # lrj:测试用AppKey c.appsecret = 'appsecret' # lrj:测试用AppSecret c.save() return applications def get_app_by_type(self, type): """lrj:根据平台类型获取对应的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): """lrj:测试微博OAuth登录流程""" # lrj:获取微博OAuth应用 weibo_app = self.get_app_by_type('weibo') assert weibo_app #lrj: 验证应用存在 # lrj:测试授权URL生成 url = weibo_app.get_authorization_url() # lrj:模拟微博API响应 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", }) # lrj:测试获取访问令牌和用户信息 userinfo = weibo_app.get_access_token_by_code('code') self.assertEqual(userinfo.token, 'access_token') # lrj:验证访问令牌 self.assertEqual(userinfo.openid, 'id') #lrj: 验证用户OpenID @patch("oauth.oauthmanager.GoogleOauthManager.do_post") @patch("oauth.oauthmanager.GoogleOauthManager.do_get") def test_google_login(self, mock_do_get, mock_do_post): """lrj:测试Google OAuth登录流程""" # lrj:获取Google OAuth应用 google_app = self.get_app_by_type('google') assert google_app # lrj:验证应用存在 # lrj:测试授权URL生成 url = google_app.get_authorization_url() # lrj:模拟Google API响应 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 = google_app.get_access_token_by_code('code') userinfo = google_app.get_oauth_userinfo() self.assertEqual(userinfo.token, 'access_token') # lrj:验证访问令牌 self.assertEqual(userinfo.openid, 'sub') # lrj:验证用户OpenID @patch("oauth.oauthmanager.GitHubOauthManager.do_post") @patch("oauth.oauthmanager.GitHubOauthManager.do_get") def test_github_login(self, mock_do_get, mock_do_post): """测试GitHub OAuth登录流程""" # lrj:获取GitHub OAuth应用 github_app = self.get_app_by_type('github') assert github_app # lrj:验证应用存在 # lrj:测试授权URL生成 url = github_app.get_authorization_url() self.assertTrue("github.com" in url) # lrj:验证GitHub域名 self.assertTrue("client_id" in url) #lrj: 验证包含client_id参数 # lrj:模拟GitHub API响应 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 = github_app.get_access_token_by_code('code') userinfo = github_app.get_oauth_userinfo() self.assertEqual(userinfo.token, 'gho_16C7e42F292c6912E7710c838347Ae178B4a') # lrj:验证访问令牌 self.assertEqual(userinfo.openid, 'id') # lrj:验证用户OpenID @patch("oauth.oauthmanager.FaceBookOauthManager.do_post") @patch("oauth.oauthmanager.FaceBookOauthManager.do_get") def test_facebook_login(self, mock_do_get, mock_do_post): """lrj:测试Facebook OAuth登录流程""" # lrj:获取Facebook OAuth应用 facebook_app = self.get_app_by_type('facebook') assert facebook_app # lrj:验证应用存在 # lrj:测试授权URL生成 url = facebook_app.get_authorization_url() self.assertTrue("facebook.com" in url) # lrj:验证Facebook域名 # lrj:模拟Facebook API响应 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 = facebook_app.get_access_token_by_code('code') userinfo = facebook_app.get_oauth_userinfo() self.assertEqual(userinfo.token, 'access_token') # lrj:验证访问令牌 @patch("oauth.oauthmanager.QQOauthManager.do_get", side_effect=[ 'access_token=access_token&expires_in=3600', # lrj:获取token响应 'callback({"client_id":"appid","openid":"openid"} );', # lrj:获取openid响应 json.dumps({ "nickname": "nickname", "email": "email", "figureurl": "figureurl", "openid": "openid", }) # lrj:获取用户信息响应 ]) def test_qq_login(self, mock_do_get): """lrj:测试QQ OAuth登录流程""" # lrj:获取QQ OAuth应用 qq_app = self.get_app_by_type('qq') assert qq_app # lrj:验证应用存在 # lrj:测试授权URL生成 url = qq_app.get_authorization_url() self.assertTrue("qq.com" in url) # lrj:验证QQ域名 # lrj:测试获取访问令牌和用户信息(使用side_effect模拟多次调用) token = qq_app.get_access_token_by_code('code') userinfo = qq_app.get_oauth_userinfo() self.assertEqual(userinfo.token, 'access_token') # lrj:验证访问令牌 @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): """lrj:测试带邮箱的微博授权登录完整流程""" # lrj:模拟微博API响应 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) # lrj:测试OAuth登录重定向 response = self.client.get('/oauth/oauthlogin?type=weibo') self.assertEqual(response.status_code, 302) # lrj:验证重定向状态码 self.assertTrue("api.weibo.com" in response.url) # lrj:验证重定向到微博 #lrj: 测试授权回调处理 response = self.client.get('/oauth/authorize?type=weibo&code=code') self.assertEqual(response.status_code, 302) #lrj: 验证重定向状态码 self.assertEqual(response.url, '/') # lrj:验证重定向到首页 # lrj:验证用户已登录 user = auth.get_user(self.client) assert user.is_authenticated # lrj:验证用户已认证 self.assertTrue(user.is_authenticated) self.assertEqual(user.username, mock_user_info['screen_name']) # lrj:验证用户名 self.assertEqual(user.email, mock_user_info['email']) # lrj:验证邮箱 # lrj:登出用户 self.client.logout() # lrj:再次测试登录(测试重复登录情况) response = self.client.get('/oauth/authorize?type=weibo&code=code') self.assertEqual(response.status_code, 302) # lrj:验证重定向状态码 self.assertEqual(response.url, '/') # lrj:证重定向到首页 # lrj:再次验证用户已登录 user = auth.get_user(self.client) assert user.is_authenticated # lrj:验证用户已认证 self.assertTrue(user.is_authenticated) self.assertEqual(user.username, mock_user_info['screen_name']) # lrj:验证用户名 self.assertEqual(user.email, mock_user_info['email']) # lrj:验证邮箱 @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): """lrj:测试不带邮箱的微博授权登录完整流程(需要补充邮箱)""" # lrj:模拟微博API响应(不含邮箱) 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) # lrj:测试OAuth登录重定向 response = self.client.get('/oauth/oauthlogin?type=weibo') self.assertEqual(response.status_code, 302) # lrj:验证重定向状态码 self.assertTrue("api.weibo.com" in response.url) # lrj:验证重定向到微博 # lrj:测试授权回调处理(应该重定向到邮箱补充页面) response = self.client.get('/oauth/authorize?type=weibo&code=code') self.assertEqual(response.status_code, 302) # lrj:验证重定向状态码 # lrj:解析OAuth用户ID oauth_user_id = int(response.url.split('/')[-1].split('.')[0]) self.assertEqual(response.url, f'/oauth/requireemail/{oauth_user_id}.html') #lrj: 验证重定向到邮箱补充页面 # lrj:测试邮箱补充表单提交 response = self.client.post(response.url, {'email': 'test@gmail.com', 'oauthid': oauth_user_id}) self.assertEqual(response.status_code, 302) # lrj:验证重定向状态码 #lrj: 生成安全签名 sign = get_sha256(settings.SECRET_KEY + str(oauth_user_id) + settings.SECRET_KEY) # lrj:验证重定向到绑定成功页面 url = reverse('oauth:bindsuccess', kwargs={ 'oauthid': oauth_user_id, }) self.assertEqual(response.url, f'{url}?type=email') # lrj:测试邮箱确认链接 path = reverse('oauth:email_confirm', kwargs={ 'id': oauth_user_id, 'sign': sign }) response = self.client.get(path) self.assertEqual(response.status_code, 302) #lrj: 验证重定向状态码 self.assertEqual(response.url, f'/oauth/bindsuccess/{oauth_user_id}.html?type=success') #lrj: 验证重定向到绑定成功页面 # lrj:验证用户已登录 user = auth.get_user(self.client) from oauth.models import OAuthUser oauth_user = OAuthUser.objects.get(author=user) self.assertTrue(user.is_authenticated) # lrj:验证用户已认证 self.assertEqual(user.username, mock_user_info['screen_name']) # lrj:验证用户名 self.assertEqual(user.email, 'test@gmail.com') #lrj: 验证补充的邮箱 self.assertEqual(oauth_user.pk, oauth_user_id) # lrj:验证OAuth用户ID匹配