经三端只读审计 + 真实端到端验证后修复,每项都带回归用例。
后端
- 游标分页 off-by-one:next_cursor 指向被丢弃的哨兵行,严格不等式又排除它,
13 个端点每页静默丢 1 条;统一收敛到 core.api.paginate_cursor
- Feed 排序键(-hot_score/-published_at)与游标键(id)不同序,翻页重复+跳过;
hot_score 恒为 0,统一按 -id
- 无关/非法 page_size 曾 500 或一次拉全表;统一 parse_page_size 容错限幅
- 匿名写评论 500(AnonymousUser 写非空 FK)→ 读公开写登录;下架视频评论区可读写 → 404
- 取消点赞不回退作者获赞、收藏删除按固定 -1 → 按实际行数回退(可刷计数)
- view_count 去重失效(+1 在缓存判断之外)→ 计数与写历史同入去重分支
- WatchHistory 无唯一约束 + update_or_create → 并发重复行后永久 500;补约束
- is_active 未校验:封禁账号可继续用 access/refresh → 三处补校验
- WS user_<id> 组无订阅者,实时通知整条链是死的 → 补 UserConsumer + 广播
- 广播系统消息已读状态跨用户共享、可改他人定向消息 → 加 per-user 已读表
- 评论回复路由双前缀(comments/comments/…)导致 Web 与 Android 均 404
- 通知 type="all" 落到 filter(type="all") 匹配 0 行 → 全部已读
- 播放上报无鉴权可刷量、无视频存在性校验 → IsAuthenticated + 404
- 生产默认密钥是公开串,可离线伪造任意用户 JWT(含管理员)→ DEBUG=0 时拒启
- 搜索热词读-改-写丢更新 → F() 原子自增
Web
- 3 处把分页 envelope 当数组读(历史/访客/系统消息)→ 页面不可用/抛 TypeError
- CommentPanel 复用不跟随视频切换,显示并写入别的视频的评论
- 上传受全局 15s 超时,500MB 文件必然失败 → 单请求放宽到 15min
- auth 闭环:新 refresh 从不落盘、刷新失败不登出、restore 是死代码、深链接被
App.vue 的 replace("/") 拽回首页
- 私信被前后端双重反转,时序颠倒;点赞/收藏/评论点赞只有 POST 取消不掉
- 通知全读、消息中心两处 filter 崩溃、WS 4401 后永久不重连
Android
- 播放统计返回类型 Map<String,Boolean> 解不了 {"status":"recorded"} → 每次上报
都抛异常;且每 2 秒插一行播放记录,完播率被灌水 10 倍以上 → 改为离开时结算一次
- 上传走通用客户端(20s readTimeout)对同步转码必然超时 → 专用长超时客户端
- BODY 级 HTTP 日志(含密码/令牌/私信)打到 release 包 → 仅 debug BASIC
- allowBackup=true 可导出含 30 天 refresh 的 prefs → 关闭并加 dataExtractionRules
- 全局明文放行 → networkSecurityConfig 白名单
- 服务器地址只改内存、重启即丢,且 remember 旧 service 导致改地址不生效
- 登录态只算一次:登录后不轮询、登出后仍轮询、401 不跳登录 → SessionState 可观察
- 昵称含 "/" 会崩导航(路由未编码);观看历史/访客页无入口不可达
测试与 CI
- backend 30 passed(新增 11 条回归:分页/计数/鉴权/越权/契约)
- web 5 passed(vitest:401 单飞刷新、滑动续期回写、失败登出)+ typecheck 绿
- android 4 passed(JVM 契约测试,无需模拟器)
- e2e smoke 补取消点赞与 4 个分页页断言;CI 增加 web/android job
验证:API 端到端 19/19、浏览器 E2E 全绿、三端构建通过
60 lines
1.7 KiB
YAML
60 lines
1.7 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
pull_request:
|
|
|
|
jobs:
|
|
backend:
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: dsp/backend
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.12"
|
|
- name: Install deps
|
|
run: |
|
|
pip install "Django>=5.2,<5.3" "djangorestframework>=3.15,<3.16" "adrf>=0.1.9" \
|
|
"psycopg[binary]>=3.2,<4.0" "redis>=5.0,<6.0" "channels>=4.1,<5.0" "channels-redis>=4.2" \
|
|
"daphne>=4.1" "django-cors-headers>=4.4" "django-redis>=5.4" "Pillow>=10.4" \
|
|
"python-decouple>=3.8" "argon2-cffi>=23.1" "PyJWT>=2.9" \
|
|
"pytest" "pytest-asyncio" "pytest-django" "httpx"
|
|
- name: Tests (DSP_TEST=1, zero external deps)
|
|
run: DSP_TEST=1 python -m pytest tests/ -q
|
|
|
|
web:
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: dsp/web
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
- run: npm ci
|
|
- run: npx vue-tsc -b
|
|
- name: Unit tests (401 单飞刷新 / 契约回归)
|
|
run: npm test
|
|
- run: npm run build
|
|
|
|
android:
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: dsp/android
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-java@v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: "17"
|
|
- uses: android-actions/setup-android@v3
|
|
- name: Install SDK platform (compileSdk=34)
|
|
run: sdkmanager "platforms;android-34" "build-tools;34.0.0"
|
|
- name: Unit tests (JSON 契约回归,无需模拟器)
|
|
run: ./gradlew testDebugUnitTest --no-daemon
|