20251222
This commit is contained in:
160
tests/backend/conftest.py
Normal file
160
tests/backend/conftest.py
Normal file
@@ -0,0 +1,160 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Pytest 配置文件
|
||||
提供测试fixtures和公共设置
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import pymysql
|
||||
import os
|
||||
from typing import Generator
|
||||
|
||||
# 测试数据库配置
|
||||
TEST_DB_CONFIG = {
|
||||
'host': os.getenv('TEST_DB_HOST', 'localhost'),
|
||||
'user': os.getenv('TEST_DB_USER', 'root'),
|
||||
'password': os.getenv('TEST_DB_PASSWORD', ''),
|
||||
'database': os.getenv('TEST_DB_NAME', 'designercep_test'),
|
||||
'charset': 'utf8mb4'
|
||||
}
|
||||
|
||||
# API配置
|
||||
API_BASE_URL = os.getenv('TEST_API_URL', 'https://backend.aidg168.uk/api/v1')
|
||||
ADMIN_TOKEN = 'admin-secret-token'
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def db_connection():
|
||||
"""数据库连接fixture(会话级别)"""
|
||||
conn = pymysql.connect(**TEST_DB_CONFIG)
|
||||
yield conn
|
||||
conn.close()
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def db_cursor(db_connection):
|
||||
"""数据库游标fixture(函数级别,自动回滚)"""
|
||||
cursor = db_connection.cursor(pymysql.cursors.DictCursor)
|
||||
yield cursor
|
||||
db_connection.rollback() # 测试后回滚
|
||||
cursor.close()
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def test_user(db_cursor):
|
||||
"""创建测试用户"""
|
||||
username = 'test_user_pytest'
|
||||
password = 'test123456'
|
||||
email = 'test@example.com'
|
||||
|
||||
# 清理可能存在的旧数据
|
||||
db_cursor.execute("DELETE FROM users WHERE username = %s", (username,))
|
||||
db_cursor.connection.commit()
|
||||
|
||||
# 创建测试用户
|
||||
db_cursor.execute("""
|
||||
INSERT INTO users (username, password, email, points, vip_type,
|
||||
consecutive_check_in, total_check_in_days)
|
||||
VALUES (%s, %s, %s, 100, 'none', 0, 0)
|
||||
""", (username, password, email))
|
||||
db_cursor.connection.commit()
|
||||
|
||||
# 获取用户信息
|
||||
db_cursor.execute("SELECT * FROM users WHERE username = %s", (username,))
|
||||
user = db_cursor.fetchone()
|
||||
|
||||
yield user
|
||||
|
||||
# 清理
|
||||
db_cursor.execute("DELETE FROM users WHERE username = %s", (username,))
|
||||
db_cursor.connection.commit()
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def vip_user(db_cursor):
|
||||
"""创建VIP测试用户"""
|
||||
username = 'test_vip_user'
|
||||
|
||||
# 清理
|
||||
db_cursor.execute("DELETE FROM users WHERE username = %s", (username,))
|
||||
db_cursor.connection.commit()
|
||||
|
||||
# 创建VIP用户
|
||||
db_cursor.execute("""
|
||||
INSERT INTO users (username, password, email, points, vip_type,
|
||||
vip_daily_quota, vip_quota_reset_date)
|
||||
VALUES (%s, 'test123', 'vip@test.com', 200, 'vip', 20, CURDATE())
|
||||
""", (username,))
|
||||
db_cursor.connection.commit()
|
||||
|
||||
db_cursor.execute("SELECT * FROM users WHERE username = %s", (username,))
|
||||
user = db_cursor.fetchone()
|
||||
|
||||
yield user
|
||||
|
||||
# 清理
|
||||
db_cursor.execute("DELETE FROM users WHERE username = %s", (username,))
|
||||
db_cursor.connection.commit()
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def test_feature(db_cursor):
|
||||
"""创建测试功能配置"""
|
||||
feature_key = 'test_feature_pytest'
|
||||
|
||||
# 清理
|
||||
db_cursor.execute("DELETE FROM features_config WHERE feature_key = %s", (feature_key,))
|
||||
db_cursor.connection.commit()
|
||||
|
||||
# 创建测试功能
|
||||
db_cursor.execute("""
|
||||
INSERT INTO features_config
|
||||
(feature_key, feature_name, category, points_cost, vip_points_cost,
|
||||
svip_points_cost, enabled)
|
||||
VALUES (%s, '测试功能', 'test', 50, 0, 0, 1)
|
||||
""", (feature_key,))
|
||||
db_cursor.connection.commit()
|
||||
|
||||
db_cursor.execute("SELECT * FROM features_config WHERE feature_key = %s", (feature_key,))
|
||||
feature = db_cursor.fetchone()
|
||||
|
||||
yield feature
|
||||
|
||||
# 清理
|
||||
db_cursor.execute("DELETE FROM features_config WHERE feature_key = %s", (feature_key,))
|
||||
db_cursor.connection.commit()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def admin_headers():
|
||||
"""管理员请求头"""
|
||||
return {'x-admin-token': ADMIN_TOKEN}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def user_headers(test_user):
|
||||
"""用户请求头(需要实际token生成逻辑)"""
|
||||
# 这里简化处理,实际应该调用登录接口获取token
|
||||
return {'Authorization': 'Bearer test_token'}
|
||||
|
||||
|
||||
# 测试数据清理
|
||||
def pytest_sessionfinish(session, exitstatus):
|
||||
"""测试会话结束后清理"""
|
||||
try:
|
||||
conn = pymysql.connect(**TEST_DB_CONFIG)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 清理测试数据
|
||||
cursor.execute("DELETE FROM users WHERE username LIKE 'test_%'")
|
||||
cursor.execute("DELETE FROM features_config WHERE feature_key LIKE 'test_%'")
|
||||
cursor.execute("DELETE FROM check_in_records WHERE username LIKE 'test_%'")
|
||||
cursor.execute("DELETE FROM points_history WHERE username LIKE 'test_%'")
|
||||
cursor.execute("DELETE FROM feature_usage_logs WHERE username LIKE 'test_%'")
|
||||
|
||||
conn.commit()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"清理测试数据失败: {e}")
|
||||
|
||||
6
tests/backend/requirements.txt
Normal file
6
tests/backend/requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
pytest==7.4.3
|
||||
requests==2.31.0
|
||||
pymysql==1.1.0
|
||||
pytest-cov==4.1.0
|
||||
pytest-html==4.1.1
|
||||
|
||||
38
tests/backend/run_tests.bat
Normal file
38
tests/backend/run_tests.bat
Normal file
@@ -0,0 +1,38 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
echo ========================================
|
||||
echo DesignerCEP 后端API测试套件
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
echo [1/3] 检查Python环境...
|
||||
python --version
|
||||
if %errorlevel% neq 0 (
|
||||
echo ❌ Python未安装!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo [2/3] 安装测试依赖...
|
||||
pip install -r requirements.txt
|
||||
if %errorlevel% neq 0 (
|
||||
echo ❌ 依赖安装失败!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo [3/3] 运行测试...
|
||||
echo ========================================
|
||||
pytest -v --cov=../../Server/app/api/v1 --cov-report=html --html=report.html --self-contained-html
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo 测试完成!
|
||||
echo 查看报告:
|
||||
echo - HTML报告: report.html
|
||||
echo - 覆盖率报告: htmlcov/index.html
|
||||
echo ========================================
|
||||
pause
|
||||
|
||||
295
tests/backend/test_admin_config.py
Normal file
295
tests/backend/test_admin_config.py
Normal file
@@ -0,0 +1,295 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
管理员配置API测试
|
||||
测试功能配置、VIP配置、签到配置的CRUD操作
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from conftest import API_BASE_URL, ADMIN_TOKEN
|
||||
|
||||
|
||||
class TestFeaturesConfig:
|
||||
"""功能配置测试"""
|
||||
|
||||
def test_get_features_config(self, admin_headers):
|
||||
"""测试获取功能配置列表"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/admin/config/features",
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert isinstance(data, list)
|
||||
assert len(data) > 0
|
||||
|
||||
# 验证数据结构
|
||||
feature = data[0]
|
||||
assert 'feature_key' in feature
|
||||
assert 'feature_name' in feature
|
||||
assert 'points_cost' in feature
|
||||
assert 'enabled' in feature
|
||||
|
||||
def test_create_feature_config(self, admin_headers):
|
||||
"""测试创建功能配置"""
|
||||
feature_data = {
|
||||
'feature_key': 'test_feature_api',
|
||||
'feature_name': 'API测试功能',
|
||||
'category': 'test',
|
||||
'points_cost': 60,
|
||||
'vip_points_cost': 0,
|
||||
'svip_points_cost': 0,
|
||||
'description': '这是一个API测试功能'
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/admin/config/features",
|
||||
json=feature_data,
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
assert result['message'] == '创建成功'
|
||||
|
||||
# 清理
|
||||
requests.delete(
|
||||
f"{API_BASE_URL}/admin/config/features/test_feature_api",
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
def test_update_feature_config(self, admin_headers, test_feature):
|
||||
"""测试更新功能配置"""
|
||||
feature_key = test_feature['feature_key']
|
||||
|
||||
update_data = {
|
||||
'points_cost': 80,
|
||||
'enabled': True
|
||||
}
|
||||
|
||||
response = requests.put(
|
||||
f"{API_BASE_URL}/admin/config/features/{feature_key}",
|
||||
json=update_data,
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
def test_update_nonexistent_feature(self, admin_headers):
|
||||
"""测试更新不存在的功能"""
|
||||
response = requests.put(
|
||||
f"{API_BASE_URL}/admin/config/features/nonexistent_feature",
|
||||
json={'points_cost': 100},
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_delete_feature_config(self, admin_headers):
|
||||
"""测试删除功能配置"""
|
||||
# 先创建
|
||||
feature_key = 'test_delete_feature'
|
||||
requests.post(
|
||||
f"{API_BASE_URL}/admin/config/features",
|
||||
json={
|
||||
'feature_key': feature_key,
|
||||
'feature_name': '待删除功能',
|
||||
'category': 'test',
|
||||
'points_cost': 50,
|
||||
'vip_points_cost': 0,
|
||||
'svip_points_cost': 0
|
||||
},
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
# 删除
|
||||
response = requests.delete(
|
||||
f"{API_BASE_URL}/admin/config/features/{feature_key}",
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
|
||||
class TestVIPConfig:
|
||||
"""VIP配置测试"""
|
||||
|
||||
def test_get_vip_config(self, admin_headers):
|
||||
"""测试获取VIP配置"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/admin/config/vip",
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert isinstance(data, list)
|
||||
assert len(data) == 2 # VIP和SVIP
|
||||
|
||||
# 验证数据结构
|
||||
vip = next((v for v in data if v['vip_type'] == 'vip'), None)
|
||||
assert vip is not None
|
||||
assert 'price' in vip
|
||||
assert 'daily_quota' in vip
|
||||
assert 'points_multiplier' in vip
|
||||
|
||||
def test_update_vip_config(self, admin_headers):
|
||||
"""测试更新VIP配置"""
|
||||
update_data = {
|
||||
'price': 35.00,
|
||||
'daily_quota': 25
|
||||
}
|
||||
|
||||
response = requests.put(
|
||||
f"{API_BASE_URL}/admin/config/vip/vip",
|
||||
json=update_data,
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
# 恢复原值
|
||||
requests.put(
|
||||
f"{API_BASE_URL}/admin/config/vip/vip",
|
||||
json={'price': 30.00, 'daily_quota': 20},
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
def test_update_invalid_vip_type(self, admin_headers):
|
||||
"""测试更新无效的VIP类型"""
|
||||
response = requests.put(
|
||||
f"{API_BASE_URL}/admin/config/vip/invalid",
|
||||
json={'price': 100},
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
class TestCheckInConfig:
|
||||
"""签到配置测试"""
|
||||
|
||||
def test_get_checkin_config(self, admin_headers):
|
||||
"""测试获取签到配置"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/admin/config/checkin",
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert isinstance(data, list)
|
||||
assert len(data) > 0
|
||||
|
||||
# 验证数据结构
|
||||
config = data[0]
|
||||
assert 'consecutive_days' in config
|
||||
assert 'base_points' in config
|
||||
assert 'bonus_points' in config
|
||||
assert 'total_points' in config
|
||||
|
||||
def test_create_checkin_config(self, admin_headers):
|
||||
"""测试创建签到档位"""
|
||||
config_data = {
|
||||
'consecutive_days': 99,
|
||||
'base_points': 10,
|
||||
'bonus_points': 200,
|
||||
'total_points': 210
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/admin/config/checkin",
|
||||
json=config_data,
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
# 清理
|
||||
requests.delete(
|
||||
f"{API_BASE_URL}/admin/config/checkin/99",
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
def test_update_checkin_config(self, admin_headers):
|
||||
"""测试更新签到档位"""
|
||||
# 假设第7天的配置存在
|
||||
update_data = {
|
||||
'bonus_points': 25,
|
||||
'total_points': 35
|
||||
}
|
||||
|
||||
response = requests.put(
|
||||
f"{API_BASE_URL}/admin/config/checkin/7",
|
||||
json=update_data,
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
# 可能成功也可能404(取决于是否存在)
|
||||
assert response.status_code in [200, 404]
|
||||
|
||||
if response.status_code == 200:
|
||||
# 恢复原值
|
||||
requests.put(
|
||||
f"{API_BASE_URL}/admin/config/checkin/7",
|
||||
json={'bonus_points': 20, 'total_points': 30},
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
def test_delete_checkin_config(self, admin_headers):
|
||||
"""测试删除签到档位"""
|
||||
# 先创建
|
||||
requests.post(
|
||||
f"{API_BASE_URL}/admin/config/checkin",
|
||||
json={
|
||||
'consecutive_days': 88,
|
||||
'base_points': 10,
|
||||
'bonus_points': 150,
|
||||
'total_points': 160
|
||||
},
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
# 删除
|
||||
response = requests.delete(
|
||||
f"{API_BASE_URL}/admin/config/checkin/88",
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code in [200, 404]
|
||||
|
||||
|
||||
class TestAdminAuth:
|
||||
"""管理员权限测试"""
|
||||
|
||||
def test_unauthorized_access(self):
|
||||
"""测试未授权访问"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/admin/config/features",
|
||||
headers={'x-admin-token': 'invalid_token'}
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
|
||||
def test_missing_token(self):
|
||||
"""测试缺少token"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/admin/config/features"
|
||||
)
|
||||
|
||||
assert response.status_code in [401, 422] # FastAPI可能返回422
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__, '-v'])
|
||||
|
||||
262
tests/backend/test_checkin.py
Normal file
262
tests/backend/test_checkin.py
Normal file
@@ -0,0 +1,262 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
签到API测试
|
||||
测试签到功能、连续天数计算、VIP倍数
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from datetime import date
|
||||
from conftest import API_BASE_URL
|
||||
|
||||
|
||||
class TestDailyCheckIn:
|
||||
"""每日签到测试"""
|
||||
|
||||
def test_first_checkin(self, test_user):
|
||||
"""测试首次签到"""
|
||||
request_data = {'username': test_user['username']}
|
||||
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/checkin/daily",
|
||||
json=request_data
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
data = result['data']
|
||||
assert data['success'] is True
|
||||
assert data['consecutive_days'] == 1
|
||||
assert data['points_earned'] > 0
|
||||
assert '签到成功' in data['message']
|
||||
|
||||
def test_duplicate_checkin(self, test_user, db_cursor):
|
||||
"""测试重复签到"""
|
||||
# 先签到一次
|
||||
requests.post(
|
||||
f"{API_BASE_URL}/checkin/daily",
|
||||
json={'username': test_user['username']}
|
||||
)
|
||||
|
||||
# 再次签到
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/checkin/daily",
|
||||
json={'username': test_user['username']}
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert '已签到' in response.json()['detail']
|
||||
|
||||
def test_consecutive_checkin_calculation(self, test_user, db_cursor):
|
||||
"""测试连续天数计算"""
|
||||
from datetime import timedelta
|
||||
|
||||
# 设置昨天签到
|
||||
yesterday = date.today() - timedelta(days=1)
|
||||
db_cursor.execute("""
|
||||
UPDATE users
|
||||
SET last_check_in_date = %s, consecutive_check_in = 5
|
||||
WHERE username = %s
|
||||
""", (yesterday, test_user['username']))
|
||||
db_cursor.connection.commit()
|
||||
|
||||
# 今天签到
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/checkin/daily",
|
||||
json={'username': test_user['username']}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()['data']
|
||||
|
||||
# 连续天数应该+1
|
||||
assert data['consecutive_days'] == 6
|
||||
|
||||
def test_broken_consecutive_checkin(self, test_user, db_cursor):
|
||||
"""测试中断连续签到"""
|
||||
from datetime import timedelta
|
||||
|
||||
# 设置3天前签到
|
||||
three_days_ago = date.today() - timedelta(days=3)
|
||||
db_cursor.execute("""
|
||||
UPDATE users
|
||||
SET last_check_in_date = %s, consecutive_check_in = 10
|
||||
WHERE username = %s
|
||||
""", (three_days_ago, test_user['username']))
|
||||
db_cursor.connection.commit()
|
||||
|
||||
# 今天签到
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/checkin/daily",
|
||||
json={'username': test_user['username']}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()['data']
|
||||
|
||||
# 连续天数应该归零重新开始
|
||||
assert data['consecutive_days'] == 1
|
||||
|
||||
def test_vip_multiplier(self, vip_user):
|
||||
"""测试VIP倍数"""
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/checkin/daily",
|
||||
json={'username': vip_user['username']}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()['data']
|
||||
|
||||
# VIP应该有倍数
|
||||
assert data['vip_multiplier'] >= 1.0
|
||||
if vip_user['vip_type'] == 'vip':
|
||||
assert data['vip_multiplier'] == 1.5
|
||||
|
||||
def test_checkin_nonexistent_user(self):
|
||||
"""测试不存在的用户签到"""
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/checkin/daily",
|
||||
json={'username': 'nonexistent_user'}
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
class TestCheckInStatus:
|
||||
"""签到状态测试"""
|
||||
|
||||
def test_get_checkin_status(self, test_user):
|
||||
"""测试获取签到状态"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/checkin/status",
|
||||
params={'username': test_user['username']}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
data = result['data']
|
||||
assert 'today_checked' in data
|
||||
assert 'consecutive_days' in data
|
||||
assert 'total_days' in data
|
||||
|
||||
def test_status_after_checkin(self, test_user):
|
||||
"""测试签到后状态更新"""
|
||||
# 签到
|
||||
requests.post(
|
||||
f"{API_BASE_URL}/checkin/daily",
|
||||
json={'username': test_user['username']}
|
||||
)
|
||||
|
||||
# 查询状态
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/checkin/status",
|
||||
params={'username': test_user['username']}
|
||||
)
|
||||
|
||||
data = response.json()['data']
|
||||
assert data['today_checked'] is True
|
||||
|
||||
|
||||
class TestCheckInCalendar:
|
||||
"""签到日历测试"""
|
||||
|
||||
def test_get_checkin_calendar(self, test_user):
|
||||
"""测试获取签到日历"""
|
||||
today = date.today()
|
||||
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/checkin/calendar/{today.year}/{today.month}",
|
||||
params={'username': test_user['username']}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
data = result['data']
|
||||
assert data['year'] == today.year
|
||||
assert data['month'] == today.month
|
||||
assert isinstance(data['checked_dates'], list)
|
||||
|
||||
def test_calendar_after_checkin(self, test_user, db_cursor):
|
||||
"""测试签到后日历更新"""
|
||||
today = date.today()
|
||||
|
||||
# 签到
|
||||
requests.post(
|
||||
f"{API_BASE_URL}/checkin/daily",
|
||||
json={'username': test_user['username']}
|
||||
)
|
||||
|
||||
# 查询日历
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/checkin/calendar/{today.year}/{today.month}",
|
||||
params={'username': test_user['username']}
|
||||
)
|
||||
|
||||
data = response.json()['data']
|
||||
assert today.day in data['checked_dates']
|
||||
|
||||
|
||||
class TestCheckInHistory:
|
||||
"""签到历史测试"""
|
||||
|
||||
def test_get_checkin_history(self, test_user):
|
||||
"""测试获取签到历史"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/checkin/history",
|
||||
params={'username': test_user['username'], 'page': 1, 'limit': 10}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
data = result['data']
|
||||
assert 'total' in data
|
||||
assert 'records' in data
|
||||
assert isinstance(data['records'], list)
|
||||
|
||||
def test_history_after_checkin(self, test_user):
|
||||
"""测试签到后历史记录增加"""
|
||||
# 获取签到前记录数
|
||||
response_before = requests.get(
|
||||
f"{API_BASE_URL}/checkin/history",
|
||||
params={'username': test_user['username']}
|
||||
)
|
||||
total_before = response_before.json()['data']['total']
|
||||
|
||||
# 签到
|
||||
requests.post(
|
||||
f"{API_BASE_URL}/checkin/daily",
|
||||
json={'username': test_user['username']}
|
||||
)
|
||||
|
||||
# 获取签到后记录数
|
||||
response_after = requests.get(
|
||||
f"{API_BASE_URL}/checkin/history",
|
||||
params={'username': test_user['username']}
|
||||
)
|
||||
total_after = response_after.json()['data']['total']
|
||||
|
||||
assert total_after == total_before + 1
|
||||
|
||||
def test_history_pagination(self, test_user):
|
||||
"""测试分页"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/checkin/history",
|
||||
params={'username': test_user['username'], 'page': 1, 'limit': 5}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()['data']
|
||||
assert len(data['records']) <= 5
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__, '-v'])
|
||||
|
||||
207
tests/backend/test_feature.py
Normal file
207
tests/backend/test_feature.py
Normal file
@@ -0,0 +1,207 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
功能使用API测试
|
||||
测试核心扣费逻辑、VIP配额管理
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from conftest import API_BASE_URL
|
||||
|
||||
|
||||
class TestFeatureUsage:
|
||||
"""功能使用测试"""
|
||||
|
||||
def test_use_feature_normal_user(self, test_user, test_feature):
|
||||
"""测试普通用户使用功能(扣积分)"""
|
||||
request_data = {
|
||||
'username': test_user['username'],
|
||||
'feature_key': test_feature['feature_key'],
|
||||
'device_id': 'test_device'
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/feature/use",
|
||||
json=request_data
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
data = result['data']
|
||||
assert data['success'] is True
|
||||
assert data['cost_type'] == 'points'
|
||||
assert data['points_cost'] == test_feature['points_cost']
|
||||
assert data['points_remaining'] == test_user['points'] - test_feature['points_cost']
|
||||
|
||||
def test_use_feature_insufficient_points(self, test_user, test_feature, db_cursor):
|
||||
"""测试积分不足"""
|
||||
# 设置用户积分为0
|
||||
db_cursor.execute(
|
||||
"UPDATE users SET points = 0 WHERE username = %s",
|
||||
(test_user['username'],)
|
||||
)
|
||||
db_cursor.connection.commit()
|
||||
|
||||
request_data = {
|
||||
'username': test_user['username'],
|
||||
'feature_key': test_feature['feature_key'],
|
||||
'device_id': 'test_device'
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/feature/use",
|
||||
json=request_data
|
||||
)
|
||||
|
||||
assert response.status_code == 402
|
||||
assert '积分不足' in response.json()['detail']
|
||||
|
||||
def test_use_feature_vip_user_with_quota(self, vip_user, test_feature):
|
||||
"""测试VIP用户使用配额"""
|
||||
request_data = {
|
||||
'username': vip_user['username'],
|
||||
'feature_key': test_feature['feature_key'],
|
||||
'device_id': 'test_device'
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/feature/use",
|
||||
json=request_data
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
|
||||
data = result['data']
|
||||
# VIP用户应该使用配额(如果vip_points_cost=0)
|
||||
assert data['cost_type'] in ['vip_quota', 'points']
|
||||
|
||||
if data['cost_type'] == 'vip_quota':
|
||||
assert data['vip_remaining_quota'] == vip_user['vip_daily_quota'] - 1
|
||||
|
||||
def test_use_disabled_feature(self, test_user, test_feature, db_cursor):
|
||||
"""测试使用已禁用的功能"""
|
||||
# 禁用功能
|
||||
db_cursor.execute(
|
||||
"UPDATE features_config SET enabled = 0 WHERE feature_key = %s",
|
||||
(test_feature['feature_key'],)
|
||||
)
|
||||
db_cursor.connection.commit()
|
||||
|
||||
request_data = {
|
||||
'username': test_user['username'],
|
||||
'feature_key': test_feature['feature_key'],
|
||||
'device_id': 'test_device'
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/feature/use",
|
||||
json=request_data
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert '禁用' in response.json()['detail']
|
||||
|
||||
# 恢复
|
||||
db_cursor.execute(
|
||||
"UPDATE features_config SET enabled = 1 WHERE feature_key = %s",
|
||||
(test_feature['feature_key'],)
|
||||
)
|
||||
db_cursor.connection.commit()
|
||||
|
||||
def test_use_nonexistent_feature(self, test_user):
|
||||
"""测试使用不存在的功能"""
|
||||
request_data = {
|
||||
'username': test_user['username'],
|
||||
'feature_key': 'nonexistent_feature',
|
||||
'device_id': 'test_device'
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/feature/use",
|
||||
json=request_data
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
def test_use_feature_nonexistent_user(self, test_feature):
|
||||
"""测试不存在的用户使用功能"""
|
||||
request_data = {
|
||||
'username': 'nonexistent_user',
|
||||
'feature_key': test_feature['feature_key'],
|
||||
'device_id': 'test_device'
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{API_BASE_URL}/feature/use",
|
||||
json=request_data
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
class TestFeatureUsageLogging:
|
||||
"""功能使用日志测试"""
|
||||
|
||||
def test_usage_log_created(self, test_user, test_feature, db_cursor):
|
||||
"""测试使用后创建日志"""
|
||||
# 使用前记录数
|
||||
db_cursor.execute(
|
||||
"SELECT COUNT(*) as count FROM feature_usage_logs WHERE username = %s",
|
||||
(test_user['username'],)
|
||||
)
|
||||
count_before = db_cursor.fetchone()['count']
|
||||
|
||||
# 使用功能
|
||||
request_data = {
|
||||
'username': test_user['username'],
|
||||
'feature_key': test_feature['feature_key'],
|
||||
'device_id': 'test_device'
|
||||
}
|
||||
|
||||
requests.post(f"{API_BASE_URL}/feature/use", json=request_data)
|
||||
|
||||
# 使用后记录数
|
||||
db_cursor.execute(
|
||||
"SELECT COUNT(*) as count FROM feature_usage_logs WHERE username = %s",
|
||||
(test_user['username'],)
|
||||
)
|
||||
count_after = db_cursor.fetchone()['count']
|
||||
|
||||
assert count_after == count_before + 1
|
||||
|
||||
def test_points_history_created(self, test_user, test_feature, db_cursor):
|
||||
"""测试积分历史记录"""
|
||||
# 使用前记录数
|
||||
db_cursor.execute(
|
||||
"SELECT COUNT(*) as count FROM points_history WHERE username = %s",
|
||||
(test_user['username'],)
|
||||
)
|
||||
count_before = db_cursor.fetchone()['count']
|
||||
|
||||
# 使用功能
|
||||
request_data = {
|
||||
'username': test_user['username'],
|
||||
'feature_key': test_feature['feature_key'],
|
||||
'device_id': 'test_device'
|
||||
}
|
||||
|
||||
response = requests.post(f"{API_BASE_URL}/feature/use", json=request_data)
|
||||
|
||||
# 只有扣积分时才记录积分历史
|
||||
if response.status_code == 200:
|
||||
data = response.json()['data']
|
||||
if data['cost_type'] == 'points':
|
||||
db_cursor.execute(
|
||||
"SELECT COUNT(*) as count FROM points_history WHERE username = %s",
|
||||
(test_user['username'],)
|
||||
)
|
||||
count_after = db_cursor.fetchone()['count']
|
||||
assert count_after == count_before + 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__, '-v'])
|
||||
|
||||
181
tests/backend/test_stats.py
Normal file
181
tests/backend/test_stats.py
Normal file
@@ -0,0 +1,181 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
统计API测试
|
||||
测试数据统计功能
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from conftest import API_BASE_URL, ADMIN_TOKEN
|
||||
|
||||
|
||||
class TestTodayStats:
|
||||
"""今日统计测试"""
|
||||
|
||||
def test_get_today_stats(self, admin_headers):
|
||||
"""测试获取今日统计"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/admin/stats/today",
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
data = result['data']
|
||||
assert 'total_users' in data
|
||||
assert 'checkin_count' in data
|
||||
assert 'feature_usage_count' in data
|
||||
assert 'vip_count' in data
|
||||
|
||||
# 验证数据类型
|
||||
assert isinstance(data['total_users'], int)
|
||||
assert isinstance(data['checkin_count'], int)
|
||||
assert isinstance(data['feature_usage_count'], int)
|
||||
assert isinstance(data['vip_count'], int)
|
||||
|
||||
# 验证数据合理性
|
||||
assert data['total_users'] >= 0
|
||||
assert data['checkin_count'] >= 0
|
||||
assert data['checkin_count'] <= data['total_users']
|
||||
|
||||
def test_stats_unauthorized(self):
|
||||
"""测试未授权访问统计"""
|
||||
response = requests.get(f"{API_BASE_URL}/admin/stats/today")
|
||||
|
||||
assert response.status_code in [401, 422]
|
||||
|
||||
|
||||
class TestFeatureUsageStats:
|
||||
"""功能使用排行测试"""
|
||||
|
||||
def test_get_feature_usage_stats(self, admin_headers):
|
||||
"""测试获取功能使用排行"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/admin/stats/feature-usage",
|
||||
params={'days': 7},
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
data = result['data']
|
||||
assert isinstance(data, list)
|
||||
|
||||
# 如果有数据,验证结构
|
||||
if len(data) > 0:
|
||||
item = data[0]
|
||||
assert 'feature_key' in item
|
||||
assert 'feature_name' in item
|
||||
assert 'usage_count' in item
|
||||
assert isinstance(item['usage_count'], int)
|
||||
|
||||
def test_feature_usage_stats_different_days(self, admin_headers):
|
||||
"""测试不同天数的统计"""
|
||||
days_list = [7, 30, 90]
|
||||
|
||||
for days in days_list:
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/admin/stats/feature-usage",
|
||||
params={'days': days},
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()['code'] == 200
|
||||
|
||||
|
||||
class TestPointsTrend:
|
||||
"""积分趋势测试"""
|
||||
|
||||
def test_get_points_trend(self, admin_headers):
|
||||
"""测试获取积分趋势"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/admin/stats/points-trend",
|
||||
params={'days': 7},
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
data = result['data']
|
||||
assert isinstance(data, list)
|
||||
|
||||
# 如果有数据,验证结构
|
||||
if len(data) > 0:
|
||||
item = data[0]
|
||||
assert 'date' in item
|
||||
assert 'earned' in item
|
||||
assert 'consumed' in item
|
||||
|
||||
# 验证数据类型
|
||||
assert isinstance(item['earned'], (int, float, type(None)))
|
||||
assert isinstance(item['consumed'], (int, float, type(None)))
|
||||
|
||||
def test_points_trend_date_format(self, admin_headers):
|
||||
"""测试日期格式"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/admin/stats/points-trend",
|
||||
params={'days': 7},
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
data = response.json()['data']
|
||||
|
||||
if len(data) > 0:
|
||||
# 验证日期格式 (YYYY-MM-DD)
|
||||
import re
|
||||
date_pattern = r'^\d{4}-\d{2}-\d{2}$'
|
||||
assert re.match(date_pattern, data[0]['date'])
|
||||
|
||||
|
||||
class TestStatsIntegration:
|
||||
"""统计集成测试"""
|
||||
|
||||
def test_stats_after_operations(self, test_user, test_feature, admin_headers, db_cursor):
|
||||
"""测试操作后统计数据变化"""
|
||||
# 获取操作前统计
|
||||
response_before = requests.get(
|
||||
f"{API_BASE_URL}/admin/stats/today",
|
||||
headers=admin_headers
|
||||
)
|
||||
stats_before = response_before.json()['data']
|
||||
|
||||
# 执行签到
|
||||
requests.post(
|
||||
f"{API_BASE_URL}/checkin/daily",
|
||||
json={'username': test_user['username']}
|
||||
)
|
||||
|
||||
# 使用功能
|
||||
requests.post(
|
||||
f"{API_BASE_URL}/feature/use",
|
||||
json={
|
||||
'username': test_user['username'],
|
||||
'feature_key': test_feature['feature_key'],
|
||||
'device_id': 'test'
|
||||
}
|
||||
)
|
||||
|
||||
# 获取操作后统计
|
||||
response_after = requests.get(
|
||||
f"{API_BASE_URL}/admin/stats/today",
|
||||
headers=admin_headers
|
||||
)
|
||||
stats_after = response_after.json()['data']
|
||||
|
||||
# 验证签到数增加
|
||||
assert stats_after['checkin_count'] >= stats_before['checkin_count']
|
||||
|
||||
# 验证功能使用数增加
|
||||
assert stats_after['feature_usage_count'] >= stats_before['feature_usage_count']
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__, '-v'])
|
||||
|
||||
208
tests/backend/test_user_profile.py
Normal file
208
tests/backend/test_user_profile.py
Normal file
@@ -0,0 +1,208 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
用户资料和积分历史API测试
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from conftest import API_BASE_URL
|
||||
|
||||
|
||||
class TestUserProfile:
|
||||
"""用户资料测试"""
|
||||
|
||||
def test_get_user_profile(self, test_user):
|
||||
"""测试获取用户资料"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/user/profile",
|
||||
params={'username': test_user['username']}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
data = result['data']
|
||||
assert data['username'] == test_user['username']
|
||||
assert 'points' in data
|
||||
assert 'vip_type' in data
|
||||
assert 'total_check_in_days' in data
|
||||
assert 'consecutive_check_in' in data
|
||||
|
||||
def test_get_nonexistent_user_profile(self):
|
||||
"""测试获取不存在的用户资料"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/user/profile",
|
||||
params={'username': 'nonexistent_user'}
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_update_user_profile(self, test_user):
|
||||
"""测试更新用户资料"""
|
||||
update_data = {
|
||||
'username': test_user['username'],
|
||||
'nickname': '测试昵称',
|
||||
'email': 'newemail@test.com'
|
||||
}
|
||||
|
||||
response = requests.put(
|
||||
f"{API_BASE_URL}/user/profile",
|
||||
json=update_data
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
assert result['message'] == '更新成功'
|
||||
|
||||
# 验证更新
|
||||
profile_response = requests.get(
|
||||
f"{API_BASE_URL}/user/profile",
|
||||
params={'username': test_user['username']}
|
||||
)
|
||||
profile_data = profile_response.json()['data']
|
||||
assert profile_data['nickname'] == '测试昵称'
|
||||
assert profile_data['email'] == 'newemail@test.com'
|
||||
|
||||
def test_update_partial_profile(self, test_user):
|
||||
"""测试部分更新"""
|
||||
update_data = {
|
||||
'username': test_user['username'],
|
||||
'nickname': '仅更新昵称'
|
||||
}
|
||||
|
||||
response = requests.put(
|
||||
f"{API_BASE_URL}/user/profile",
|
||||
json=update_data
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_update_nonexistent_user_profile(self):
|
||||
"""测试更新不存在的用户资料"""
|
||||
update_data = {
|
||||
'username': 'nonexistent_user',
|
||||
'nickname': '测试'
|
||||
}
|
||||
|
||||
response = requests.put(
|
||||
f"{API_BASE_URL}/user/profile",
|
||||
json=update_data
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
class TestPointsHistory:
|
||||
"""积分历史测试"""
|
||||
|
||||
def test_get_points_history(self, test_user):
|
||||
"""测试获取积分历史"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/points/history",
|
||||
params={'username': test_user['username'], 'page': 1, 'limit': 10}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
result = response.json()
|
||||
assert result['code'] == 200
|
||||
|
||||
data = result['data']
|
||||
assert 'total' in data
|
||||
assert 'current_balance' in data
|
||||
assert 'records' in data
|
||||
assert isinstance(data['records'], list)
|
||||
|
||||
def test_points_history_with_type_filter(self, test_user):
|
||||
"""测试按类型筛选积分历史"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/points/history",
|
||||
params={
|
||||
'username': test_user['username'],
|
||||
'type': 'checkin',
|
||||
'page': 1,
|
||||
'limit': 10
|
||||
}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()['data']
|
||||
|
||||
# 验证所有记录都是签到类型
|
||||
for record in data['records']:
|
||||
assert record['type'] == 'checkin'
|
||||
|
||||
def test_points_history_pagination(self, test_user):
|
||||
"""测试分页"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/points/history",
|
||||
params={'username': test_user['username'], 'page': 1, 'limit': 5}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()['data']
|
||||
assert len(data['records']) <= 5
|
||||
|
||||
def test_points_history_structure(self, test_user, db_cursor):
|
||||
"""测试记录结构"""
|
||||
# 创建一条测试记录
|
||||
db_cursor.execute("""
|
||||
INSERT INTO points_history
|
||||
(user_id, username, type, amount, balance, description)
|
||||
VALUES (%s, %s, 'reward', 50, 150, '测试奖励')
|
||||
""", (test_user['id'], test_user['username']))
|
||||
db_cursor.connection.commit()
|
||||
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/points/history",
|
||||
params={'username': test_user['username']}
|
||||
)
|
||||
|
||||
data = response.json()['data']
|
||||
if len(data['records']) > 0:
|
||||
record = data['records'][0]
|
||||
assert 'type' in record
|
||||
assert 'amount' in record
|
||||
assert 'balance' in record
|
||||
assert 'description' in record
|
||||
assert 'created_at' in record
|
||||
|
||||
def test_points_history_after_checkin(self, test_user):
|
||||
"""测试签到后积分历史增加"""
|
||||
# 获取签到前记录数
|
||||
response_before = requests.get(
|
||||
f"{API_BASE_URL}/points/history",
|
||||
params={'username': test_user['username']}
|
||||
)
|
||||
total_before = response_before.json()['data']['total']
|
||||
|
||||
# 签到
|
||||
requests.post(
|
||||
f"{API_BASE_URL}/checkin/daily",
|
||||
json={'username': test_user['username']}
|
||||
)
|
||||
|
||||
# 获取签到后记录数
|
||||
response_after = requests.get(
|
||||
f"{API_BASE_URL}/points/history",
|
||||
params={'username': test_user['username']}
|
||||
)
|
||||
total_after = response_after.json()['data']['total']
|
||||
|
||||
# 签到会增加一条积分历史
|
||||
assert total_after == total_before + 1
|
||||
|
||||
def test_points_history_nonexistent_user(self):
|
||||
"""测试不存在的用户的积分历史"""
|
||||
response = requests.get(
|
||||
f"{API_BASE_URL}/points/history",
|
||||
params={'username': 'nonexistent_user'}
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__, '-v'])
|
||||
|
||||
Reference in New Issue
Block a user