635f2fd9fc
- #3 nudge-on-own-turn: distinct result code nudge_own_turn + i18n (was reused 'not_your_turn') - #2 sanitize connector registration name to the editable format; Player/Игрок-XXXXX fallback - #5 variant-aware robot name pools (composed full/colloquial first + surname forms; ru gets <=20% latin) - #4 move-number-aware robot move timing (early 1-5min -> late 10-90min, skew k=4) - #7 emit move event to the actor too (multi-device sync); opponent_moved stays in-app only - #1 live game_move_duration{variant,phase} histogram + admin console per-user min/avg/max columns and an inline-SVG move-time-by-move-number chart (offline from the journal) - ProvisionRobot bypasses editor name validation (system names like 'Peter J.')
53 lines
1.7 KiB
Go
53 lines
1.7 KiB
Go
package game
|
|
|
|
import (
|
|
"slices"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"scrabble/backend/internal/engine"
|
|
"scrabble/backend/internal/notify"
|
|
)
|
|
|
|
// recordingPublisher captures every published intent for assertions.
|
|
type recordingPublisher struct{ intents []notify.Intent }
|
|
|
|
func (p *recordingPublisher) Publish(in ...notify.Intent) { p.intents = append(p.intents, in...) }
|
|
|
|
// TestEmitMoveNotifiesActor checks a committed move sends opponent_moved to every
|
|
// seat — including the actor's own account, so the mover's other devices refresh —
|
|
// and your_turn only to the next mover.
|
|
func TestEmitMoveNotifiesActor(t *testing.T) {
|
|
actor, opp := uuid.New(), uuid.New()
|
|
pub := &recordingPublisher{}
|
|
svc := &Service{pub: pub}
|
|
g := Game{
|
|
ID: uuid.New(),
|
|
Status: StatusActive,
|
|
ToMove: 1,
|
|
TurnStartedAt: time.Now(),
|
|
TurnTimeout: time.Hour,
|
|
Seats: []Seat{{Seat: 0, AccountID: actor}, {Seat: 1, AccountID: opp}},
|
|
}
|
|
svc.emitMove(g, engine.MoveRecord{Player: 0, Action: engine.ActionPlay, Score: 10, Total: 10})
|
|
|
|
kinds := map[uuid.UUID][]string{}
|
|
for _, in := range pub.intents {
|
|
kinds[in.UserID] = append(kinds[in.UserID], in.Kind)
|
|
}
|
|
if !slices.Contains(kinds[actor], notify.KindOpponentMoved) {
|
|
t.Errorf("actor should get opponent_moved, got %v", kinds[actor])
|
|
}
|
|
if !slices.Contains(kinds[opp], notify.KindOpponentMoved) {
|
|
t.Errorf("opponent should get opponent_moved, got %v", kinds[opp])
|
|
}
|
|
if !slices.Contains(kinds[opp], notify.KindYourTurn) {
|
|
t.Errorf("next mover should get your_turn, got %v", kinds[opp])
|
|
}
|
|
if slices.Contains(kinds[actor], notify.KindYourTurn) {
|
|
t.Errorf("actor is not next to move, should not get your_turn")
|
|
}
|
|
}
|