Files
EchoChat/.playwright-mcp/task16/scenarios/04-host-toolkit-and-transfer.md
bujinyuan 5ed14c2aac docs(meeting): Task 16 E2E 剧本落盘 + .gitignore 精细化
Playwright MCP 4 场景剧本(.playwright-mcp/task16/):
- README.md:总览 + 账号/环境/资源断言指南
- scenarios/01-create-and-join-by-code.md:创建→会议号加入→媒体打通→结束,含 Redis/MySQL/media-server 三维资源断言
- scenarios/02-invite-link-with-password.md:邀请链接带密码 + P0-4 "密码不走 URL query" 回归
- scenarios/03-notify-quickjoin.md:通知中心「立即加入」跳转 + 过期卡片降级
- scenarios/04-host-toolkit-and-transfer.md:主持人四件套 + 宽限期重连/到期自动转让(P1-1 原子性 + P1-7 处理锁验证)
- scenarios/05-reopen-5times-resource-cleanup.md:反复开关 5 次 + 前端 heap 快照/ Redis 累积对比/ media-server 观测端点对比,验证 Step A(f5ae095)资源清理修复

手工回归点(manual-regression.md):17 项 MCP 难以稳定覆盖的交互场景
(HTTPS 自签 / 跨网段 iOS / 网络抖动 / 物理返回 / 跨 tab 并发 / 摄像头抢占 等)

