sync from local backup
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.core.cache import caches
|
||||
from unittest.mock import patch, MagicMock
|
||||
from rest_framework.test import APIClient
|
||||
from rest_framework import status
|
||||
|
||||
from user.models import FUser
|
||||
from utils.slider_captcha import generate_slider_captcha, verify_slider_captcha
|
||||
from utils.captcha import check_captcha_required, record_failure
|
||||
|
||||
default_cache = caches['default']
|
||||
|
||||
|
||||
class TestLoginWithSliderCaptcha(TestCase):
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
self.login_url = reverse('login_with_slider')
|
||||
self.user = FUser.objects.create_user(
|
||||
username='testuser',
|
||||
email='test@example.com',
|
||||
password='testpass123'
|
||||
)
|
||||
|
||||
def _post(self, data):
|
||||
return self.client.post(
|
||||
self.login_url,
|
||||
data,
|
||||
format='json'
|
||||
)
|
||||
|
||||
def test_login_without_captcha_required(self):
|
||||
response = self._post({
|
||||
'account': 'testuser',
|
||||
'password': 'testpass123'
|
||||
})
|
||||
self.assertNotEqual(response.status_code, 20022)
|
||||
|
||||
@patch('user.views.user.check_captcha_required')
|
||||
def test_login_with_captcha_required_no_captcha_provided(self, mock_check):
|
||||
mock_check.return_value = True
|
||||
|
||||
response = self._post({
|
||||
'account': 'testuser',
|
||||
'password': 'testpass123'
|
||||
})
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(response.data['code'], 20022)
|
||||
|
||||
@patch('user.views.user.check_captcha_required')
|
||||
@patch('user.views.user.verify_slider_captcha')
|
||||
def test_login_with_valid_captcha(self, mock_verify, mock_check):
|
||||
mock_check.return_value = True
|
||||
mock_verify.return_value = True
|
||||
|
||||
response = self._post({
|
||||
'account': 'testuser',
|
||||
'password': 'testpass123',
|
||||
'slider_captcha_key': 'test-key',
|
||||
'slider_captcha_x': 100
|
||||
})
|
||||
|
||||
mock_verify.assert_called_once_with('test-key', 100)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertIn('access', response.data['data'])
|
||||
self.assertIn('refresh', response.data['data'])
|
||||
|
||||
@patch('user.views.user.check_captcha_required')
|
||||
@patch('user.views.user.verify_slider_captcha')
|
||||
def test_login_with_invalid_captcha(self, mock_verify, mock_check):
|
||||
mock_check.return_value = True
|
||||
mock_verify.return_value = False
|
||||
|
||||
response = self._post({
|
||||
'account': 'testuser',
|
||||
'password': 'testpass123',
|
||||
'slider_captcha_key': 'test-key',
|
||||
'slider_captcha_x': 50
|
||||
})
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(response.data['code'], 20020)
|
||||
|
||||
@patch('user.views.user.check_captcha_required')
|
||||
@patch('user.views.user.verify_slider_captcha')
|
||||
def test_login_with_expired_captcha(self, mock_verify, mock_check):
|
||||
mock_check.return_value = True
|
||||
from utils.slider_captcha import SliderCaptchaError
|
||||
mock_verify.side_effect = SliderCaptchaError('验证码已过期')
|
||||
|
||||
response = self._post({
|
||||
'account': 'testuser',
|
||||
'password': 'testpass123',
|
||||
'slider_captcha_key': 'expired-key',
|
||||
'slider_captcha_x': 100
|
||||
})
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(response.data['code'], 20021)
|
||||
|
||||
def test_get_identifier_with_ip(self):
|
||||
from user.views.user import LoginView
|
||||
view = LoginView()
|
||||
|
||||
request = MagicMock()
|
||||
request.META = {'HTTP_X_FORWARDED_FOR': '192.168.1.1'}
|
||||
|
||||
identifier = view._get_identifier(request)
|
||||
self.assertEqual(identifier, '192.168.1.1')
|
||||
|
||||
def test_get_identifier_without_forwarded_for(self):
|
||||
from user.views.user import LoginView
|
||||
view = LoginView()
|
||||
|
||||
request = MagicMock()
|
||||
request.META = {'REMOTE_ADDR': '127.0.0.1'}
|
||||
|
||||
identifier = view._get_identifier(request)
|
||||
self.assertEqual(identifier, '127.0.0.1')
|
||||
@@ -0,0 +1,101 @@
|
||||
import json
|
||||
from django.test import TestCase
|
||||
from django.core.cache import caches
|
||||
from utils.slider_captcha import (
|
||||
generate_slider_captcha,
|
||||
verify_slider_captcha,
|
||||
SliderCaptchaError
|
||||
)
|
||||
|
||||
default_cache = caches['default']
|
||||
|
||||
|
||||
class TestGenerateSliderCaptcha(TestCase):
|
||||
def test_generate_returns_required_keys(self):
|
||||
result = generate_slider_captcha()
|
||||
self.assertIn('captcha_key', result)
|
||||
self.assertIn('bg_image', result)
|
||||
self.assertIn('slider_image', result)
|
||||
self.assertIn('y_position', result)
|
||||
|
||||
def test_generate_returns_valid_captcha_key(self):
|
||||
result = generate_slider_captcha()
|
||||
captcha_key = result['captcha_key']
|
||||
self.assertIsInstance(captcha_key, str)
|
||||
self.assertEqual(len(captcha_key), 36)
|
||||
|
||||
def test_generate_returns_base64_images(self):
|
||||
result = generate_slider_captcha()
|
||||
self.assertTrue(result['bg_image'].startswith('data:image/png;base64,'))
|
||||
self.assertTrue(result['slider_image'].startswith('data:image/png;base64,'))
|
||||
|
||||
def test_generate_stores_position_in_cache(self):
|
||||
result = generate_slider_captcha()
|
||||
captcha_key = result['captcha_key']
|
||||
cached_data = default_cache.get(f'slider_captcha_{captcha_key}')
|
||||
self.assertIsNotNone(cached_data)
|
||||
self.assertIn('x_position', cached_data)
|
||||
self.assertIn('y_position', cached_data)
|
||||
self.assertIn('timestamp', cached_data)
|
||||
|
||||
|
||||
class TestVerifySliderCaptcha(TestCase):
|
||||
def setUp(self):
|
||||
self.captcha_data = generate_slider_captcha()
|
||||
self.captcha_key = self.captcha_data['captcha_key']
|
||||
cached = default_cache.get(f'slider_captcha_{self.captcha_key}')
|
||||
self.correct_x = cached['x_position']
|
||||
|
||||
def test_verify_correct_position(self):
|
||||
result = verify_slider_captcha(self.captcha_key, self.correct_x)
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_verify_wrong_position(self):
|
||||
result = verify_slider_captcha(self.captcha_key, self.correct_x + 50)
|
||||
self.assertFalse(result)
|
||||
|
||||
def test_verify_expired_captcha(self):
|
||||
default_cache.delete(f'slider_captcha_{self.captcha_key}')
|
||||
with self.assertRaises(SliderCaptchaError):
|
||||
verify_slider_captcha(self.captcha_key, self.correct_x)
|
||||
|
||||
def test_verify_tolerance(self):
|
||||
result = verify_slider_captcha(self.captcha_key, self.correct_x + 3)
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_verify_clears_cache_after_success(self):
|
||||
verify_slider_captcha(self.captcha_key, self.correct_x)
|
||||
cached = default_cache.get(f'slider_captcha_{self.captcha_key}')
|
||||
self.assertIsNone(cached)
|
||||
|
||||
|
||||
class TestVerifySliderTrajectory(TestCase):
|
||||
def setUp(self):
|
||||
self.captcha_data = generate_slider_captcha()
|
||||
self.captcha_key = self.captcha_data['captcha_key']
|
||||
cached = default_cache.get(f'slider_captcha_{self.captcha_key}')
|
||||
self.correct_x = cached['x_position']
|
||||
|
||||
def test_verify_with_valid_trajectory(self):
|
||||
from utils.slider_captcha import verify_slider_trajectory
|
||||
|
||||
trajectory = [
|
||||
{'x': 0, 'y': 150, 't': 0},
|
||||
{'x': 50, 'y': 152, 't': 100},
|
||||
{'x': 100, 'y': 148, 't': 200},
|
||||
{'x': 150, 'y': 151, 't': 300},
|
||||
{'x': self.correct_x, 'y': 150, 't': 400},
|
||||
]
|
||||
result = verify_slider_trajectory(self.captcha_key, self.correct_x, trajectory)
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_verify_with_suspicious_trajectory(self):
|
||||
from utils.slider_captcha import verify_slider_trajectory
|
||||
|
||||
trajectory = [
|
||||
{'x': 0, 'y': 150, 't': 0},
|
||||
{'x': 100, 'y': 150, 't': 100},
|
||||
{'x': 200, 'y': 150, 't': 200},
|
||||
]
|
||||
result = verify_slider_trajectory(self.captcha_key, self.correct_x, trajectory)
|
||||
self.assertFalse(result)
|
||||
@@ -0,0 +1,109 @@
|
||||
import json
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from rest_framework.test import APIClient
|
||||
from rest_framework import status
|
||||
from django.core.cache import caches
|
||||
|
||||
default_cache = caches['default']
|
||||
|
||||
|
||||
class TestSliderCaptchaAPIView(TestCase):
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
self.generate_url = reverse('slider-captcha-generate')
|
||||
self.verify_url = reverse('slider-captcha-verify')
|
||||
|
||||
def test_generate_captcha(self):
|
||||
response = self.client.get(self.generate_url)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
data = response.data['data']
|
||||
self.assertIn('captcha_key', data)
|
||||
self.assertIn('bg_image', data)
|
||||
self.assertIn('slider_image', data)
|
||||
self.assertIn('y_position', data)
|
||||
|
||||
def test_verify_captcha_success(self):
|
||||
gen_response = self.client.get(self.generate_url)
|
||||
captcha_key = gen_response.data['data']['captcha_key']
|
||||
|
||||
cached = default_cache.get(f'slider_captcha_{captcha_key}')
|
||||
correct_x = cached['x_position']
|
||||
|
||||
verify_data = {
|
||||
'captcha_key': captcha_key,
|
||||
'x_position': correct_x
|
||||
}
|
||||
response = self.client.post(
|
||||
self.verify_url,
|
||||
data=json.dumps(verify_data),
|
||||
content_type='application/json'
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['data']['verified'])
|
||||
|
||||
def test_verify_captcha_failure(self):
|
||||
gen_response = self.client.get(self.generate_url)
|
||||
captcha_key = gen_response.data['data']['captcha_key']
|
||||
|
||||
verify_data = {
|
||||
'captcha_key': captcha_key,
|
||||
'x_position': 999
|
||||
}
|
||||
response = self.client.post(
|
||||
self.verify_url,
|
||||
data=json.dumps(verify_data),
|
||||
content_type='application/json'
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertFalse(response.data['data']['verified'])
|
||||
|
||||
def test_verify_expired_captcha(self):
|
||||
verify_data = {
|
||||
'captcha_key': 'non-existent-key',
|
||||
'x_position': 100
|
||||
}
|
||||
response = self.client.post(
|
||||
self.verify_url,
|
||||
data=json.dumps(verify_data),
|
||||
content_type='application/json'
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_verify_with_trajectory(self):
|
||||
gen_response = self.client.get(self.generate_url)
|
||||
captcha_key = gen_response.data['data']['captcha_key']
|
||||
|
||||
cached = default_cache.get(f'slider_captcha_{captcha_key}')
|
||||
correct_x = cached['x_position']
|
||||
|
||||
trajectory = [
|
||||
{'x': 0, 'y': 150, 't': 0},
|
||||
{'x': 50, 'y': 152, 't': 100},
|
||||
{'x': 100, 'y': 148, 't': 200},
|
||||
{'x': 150, 'y': 151, 't': 300},
|
||||
{'x': 200, 'y': 149, 't': 400},
|
||||
{'x': correct_x, 'y': 150, 't': 500},
|
||||
]
|
||||
|
||||
verify_data = {
|
||||
'captcha_key': captcha_key,
|
||||
'x_position': correct_x,
|
||||
'trajectory': trajectory
|
||||
}
|
||||
response = self.client.post(
|
||||
self.verify_url,
|
||||
data=json.dumps(verify_data),
|
||||
content_type='application/json'
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['data']['verified'])
|
||||
|
||||
def test_verify_missing_fields(self):
|
||||
response = self.client.post(
|
||||
self.verify_url,
|
||||
data=json.dumps({}),
|
||||
content_type='application/json'
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
Reference in New Issue
Block a user