chore: 清理敏感与开发文档,仅同步代码
- 永久忽略并从仓库移除 开发文档/ - 移除并忽略 .env 与小程序私有配置 - 同步小程序/管理端/API与脚本改动 Made-with: Cursor
This commit is contained in:
19
scripts/test/web/README.md
Normal file
19
scripts/test/web/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# 管理端测试 (web)
|
||||
|
||||
> 管理后台功能测试用例。对应 soul-admin,API 路径:`/api/admin/*`、`/api/db/*`
|
||||
|
||||
---
|
||||
|
||||
## 测试范围
|
||||
|
||||
- 内容管理(文章、章节、书籍 CRUD)
|
||||
- 用户管理
|
||||
- 订单、提现
|
||||
- VIP 角色、推广设置
|
||||
- 导师、导师预约、二维码、站点、支付配置
|
||||
|
||||
---
|
||||
|
||||
## 用例编写
|
||||
|
||||
在此目录下新增 `.md` 或测试脚本,按场景组织用例。
|
||||
2
scripts/test/web/conftest.py
Normal file
2
scripts/test/web/conftest.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""web 专用 fixtures,继承 scripts/test/conftest.py"""
|
||||
42
scripts/test/web/test_admin_auth.py
Normal file
42
scripts/test/web/test_admin_auth.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
管理端鉴权测试。POST /api/admin 登录,GET /api/admin 鉴权检查。
|
||||
"""
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
from util import admin_headers
|
||||
|
||||
|
||||
def test_admin_login(base_url):
|
||||
"""POST /api/admin 登录成功"""
|
||||
r = requests.post(
|
||||
f"{base_url}/api/admin",
|
||||
json={"username": "admin", "password": "admin123"},
|
||||
timeout=10,
|
||||
)
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert data.get("success") is True
|
||||
assert "token" in data
|
||||
assert "user" in data
|
||||
|
||||
|
||||
def test_admin_check_with_token(admin_token, base_url):
|
||||
"""GET /api/admin 带 token 鉴权通过"""
|
||||
if not admin_token:
|
||||
pytest.skip("admin 登录失败,跳过鉴权测试")
|
||||
r = requests.get(
|
||||
f"{base_url}/api/admin",
|
||||
headers=admin_headers(admin_token),
|
||||
timeout=10,
|
||||
)
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert data.get("success") is True
|
||||
|
||||
|
||||
def test_admin_check_without_token(base_url):
|
||||
"""GET /api/admin 无 token 返回 401"""
|
||||
r = requests.get(f"{base_url}/api/admin", timeout=10)
|
||||
assert r.status_code == 401
|
||||
121
scripts/test/web/test_upload.py
Normal file
121
scripts/test/web/test_upload.py
Normal file
@@ -0,0 +1,121 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
文件上传测试。POST /api/upload 上传图片,DELETE /api/upload 删除。
|
||||
验证:本地存储(OSS 未配置时)、响应格式、删除流程。
|
||||
"""
|
||||
import io
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
|
||||
# 最小有效 JPEG(1x1 像素,约 100 字节)
|
||||
_MIN_JPEG = (
|
||||
b"\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00"
|
||||
b"\xff\xdb\x00C\x00\x08\x06\x06\x07\x06\x05\x08\x07\x07\x07\t\t\x08\n\x0c\x14\r\x0c\x0b\x0b\x0c\x19\x12\x13\x0f\x14\x1d\x1a\x1f\x1e\x1d\x1a\x1c\x1c"
|
||||
b" $.' \",#\x1c\x1c(7),01444\x1f'9=82<.342\xff\xc0\x00\x0b\x08\x00\x01\x00\x01\x01\x01\x11\x00\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\xff\xda\x00\x08\x01\x01\x00\x00\x00?\x00\xfe\x02\x1f\xff\xd9"
|
||||
)
|
||||
|
||||
|
||||
def test_upload_post_success(base_url):
|
||||
"""POST /api/upload 上传图片成功,返回 url、fileName、size、type"""
|
||||
files = {"file": ("test_upload.jpg", io.BytesIO(_MIN_JPEG), "image/jpeg")}
|
||||
data = {"folder": "test"}
|
||||
r = requests.post(
|
||||
f"{base_url}/api/upload",
|
||||
files=files,
|
||||
data=data,
|
||||
timeout=10,
|
||||
)
|
||||
assert r.status_code == 200, f"期望 200,实际 {r.status_code}: {r.text}"
|
||||
body = r.json()
|
||||
assert body.get("success") is True, body
|
||||
assert "url" in body, body
|
||||
assert body["url"], "url 不应为空"
|
||||
data_out = body.get("data", {})
|
||||
assert "url" in data_out
|
||||
assert "fileName" in data_out
|
||||
assert "size" in data_out
|
||||
assert data_out["size"] == len(_MIN_JPEG)
|
||||
assert "type" in data_out
|
||||
assert "image" in str(data_out.get("type", "")).lower()
|
||||
|
||||
|
||||
def test_upload_post_with_admin_token(base_url, admin_token):
|
||||
"""POST /api/upload 带管理端 token 也可上传(接口不强制鉴权)"""
|
||||
if not admin_token:
|
||||
pytest.skip("admin 登录失败")
|
||||
files = {"file": ("avatar.jpg", io.BytesIO(_MIN_JPEG), "image/jpeg")}
|
||||
# multipart 上传不设 Content-Type,让 requests 自动带 boundary
|
||||
headers = {"Authorization": f"Bearer {admin_token}"}
|
||||
r = requests.post(
|
||||
f"{base_url}/api/upload",
|
||||
files=files,
|
||||
headers=headers,
|
||||
timeout=10,
|
||||
)
|
||||
assert r.status_code == 200
|
||||
body = r.json()
|
||||
assert body.get("success") is True
|
||||
assert body.get("url")
|
||||
|
||||
|
||||
def test_upload_post_no_file(base_url):
|
||||
"""POST /api/upload 无 file 返回 400"""
|
||||
r = requests.post(
|
||||
f"{base_url}/api/upload",
|
||||
data={"folder": "test"},
|
||||
timeout=10,
|
||||
)
|
||||
assert r.status_code == 400
|
||||
body = r.json()
|
||||
assert body.get("success") is False
|
||||
assert "error" in body or "请选择" in body.get("error", "")
|
||||
|
||||
|
||||
def test_upload_post_invalid_type(base_url):
|
||||
"""POST /api/upload 非图片格式返回 400"""
|
||||
files = {"file": ("test.txt", io.BytesIO(b"hello"), "text/plain")}
|
||||
r = requests.post(
|
||||
f"{base_url}/api/upload",
|
||||
files=files,
|
||||
timeout=10,
|
||||
)
|
||||
assert r.status_code == 400
|
||||
body = r.json()
|
||||
assert body.get("success") is False
|
||||
|
||||
|
||||
def test_upload_delete_local(base_url):
|
||||
"""DELETE /api/upload 删除本地文件:先上传再删除"""
|
||||
# 1. 上传
|
||||
files = {"file": ("del_test.jpg", io.BytesIO(_MIN_JPEG), "image/jpeg")}
|
||||
r1 = requests.post(
|
||||
f"{base_url}/api/upload",
|
||||
files=files,
|
||||
data={"folder": "test"},
|
||||
timeout=10,
|
||||
)
|
||||
assert r1.status_code == 200
|
||||
url = r1.json().get("url")
|
||||
assert url, "上传应返回 url"
|
||||
# path 支持 /uploads/xxx 或含 /uploads/ 的完整 URL
|
||||
path = url
|
||||
|
||||
# 2. 删除
|
||||
r2 = requests.delete(
|
||||
f"{base_url}/api/upload",
|
||||
params={"path": path},
|
||||
timeout=10,
|
||||
)
|
||||
assert r2.status_code == 200
|
||||
body = r2.json()
|
||||
assert body.get("success") is True
|
||||
assert "删除成功" in body.get("message", "")
|
||||
|
||||
|
||||
def test_upload_delete_no_path(base_url):
|
||||
"""DELETE /api/upload 无 path 返回 400"""
|
||||
r = requests.delete(f"{base_url}/api/upload", timeout=10)
|
||||
assert r.status_code == 400
|
||||
body = r.json()
|
||||
assert body.get("success") is False
|
||||
Reference in New Issue
Block a user