FILE / ScuroNeko/Laniakea

tgfmt/richtext/richblock_test.go

Исходный файл и его история в репозитории.
FILE b563e695df50e9cdf78e1c1c90243bcca783eac5
Files
Laniakea/tgfmt/richtext/richblock_test.go
T
ScuroNeko b563e695df
Golang lint / lint (push) Successful in 2m5s
(new): Telegram API 10.1
(new): rich text
2026-07-01 09:55:02 +03:00

187 lines
4.6 KiB
Go

package richtext
import (
"encoding/json"
"testing"
)
func roundtripBlock(t *testing.T, in RichBlock) {
t.Helper()
b, err := json.Marshal(in)
if err != nil {
t.Fatalf("marshal: %v", err)
}
out, err := UnmarshalBlock(b)
if err != nil {
t.Fatalf("unmarshal %s: %v", b, err)
}
b2, err := json.Marshal(out)
if err != nil {
t.Fatalf("remarshal: %v", err)
}
if string(b) != string(b2) {
t.Fatalf("not stable:\n %s\n %s", b, b2)
}
}
func TestBlockRoundtrip(t *testing.T) {
cases := []RichBlock{
// wrap-блоки
Paragraph(String("Hello, world")),
SectionHeading(Bold(String("Chapter 1"))),
Footer(String("© 2024")),
Thinking(String("Let me reason step by step.")),
// preformatted
BlockPreformatted{Text: String("fmt.Println(\"hi\")"), Language: "go"},
BlockPreformatted{Text: String("no language")},
// цитаты
BlockBlockQuotation{Text: String("To be or not to be"), Caption: String("Shakespeare")},
BlockPullQuotation{Text: String("Pull me"), Caption: nil},
// список
BlockList{
Items: []RichBlockListItem{
{Blocks: []RichBlock{Paragraph(String("item 1"))}},
{Blocks: []RichBlock{Paragraph(String("item 2"))}},
},
Ordered: true,
},
BlockList{
Items: []RichBlockListItem{
{Blocks: []RichBlock{Paragraph(String("bullet"))}},
},
Ordered: false,
},
// коллаж и слайдшоу
BlockCollage{
Items: []RichBlock{BlockPhoto{FileID: "abc123"}},
Caption: String("A photo"),
},
BlockSlideshow{
Items: []RichBlock{BlockVideo{FileID: "vid1", Autoplay: true, Loop: false}},
Caption: nil,
},
// details
BlockDetails{
Title: String("Spoiler"),
Blocks: []RichBlock{Paragraph(String("Hidden content"))},
Open: false,
},
BlockDetails{
Title: Bold(String("Open details")),
Blocks: []RichBlock{BlockDivider{}, Paragraph(String("content"))},
Open: true,
},
// таблица
BlockTable{
Title: String("Results"),
Rows: [][]RichBlockTableCell{
{
{Content: []RichBlock{Paragraph(String("Cell A1"))}},
{Content: []RichBlock{Paragraph(String("Cell A2"))}, ColumnSpan: 2},
},
{
{Content: []RichBlock{Paragraph(String("Cell B1"))}, RowSpan: 2},
{Content: []RichBlock{Paragraph(String("Cell B2"))}},
},
},
Bordered: true,
Striped: false,
},
// карта
BlockMap{
Latitude: 55.7558, Longitude: 37.6173,
Zoom: 12, Width: 800, Height: 400,
Caption: String("Moscow"),
},
// медиа
BlockPhoto{FileID: "photo_file_id", Caption: String("A cat"), URL: "https://example.com/cat.jpg"},
BlockPhoto{FileID: "bare_photo"},
BlockVideo{FileID: "video_file_id", Caption: String("Demo"), Autoplay: true, Loop: true},
BlockAudio{FileID: "audio_file_id", Caption: String("Podcast ep. 1")},
BlockAnimation{FileID: "anim_file_id"},
BlockVoiceNote{FileID: "voice_file_id"},
// листья
BlockDivider{},
BlockMathematicalExpression{Expression: "E = mc^2"},
BlockAnchor{Name: "section-2"},
}
for _, c := range cases {
roundtripBlock(t, c)
}
}
func TestRichMessageRoundtrip(t *testing.T) {
msg := RichMessage{
Blocks: []RichBlock{
SectionHeading(String("Title")),
Paragraph(Array{String("Some "), Bold(String("bold")), String(" text")}),
BlockDivider{},
BlockList{
Items: []RichBlockListItem{
{Blocks: []RichBlock{Paragraph(String("First"))}},
{Blocks: []RichBlock{Paragraph(String("Second"))}},
},
Ordered: true,
},
BlockPhoto{FileID: "img1", Caption: String("Fig. 1")},
},
}
b, err := json.Marshal(msg)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var out RichMessage
if err := json.Unmarshal(b, &out); err != nil {
t.Fatalf("unmarshal: %v", err)
}
b2, err := json.Marshal(out)
if err != nil {
t.Fatalf("remarshal: %v", err)
}
if string(b) != string(b2) {
t.Fatalf("not stable:\n %s\n %s", b, b2)
}
}
func TestBlockDividerHasNoContent(t *testing.T) {
b, _ := json.Marshal(BlockDivider{})
var m map[string]any
_ = json.Unmarshal(b, &m)
if len(m) != 1 {
t.Fatalf("divider must only have type field: %s", b)
}
if m["type"] != "divider" {
t.Fatalf("unexpected type: %s", b)
}
}
func TestBlockUnknownTypeWithTextIsForwardCompat(t *testing.T) {
raw := []byte(`{"type":"future_tag","text":"hello"}`)
b, err := UnmarshalBlock(raw)
if err != nil {
t.Fatalf("forward-compat failed: %v", err)
}
w, ok := b.(BlockWrap)
if !ok || w.Tag != "future_tag" {
t.Fatalf("expected BlockWrap{future_tag}, got %T", b)
}
}
func TestBlockUnknownTypeWithoutTextIsError(t *testing.T) {
raw := []byte(`{"type":"mystery_leaf","value":42}`)
_, err := UnmarshalBlock(raw)
if err == nil {
t.Fatal("expected error for unknown type without text")
}
}