75 lines
2.7 KiB
Go
75 lines
2.7 KiB
Go
package service
|
||
|
||
import (
|
||
"testing"
|
||
|
||
"hyapp/services/room-service/internal/room/command"
|
||
"hyapp/services/room-service/internal/room/state"
|
||
)
|
||
|
||
func replayHeartbeat(t *testing.T, current *state.RoomState, userID int64, sentAtMS int64) {
|
||
t.Helper()
|
||
cmd := &command.RoomHeartbeat{Base: command.Base{
|
||
AppCode: "lalu",
|
||
RequestID: "req-replay-heartbeat",
|
||
CommandID: "cmd-replay-heartbeat",
|
||
ActorID: userID,
|
||
Room: current.RoomID,
|
||
SentAtMS: sentAtMS,
|
||
}}
|
||
if err := replay(current, cmd, sentAtMS); err != nil {
|
||
t.Fatalf("replay heartbeat for user %d failed: %v", userID, err)
|
||
}
|
||
}
|
||
|
||
// 线上心跳会静默恢复丢失的 presence 后才提交命令日志;回放必须重现恢复(含 Version++),
|
||
// 否则恢复出的 OnlineUsers/版本与快照谱系分叉。
|
||
func TestReplayRoomHeartbeatRecreatesMissingPresence(t *testing.T) {
|
||
ownerID := int64(11)
|
||
adminID := int64(22)
|
||
viewerID := int64(33)
|
||
current := state.NewRoomState("room-replay-heartbeat", ownerID, 4, "voice")
|
||
current.AdminUsers[adminID] = true
|
||
baseVersion := current.Version
|
||
|
||
replayHeartbeat(t, current, viewerID, 1_000)
|
||
viewer, exists := current.OnlineUsers[viewerID]
|
||
if !exists || viewer.Role != "audience" || viewer.JoinedAtMS != 1_000 || viewer.LastSeenAtMS != 1_000 {
|
||
t.Fatalf("replayed heal must recreate audience presence from SentAtMS: %+v", viewer)
|
||
}
|
||
if current.Version != baseVersion+1 {
|
||
t.Fatalf("replayed heal must advance version exactly once: base=%d now=%d", baseVersion, current.Version)
|
||
}
|
||
|
||
// 角色从权威字段推导:owner/admin 恢复后徽标不丢失。
|
||
replayHeartbeat(t, current, ownerID, 2_000)
|
||
if owner := current.OnlineUsers[ownerID]; owner == nil || owner.Role != "owner" {
|
||
t.Fatalf("replayed heal must restore owner role: %+v", owner)
|
||
}
|
||
replayHeartbeat(t, current, adminID, 3_000)
|
||
if admin := current.OnlineUsers[adminID]; admin == nil || admin.Role != "admin" {
|
||
t.Fatalf("replayed heal must restore admin role: %+v", admin)
|
||
}
|
||
}
|
||
|
||
func TestReplayRoomHeartbeatRefreshesExistingPresence(t *testing.T) {
|
||
viewerID := int64(44)
|
||
current := state.NewRoomState("room-replay-heartbeat-refresh", 11, 4, "voice")
|
||
current.OnlineUsers[viewerID] = &state.RoomUserState{
|
||
UserID: viewerID,
|
||
Role: "audience",
|
||
JoinedAtMS: 500,
|
||
LastSeenAtMS: 500,
|
||
}
|
||
baseVersion := current.Version
|
||
|
||
replayHeartbeat(t, current, viewerID, 9_000)
|
||
viewer := current.OnlineUsers[viewerID]
|
||
if viewer.LastSeenAtMS != 9_000 || viewer.JoinedAtMS != 500 {
|
||
t.Fatalf("replayed refresh must only update last_seen: %+v", viewer)
|
||
}
|
||
if current.Version != baseVersion+1 {
|
||
t.Fatalf("replayed refresh must advance version exactly once: base=%d now=%d", baseVersion, current.Version)
|
||
}
|
||
}
|