spark-server/internal/module/message/entity/content_test.go
2026-09-18 14:50:41 +08:00

135 lines
3.6 KiB
Go

package entity
import (
"bytes"
"encoding/json"
"strings"
"testing"
)
func TestTextMarshalIsJSONBytesNotRawString(t *testing.T) {
c := NewText("hello")
raw, err := c.Marshal()
if err != nil {
t.Fatal(err)
}
if bytes.Equal(raw, []byte("hello")) || !json.Valid(raw) {
t.Fatalf("want json bytes, got %q", raw)
}
if !bytes.Contains(raw, []byte(`"t":1`)) || !bytes.Contains(raw, []byte(`"body":"hello"`)) {
t.Fatalf("%s", raw)
}
got, err := UnmarshalContent(raw)
if err != nil {
t.Fatal(err)
}
if got.Type != TypeText || got.Text == nil || got.Text.Body != "hello" {
t.Fatalf("%+v", got)
}
if got.ReplyTo != 0 {
t.Fatalf("reply_to %d", got.ReplyTo)
}
}
func TestReplyToRoundTrip(t *testing.T) {
c := NewText("re")
c.ReplyTo = 42
raw, err := c.Marshal()
if err != nil {
t.Fatal(err)
}
got, err := UnmarshalContent(raw)
if err != nil || got.ReplyTo != 42 || got.Text.Body != "re" {
t.Fatalf("%v %+v", err, got)
}
}
func TestMediaTypesRequireObjectKey(t *testing.T) {
cases := []*Content{
NewImage(&Image{Key: "obj/a.jpg", Width: 10, Height: 10, Mime: "image/jpeg"}),
NewVoice(&Voice{Key: "obj/a.m4a", DurationMS: 1200}),
NewVideo(&Video{Key: "obj/a.mp4", CoverKey: "obj/c.jpg"}),
NewGIF(&GIF{Key: "obj/a.gif", Mime: "image/gif"}),
NewFile(&File{Key: "obj/a.pdf", Name: "a.pdf", Size: 9}),
NewEmoji(&Emoji{Pack: "spark", ID: "smile"}),
NewLocation(&Location{Lat: 31.2, Lng: 121.5, Name: "Shanghai"}),
NewSystem(&System{Kind: "member_join", Body: "joined"}),
}
for _, c := range cases {
raw, err := c.Marshal()
if err != nil {
t.Fatalf("type %d: %v", c.Type, err)
}
if !json.Valid(raw) {
t.Fatalf("type %d not json %q", c.Type, raw)
}
got, err := UnmarshalContent(raw)
if err != nil || got.Type != c.Type {
t.Fatalf("type %d roundtrip %v %+v", c.Type, err, got)
}
}
}
func TestRejectsRawStringAndEmptyKey(t *testing.T) {
if _, err := UnmarshalContent([]byte("just a string")); err == nil {
t.Fatal("raw string")
}
if _, err := NewText(" ").Marshal(); err == nil {
t.Fatal("blank text")
}
if _, err := NewImage(&Image{}).Marshal(); err == nil || !strings.Contains(err.Error(), "key") {
t.Fatalf("image key: %v", err)
}
bad := &Content{Type: TypeText, Text: &Text{Body: "x"}, Image: &Image{Key: "k"}}
if _, err := bad.Marshal(); err == nil {
t.Fatal("two payloads")
}
}
func TestMessagePayloadChecksColumnType(t *testing.T) {
raw, err := NewText("hi").Marshal()
if err != nil {
t.Fatal(err)
}
m := &Message{Type: TypeImage, Content: raw}
if _, err := m.Payload(); err == nil {
t.Fatal("mismatch")
}
m.Type = TypeText
c, err := m.Payload()
if err != nil || c.Text.Body != "hi" {
t.Fatalf("%v %+v", err, c)
}
}
func TestMentionsRoundTrip(t *testing.T) {
c := NewText("hello @all")
c.AtAll = true
c.AtUIDs = []int64{2, 2, 3}
raw, err := c.Marshal()
if err != nil {
t.Fatal(err)
}
got, err := UnmarshalContent(raw)
if err != nil || !got.AtAll || len(got.AtUIDs) != 2 || got.AtUIDs[0] != 2 || got.AtUIDs[1] != 3 {
t.Fatalf("%v %+v", err, got)
}
sys := NewSystem(&System{Kind: "x", Body: "y"})
sys.AtAll = true
if _, err := sys.Marshal(); err == nil {
t.Fatal("system mentions")
}
}
func TestObjectKeysAndReplace(t *testing.T) {
img := NewImage(&Image{Key: "chat/a.jpg", ThumbKey: "chat/t.jpg"})
keys := img.ObjectKeys()
if len(keys) != 2 || keys[0] != "chat/a.jpg" {
t.Fatalf("%v", keys)
}
img.ReplaceObjectKeys(map[string]string{"chat/a.jpg": "perm/fav/1/x.jpg", "chat/t.jpg": "perm/fav/1/y.jpg"})
if img.Image.Key != "perm/fav/1/x.jpg" || img.Image.ThumbKey != "perm/fav/1/y.jpg" {
t.Fatalf("%+v", img.Image)
}
}