113 lines
3.7 KiB
Go
113 lines
3.7 KiB
Go
package service_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
roomv1 "hyapp.local/api/proto/room/v1"
|
|
"hyapp/services/room-service/internal/testutil/mysqltest"
|
|
)
|
|
|
|
func TestMicHeartbeatRefreshesPublishingSessionAndPresence(t *testing.T) {
|
|
ctx := context.Background()
|
|
repository := mysqltest.NewRepository(t)
|
|
now := &fixedRoomTreasureClock{now: time.Date(2026, 6, 4, 8, 0, 0, 0, time.UTC)}
|
|
svc := newTreasureTestService(t, repository, &treasureTestWallet{}, now)
|
|
|
|
roomID := "room-mic-heartbeat"
|
|
ownerID := int64(8601)
|
|
speakerID := int64(8602)
|
|
createTreasureRoom(t, ctx, svc, roomID, ownerID, 9101)
|
|
joinTreasureRoom(t, ctx, svc, roomID, speakerID)
|
|
|
|
upResp, err := svc.MicUp(ctx, &roomv1.MicUpRequest{
|
|
Meta: treasureMeta(roomID, speakerID, "mic-heartbeat-up"),
|
|
SeatNo: 2,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("mic up failed: %v", err)
|
|
}
|
|
now.now = now.now.Add(500 * time.Millisecond)
|
|
confirmResp, err := svc.ConfirmMicPublishing(ctx, &roomv1.ConfirmMicPublishingRequest{
|
|
Meta: treasureMeta(roomID, speakerID, "mic-heartbeat-confirm"),
|
|
MicSessionId: upResp.GetMicSessionId(),
|
|
RoomVersion: upResp.GetRoom().GetVersion(),
|
|
EventTimeMs: now.Now().UnixMilli(),
|
|
Source: "client",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("confirm mic publishing failed: %v", err)
|
|
}
|
|
confirmedSeat := seatByNo(confirmResp.GetRoom(), 2)
|
|
if confirmedSeat == nil || confirmedSeat.GetPublishState() != "publishing" || confirmedSeat.GetMicHeartbeatAtMs() == 0 {
|
|
t.Fatalf("publish confirmation must initialize heartbeat: %+v", confirmedSeat)
|
|
}
|
|
|
|
now.now = now.now.Add(3 * time.Second)
|
|
heartbeatAtMS := now.Now().UnixMilli()
|
|
heartbeatResp, err := svc.MicHeartbeat(ctx, &roomv1.MicHeartbeatRequest{
|
|
Meta: treasureMeta(roomID, speakerID, "mic-heartbeat-refresh"),
|
|
MicSessionId: upResp.GetMicSessionId(),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("mic heartbeat failed: %v", err)
|
|
}
|
|
if heartbeatResp.GetSeatNo() != 2 || heartbeatResp.GetMicHeartbeatAtMs() != heartbeatAtMS {
|
|
t.Fatalf("heartbeat response mismatch: %+v want_at=%d", heartbeatResp, heartbeatAtMS)
|
|
}
|
|
seat := seatByNo(heartbeatResp.GetRoom(), 2)
|
|
if seat == nil || seat.GetMicHeartbeatAtMs() != heartbeatAtMS {
|
|
t.Fatalf("seat heartbeat must be visible in snapshot: %+v", seat)
|
|
}
|
|
user := onlineUserByID(heartbeatResp.GetRoom(), speakerID)
|
|
if user == nil || user.GetLastSeenAtMs() != heartbeatAtMS {
|
|
t.Fatalf("mic heartbeat must refresh room presence: %+v", user)
|
|
}
|
|
}
|
|
|
|
func TestMicHeartbeatRejectsPendingSession(t *testing.T) {
|
|
ctx := context.Background()
|
|
repository := mysqltest.NewRepository(t)
|
|
now := &fixedRoomTreasureClock{now: time.Date(2026, 6, 4, 8, 0, 0, 0, time.UTC)}
|
|
svc := newTreasureTestService(t, repository, &treasureTestWallet{}, now)
|
|
|
|
roomID := "room-mic-heartbeat-pending"
|
|
ownerID := int64(8701)
|
|
speakerID := int64(8702)
|
|
createTreasureRoom(t, ctx, svc, roomID, ownerID, 9101)
|
|
joinTreasureRoom(t, ctx, svc, roomID, speakerID)
|
|
|
|
upResp, err := svc.MicUp(ctx, &roomv1.MicUpRequest{
|
|
Meta: treasureMeta(roomID, speakerID, "mic-heartbeat-pending-up"),
|
|
SeatNo: 1,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("mic up failed: %v", err)
|
|
}
|
|
if _, err := svc.MicHeartbeat(ctx, &roomv1.MicHeartbeatRequest{
|
|
Meta: treasureMeta(roomID, speakerID, "mic-heartbeat-pending-refresh"),
|
|
MicSessionId: upResp.GetMicSessionId(),
|
|
}); err == nil {
|
|
t.Fatalf("pending_publish session must not accept mic heartbeat before publish confirmation")
|
|
}
|
|
}
|
|
|
|
func seatByNo(snapshot *roomv1.RoomSnapshot, seatNo int32) *roomv1.SeatState {
|
|
for _, seat := range snapshot.GetMicSeats() {
|
|
if seat.GetSeatNo() == seatNo {
|
|
return seat
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func onlineUserByID(snapshot *roomv1.RoomSnapshot, userID int64) *roomv1.RoomUser {
|
|
for _, user := range snapshot.GetOnlineUsers() {
|
|
if user.GetUserId() == userID {
|
|
return user
|
|
}
|
|
}
|
|
return nil
|
|
}
|