(new): support Bot API 10.3
Golang lint / lint (push) Failing after 1m37s

(fix): finalize v2 contracts
(tests): cover v2 migration
(doc): prepare release guidance
This commit is contained in:
2026-09-08 23:21:38 +03:00
parent 24040fe164
commit d78526242b
88 changed files with 2321 additions and 918 deletions
+93 -15
View File
@@ -3,6 +3,7 @@ package tgapi
import (
"encoding/json"
"errors"
"reflect"
"strings"
"testing"
)
@@ -233,23 +234,98 @@ func TestRichBlockDividerHasNoContent(t *testing.T) {
}
}
func TestRichBlockUnknownTypeWithTextIsForwardCompat(t *testing.T) {
raw := []byte(`{"type":"future_tag","text":"hello"}`)
b, err := UnmarshalRichBlock(raw)
if err != nil {
t.Fatalf("forward-compat failed: %v", err)
func TestUnknownRichObjectsRoundTripLosslessly(t *testing.T) {
tests := []struct {
name string
raw string
parse func([]byte) (any, error)
}{
{
name: "text",
raw: `{"type":"future_text","text":"hello","metadata":{"flag":true},"items":[1,2]}`,
parse: func(data []byte) (any, error) {
return UnmarshalRichText(data)
},
},
{
name: "block",
raw: `{"type":"future_block","value":42,"metadata":{"flag":true},"items":[1,2]}`,
parse: func(data []byte) (any, error) {
return UnmarshalRichBlock(data)
},
},
}
w, ok := b.(RichBlockWrap)
if !ok || w.Tag != "future_tag" {
t.Fatalf("expected RichBlockWrap{future_tag}, got %T", b)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
input := []byte(tt.raw)
value, err := tt.parse(input)
if err != nil {
t.Fatalf("parse failed: %v", err)
}
for i := range input {
input[i] = ' '
}
encoded, err := json.Marshal(value)
if err != nil {
t.Fatalf("Marshal failed: %v", err)
}
var got, want any
if err := json.Unmarshal(encoded, &got); err != nil {
t.Fatal(err)
}
if err := json.Unmarshal([]byte(tt.raw), &want); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("unknown object changed: got %s want %s", encoded, tt.raw)
}
switch v := value.(type) {
case RichTextUnknown:
if len(v.Raw) == 0 {
t.Fatal("empty preserved rich text")
}
case RichBlockUnknown:
if len(v.Raw) == 0 {
t.Fatal("empty preserved rich block")
}
default:
t.Fatalf("unexpected fallback type %T", value)
}
})
}
}
func TestRichBlockUnknownTypeWithoutTextIsError(t *testing.T) {
raw := []byte(`{"type":"mystery_leaf","value":42}`)
_, err := UnmarshalRichBlock(raw)
if err == nil {
t.Fatal("expected error for unknown type without text")
func TestUnknownRichObjectsRoundTripInsideMessage(t *testing.T) {
raw := []byte(`{"blocks":[{"type":"future_block","metadata":{"version":2}},{"type":"paragraph","text":{"type":"future_text","payload":[1,2,3]}}],"is_rtl":true}`)
message, err := UnmarshalRichMessage(raw)
if err != nil {
t.Fatalf("UnmarshalRichMessage failed: %v", err)
}
if _, ok := message.Blocks[0].(RichBlockUnknown); !ok {
t.Fatalf("first block type = %T, want RichBlockUnknown", message.Blocks[0])
}
paragraph, ok := message.Blocks[1].(RichBlockWrap)
if !ok {
t.Fatalf("second block type = %T, want RichBlockWrap", message.Blocks[1])
}
if _, ok := paragraph.Text.(RichTextUnknown); !ok {
t.Fatalf("paragraph text type = %T, want RichTextUnknown", paragraph.Text)
}
encoded, err := json.Marshal(message)
if err != nil {
t.Fatalf("Marshal failed: %v", err)
}
var got, want any
if err := json.Unmarshal(encoded, &got); err != nil {
t.Fatal(err)
}
if err := json.Unmarshal(raw, &want); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("message changed: got %s want %s", encoded, raw)
}
}
@@ -271,8 +347,10 @@ func TestUnmarshalRichBlockRejectsMalformedFields(t *testing.T) {
func TestUnmarshalRichMessageStrict(t *testing.T) {
for _, raw := range []string{`null`, `{}`, `{"blocks":null}`, `{"blocks":{}}`} {
t.Run(raw, func(t *testing.T) {
if _, err := UnmarshalRichMessageStrict([]byte(raw)); err == nil {
t.Fatal("expected strict decoder error")
for _, parse := range []func([]byte) (RichMessage, error){UnmarshalRichMessage, UnmarshalRichMessageStrict} {
if _, err := parse([]byte(raw)); err == nil {
t.Fatal("expected strict decoder error")
}
}
})
}