A8 快手 (Kuaishou) — 真机 E2E 测试计划
Status: v0.3 计划 (2026-05-25 update) — v0.2 stub A8KuaishouE2ETest.kt 已落;v0.3 新增 KuaishouSignBridge (hidden WebView 跑 NS_sig3 + kpf/kpn) + ApiClient GraphQL POST 3 endpoint (visionFeedRecommend / visionProfilePhotoList / visionSearchPhoto) + collector fan-out。Win dev box JVM 单测 (KuaishouApiClientV03Test + KuaishouLocalCollectorV03Test) ✅;真机 E2E 仍需 Mac/Linux + 真机 + 真账号。
范围
v0.3 surface (本轮新增):
- ✅ profile (cookie-parse
kuaishou.web.cp.api_ph, v0.2 已通) - 🆕 watch history → KIND_WATCH (
visionFeedRecommendGraphQL, 需__NS_sig3+ kpf/kpn headers) - 🆕 profile photos → KIND_COLLECT (
visionProfilePhotoList, 用户自己发的视频) - 🆕 search history → KIND_SEARCH (
visionSearchPhotowith empty keyword)
NS_sig3 由 KuaishouSignBridge 在登录 cookie 注入后的 hidden WebView 里跑平台自带签名 JS — probe 4 候选 (window.__APP__.encryptParams / window.NS.sign / window.GraphQL.fetch.sign / window.__SIGN__),与 Toutiao/Douyin (ByteDance acrawler 家族) 完全不同。signing JS 输入是 URL + operationName + 完整 POST body (GraphQL spec),输出 {__NS_sig3, kpf, kpn}:__NS_sig3 进 URL 查询,kpf/kpn 进 header。Bridge 单 JS eval 产双输出,单槽缓存配对调用 (signUrl → signedHeaders 严格相等命中)。
GraphQL POST 形态 vs Toutiao/Douyin GET:ApiClient 走 /graphql POST + JSON body,doPostJson helper 处理 GraphQL errors ({errors:[...]} → lastErrorCode=-5)。三个 endpoint 同 endpoint 不同 operationName,区分靠 body.operationName 字段。
Graceful degrade:
- bridge warmUp 失败 → 跳过 3 个 v0.3 端点, v0.2 profile (cookie-parse) 仍 emit
- signUrl 返 null (函数名 rotate / NS_sig3 算法漂) → 短路 (不发 HTTP), lastErrorCode=-99
- GraphQL errors → lastErrorCode=-5 + 错误消息 propagate
v03Attempted 字段在 SnapshotResult.Ok 透出。UI banner 三档与 Toutiao/Douyin 对齐。
WebView 登录 → SocialCookieWebViewScreen 提取 cookie
↓ (userId / kuaishou.web.cp.api_ph / did / passToken)
HubLocalViewModel.syncKuaishou()
↓
KuaishouLocalCollector.snapshot()
↓ KuaishouApiClient.fetchProfile (无 HTTP — 解 api_ph cookie JSON)
↓ JSON 拼装写 filesDir/.chainlesschain/staging/social-kuaishou.json (1 profile event 或 0)
↓
LocalCcRunner.syncAdapter("social-kuaishou", path)
↓ adapter._syncViaSnapshot → person-self with kuaishou-uid + kuaishou-id identifiers
↓
UI 显示 "已同步账号 profile (v0.2)。推荐/收藏/搜索需 v0.3 NS_sig3 接通"前置(一次性)
- 真快手账号 (v0.2 不需要内容,但 cookie 必须含 api_ph — 同手机一键登录路径稳定产出,扫码登录可能缺)
- Mac/Linux dev box
- 真 Android 设备
- APK 已 bundle in-APK cc + PDH ≥ 0.3.0
- (场景 7 / 8 需)能编辑 EncryptedSharedPreferences 的 root shell
注意: 因 v0.2 无网络调用,反爬场景(412 / NS_sig3 失败)不在 E2E 范围内 — 这些只能等 v0.3 接通 GraphQL 后才有意义。
8 个 E2E 场景
场景 1 — 首次登录成功(happy path)
步骤:
- 打开 ChainlessChain → 个人数据中台 → 本机数据 tab
- 看到"快手"卡片,状态"未登录"
- 点"登录"→ WebView 加载
https://www.kuaishou.com/(banner 推一键登录 deep-link) - 用真账号登录 — 推荐"本机一键登录",扫码登录可能缺 api_ph
- WebView 跳到
www.kuaishou.com主页 → ChainlessChain 命中 isLoginSuccess - 提取 cookie + 调 fetchProfile 解 api_ph JSON → 关 WebView
断言:
- WebView 在登录后 ≤ 3s 内自动关闭
- 卡片状态变"已登录
<user_name>" pdh_social_kuaishouEncryptedSharedPreferences 中cookie/uid/displayName全非空- cookie 中
userId+kuaishou.web.cp.api_ph字段都在 - displayName 是 api_ph JSON 里的 user_name(不是 fallback "(unnamed)")
场景 2 — 同步 profile 成功(cookie-parse 路径)
前置: 场景 1 已完成
步骤:
- 在卡片点"同步"
- ~1-3s 后 (纯本地 cookie parse 无网络),toast "social-kuaishou 同步完成 (+1 事件)"
- 卡片显示"上次同步: <时间>"
- banner 文本提示"已同步账号 profile(v0.2 含昵称/头像 cookie 解析)。推荐/收藏/搜索需 v0.3 NS_sig3 签名接通。"
断言:
+1是 profile event (KIND_PROFILE)- vault 中可见
person-kuaishou-<uid>记录 - person.identifiers 含
kuaishou-uid+kuaishou-id(快手 ID 字符串别名) - person.extra 含 avatarUrl / sex / city / constellation / description(不是 followingCount/followerCount — 这些 cookie 没有,v0.3 GraphQL 才能拿)
- 子进程日志含"social-kuaishou snapshot ok (profileCount=1)"
场景 3 — Cookie 缺 api_ph (-8 missing)
前置: 已登录但 cookie 不含 kuaishou.web.cp.api_ph(某些跨端登录路径会跳过 api_ph 写入,例如扫码登录)
模拟:
- 全新 APK,scroll WebView 到"扫码登录"用 PC 扫码(不用一键登录)
- 或
adb shell run-as com.chainlesschain.android编辑 cookie 文件删除 api_ph
步骤:
- 点"同步"
断言:
- profile 返 null,lastErrorCode = -8
- 卡片显示"缺 api_ph cookie — 请用一键登录而非扫码"或 "登录数据不完整"
- 但
account.uid仍持有(extractUid 拿到 userId),不是完全未登录态 - 同步 result 是 Ok with profileCount=0,不是 NoCredentials
场景 4 — api_ph 非 JSON (-9 non-JSON payload)
前置: 已登录但 api_ph 被快手新版改成 base64 / opaque token(未来可能发生)
模拟:
adb shell run-as改 EncryptedSharedPreferences 让 api_ph 字段是 base64-like 字符串
步骤:
- 点"同步"
断言:
- profile 返 null,lastErrorCode = -9
- 卡片显示"api_ph 解码后非 JSON (likely base64 — v0.3 加 fallback)"
- 不 crash
- v0.3 计划: 加 base64 解码 fallback 路径
场景 5 — WebView 取消登录
步骤:
- 全新装 APK
- 点"登录"进 WebView
- 在快手登录页不操作,按系统返回键
断言:
- WebView 关闭返回卡片列表
- 状态仍"未登录"
- 无 toast,无 errorMessage
- store 中 cookie / uid 字段空
场景 6 — 反复同步幂等性
前置: 已登录 + 已同步过一次
步骤:
- 立即再点"同步"
- 再点一次
断言:
- 三次 sync 后
vault.queryPersons()数量不变 (同 uid 走 UPSERT) - person.extra.snapshottedAt 每次更新
- person.names 不变 (除非用户改了昵称)
- raw_events 表只有 1 行 (profile originalId 是
kuaishou:profile:<uid>) - 同步速度稳定 ~1-3s (纯本地)
场景 7 — EncryptedSharedPreferences keystore corruption
模拟: 改 pdh_social_kuaishou.xml 让 AES-256-GCM 解密失败
步骤:
adb shell进 app data dir,备份 xml- 手改 1 byte
- 重启 app
断言:
- App 不 crash
- 快手卡片显"未登录"
- Timber log 含 "KuaishouCredentialsStore: read failed"
- 用户能重新登录恢复
场景 8 — 退出登录 + 重新登录
步骤:
- 已登录点"退出登录"
- 卡片状态变"未登录"
- 点"登录"重登同一账号
- 同步
断言:
- 退出后 store 中 cookie / uid / displayName 全空
- 重新登录 uid 同上次 (同账号)
- 同步成功,person 记录同 ID (走 UPSERT 不是新建)
- 重新登录后 api_ph cookie 重新 rotate,但 user_id 字段不变
执行方式
- 手动: 按 8 场景逐一跑,每个 ~3-8 分钟,总 ~1h
- 自动化: 暂不可 (快手登录需短信 / 一键 / 扫码)
- CI: 仅跑场景 5 + 7 (非真账号);其余手动
v0.3 待补 (NS_sig3 接通后)
/graphql visionFeedRecommend推荐流 — 新场景: events 含 BROWSE 子类 (watch)/graphql visionProfilePhotoList主页 / 收藏 — 新场景: events 含 LIKE 子类 (collect)/graphql visionSearchPhoto搜索历史 — 新场景: events 含 SEARCH/POST 子类/graphql currentUser动态 following/follower counts — 新场景: person.extra 包含动态计数- NS_sig3 签名漂移 — 新场景类比 Xhs X-S 失败 + Douyin X-Bogus 漂移
- HTTP 反爬 412 — 新场景 (v0.2 无网络不需要)
反爬 caveats (v0.3 用)
- NS_sig3 比 X-Bogus 复杂,每月可能 rotate;v0.3 需 WebView JS injection 而非纯 Kotlin 端口
- visitor_id 是 cookie 字段,每登一次 rotate;NS_sig3 签名输入依赖它
- 单 IP 短时间频繁 GraphQL 请求触发"网络繁忙" 5-10 分钟封锁
stub 占位
androidTest/.../A8KuaishouE2ETest.kt 8 个测试方法已 @Ignore 占位。
关联文档
docs/design/A8_Douyin_E2E_Plan.md— 同 v0.2 profile-only 但走 HTTP passport endpoint (不同实现)docs/design/A8_Toutiao_E2E_Plan.md— 同 v0.2 profile-only 通过 HTTP passport endpoint- memory
pdh_social_collector_test_gap_audit.md— 6 platform 测试覆盖审计 - memory
pdh_social_webview_deeplink_cookie_capture.md— WebView 一键登录 deep-link 协议(快手 banner 必须推一键而非扫码)
