mirror of
https://github.com/HugeFrog24/go-telegram-bot.git
synced 2026-08-28 22:11:38 +00:00
134 lines
4.4 KiB
Go
134 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-telegram/bot"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestStartChatAction_SendsImmediately(t *testing.T) { //NOSONAR go:S100 -- underscore separation is idiomatic in Go test names
|
|
b, mockTg := setupBotForTest(t, 123)
|
|
|
|
var mu sync.Mutex
|
|
var got []*bot.SendChatActionParams
|
|
mockTg.SendChatActionFunc = func(_ context.Context, p *bot.SendChatActionParams) (bool, error) {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
got = append(got, p)
|
|
return true, nil
|
|
}
|
|
|
|
stop := b.startChatAction(context.Background(), 42, "biz-7", "typing")
|
|
stop()
|
|
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
require.Len(t, got, 1, "the indicator must show before the slow work starts, not after")
|
|
assert.Equal(t, int64(42), got[0].ChatID)
|
|
assert.EqualValues(t, "typing", got[0].Action)
|
|
assert.Equal(t, "biz-7", got[0].BusinessConnectionID,
|
|
"business chats need the connection id or the indicator never renders")
|
|
}
|
|
|
|
func TestStartChatAction_OmitsEmptyBusinessConnectionID(t *testing.T) { //NOSONAR go:S100 -- underscore separation is idiomatic in Go test names
|
|
b, mockTg := setupBotForTest(t, 123)
|
|
|
|
var mu sync.Mutex
|
|
var captured *bot.SendChatActionParams
|
|
mockTg.SendChatActionFunc = func(_ context.Context, p *bot.SendChatActionParams) (bool, error) {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
captured = p
|
|
return true, nil
|
|
}
|
|
|
|
stop := b.startChatAction(context.Background(), 42, "", "typing")
|
|
stop()
|
|
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
require.NotNil(t, captured)
|
|
assert.Empty(t, captured.BusinessConnectionID)
|
|
}
|
|
|
|
// The openclaw#27177 guard: once the turn ends, the keepalive must stop. A loop
|
|
// that can re-arm after completion left the indicator stuck on until restart.
|
|
func TestStartChatAction_StopHaltsKeepalive(t *testing.T) { //NOSONAR go:S100 -- underscore separation is idiomatic in Go test names
|
|
b, mockTg := setupBotForTest(t, 123)
|
|
|
|
var calls atomic.Int32
|
|
mockTg.SendChatActionFunc = func(_ context.Context, _ *bot.SendChatActionParams) (bool, error) {
|
|
calls.Add(1)
|
|
return true, nil
|
|
}
|
|
|
|
stop := b.startChatAction(context.Background(), 42, "", "typing")
|
|
stop()
|
|
after := calls.Load()
|
|
|
|
// Well past a refresh tick had the loop survived.
|
|
time.Sleep(50 * time.Millisecond)
|
|
assert.Equal(t, after, calls.Load(), "no chat action may be sent after stop")
|
|
}
|
|
|
|
// Cancelling the parent context must also tear the loop down, so a turn aborted
|
|
// upstream cannot leak a goroutine that keeps calling Telegram.
|
|
func TestStartChatAction_ParentCancelHaltsKeepalive(t *testing.T) { //NOSONAR go:S100 -- underscore separation is idiomatic in Go test names
|
|
b, mockTg := setupBotForTest(t, 123)
|
|
|
|
var calls atomic.Int32
|
|
mockTg.SendChatActionFunc = func(_ context.Context, _ *bot.SendChatActionParams) (bool, error) {
|
|
calls.Add(1)
|
|
return true, nil
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
stop := b.startChatAction(ctx, 42, "", "typing")
|
|
defer stop()
|
|
|
|
cancel()
|
|
after := calls.Load()
|
|
time.Sleep(50 * time.Millisecond)
|
|
assert.Equal(t, after, calls.Load(), "parent cancellation must stop the keepalive")
|
|
}
|
|
|
|
func TestStartChatAction_StopIsIdempotent(t *testing.T) { //NOSONAR go:S100 -- underscore separation is idiomatic in Go test names
|
|
b, mockTg := setupBotForTest(t, 123)
|
|
mockTg.SendChatActionFunc = func(_ context.Context, _ *bot.SendChatActionParams) (bool, error) {
|
|
return true, nil
|
|
}
|
|
|
|
stop := b.startChatAction(context.Background(), 42, "", "typing")
|
|
// The voice path calls stop early and again via defer.
|
|
assert.NotPanics(t, func() {
|
|
stop()
|
|
stop()
|
|
})
|
|
}
|
|
|
|
// A failed indicator is cosmetic and must never surface as a turn failure.
|
|
func TestStartChatAction_SendErrorIsNonFatal(t *testing.T) { //NOSONAR go:S100 -- underscore separation is idiomatic in Go test names
|
|
b, mockTg := setupBotForTest(t, 123)
|
|
mockTg.SendChatActionFunc = func(_ context.Context, _ *bot.SendChatActionParams) (bool, error) {
|
|
return false, assert.AnError
|
|
}
|
|
|
|
assert.NotPanics(t, func() {
|
|
stop := b.startChatAction(context.Background(), 42, "", "typing")
|
|
stop()
|
|
})
|
|
}
|
|
|
|
// Telegram clears the status after "5 seconds or less", so the refresh must be
|
|
// strictly under that or the indicator visibly drops out mid-turn.
|
|
func TestTypingRefreshInterval_UnderTelegramExpiry(t *testing.T) { //NOSONAR go:S100 -- underscore separation is idiomatic in Go test names
|
|
assert.Less(t, typingRefreshInterval, 5*time.Second,
|
|
"Telegram expires a chat action after at most 5s")
|
|
}
|