FILE / ScuroNeko/Laniakea

drafts_test.go

Исходный файл и его история в репозитории.
FILE v1.1.0
Files
Laniakea/drafts_test.go
T
ScuroNeko f03a081ed6
Golang lint / lint (pull_request) Successful in 1m20s
Golang lint / lint (push) Successful in 4m8s
(new): rich message support
(fix): runtime reliability
(tests): regression coverage
(doc): v1.1 release notes
2026-08-12 16:34:44 +03:00

113 lines
3.3 KiB
Go

package laniakea
import (
"errors"
"strings"
"testing"
"git.scuroneko.dev/scuroneko/laniakea/tgapi"
"git.scuroneko.dev/scuroneko/sneklog/v2"
)
type sequenceDraftIDGenerator struct {
ids []uint64
pos int
}
func (g *sequenceDraftIDGenerator) Next() uint64 {
id := g.ids[g.pos]
g.pos++
return id
}
func TestDraftFlushRequiresChatID(t *testing.T) {
draft := NewRandomDraftProvider(&tgapi.API{}).NewDraft(tgapi.ParseNone)
draft.Message = "hello"
if err := draft.Flush(); !errors.Is(err, ErrDraftChatIDZero) {
t.Fatalf("expected ErrDraftChatIDZero, got %v", err)
}
}
func TestMsgContextNewDraftWorksWithoutLimiter(t *testing.T) {
ctx := &MessageContext{
API: &tgapi.API{},
Msg: &tgapi.Message{
Chat: &tgapi.Chat{ID: 42, Type: tgapi.ChatTypePrivate},
},
Logger: sneklog.NewLogger(),
draftProvider: NewRandomDraftProvider(&tgapi.API{}),
}
draft := ctx.NewDraft()
if draft == nil {
t.Fatal("expected draft")
return
}
if draft.chatID != 42 {
t.Fatalf("unexpected chat id: %d", draft.chatID)
}
}
func TestDraftProviderSkipsZeroAndCollidingIDs(t *testing.T) {
provider := &DraftProvider{
api: &tgapi.API{},
drafts: make(map[uint64]*Draft),
generator: &sequenceDraftIDGenerator{ids: []uint64{0, 7, 7, 8}},
}
first := provider.NewDraft(tgapi.ParseNone)
second := provider.NewDraft(tgapi.ParseNone)
if first.ID != 7 || second.ID != 8 {
t.Fatalf("unexpected draft IDs: first=%d second=%d", first.ID, second.ID)
}
if got := len(provider.drafts); got != 2 {
t.Fatalf("collision overwrote a draft: got %d drafts", got)
}
}
func TestDraftReturnsErrorWhenAPIIsNil(t *testing.T) {
draft := NewLinearDraftProvider(nil, 0).NewDraft(tgapi.ParseNone).SetChat(42, 0)
if err := draft.Push("hello"); !errors.Is(err, ErrAPIIsNil) {
t.Fatalf("expected ErrAPIIsNil from Push, got %v", err)
}
draft.Message = "hello"
if err := draft.Flush(); !errors.Is(err, ErrAPIIsNil) {
t.Fatalf("expected ErrAPIIsNil from Flush, got %v", err)
}
}
func TestDraftFlushRejectsLongMessage(t *testing.T) {
draft := NewRandomDraftProvider(&tgapi.API{}).NewDraft(tgapi.ParseNone).SetChat(42, 0)
draft.Message = strings.Repeat("a", maxMessageTextLen+1)
if err := draft.Flush(); !errors.Is(err, ErrMessageTooLong) {
t.Fatalf("expected ErrMessageTooLong, got %v", err)
}
}
func TestDraftPushRejectsLongMessage(t *testing.T) {
draft := NewRandomDraftProvider(&tgapi.API{}).NewDraft(tgapi.ParseNone).SetChat(42, 0)
if err := draft.Push(strings.Repeat("a", maxMessageTextLen+1)); !errors.Is(err, ErrMessageTooLong) {
t.Fatalf("expected ErrMessageTooLong, got %v", err)
}
}
// TestDraftPushLeavesMessageUnchangedOnValidationFailure covers the validation
// order fix: when the candidate Message (current + new text) overflows the
// Telegram limit, the existing Message must remain intact so callers can
// recover and retry with a shorter payload instead of finding the draft in
// a half-mutated state.
func TestDraftPushLeavesMessageUnchangedOnValidationFailure(t *testing.T) {
draft := NewRandomDraftProvider(&tgapi.API{}).NewDraft(tgapi.ParseNone).SetChat(42, 0)
draft.Message = "hello"
if err := draft.Push(strings.Repeat("a", maxMessageTextLen+1)); !errors.Is(err, ErrMessageTooLong) {
t.Fatalf("expected ErrMessageTooLong, got %v", err)
}
if draft.Message != "hello" {
t.Fatalf("expected draft Message to stay %q, got %q", "hello", draft.Message)
}
}