2026-07-07 18:28:50 +08:00

100 lines
3.5 KiB
Go

package service
import (
"context"
"testing"
"time"
"hyapp/pkg/appcode"
"hyapp/services/room-service/internal/room/cell"
"hyapp/services/room-service/internal/room/rank"
"hyapp/services/room-service/internal/room/state"
"hyapp/services/room-service/internal/router"
)
func TestLoadedRoomLeaseDropsCellWhenLeaseTokenChanges(t *testing.T) {
ctx := appcode.WithContext(context.Background(), "lalu")
roomID := "room-token-gap"
now := time.Date(2026, 7, 7, 9, 0, 0, 0, time.UTC)
directory := router.NewMemoryDirectory()
svc := &Service{
nodeID: "room-node-a",
leaseTTL: time.Second,
directory: directory,
cells: make(map[string]*loadedCell),
}
lease, err := directory.EnsureOwner(ctx, runtimeRoomKey("lalu", roomID), svc.nodeID, now, svc.leaseTTL)
if err != nil {
t.Fatalf("ensure first lease: %v", err)
}
roomState := state.NewRoomState(roomID, 1001, 8, "voice")
roomState.Version = 7
svc.installCell(ctx, roomID, cell.New(roomState, rank.FromState(roomState.GiftRank, 20)), lease.LeaseToken)
if got := svc.loadCellForLease(ctx, roomID, lease); got == nil {
t.Fatal("freshly installed cell should be reusable with the original lease token")
}
if _, owned, err := svc.loadedRoomLease(ctx, loadedRoomRef{AppCode: "lalu", RoomID: roomID}, now.Add(2*time.Second)); err != nil {
t.Fatalf("load expired lease: %v", err)
} else if owned {
t.Fatal("expired lease re-acquisition must not keep using the old in-memory cell")
}
if got := svc.loadCell(ctx, roomID); got != nil {
t.Fatal("old in-memory cell must be forgotten after a lease-token discontinuity")
}
currentLease, exists, err := directory.Lookup(ctx, runtimeRoomKey("lalu", roomID))
if err != nil {
t.Fatalf("lookup current lease: %v", err)
}
if !exists || currentLease.LeaseToken == lease.LeaseToken {
t.Fatal("test setup expected the directory to issue a new lease token after expiry")
}
}
func TestLoadCellForCurrentOwnerRejectsReadOnlyAndExpiredCells(t *testing.T) {
ctx := appcode.WithContext(context.Background(), "lalu")
roomID := "room-read-current-owner"
now := time.Date(2026, 7, 7, 10, 0, 0, 0, time.UTC)
directory := router.NewMemoryDirectory()
svc := &Service{
nodeID: "room-node-a",
leaseTTL: time.Second,
clock: fixedRoomTestClock{now: now},
directory: directory,
cells: make(map[string]*loadedCell),
}
roomState := state.NewRoomState(roomID, 1001, 8, "voice")
lease, err := directory.EnsureOwner(ctx, runtimeRoomKey("lalu", roomID), svc.nodeID, now, svc.leaseTTL)
if err != nil {
t.Fatalf("ensure lease: %v", err)
}
svc.installCell(ctx, roomID, cell.New(roomState.Clone(), rank.FromState(roomState.GiftRank, 20)), "")
if got, err := svc.loadCellForCurrentOwner(ctx, roomID); err != nil {
t.Fatalf("load read-only token cell: %v", err)
} else if got != nil {
t.Fatal("read-only recovered cell must not be reused as current-owner state")
}
if got := svc.loadCell(ctx, roomID); got != nil {
t.Fatal("read-only cell should be forgotten after current-owner token check")
}
svc.installCell(ctx, roomID, cell.New(roomState.Clone(), rank.FromState(roomState.GiftRank, 20)), lease.LeaseToken)
svc.clock = fixedRoomTestClock{now: now.Add(2 * time.Second)}
if got, err := svc.loadCellForCurrentOwner(ctx, roomID); err != nil {
t.Fatalf("load expired token cell: %v", err)
} else if got != nil {
t.Fatal("expired owner lease must not authorize reading the old in-memory cell")
}
}
type fixedRoomTestClock struct {
now time.Time
}
func (c fixedRoomTestClock) Now() time.Time {
return c.now
}