package wire import ( "bytes" "testing" "github.com/google/uuid" "github.com/stretchr/testify/assert" ) func TestBARTInfo_HasClearIconHash(t *testing.T) { tests := []struct { name string bartInfo BARTInfo want bool }{ { bartInfo: BARTInfo{ Hash: GetClearIconHash(), }, want: true, }, { bartInfo: BARTInfo{ Hash: []byte{'s', 'o', 'm', 'e', 'd', 'a', 't', 'a'}, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.want, tt.bartInfo.HasClearIconHash()) }) } } func TestBARTID_StatusText(t *testing.T) { marshaled := func(t *testing.T, status string) []byte { t.Helper() b := &bytes.Buffer{} assert.NoError(t, MarshalBE(bartStatus{Status: status}, b)) return b.Bytes() } tests := []struct { name string id BARTID want string // wantErr reports whether the item's payload fails to decode. wantErr bool }{ { name: "a data-bearing status item carries its text", id: BARTID{ Type: BARTTypesStatusStr, BARTInfo: BARTInfo{Flags: BARTFlagsData, Hash: marshaled(t, "out to lunch")}, }, want: "out to lunch", }, { name: "empty text reads as no status message", id: BARTID{ Type: BARTTypesStatusStr, BARTInfo: BARTInfo{Flags: BARTFlagsData, Hash: marshaled(t, "")}, }, }, { name: "a status item without the data flag reads as no status message", id: BARTID{ Type: BARTTypesStatusStr, BARTInfo: BARTInfo{Flags: BARTFlagsKnown, Hash: marshaled(t, "out to lunch")}, }, }, { name: "another BART type is not a status message", id: BARTID{ Type: BARTTypesBuddyIcon, BARTInfo: BARTInfo{Flags: BARTFlagsData, Hash: marshaled(t, "out to lunch")}, }, }, { name: "a malformed item is an error", id: BARTID{ Type: BARTTypesStatusStr, BARTInfo: BARTInfo{Flags: BARTFlagsData, Hash: []byte{0x00}}, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := tt.id.StatusText() if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } assert.Equal(t, tt.want, got) }) } } func TestSNAC_0x01_0x14_OServiceSetPrivacyFlags_IdleFlag(t *testing.T) { type fields struct { PrivacyFlags uint32 } tests := []struct { name string fields fields want bool }{ { name: "flag is set", fields: fields{ PrivacyFlags: OServicePrivacyFlagIdle | OServicePrivacyFlagMember, }, want: true, }, { name: "flag is not set", fields: fields{ PrivacyFlags: OServicePrivacyFlagMember, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s := SNAC_0x01_0x14_OServiceSetPrivacyFlags{ PrivacyFlags: tt.fields.PrivacyFlags, } assert.Equal(t, tt.want, s.IdleFlag()) }) } } func TestSNAC_0x01_0x14_OServiceSetPrivacyFlags_MemberFlag(t *testing.T) { type fields struct { PrivacyFlags uint32 } tests := []struct { name string fields fields want bool }{ { name: "flag is set", fields: fields{ PrivacyFlags: OServicePrivacyFlagIdle | OServicePrivacyFlagMember, }, want: true, }, { name: "flag is not set", fields: fields{ PrivacyFlags: OServicePrivacyFlagIdle, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s := SNAC_0x01_0x14_OServiceSetPrivacyFlags{ PrivacyFlags: tt.fields.PrivacyFlags, } assert.Equal(t, tt.want, s.MemberFlag()) }) } } func TestUnmarshalChatMessageText(t *testing.T) { tests := []struct { name string b []byte want string wantErr string }{ { name: "happy path", b: func() []byte { tlv := TLVRestBlock{ TLVList: TLVList{ NewTLVBE(ChatTLVMessageInfoText, "
hello world!
"), }, } b := &bytes.Buffer{} err := MarshalBE(tlv, b) assert.NoError(t, err) return b.Bytes() }(), want: "hello world!
", }, { name: "missing ChatTLVMessageInfoText", b: func() []byte { tlv := TLVRestBlock{TLVList: TLVList{}} b := &bytes.Buffer{} err := MarshalBE(tlv, b) assert.NoError(t, err) return b.Bytes() }(), wantErr: "has no chat msg text TLV", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := UnmarshalChatMessageText(tt.b) if tt.wantErr != "" { assert.ErrorContains(t, err, tt.wantErr) } else { assert.NoError(t, err) assert.Equal(t, tt.want, got) } }) } } func TestTLVUserInfo_IsAway(t *testing.T) { tests := []struct { name string userInfo TLVUserInfo want bool }{ { name: "flag is set", userInfo: TLVUserInfo{ TLVBlock: TLVBlock{ TLVList: TLVList{ NewTLVBE(OServiceUserInfoUserFlags, OServiceUserFlagUnavailable), }, }, }, want: true, }, { name: "flag is not set", userInfo: TLVUserInfo{ TLVBlock: TLVBlock{ TLVList: TLVList{ NewTLVBE(OServiceUserInfoUserFlags, OServiceUserFlagOSCARFree), }, }, }, want: false, }, { name: "TLV is missing", userInfo: TLVUserInfo{ TLVBlock: TLVBlock{ TLVList: TLVList{}, }, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.want, tt.userInfo.IsAway()) }) } } func TestTLVUserInfo_IsInvisible(t *testing.T) { tests := []struct { name string userInfo TLVUserInfo want bool }{ { name: "status mask has invisible bit set", userInfo: TLVUserInfo{ TLVBlock: TLVBlock{ TLVList: TLVList{ NewTLVBE(OServiceUserInfoStatus, OServiceUserStatusInvisible), }, }, }, want: true, }, { name: "status mask does not have invisible bit set", userInfo: TLVUserInfo{ TLVBlock: TLVBlock{ TLVList: TLVList{ NewTLVBE(OServiceUserInfoStatus, OServiceUserStatusAvailable), }, }, }, want: false, }, { name: "TLV is missing", userInfo: TLVUserInfo{ TLVBlock: TLVBlock{ TLVList: TLVList{}, }, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.want, tt.userInfo.IsInvisible()) }) } } func TestCapabilityUUIDs(t *testing.T) { tests := []struct { name string cap uuid.UUID expected string }{ // Original capabilities {"CapChat", CapChat, "748f2420-6287-11d1-8222-444553540000"}, {"CapFileTransfer", CapFileTransfer, "09461343-4c7f-11d1-8222-444553540000"}, // Short caps {"CapShortCaps", CapShortCaps, "09460000-4c7f-11d1-8222-444553540000"}, {"CapSecureIM", CapSecureIM, "09460001-4c7f-11d1-8222-444553540000"}, {"CapXHTMLIM", CapXHTMLIM, "09460002-4c7f-11d1-8222-444553540000"}, {"CapRTCVideo", CapRTCVideo, "09460101-4c7f-11d1-8222-444553540000"}, {"CapHasCamera", CapHasCamera, "09460102-4c7f-11d1-8222-444553540000"}, {"CapHasMicrophone", CapHasMicrophone, "09460103-4c7f-11d1-8222-444553540000"}, {"CapRTCAudio", CapRTCAudio, "09460104-4c7f-11d1-8222-444553540000"}, {"CapHostStatusTextAware", CapHostStatusTextAware, "0946010a-4c7f-11d1-8222-444553540000"}, {"CapRTIM", CapRTIM, "0946010b-4c7f-11d1-8222-444553540000"}, {"CapSmartCaps", CapSmartCaps, "094601ff-4c7f-11d1-8222-444553540000"}, {"CapVoiceChat", CapVoiceChat, "09461341-4c7f-11d1-8222-444553540000"}, {"CapDirectPlay", CapDirectPlay, "09461342-4c7f-11d1-8222-444553540000"}, {"CapRouteFinder", CapRouteFinder, "09461344-4c7f-11d1-8222-444553540000"}, {"CapDirectICBM", CapDirectICBM, "09461345-4c7f-11d1-8222-444553540000"}, {"CapAvatarService", CapAvatarService, "09461346-4c7f-11d1-8222-444553540000"}, {"CapStocksAddins", CapStocksAddins, "09461347-4c7f-11d1-8222-444553540000"}, {"CapFileSharing", CapFileSharing, "09461348-4c7f-11d1-8222-444553540000"}, {"CapICQCh2Extended", CapICQCh2Extended, "09461349-4c7f-11d1-8222-444553540000"}, {"CapGames", CapGames, "0946134a-4c7f-11d1-8222-444553540000"}, {"CapBuddyListTransfer", CapBuddyListTransfer, "0946134b-4c7f-11d1-8222-444553540000"}, {"CapSupportICQ", CapSupportICQ, "0946134d-4c7f-11d1-8222-444553540000"}, {"CapUTF8Messages", CapUTF8Messages, "0946134e-4c7f-11d1-8222-444553540000"}, // Full UUIDs {"CapRTFMessages", CapRTFMessages, "97b12751-243c-4334-ad22-d6abf73f1492"}, {"CapTrillianSecureIM", CapTrillianSecureIM, "f2e7c7f4-fead-4dfb-b235-36798bdf0000"}, {"CapXtrazScript", CapXtrazScript, "3b60b3ef-d82a-6c45-a4e0-9c5a5e67e865"}, // Unknown/Legacy {"CapUnknownICQ2001_2002", CapUnknownICQ2001_2002, "a0e93f37-4c7f-11d1-8222-444553540000"}, {"CapUnknownICQ2002", CapUnknownICQ2002, "10cf40d1-4c7f-11d1-8222-444553540000"}, {"CapUnknownICQ2001", CapUnknownICQ2001, "2e7a6475-fadf-4dc8-886f-ea3595fdb6df"}, {"CapUnknownICQLite", CapUnknownICQLite, "563fc809-0b6f-41bd-9f79-422609dfa2f3"}, {"CapGamesAlt", CapGamesAlt, "0946134a-4c7f-11d1-2282-444553540000"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.expected, tt.cap.String()) }) } } func TestShortCapHexToUUID(t *testing.T) { tests := []struct { name string hexStr string wantUUID uuid.UUID wantOK bool }{ {"1348 -> CapFileSharing", "1348", CapFileSharing, true}, {"134B -> CapBuddyListTransfer", "134B", CapBuddyListTransfer, true}, {"1FF -> CapSmartCaps", "1FF", CapSmartCaps, true}, {"1341 -> CapVoiceChat", "1341", CapVoiceChat, true}, {"0000 -> CapShortCaps", "0000", CapShortCaps, true}, {"empty string", "", uuid.Nil, false}, {"invalid hex", "GGGG", uuid.Nil, false}, {"too large for uint16", "10000", uuid.Nil, false}, {"whitespace trimmed", " 1348 ", CapFileSharing, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, ok := ShortCapHexToUUID(tt.hexStr) assert.Equal(t, tt.wantOK, ok) if tt.wantOK { assert.Equal(t, tt.wantUUID, got) } else { assert.Equal(t, uuid.Nil, got) } }) } } func TestFeedbagItem_AppendOrderMembers(t *testing.T) { t.Run("creates order TLV when none exists", func(t *testing.T) { item := FeedbagItem{ClassID: FeedbagClassIdGroup, GroupID: 1} item.AppendOrderMembers(10, 20) order, ok := item.Uint16SliceBE(FeedbagAttributesOrder) assert.True(t, ok) assert.Equal(t, []uint16{10, 20}, order) }) t.Run("appends to existing order TLV", func(t *testing.T) { item := FeedbagItem{ ClassID: FeedbagClassIdGroup, GroupID: 1, TLVLBlock: TLVLBlock{ TLVList: TLVList{ NewTLVBE(FeedbagAttributesOrder, []uint16{5, 10}), }, }, } item.AppendOrderMembers(15, 20) order, ok := item.Uint16SliceBE(FeedbagAttributesOrder) assert.True(t, ok) assert.Equal(t, []uint16{5, 10, 15, 20}, order) }) t.Run("single member", func(t *testing.T) { item := FeedbagItem{ClassID: FeedbagClassIdGroup} item.AppendOrderMembers(42) order, ok := item.Uint16SliceBE(FeedbagAttributesOrder) assert.True(t, ok) assert.Equal(t, []uint16{42}, order) }) } func TestFeedbagItem_RemoveOrderMembers(t *testing.T) { t.Run("removes specified members", func(t *testing.T) { item := FeedbagItem{ ClassID: FeedbagClassIdGroup, TLVLBlock: TLVLBlock{ TLVList: TLVList{ NewTLVBE(FeedbagAttributesOrder, []uint16{5, 10, 15, 20}), }, }, } item.RemoveOrderMembers(10, 20) order, ok := item.Uint16SliceBE(FeedbagAttributesOrder) assert.True(t, ok) assert.Equal(t, []uint16{5, 15}, order) }) t.Run("no-op when order TLV does not exist", func(t *testing.T) { item := FeedbagItem{ClassID: FeedbagClassIdGroup} item.RemoveOrderMembers(10) _, ok := item.Uint16SliceBE(FeedbagAttributesOrder) assert.False(t, ok) }) t.Run("removes all members", func(t *testing.T) { item := FeedbagItem{ ClassID: FeedbagClassIdGroup, TLVLBlock: TLVLBlock{ TLVList: TLVList{ NewTLVBE(FeedbagAttributesOrder, []uint16{5, 10}), }, }, } item.RemoveOrderMembers(5, 10) order, ok := item.Uint16SliceBE(FeedbagAttributesOrder) assert.True(t, ok) assert.Empty(t, order) }) t.Run("ignores IDs not in order", func(t *testing.T) { item := FeedbagItem{ ClassID: FeedbagClassIdGroup, TLVLBlock: TLVLBlock{ TLVList: TLVList{ NewTLVBE(FeedbagAttributesOrder, []uint16{5, 10}), }, }, } item.RemoveOrderMembers(99) order, ok := item.Uint16SliceBE(FeedbagAttributesOrder) assert.True(t, ok) assert.Equal(t, []uint16{5, 10}, order) }) } func TestAllASCII(t *testing.T) { tests := []struct { name string msg string want bool }{ {name: "empty", msg: "", want: true}, {name: "ASCII", msg: "hello world!", want: true}, {name: "DEL is ASCII", msg: "\x7f", want: true}, {name: "Latin-1 byte", msg: "\xe9", want: false}, {name: "multibyte UTF-8", msg: "héllo", want: false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.want, AllASCII(tt.msg)) assert.Equal(t, tt.want, AllASCII([]byte(tt.msg))) }) } } func TestICBMFragmentList(t *testing.T) { tests := []struct { name string text string want func(string) ([]ICBMCh1Fragment, error) }{ {name: "ASCII text is encoded as ASCII", text: "hello world!", want: ICBMFragmentListASCII}, {name: "empty text is encoded as ASCII", text: "", want: ICBMFragmentListASCII}, {name: "non-ASCII text is encoded as UCS-2", text: "héllo 日本", want: ICBMFragmentListUnicode}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { want, err := tt.want(tt.text) assert.NoError(t, err) got, err := ICBMFragmentList(tt.text) assert.NoError(t, err) assert.Equal(t, want, got) }) } }