package arcade import ( "testing" tea "github.com/charmbracelet/bubbletea" "github.com/profullstack/agentbbs/internal/auth" "github.com/profullstack/agentbbs/internal/plugin" ) // key builds a single-rune key press the way bubbletea delivers it. func key(r rune) tea.KeyMsg { return tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{r}} } // guest avoids the store path (only members persist), so ctx.Store can be nil. func newTestHangman(word string) *hangman { h := newHangman(auth.User{Kind: auth.Guest}, plugin.Context{}) h.word = word h.guessed = make(map[byte]bool) h.wrong = 0 h.won = false return h } func TestHangmanSolveScores(t *testing.T) { h := newTestHangman("CAT") for _, r := range "CAT" { m, _ := h.Update(key(r)) h = m.(*hangman) } if !h.won { t.Fatalf("expected won after guessing every letter") } if h.solved != 1 { t.Fatalf("solved = %d, want 1", h.solved) } // len 3 *10 + (6-0)*5 = 60. if h.score != 60 { t.Fatalf("score = %d, want 60", h.score) } if h.dead { t.Fatalf("should not be dead after a solve") } } func TestHangmanWrongGuessIsCountedOnce(t *testing.T) { h := newTestHangman("CAT") for i := 0; i < 3; i++ { // repeat the same wrong letter m, _ := h.Update(key('Z')) h = m.(*hangman) } if h.wrong != 1 { t.Fatalf("wrong = %d, want 1 (repeat guesses must not stack)", h.wrong) } } func TestHangmanDeathAfterSixMisses(t *testing.T) { h := newTestHangman("CAT") for _, r := range "BDEFGH" { // six letters absent from CAT m, _ := h.Update(key(r)) h = m.(*hangman) } if h.wrong != hangmanMaxWrong { t.Fatalf("wrong = %d, want %d", h.wrong, hangmanMaxWrong) } if !h.dead { t.Fatalf("expected dead after %d misses", hangmanMaxWrong) } } // The "r" restart binding must not swallow r as a letter guess during play. func TestHangmanLowercaseRIsAGuess(t *testing.T) { h := newTestHangman("ROUTER") for _, r := range "router" { m, _ := h.Update(key(r)) h = m.(*hangman) } if !h.won { t.Fatalf("lowercase r should count as a guess and solve ROUTER") } } func TestHangmanLowercaseInputAndAdvance(t *testing.T) { h := newTestHangman("CAT") for _, r := range "cat" { // lowercase should still solve m, _ := h.Update(key(r)) h = m.(*hangman) } if !h.won { t.Fatalf("lowercase input should solve the word") } // space deals a fresh word and clears the round flags. m, _ := h.Update(tea.KeyMsg{Type: tea.KeySpace}) h = m.(*hangman) if h.won || h.wrong != 0 || len(h.guessed) != 0 { t.Fatalf("space should deal a fresh round: won=%v wrong=%d guessed=%d", h.won, h.wrong, len(h.guessed)) } }