.gitignore 调整:
- 放行 .playwright-mcp/task16/ 下的 Markdown 剧本文档进入版本控制
- 仍忽略运行时产物(*.log/*.png/*.yml/截图)
- task15/ 保持未跟踪状态,避免历史截图噪声入库

Made-with: Cursor
2026-04-23 17:21:42 +08:00

5.4 KiB
Raw Blame History

场景 04主持人四件套 + 宽限期自动转让

目标

  • 验证 Task 15 主持人四件套:请他静音、请他开麦、转让主持人、踢出会议
  • 验证 Task 8 host 宽限期状态机P1-1 原子转让 + P1-7 处理锁):
    • host 主动断开 WS → 宽限期内重连 → 撤销转让
    • host 宽限期到期 → 自动转让给最早加入的参会人
  • 覆盖设计 §6.5生命周期状态机、§6.6(权限矩阵)

前置条件

../README.md 通用前置;需 3 个账号 Alice/Bob/Carol 同时入会

MCP 执行脚本 A主持人四件套

步骤 1三人入会

  1. Alice 创建即时会议 → $ROOM_CODE
  2. Bob 用会议号加入
  3. Carol 用会议号加入
  4. 截图 → 01-three-joined.png

步骤 2Alice 请他静音 Bob

  1. Alice tab 打开成员面板 → Bob 右侧 「⋯」 菜单 → 「请他静音」
  2. Bob tab 期望:
    • Toast「主持人已将你的麦克风关闭」
    • localAudioEnabled=false
    • 成员面板自己的 AV 图标变灰
  3. Alice tab 期望Bob 行 AV 图标变灰
  4. 截图 → 02-bob-muted-by-host.png

步骤 3Alice 请他开麦 Bob

  1. Alice tab → Bob 「⋯」菜单 → 「请他开麦」
  2. Bob tab 期望:
    • 弹窗请求确认「主持人请你开启麦克风,是否同意?」
    • 点「同意」后 localAudioEnabled=true
  3. 截图 → 03-bob-ask-unmute.png

步骤 4Alice 踢出 Carol

  1. Alice tab → Carol 「⋯」菜单 → 「踢出会议」
  2. 弹窗确认「踢出 Carol」→ 确认
  3. Carol tab 期望:
    • 收到 meeting.member.kicked 广播
    • Toast「主持人已将你移出会议」
    • 跳回会议首页
  4. Alice / Bob tab 期望Carol 行从成员列表消失
  5. 截图 → 04-carol-kicked.png

步骤 5Alice 转让主持人给 Bob

  1. Alice tab → Bob 「⋯」菜单 → 「转让主持人」
  2. 弹窗确认 → 确认
  3. 原子性验证P1-1
    mysql -e "SELECT host_id, (SELECT role FROM meeting_participants WHERE room_id=r.id AND user_id=55) AS alice_role, \
              (SELECT role FROM meeting_participants WHERE room_id=r.id AND user_id=56) AS bob_role \
              FROM meeting_rooms r WHERE room_code='$ROOM_CODE'" echochat
    
    期望同时满足:host_id=56 + alice_role=participant + bob_role=host(同一事务原子更新)
  4. Alice tab / Bob tab 工具栏切换Alice 看到"结束会议"按钮消失Bob 看到它出现)
  5. 截图 → 05-host-transferred.png

MCP 执行脚本 B宽限期自动转让

前置:接续场景 A 的会议;此时 Bob 是主持人Alice 是参会人

步骤 6Bob 断开 WS模拟网络断开

  1. Bob tab DevTools → Network 面板 → Throttling 切换到 Offline
  2. 等待 WS 心跳超时(~15sBob 客户端 _onWsDisconnected 触发
  3. 期望 Redis 立即写入 echo:meeting:host_grace:$ROOM_CODE
    redis-cli GET "echo:meeting:host_grace:$ROOM_CODE"
    # 期望JSON {host_id:56, started_at:..., grace_until:...}
    redis-cli TTL "echo:meeting:host_grace:$ROOM_CODE"
    # 期望:约 120 秒(配置 `meeting.host_grace_seconds`
    
  4. Alice tab 期望:工具栏显示「主持人掉线中(宽限期 2:00」倒计时若 UI 已实现)

步骤 7a宽限期内重连撤销转让

  1. 15 秒后 Bob tab Network 恢复 Online
  2. 期望 _onWsReconnected 触发,执行 OnHostReconnect
    redis-cli EXISTS "echo:meeting:host_grace:$ROOM_CODE"
    # 期望0key 已 DEL
    
  3. Alice tab 工具栏倒计时消失
  4. 查日志:
    tail -50 backend/go-service/logs/*.log | grep "host 宽限期内重连"
    # 期望:`host 宽限期内重连,撤销转让计划`
    

步骤 7b宽限期到期自动转让

若步骤 7a 已执行,请先让 Bob 重新 Offline 并等待整个 120s 周期完成。

  1. Bob tab Offline保持 120+s
  2. 期望本地 timer 到期触发 HandleHostGraceExpired
    • Alice tab 收到 meeting.host.changed 广播,auto_reason='host_grace_expired'
    • 工具栏/成员面板 Alice 头像变为主持人标识
    • meeting_rooms.host_id=55 + meeting_participants.role 原子变更
  3. 处理锁验证P1-7
    # 宽限期到期瞬间抓一下处理锁
    redis-cli GET "echo:meeting:host_grace_handling:$ROOM_CODE"
    # 期望:`1`SETNX 成功 60s TTL60s 后自然过期
    
  4. 截图 → 06-host-auto-transferred.png

步骤 8清理

Alice现任主持人结束会议走场景 01 的资源清理断言。

预期结果汇总

断言 预期
请他静音 Bob localAudioEnabled=false + toast
请他开麦 Bob 弹窗确认后 localAudioEnabled=true
踢出 Carol 跳首页 + 成员列表移除
主动转让原子性 host_id + 两行 role 同事务更新
host 宽限期 Redis key 写入 + TTL≈120s
宽限内重连 key DEL + 本地 timer cancel
宽限到期自动转让 最早加入者成为新 host + 广播 host.changed
处理锁 SETNX host_grace_handling:* 存在 60s

失败排查

现象 定位
转让后 host_idrole 不一致 P1-1 回归:检查 TransferHost 是否包在单事务
宽限到期但 host 不变 看 handling 锁是否被"自然过期 + DEL=0"误拦P1-7 应已修)
多节点部署下双重转让 SETNX 处理锁应保证单节点唯一处理