| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- package wire
- import (
- "encoding/binary"
- "encoding/hex"
- "math"
- "strings"
- "testing"
- "github.com/google/uuid"
- "github.com/stretchr/testify/assert"
- )
- func TestUnmangleXtrazXML(t *testing.T) {
- tests := []struct {
- name string
- mangled string
- want string
- }{
- {
- name: "unmangle basic entities",
- mangled: "<N><QUERY></QUERY></N>",
- want: "<N><QUERY></QUERY></N>",
- },
- {
- name: "unmangle all entities",
- mangled: "<tag attr="value">text & more</tag>",
- want: `<tag attr="value">text & more</tag>`,
- },
- {
- name: "plain text unchanged",
- mangled: "plain text",
- want: "plain text",
- },
- {
- name: "empty string",
- mangled: "",
- want: "",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got := UnmangleXtrazXML(tt.mangled)
- assert.Equal(t, tt.want, got)
- })
- }
- }
- func TestMangleXtrazXML(t *testing.T) {
- tests := []struct {
- name string
- plain string
- want string
- }{
- {
- name: "mangle basic XML",
- plain: "<N><QUERY></QUERY></N>",
- want: "<N><QUERY></QUERY></N>",
- },
- {
- name: "mangle special chars",
- plain: `<tag attr="value">text & more</tag>`,
- want: "<tag attr="value">text & more</tag>",
- },
- {
- name: "plain text unchanged",
- plain: "plain text",
- want: "plain text",
- },
- {
- name: "empty string",
- plain: "",
- want: "",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got := MangleXtrazXML(tt.plain)
- assert.Equal(t, tt.want, got)
- })
- }
- }
- func TestParseXtrazNotifyRequest(t *testing.T) {
- tests := []struct {
- name string
- xml string
- want *XtrazNotifyRequest
- wantErr bool
- }{
- {
- name: "parse valid XStatus request",
- xml: `<N><QUERY><PluginID>srvMng</PluginID></QUERY>` +
- `<NOTIFY><srv><id>cAwaySrv</id>` +
- `<req><id>AwayStat</id><trans>1</trans><senderId>123456</senderId></req>` +
- `</srv></NOTIFY></N>`,
- want: &XtrazNotifyRequest{
- PluginID: "srvMng",
- ServiceID: "cAwaySrv",
- RequestID: "AwayStat",
- TransID: "1",
- SenderID: "123456",
- },
- wantErr: false,
- },
- {
- name: "parse invalid XML",
- xml: "<invalid>",
- want: nil,
- wantErr: true,
- },
- {
- name: "parse empty XML",
- xml: "",
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ParseXtrazNotifyRequest([]byte(tt.xml))
- if tt.wantErr {
- assert.Error(t, err)
- return
- }
- assert.NoError(t, err)
- assert.Equal(t, tt.want, got)
- })
- }
- }
- func TestParseXtrazNotifyResponse(t *testing.T) {
- tests := []struct {
- name string
- xml string
- want *XtrazNotifyResponse
- wantErr bool
- }{
- {
- name: "parse valid XStatus response",
- xml: `<NR><RES><ret event="OnRemoteNotification"><srv><id></id>` +
- `<val srv_id="cAwaySrv"><Root><CASXtraSetAwayMessage></CASXtraSetAwayMessage>` +
- `<uin>123456</uin><index>5</index><title>Having a beer</title>` +
- `<desc>Cheers!</desc></Root></val></srv></ret></RES></NR>`,
- want: &XtrazNotifyResponse{
- UIN: "123456",
- Index: 5,
- Title: "Having a beer",
- Message: "Cheers!",
- },
- wantErr: false,
- },
- {
- name: "parse XML without Root element",
- xml: "<NR><RES></RES></NR>",
- want: nil,
- wantErr: true,
- },
- {
- name: "parse empty XML",
- xml: "",
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ParseXtrazNotifyResponse([]byte(tt.xml))
- if tt.wantErr {
- assert.Error(t, err)
- return
- }
- assert.NoError(t, err)
- assert.Equal(t, tt.want, got)
- })
- }
- }
- func TestBuildXtrazNotifyRequest(t *testing.T) {
- senderUIN := "123456"
- result := BuildXtrazNotifyRequest(senderUIN)
- // Unmangle and verify the structure
- unmangled := UnmangleXtrazXML(result)
- assert.Contains(t, unmangled, "<N>")
- assert.Contains(t, unmangled, "<PluginID>srvMng</PluginID>")
- assert.Contains(t, unmangled, "<senderId>123456</senderId>")
- assert.Contains(t, unmangled, "<id>AwayStat</id>")
- assert.Contains(t, unmangled, "<id>cAwaySrv</id>")
- // Verify it can be parsed back
- parsed, err := ParseXtrazNotifyRequest([]byte(unmangled))
- assert.NoError(t, err)
- assert.Equal(t, "srvMng", parsed.PluginID)
- assert.Equal(t, "cAwaySrv", parsed.ServiceID)
- assert.Equal(t, "AwayStat", parsed.RequestID)
- assert.Equal(t, senderUIN, parsed.SenderID)
- }
- func TestBuildXtrazNotifyResponse(t *testing.T) {
- uin := "123456"
- index := uint8(5)
- title := "Having a beer"
- message := "Cheers!"
- result := BuildXtrazNotifyResponse(uin, index, title, message)
- // Unmangle and verify the structure
- unmangled := UnmangleXtrazXML(result)
- assert.Contains(t, unmangled, "<NR>")
- assert.Contains(t, unmangled, "<uin>123456</uin>")
- assert.Contains(t, unmangled, "<index>5</index>")
- // Verify it can be parsed back
- parsed, err := ParseXtrazNotifyResponse([]byte(unmangled))
- assert.NoError(t, err)
- assert.Equal(t, uin, parsed.UIN)
- assert.Equal(t, index, parsed.Index)
- }
- func TestXtrazCapabilityGUID(t *testing.T) {
- // Verify the GUID matches the expected GUID
- expected := "3b60b3ef-d82a-6c45-a4e0-9c5a5e67e865"
- assert.Equal(t, expected, CapXtrazScript.String())
- }
- func TestXStatusConstants(t *testing.T) {
- assert.Equal(t, uint8(1), XStatusAngry)
- assert.Equal(t, uint8(32), XStatusCoffee2)
- }
- func TestMoodByID(t *testing.T) {
- tests := []struct {
- name string
- id string
- want Mood
- ok bool
- }{
- {
- name: "mood with a real ICQ capability",
- id: "0icqmood6",
- want: Mood{ID: "0icqmood6", Cap: CapXStatusPlate},
- ok: true,
- },
- {
- name: "mood with a placeholder capability",
- id: "0icqmood13",
- want: Mood{ID: "0icqmood13", Cap: CapMoodHavingFun},
- ok: true,
- },
- {
- name: "unknown token",
- id: "0icqmood999",
- },
- {
- name: "token missing the leading zero",
- id: "icqmood6",
- },
- {
- name: "empty token",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, ok := MoodByID(tt.id)
- assert.Equal(t, tt.ok, ok)
- assert.Equal(t, tt.want, got)
- })
- }
- }
- func TestMoodByCap(t *testing.T) {
- tests := []struct {
- name string
- cap uuid.UUID
- wantID string
- ok bool
- }{
- {
- name: "capability used by one mood",
- cap: CapXStatusBeer,
- wantID: "0icqmood4",
- ok: true,
- },
- {
- name: "capability shared by two moods returns the canonical one",
- cap: CapXStatusConsole,
- wantID: "0icqmood81",
- ok: true,
- },
- {
- name: "other shared capability returns the canonical one",
- cap: CapXStatusSleeping,
- wantID: "0icqmood70",
- ok: true,
- },
- {
- name: "placeholder capability",
- cap: CapMoodOnTheWay,
- wantID: "0icqmood83",
- ok: true,
- },
- {
- name: "capability that is not a mood",
- cap: CapChat,
- },
- {
- name: "nil capability",
- cap: uuid.Nil,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, ok := MoodByCap(tt.cap)
- assert.Equal(t, tt.ok, ok)
- assert.Equal(t, tt.wantID, got.ID)
- assert.Equal(t, tt.ok, IsMoodCap(tt.cap))
- })
- }
- }
- // TestMoodsRoundTrip verifies every token resolves to itself and every
- // capability to a token that carries it.
- func TestMoodsRoundTrip(t *testing.T) {
- canonical := make(map[uuid.UUID]string)
- for _, m := range Moods {
- t.Run(m.ID, func(t *testing.T) {
- byID, ok := MoodByID(m.ID)
- assert.True(t, ok)
- assert.Equal(t, m, byID)
- // A mood with no capability is invisible to other users.
- assert.NotEqual(t, uuid.Nil, m.Cap)
- byCap, ok := MoodByCap(m.Cap)
- assert.True(t, ok)
- assert.Equal(t, m.Cap, byCap.Cap)
- })
- if first, seen := canonical[m.Cap]; seen {
- // A shared capability resolves to whichever mood is listed first.
- byCap, _ := MoodByCap(m.Cap)
- assert.Equal(t, first, byCap.ID)
- continue
- }
- canonical[m.Cap] = m.ID
- }
- }
- func TestMoodIconID(t *testing.T) {
- tests := []struct {
- name string
- id string
- want string
- }{
- {
- name: "encodes length prefix and token bytes",
- id: "0icqmood6",
- want: "0009306963716d6f6f6436",
- },
- {
- name: "encodes a two digit mood",
- id: "0icqmood23",
- want: "000a306963716d6f6f643233",
- },
- {
- name: "empty token encodes to nothing",
- id: "",
- want: "",
- },
- {
- name: "oversized token encodes to nothing",
- id: strings.Repeat("a", math.MaxUint16+1),
- want: "",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- assert.Equal(t, tt.want, MoodIconID(tt.id))
- })
- }
- }
- // TestMoodIconIDDecodes verifies every token encodes to something ICQ clients can
- // decode: even-length hex whose length prefix matches the token that follows.
- func TestMoodIconIDDecodes(t *testing.T) {
- for _, m := range Moods {
- t.Run(m.ID, func(t *testing.T) {
- encoded := MoodIconID(m.ID)
- assert.Zero(t, len(encoded)%2)
- raw, err := hex.DecodeString(encoded)
- assert.NoError(t, err)
- assert.Equal(t, len(m.ID), int(binary.BigEndian.Uint16(raw[:2])))
- assert.Equal(t, m.ID, string(raw[2:]))
- })
- }
- }
|