FILE / ScuroNeko/Laniakea

tgrich/rich_103_test.go

Исходный файл и его история в репозитории.
FILE d78526242bc7aa184ce5e030b95d8afe2d1243c4
Files
Laniakea/tgrich/rich_103_test.go
T
ScuroNeko d78526242b
Golang lint / lint (push) Failing after 1m37s
(new): support Bot API 10.3
(fix): finalize v2 contracts
(tests): cover v2 migration
(doc): prepare release guidance
2026-09-08 23:21:38 +03:00

143 lines
5.8 KiB
Go

package tgrich
import (
"encoding/json"
"errors"
"strings"
"testing"
"git.scuroneko.dev/scuroneko/laniakea/v2/tgapi"
)
func TestRich103HTML(t *testing.T) {
empty := ""
for _, tc := range []struct {
name string
button tgapi.RichMessageButton
fragment string
}{
{"url", tgapi.RichMessageButton{URL: "https://example.com/?a=1&b=2"}, `url="https://example.com/?a=1&b=2"`},
{"callback", tgapi.RichMessageButton{CallbackData: `a"<&`, Style: tgapi.RichMessageButtonStyleLink}, `data="a&quot;&lt;&amp;"`},
{"web app", tgapi.RichMessageButton{WebApp: &tgapi.WebAppInfo{URL: "https://example.com"}}, `type="web_app"`},
{"login", tgapi.RichMessageButton{LoginURL: &tgapi.LoginURL{URL: "https://example.com", ForwardText: "Forward", RequestWriteAccess: true}}, `request-write-access`},
{"inline", tgapi.RichMessageButton{SwitchInlineQuery: &empty}, `query=""`},
{"current", tgapi.RichMessageButton{SwitchInlineQueryCurrentChat: &empty}, `type="switch_inline_query_current_chat"`},
{"chosen", tgapi.RichMessageButton{SwitchInlineQueryChosenChat: &tgapi.SwitchInlineQueryChosenChat{AllowUserChats: true}}, `allow-user-chats`},
{"copy", tgapi.RichMessageButton{CopyText: &tgapi.CopyTextButton{Text: "copy"}}, `text="copy"`},
{"disabled", tgapi.RichMessageButton{Disabled: &tgapi.DisabledButton{}}, `type="disabled"`},
} {
t.Run(tc.name, func(t *testing.T) {
tc.button.Text = Text("<Go>")
for _, block := range []tgapi.InputRichBlock{P(Button(tc.button)), ButtonsWithAlign([]tgapi.RichMessageButton{tc.button}, tgapi.RichBlockButtonCenter)} {
msg, err := BuildHTML(block)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(msg.HTML, tc.fragment) || !strings.Contains(msg.HTML, "&lt;Go&gt;</tg-button>") {
t.Fatalf("unexpected HTML: %s", msg.HTML)
}
}
})
}
msg, err := BuildHTML(ExpandableBlockquoteWithCredit(Bold(Text("Quote")), Text("Author")), DocumentWithCaption(tgapi.InputMedia{Media: "attach://notes"}, Caption(Text("Notes"))), NewTable().SetCompact(true).Build())
if err != nil {
t.Fatal(err)
}
for _, fragment := range []string{"<blockquote expandable><b>Quote</b><cite>Author</cite></blockquote>", `<tg-document src="tg://document?id=media_1"></tg-document>`, "<table compact>"} {
if !strings.Contains(msg.HTML, fragment) {
t.Fatalf("missing %s in %s", fragment, msg.HTML)
}
}
if len(msg.Media) != 1 || msg.Media[0].Media.Type != tgapi.InputMediaTypeDocument || msg.Media[0].Media.Media != "attach://notes" {
t.Fatalf("bad media: %#v", msg.Media)
}
if _, err := BuildDraftHTML(Document(tgapi.InputMedia{Media: "attach://notes"})); !errors.Is(err, tgapi.ErrRichMessageDraftUploadUnsupported) {
t.Fatalf("draft: %v", err)
}
}
func TestRich103Validation(t *testing.T) {
good := tgapi.RichMessageButton{Text: Text("Go"), CallbackData: "x"}
for _, n := range []int{0, 1, 8, 9} {
buttons := make([]tgapi.RichMessageButton, n)
for i := range buttons {
buttons[i] = good
}
_, err := BuildHTML(Buttons(buttons))
if (err == nil) != (n >= 1 && n <= 8) {
t.Fatalf("row %d: %v", n, err)
}
}
for _, tc := range []struct {
name string
change func(*tgapi.RichMessageButton)
}{
{"no action", func(b *tgapi.RichMessageButton) { b.CallbackData = "" }},
{"two actions", func(b *tgapi.RichMessageButton) { b.URL = "https://example.com" }},
{"long callback", func(b *tgapi.RichMessageButton) { b.CallbackData = strings.Repeat("я", 33) }},
{"formatting", func(b *tgapi.RichMessageButton) { b.Text = Bold(Text("Go")) }},
{"style", func(b *tgapi.RichMessageButton) { b.Style = "disable" }},
{"link url", func(b *tgapi.RichMessageButton) { b.CallbackData = ""; b.URL = "https://example.com"; b.Style = "link" }},
{"login bot", func(b *tgapi.RichMessageButton) {
b.CallbackData = ""
b.LoginURL = &tgapi.LoginURL{URL: "https://example.com", BotUsername: "other"}
}},
} {
t.Run(tc.name, func(t *testing.T) {
b := good
tc.change(&b)
if _, err := BuildHTML(P(Button(b))); !errors.Is(err, ErrRichInvalidButton) {
t.Fatalf("got %v", err)
}
})
}
good.CallbackData = strings.Repeat("я", 32)
if _, err := BuildHTML(P(Button(good))); err != nil {
t.Fatal(err)
}
if _, err := BuildHTML(ButtonsWithAlign([]tgapi.RichMessageButton{good}, "bottom")); !errors.Is(err, ErrRichInvalidButton) {
t.Fatalf("alignment: %v", err)
}
}
func TestRich103InputJSON(t *testing.T) {
for _, block := range []tgapi.InputRichBlock{ExpandableBlockquote(Text("quote")), Document(tgapi.InputMedia{Media: "file"}), Buttons([]tgapi.RichMessageButton{{Text: Text("Off"), Disabled: &tgapi.DisabledButton{}}})} {
data, err := json.Marshal(block)
if err != nil {
t.Fatal(err)
}
var fields map[string]json.RawMessage
if err := json.Unmarshal(data, &fields); err != nil {
t.Fatal(err)
}
if len(fields["type"]) == 0 {
t.Fatalf("missing discriminator: %s", data)
}
}
}
func TestRichButtonLabelEntitiesAndLimits(t *testing.T) {
label := tgapi.RichTextArray{
Text("At "),
tgapi.RichTextDateTime{Text: Text("noon"), UnixTime: 1000, DateTimeFormat: "t"},
tgapi.RichTextCustomEmoji{CustomEmojiID: "123", AlternativeText: "!"},
}
if _, err := BuildHTML(P(Button(tgapi.RichMessageButton{Text: label, Disabled: &tgapi.DisabledButton{}}))); err != nil {
t.Fatal(err)
}
for _, n := range []int{0, 1, 256, 257} {
_, err := BuildHTML(P(Button(tgapi.RichMessageButton{Text: Text("Copy"), CopyText: &tgapi.CopyTextButton{Text: strings.Repeat("я", n)}})))
if (err == nil) != (n >= 1 && n <= 256) {
t.Fatalf("copy length %d: %v", n, err)
}
}
for _, text := range []tgapi.RichText{
Bold(Text(strings.Repeat("я", maxRichTextChars+1))),
URL(Text(strings.Repeat("я", maxRichTextChars+1)), "https://example.com"),
} {
if _, err := BuildHTML(P(text)); !errors.Is(err, ErrRichTextTooLong) {
t.Fatalf("nested text bypassed limit: %v", err)
}
}
}