100 lines
3.5 KiB
Go
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
|
|
}
|