snacs_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. package wire
  2. import (
  3. "bytes"
  4. "testing"
  5. "github.com/google/uuid"
  6. "github.com/stretchr/testify/assert"
  7. )
  8. func TestBARTInfo_HasClearIconHash(t *testing.T) {
  9. tests := []struct {
  10. name string
  11. bartInfo BARTInfo
  12. want bool
  13. }{
  14. {
  15. bartInfo: BARTInfo{
  16. Hash: GetClearIconHash(),
  17. },
  18. want: true,
  19. },
  20. {
  21. bartInfo: BARTInfo{
  22. Hash: []byte{'s', 'o', 'm', 'e', 'd', 'a', 't', 'a'},
  23. },
  24. want: false,
  25. },
  26. }
  27. for _, tt := range tests {
  28. t.Run(tt.name, func(t *testing.T) {
  29. assert.Equal(t, tt.want, tt.bartInfo.HasClearIconHash())
  30. })
  31. }
  32. }
  33. func TestSNAC_0x01_0x14_OServiceSetPrivacyFlags_IdleFlag(t *testing.T) {
  34. type fields struct {
  35. PrivacyFlags uint32
  36. }
  37. tests := []struct {
  38. name string
  39. fields fields
  40. want bool
  41. }{
  42. {
  43. name: "flag is set",
  44. fields: fields{
  45. PrivacyFlags: OServicePrivacyFlagIdle | OServicePrivacyFlagMember,
  46. },
  47. want: true,
  48. },
  49. {
  50. name: "flag is not set",
  51. fields: fields{
  52. PrivacyFlags: OServicePrivacyFlagMember,
  53. },
  54. want: false,
  55. },
  56. }
  57. for _, tt := range tests {
  58. t.Run(tt.name, func(t *testing.T) {
  59. s := SNAC_0x01_0x14_OServiceSetPrivacyFlags{
  60. PrivacyFlags: tt.fields.PrivacyFlags,
  61. }
  62. assert.Equal(t, tt.want, s.IdleFlag())
  63. })
  64. }
  65. }
  66. func TestSNAC_0x01_0x14_OServiceSetPrivacyFlags_MemberFlag(t *testing.T) {
  67. type fields struct {
  68. PrivacyFlags uint32
  69. }
  70. tests := []struct {
  71. name string
  72. fields fields
  73. want bool
  74. }{
  75. {
  76. name: "flag is set",
  77. fields: fields{
  78. PrivacyFlags: OServicePrivacyFlagIdle | OServicePrivacyFlagMember,
  79. },
  80. want: true,
  81. },
  82. {
  83. name: "flag is not set",
  84. fields: fields{
  85. PrivacyFlags: OServicePrivacyFlagIdle,
  86. },
  87. want: false,
  88. },
  89. }
  90. for _, tt := range tests {
  91. t.Run(tt.name, func(t *testing.T) {
  92. s := SNAC_0x01_0x14_OServiceSetPrivacyFlags{
  93. PrivacyFlags: tt.fields.PrivacyFlags,
  94. }
  95. assert.Equal(t, tt.want, s.MemberFlag())
  96. })
  97. }
  98. }
  99. func TestUnmarshalChatMessageText(t *testing.T) {
  100. tests := []struct {
  101. name string
  102. b []byte
  103. want string
  104. wantErr string
  105. }{
  106. {
  107. name: "happy path",
  108. b: func() []byte {
  109. tlv := TLVRestBlock{
  110. TLVList: TLVList{
  111. NewTLVBE(ChatTLVMessageInfoText, "<p>hello world!</p>"),
  112. },
  113. }
  114. b := &bytes.Buffer{}
  115. err := MarshalBE(tlv, b)
  116. assert.NoError(t, err)
  117. return b.Bytes()
  118. }(),
  119. want: "<p>hello world!</p>",
  120. },
  121. {
  122. name: "missing ChatTLVMessageInfoText",
  123. b: func() []byte {
  124. tlv := TLVRestBlock{TLVList: TLVList{}}
  125. b := &bytes.Buffer{}
  126. err := MarshalBE(tlv, b)
  127. assert.NoError(t, err)
  128. return b.Bytes()
  129. }(),
  130. wantErr: "has no chat msg text TLV",
  131. },
  132. }
  133. for _, tt := range tests {
  134. t.Run(tt.name, func(t *testing.T) {
  135. got, err := UnmarshalChatMessageText(tt.b)
  136. if tt.wantErr != "" {
  137. assert.ErrorContains(t, err, tt.wantErr)
  138. } else {
  139. assert.NoError(t, err)
  140. assert.Equal(t, tt.want, got)
  141. }
  142. })
  143. }
  144. }
  145. func TestTLVUserInfo_IsAway(t *testing.T) {
  146. tests := []struct {
  147. name string
  148. userInfo TLVUserInfo
  149. want bool
  150. }{
  151. {
  152. name: "flag is set",
  153. userInfo: TLVUserInfo{
  154. TLVBlock: TLVBlock{
  155. TLVList: TLVList{
  156. NewTLVBE(OServiceUserInfoUserFlags, OServiceUserFlagUnavailable),
  157. },
  158. },
  159. },
  160. want: true,
  161. },
  162. {
  163. name: "flag is not set",
  164. userInfo: TLVUserInfo{
  165. TLVBlock: TLVBlock{
  166. TLVList: TLVList{
  167. NewTLVBE(OServiceUserInfoUserFlags, OServiceUserFlagOSCARFree),
  168. },
  169. },
  170. },
  171. want: false,
  172. },
  173. {
  174. name: "TLV is missing",
  175. userInfo: TLVUserInfo{
  176. TLVBlock: TLVBlock{
  177. TLVList: TLVList{},
  178. },
  179. },
  180. want: false,
  181. },
  182. }
  183. for _, tt := range tests {
  184. t.Run(tt.name, func(t *testing.T) {
  185. assert.Equal(t, tt.want, tt.userInfo.IsAway())
  186. })
  187. }
  188. }
  189. func TestTLVUserInfo_IsInvisible(t *testing.T) {
  190. tests := []struct {
  191. name string
  192. userInfo TLVUserInfo
  193. want bool
  194. }{
  195. {
  196. name: "status mask has invisible bit set",
  197. userInfo: TLVUserInfo{
  198. TLVBlock: TLVBlock{
  199. TLVList: TLVList{
  200. NewTLVBE(OServiceUserInfoStatus, OServiceUserStatusInvisible),
  201. },
  202. },
  203. },
  204. want: true,
  205. },
  206. {
  207. name: "status mask does not have invisible bit set",
  208. userInfo: TLVUserInfo{
  209. TLVBlock: TLVBlock{
  210. TLVList: TLVList{
  211. NewTLVBE(OServiceUserInfoStatus, OServiceUserStatusAvailable),
  212. },
  213. },
  214. },
  215. want: false,
  216. },
  217. {
  218. name: "TLV is missing",
  219. userInfo: TLVUserInfo{
  220. TLVBlock: TLVBlock{
  221. TLVList: TLVList{},
  222. },
  223. },
  224. want: false,
  225. },
  226. }
  227. for _, tt := range tests {
  228. t.Run(tt.name, func(t *testing.T) {
  229. assert.Equal(t, tt.want, tt.userInfo.IsInvisible())
  230. })
  231. }
  232. }
  233. func TestCapabilityUUIDs(t *testing.T) {
  234. tests := []struct {
  235. name string
  236. cap uuid.UUID
  237. expected string
  238. }{
  239. // Original capabilities
  240. {"CapChat", CapChat, "748f2420-6287-11d1-8222-444553540000"},
  241. {"CapFileTransfer", CapFileTransfer, "09461343-4c7f-11d1-8222-444553540000"},
  242. // Short caps
  243. {"CapShortCaps", CapShortCaps, "09460000-4c7f-11d1-8222-444553540000"},
  244. {"CapSecureIM", CapSecureIM, "09460001-4c7f-11d1-8222-444553540000"},
  245. {"CapXHTMLIM", CapXHTMLIM, "09460002-4c7f-11d1-8222-444553540000"},
  246. {"CapRTCVideo", CapRTCVideo, "09460101-4c7f-11d1-8222-444553540000"},
  247. {"CapHasCamera", CapHasCamera, "09460102-4c7f-11d1-8222-444553540000"},
  248. {"CapHasMicrophone", CapHasMicrophone, "09460103-4c7f-11d1-8222-444553540000"},
  249. {"CapRTCAudio", CapRTCAudio, "09460104-4c7f-11d1-8222-444553540000"},
  250. {"CapHostStatusTextAware", CapHostStatusTextAware, "0946010a-4c7f-11d1-8222-444553540000"},
  251. {"CapRTIM", CapRTIM, "0946010b-4c7f-11d1-8222-444553540000"},
  252. {"CapSmartCaps", CapSmartCaps, "094601ff-4c7f-11d1-8222-444553540000"},
  253. {"CapVoiceChat", CapVoiceChat, "09461341-4c7f-11d1-8222-444553540000"},
  254. {"CapDirectPlay", CapDirectPlay, "09461342-4c7f-11d1-8222-444553540000"},
  255. {"CapRouteFinder", CapRouteFinder, "09461344-4c7f-11d1-8222-444553540000"},
  256. {"CapDirectICBM", CapDirectICBM, "09461345-4c7f-11d1-8222-444553540000"},
  257. {"CapAvatarService", CapAvatarService, "09461346-4c7f-11d1-8222-444553540000"},
  258. {"CapStocksAddins", CapStocksAddins, "09461347-4c7f-11d1-8222-444553540000"},
  259. {"CapFileSharing", CapFileSharing, "09461348-4c7f-11d1-8222-444553540000"},
  260. {"CapICQCh2Extended", CapICQCh2Extended, "09461349-4c7f-11d1-8222-444553540000"},
  261. {"CapGames", CapGames, "0946134a-4c7f-11d1-8222-444553540000"},
  262. {"CapBuddyListTransfer", CapBuddyListTransfer, "0946134b-4c7f-11d1-8222-444553540000"},
  263. {"CapSupportICQ", CapSupportICQ, "0946134d-4c7f-11d1-8222-444553540000"},
  264. {"CapUTF8Messages", CapUTF8Messages, "0946134e-4c7f-11d1-8222-444553540000"},
  265. // Full UUIDs
  266. {"CapRTFMessages", CapRTFMessages, "97b12751-243c-4334-ad22-d6abf73f1492"},
  267. {"CapTrillianSecureIM", CapTrillianSecureIM, "f2e7c7f4-fead-4dfb-b235-36798bdf0000"},
  268. {"CapXtrazScript", CapXtrazScript, "3b60b3ef-d82a-6c45-a4e0-9c5a5e67e865"},
  269. // Unknown/Legacy
  270. {"CapUnknownICQ2001_2002", CapUnknownICQ2001_2002, "a0e93f37-4c7f-11d1-8222-444553540000"},
  271. {"CapUnknownICQ2002", CapUnknownICQ2002, "10cf40d1-4c7f-11d1-8222-444553540000"},
  272. {"CapUnknownICQ2001", CapUnknownICQ2001, "2e7a6475-fadf-4dc8-886f-ea3595fdb6df"},
  273. {"CapUnknownICQLite", CapUnknownICQLite, "563fc809-0b6f-41bd-9f79-422609dfa2f3"},
  274. {"CapGamesAlt", CapGamesAlt, "0946134a-4c7f-11d1-2282-444553540000"},
  275. }
  276. for _, tt := range tests {
  277. t.Run(tt.name, func(t *testing.T) {
  278. assert.Equal(t, tt.expected, tt.cap.String())
  279. })
  280. }
  281. }
  282. func TestShortCapHexToUUID(t *testing.T) {
  283. tests := []struct {
  284. name string
  285. hexStr string
  286. wantUUID uuid.UUID
  287. wantOK bool
  288. }{
  289. {"1348 -> CapFileSharing", "1348", CapFileSharing, true},
  290. {"134B -> CapBuddyListTransfer", "134B", CapBuddyListTransfer, true},
  291. {"1FF -> CapSmartCaps", "1FF", CapSmartCaps, true},
  292. {"1341 -> CapVoiceChat", "1341", CapVoiceChat, true},
  293. {"0000 -> CapShortCaps", "0000", CapShortCaps, true},
  294. {"empty string", "", uuid.Nil, false},
  295. {"invalid hex", "GGGG", uuid.Nil, false},
  296. {"too large for uint16", "10000", uuid.Nil, false},
  297. {"whitespace trimmed", " 1348 ", CapFileSharing, true},
  298. }
  299. for _, tt := range tests {
  300. t.Run(tt.name, func(t *testing.T) {
  301. got, ok := ShortCapHexToUUID(tt.hexStr)
  302. assert.Equal(t, tt.wantOK, ok)
  303. if tt.wantOK {
  304. assert.Equal(t, tt.wantUUID, got)
  305. } else {
  306. assert.Equal(t, uuid.Nil, got)
  307. }
  308. })
  309. }
  310. }
  311. func TestFeedbagItem_AppendOrderMembers(t *testing.T) {
  312. t.Run("creates order TLV when none exists", func(t *testing.T) {
  313. item := FeedbagItem{ClassID: FeedbagClassIdGroup, GroupID: 1}
  314. item.AppendOrderMembers(10, 20)
  315. order, ok := item.Uint16SliceBE(FeedbagAttributesOrder)
  316. assert.True(t, ok)
  317. assert.Equal(t, []uint16{10, 20}, order)
  318. })
  319. t.Run("appends to existing order TLV", func(t *testing.T) {
  320. item := FeedbagItem{
  321. ClassID: FeedbagClassIdGroup,
  322. GroupID: 1,
  323. TLVLBlock: TLVLBlock{
  324. TLVList: TLVList{
  325. NewTLVBE(FeedbagAttributesOrder, []uint16{5, 10}),
  326. },
  327. },
  328. }
  329. item.AppendOrderMembers(15, 20)
  330. order, ok := item.Uint16SliceBE(FeedbagAttributesOrder)
  331. assert.True(t, ok)
  332. assert.Equal(t, []uint16{5, 10, 15, 20}, order)
  333. })
  334. t.Run("single member", func(t *testing.T) {
  335. item := FeedbagItem{ClassID: FeedbagClassIdGroup}
  336. item.AppendOrderMembers(42)
  337. order, ok := item.Uint16SliceBE(FeedbagAttributesOrder)
  338. assert.True(t, ok)
  339. assert.Equal(t, []uint16{42}, order)
  340. })
  341. }
  342. func TestFeedbagItem_RemoveOrderMembers(t *testing.T) {
  343. t.Run("removes specified members", func(t *testing.T) {
  344. item := FeedbagItem{
  345. ClassID: FeedbagClassIdGroup,
  346. TLVLBlock: TLVLBlock{
  347. TLVList: TLVList{
  348. NewTLVBE(FeedbagAttributesOrder, []uint16{5, 10, 15, 20}),
  349. },
  350. },
  351. }
  352. item.RemoveOrderMembers(10, 20)
  353. order, ok := item.Uint16SliceBE(FeedbagAttributesOrder)
  354. assert.True(t, ok)
  355. assert.Equal(t, []uint16{5, 15}, order)
  356. })
  357. t.Run("no-op when order TLV does not exist", func(t *testing.T) {
  358. item := FeedbagItem{ClassID: FeedbagClassIdGroup}
  359. item.RemoveOrderMembers(10)
  360. _, ok := item.Uint16SliceBE(FeedbagAttributesOrder)
  361. assert.False(t, ok)
  362. })
  363. t.Run("removes all members", func(t *testing.T) {
  364. item := FeedbagItem{
  365. ClassID: FeedbagClassIdGroup,
  366. TLVLBlock: TLVLBlock{
  367. TLVList: TLVList{
  368. NewTLVBE(FeedbagAttributesOrder, []uint16{5, 10}),
  369. },
  370. },
  371. }
  372. item.RemoveOrderMembers(5, 10)
  373. order, ok := item.Uint16SliceBE(FeedbagAttributesOrder)
  374. assert.True(t, ok)
  375. assert.Empty(t, order)
  376. })
  377. t.Run("ignores IDs not in order", func(t *testing.T) {
  378. item := FeedbagItem{
  379. ClassID: FeedbagClassIdGroup,
  380. TLVLBlock: TLVLBlock{
  381. TLVList: TLVList{
  382. NewTLVBE(FeedbagAttributesOrder, []uint16{5, 10}),
  383. },
  384. },
  385. }
  386. item.RemoveOrderMembers(99)
  387. order, ok := item.Uint16SliceBE(FeedbagAttributesOrder)
  388. assert.True(t, ok)
  389. assert.Equal(t, []uint16{5, 10}, order)
  390. })
  391. }