plugins/navManager:导航 CRUD + 导航分类 CRUD(独立 blueprint) plugins/friendManager:友情链接 CRUD plugins/aboutManager:关于本站 CRUD plugins/siteStats:访问统计主页 + JSON 接口 + /site/* GET 埋点 + nav 卡片点击埋点 applications/view/public/nav.py:导航聚合页 / 分类详情 / JSON 接口,支持 visit_count 注入 applications/view/public/about.py:关于本站公开页 applications/view/public/friend.py:友情链接公开页 templates/public/base.html:响应式首页(搜索框 / 分类抽屉 / 5 主题切换 / footer 隐私小字) templates/public/index.html / category.html:卡片右下角访问次数 templates/public/about.html / friend.html:关于 + 友链前台页 docs/plugins-development.md:插件开发完整指南(生命周期 + 4 个示例 + FAQ + framework 迁移) scripts/:探针与端到端验证脚本(probe_*/verify_*/test_*)
142 lines
5.1 KiB
Python
142 lines
5.1 KiB
Python
"""
|
||
导航分类功能端到端探针
|
||
======================
|
||
|
||
不依赖浏览器 + ddddocr + 调试 header,直接用 Flask test_client 走完整流程:
|
||
|
||
1. 登录 admin
|
||
2. GET /system/nav-category/ (主页面 HTML)
|
||
3. GET /system/nav-category/data (table_api JSON)
|
||
4. POST /system/nav-category/save (新增)
|
||
5. PUT /system/nav-category/update (更新)
|
||
6. PUT /system/nav-category/enable (启用/禁用)
|
||
7. DELETE /system/nav-category/remove/<id> (删除)
|
||
8. 验证每个 API 的 {code, message, data} 协议
|
||
|
||
退出码:全部通过 → 0;任一失败 → 1
|
||
"""
|
||
import json
|
||
import sys
|
||
import traceback
|
||
|
||
sys.path.insert(0, r'D:\bwstudio\pear-admin-flask')
|
||
|
||
from applications import create_app
|
||
|
||
app = create_app()
|
||
client = app.test_client()
|
||
|
||
|
||
def _login():
|
||
"""admin/123456 登录拿 cookie session。"""
|
||
# 拿一张 captcha —— 通过 test_client 共享 session_transaction
|
||
r = client.get('/system/passport/getCaptcha')
|
||
assert r.status_code == 200, r.status_code
|
||
|
||
with client.session_transaction() as sess:
|
||
code = sess.get('code') or ''
|
||
code = code.lower()
|
||
assert code, 'captcha code missing'
|
||
|
||
r = client.post('/system/passport/login', data={
|
||
'username': 'admin',
|
||
'password': '123456',
|
||
'captcha': code,
|
||
}, follow_redirects=False)
|
||
assert r.status_code in (200, 302), r.status_code
|
||
|
||
|
||
def _show(label, resp):
|
||
body = resp.get_data(as_text=True)
|
||
print(f' [{resp.status_code}] {label} -> {body[:200]}{"..." if len(body) > 200 else ""}')
|
||
|
||
|
||
def main():
|
||
_login()
|
||
print('=== 已登录,开始 E2E 探针 ===\n')
|
||
|
||
# 1. 分类主页面 HTML
|
||
r = client.get('/system/nav-category/')
|
||
print(f'[HTML main] {r.status_code} content-type={r.content_type}')
|
||
assert r.status_code == 200
|
||
assert b'nav-category/data' in r.data or b'\u5bfc\u822a\u5206\u7c7b' in r.data, 'main 页面未包含预期内容'
|
||
print(' [OK] 分类主页面返回 200,含预期关键字\n')
|
||
|
||
# 2. data 接口(table_api 协议:{code, data, count, msg, limit})
|
||
r = client.get('/system/nav-category/data')
|
||
assert r.status_code == 200
|
||
payload = r.get_json()
|
||
assert payload['code'] == 0, payload
|
||
before_ids = {row['id'] for row in payload['data']}
|
||
print(f' [OK] data 接口 code=0,count={payload["count"]},已有分类 {len(before_ids)} 条:{sorted(before_ids)}\n')
|
||
|
||
# 3. 新增(success_api 协议:{success, msg})
|
||
unique_name = f'E2E临时分类{int(__import__("time").time())}'
|
||
r = client.post('/system/nav-category/save',
|
||
json={'name': unique_name, 'icon': 'layui-icon-test',
|
||
'description': 'e2e created', 'sort': 1, 'enable': 1})
|
||
payload = r.get_json()
|
||
assert payload.get('success') is True, payload
|
||
print(f' [OK] 新增成功:{unique_name}\n')
|
||
|
||
# 4. 拿到 id
|
||
r = client.get('/system/nav-category/data')
|
||
payload = r.get_json()
|
||
new_id = next((row['id'] for row in payload['data'] if row['name'] == unique_name), None)
|
||
assert new_id is not None, '新增的分类查不到'
|
||
print(f' [OK] 查回新增分类 id={new_id}\n')
|
||
|
||
# 5. 更新
|
||
r = client.put('/system/nav-category/update',
|
||
json={'catId': new_id, 'name': unique_name + '_改',
|
||
'icon': 'layui-icon-edit', 'description': 'e2e updated',
|
||
'sort': 99, 'enable': 1})
|
||
payload = r.get_json()
|
||
assert payload.get('success') is True, payload
|
||
print(f' [OK] 更新成功\n')
|
||
|
||
# 6. 禁用 → 启用
|
||
r = client.put('/system/nav-category/disable', json={'catId': new_id})
|
||
assert r.get_json().get('success') is True
|
||
r = client.put('/system/nav-category/enable', json={'catId': new_id})
|
||
assert r.get_json().get('success') is True
|
||
print(f' [OK] 禁用→启用通过\n')
|
||
|
||
# 7. 删除
|
||
r = client.delete(f'/system/nav-category/remove/{new_id}')
|
||
assert r.get_json().get('success') is True, r.get_json()
|
||
print(f' [OK] 删除成功\n')
|
||
|
||
# 8. 查回 count 必须等于开始前
|
||
r = client.get('/system/nav-category/data')
|
||
payload = r.get_json()
|
||
after_ids = {row['id'] for row in payload['data']}
|
||
assert before_ids == after_ids, f'分类集合变化:before={before_ids}, after={after_ids}'
|
||
print(f' [OK] 删除后 count 还原,新增的 id 已不在集合中\n')
|
||
|
||
# 9. 重名检测
|
||
existing_name = None
|
||
if payload['data']:
|
||
existing_name = payload['data'][0]['name']
|
||
if existing_name:
|
||
r = client.post('/system/nav-category/save',
|
||
json={'name': existing_name, 'icon': 'x', 'description': '', 'sort': 0, 'enable': 1})
|
||
body = r.get_json()
|
||
assert body.get('success') is False, body
|
||
print(f' [OK] 重名拦截正常:{body["msg"]}\n')
|
||
|
||
print('=== ALL PASS ===')
|
||
|
||
|
||
if __name__ == '__main__':
|
||
try:
|
||
main()
|
||
sys.exit(0)
|
||
except AssertionError as e:
|
||
print(f'\n!!! ASSERT FAIL: {e}')
|
||
traceback.print_exc()
|
||
sys.exit(1)
|
||
except Exception as e:
|
||
print(f'\n!!! UNEXPECTED: {e}')
|
||
traceback.print_exc()
|
||
sys.exit(1) |