icbm_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. package server
  2. import (
  3. "bytes"
  4. "github.com/mkaminski/goaim/oscar"
  5. "github.com/stretchr/testify/assert"
  6. "github.com/stretchr/testify/mock"
  7. "testing"
  8. )
  9. func TestSendAndReceiveChannelMsgTohost(t *testing.T) {
  10. cases := []struct {
  11. // name is the unit test name
  12. name string
  13. // blockedState is the response to the sender/recipient block check
  14. blockedState BlockedState
  15. // recipRetrieveErr is the error returned by the recipient session
  16. // lookup
  17. recipRetrieveErr error
  18. senderSession *Session
  19. recipientSession *Session
  20. // inputSNAC is the SNAC sent by the sender client
  21. inputSNAC oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost
  22. // expectSNACToClient is the SNAC sent from the server to the
  23. // recipient client
  24. expectSNACToClient XMessage
  25. // inputSNAC is the SNAC frame sent from the server to the recipient
  26. // client
  27. expectOutput *XMessage
  28. }{
  29. {
  30. name: "transmit message from sender to recipient, ack message back to sender",
  31. blockedState: BlockedNo,
  32. senderSession: &Session{
  33. ScreenName: "sender-screen-name",
  34. Warning: 10,
  35. },
  36. recipientSession: &Session{
  37. ScreenName: "recipient-screen-name",
  38. Warning: 20,
  39. },
  40. inputSNAC: oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  41. ScreenName: "recipient-screen-name",
  42. TLVRestBlock: oscar.TLVRestBlock{
  43. TLVList: oscar.TLVList{
  44. {
  45. TType: oscar.ICBMTLVTagRequestHostAck,
  46. Val: []byte{},
  47. },
  48. },
  49. },
  50. },
  51. expectSNACToClient: XMessage{
  52. snacFrame: oscar.SnacFrame{
  53. FoodGroup: oscar.ICBM,
  54. SubGroup: oscar.ICBMChannelMsgToclient,
  55. },
  56. snacOut: oscar.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  57. TLVUserInfo: oscar.TLVUserInfo{
  58. ScreenName: "sender-screen-name",
  59. WarningLevel: 10,
  60. },
  61. TLVRestBlock: oscar.TLVRestBlock{
  62. TLVList: oscar.TLVList{
  63. {
  64. TType: oscar.ICBMTLVTagsWantEvents,
  65. Val: []byte{},
  66. },
  67. {
  68. TType: oscar.ICBMTLVTagRequestHostAck,
  69. Val: []byte{},
  70. },
  71. },
  72. },
  73. },
  74. },
  75. expectOutput: &XMessage{
  76. snacFrame: oscar.SnacFrame{
  77. FoodGroup: oscar.ICBM,
  78. SubGroup: oscar.ICBMHostAck,
  79. },
  80. snacOut: oscar.SNAC_0x04_0x0C_ICBMHostAck{
  81. ScreenName: "recipient-screen-name",
  82. },
  83. },
  84. },
  85. {
  86. name: "transmit message from sender to recipient, don't ack message back to sender",
  87. blockedState: BlockedNo,
  88. senderSession: &Session{
  89. ScreenName: "sender-screen-name",
  90. Warning: 10,
  91. },
  92. recipientSession: &Session{
  93. ScreenName: "recipient-screen-name",
  94. Warning: 20,
  95. },
  96. inputSNAC: oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  97. ScreenName: "recipient-screen-name",
  98. TLVRestBlock: oscar.TLVRestBlock{
  99. TLVList: oscar.TLVList{},
  100. },
  101. },
  102. expectSNACToClient: XMessage{
  103. snacFrame: oscar.SnacFrame{
  104. FoodGroup: oscar.ICBM,
  105. SubGroup: oscar.ICBMChannelMsgToclient,
  106. },
  107. snacOut: oscar.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  108. TLVUserInfo: oscar.TLVUserInfo{
  109. ScreenName: "sender-screen-name",
  110. WarningLevel: 10,
  111. },
  112. TLVRestBlock: oscar.TLVRestBlock{
  113. TLVList: oscar.TLVList{
  114. {
  115. TType: oscar.ICBMTLVTagsWantEvents,
  116. Val: []byte{},
  117. },
  118. },
  119. },
  120. },
  121. },
  122. expectOutput: nil,
  123. },
  124. {
  125. name: "don't transmit message from sender to recipient because sender has blocked recipient",
  126. blockedState: BlockedA,
  127. senderSession: &Session{
  128. ScreenName: "sender-screen-name",
  129. Warning: 10,
  130. },
  131. recipientSession: &Session{
  132. ScreenName: "recipient-screen-name",
  133. Warning: 20,
  134. },
  135. inputSNAC: oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  136. ScreenName: "recipient-screen-name",
  137. TLVRestBlock: oscar.TLVRestBlock{
  138. TLVList: oscar.TLVList{
  139. {
  140. TType: oscar.ICBMTLVTagRequestHostAck,
  141. Val: []byte{},
  142. },
  143. },
  144. },
  145. },
  146. expectOutput: &XMessage{
  147. snacFrame: oscar.SnacFrame{
  148. FoodGroup: oscar.ICBM,
  149. SubGroup: oscar.ICBMErr,
  150. },
  151. snacOut: oscar.SnacError{
  152. Code: ErrorCodeInLocalPermitDeny,
  153. },
  154. },
  155. },
  156. {
  157. name: "don't transmit message from sender to recipient because recipient has blocked sender",
  158. blockedState: BlockedB,
  159. senderSession: &Session{
  160. ScreenName: "sender-screen-name",
  161. Warning: 10,
  162. },
  163. recipientSession: &Session{
  164. ScreenName: "recipient-screen-name",
  165. Warning: 20,
  166. },
  167. inputSNAC: oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  168. ScreenName: "recipient-screen-name",
  169. TLVRestBlock: oscar.TLVRestBlock{
  170. TLVList: oscar.TLVList{
  171. {
  172. TType: oscar.ICBMTLVTagRequestHostAck,
  173. Val: []byte{},
  174. },
  175. },
  176. },
  177. },
  178. expectOutput: &XMessage{
  179. snacFrame: oscar.SnacFrame{
  180. FoodGroup: oscar.ICBM,
  181. SubGroup: oscar.ICBMErr,
  182. },
  183. snacOut: oscar.SnacError{
  184. Code: ErrorCodeNotLoggedOn,
  185. },
  186. },
  187. },
  188. {
  189. name: "don't transmit message from sender to recipient because recipient doesn't exist",
  190. blockedState: BlockedNo,
  191. recipRetrieveErr: ErrSessNotFound,
  192. senderSession: &Session{
  193. ScreenName: "sender-screen-name",
  194. Warning: 10,
  195. },
  196. recipientSession: &Session{
  197. ScreenName: "recipient-screen-name",
  198. Warning: 20,
  199. },
  200. inputSNAC: oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  201. ScreenName: "recipient-screen-name",
  202. TLVRestBlock: oscar.TLVRestBlock{
  203. TLVList: oscar.TLVList{
  204. {
  205. TType: oscar.ICBMTLVTagRequestHostAck,
  206. Val: []byte{},
  207. },
  208. },
  209. },
  210. },
  211. expectOutput: &XMessage{
  212. snacFrame: oscar.SnacFrame{
  213. FoodGroup: oscar.ICBM,
  214. SubGroup: oscar.ICBMErr,
  215. },
  216. snacOut: oscar.SnacError{
  217. Code: ErrorCodeNotLoggedOn,
  218. },
  219. },
  220. },
  221. }
  222. for _, tc := range cases {
  223. t.Run(tc.name, func(t *testing.T) {
  224. //
  225. // initialize dependencies
  226. //
  227. fm := NewMockFeedbagManager(t)
  228. fm.EXPECT().
  229. Blocked(tc.senderSession.ScreenName, tc.recipientSession.ScreenName).
  230. Return(tc.blockedState, nil).
  231. Maybe()
  232. sm := NewMockSessionManager(t)
  233. sm.EXPECT().
  234. RetrieveByScreenName(tc.recipientSession.ScreenName).
  235. Return(tc.recipientSession, tc.recipRetrieveErr).
  236. Maybe()
  237. sm.EXPECT().
  238. SendToScreenName(tc.recipientSession.ScreenName, tc.expectSNACToClient).
  239. Maybe()
  240. //
  241. // send input SNAC
  242. //
  243. svc := ICBMService{}
  244. outputSNAC, err := svc.ChannelMsgToHostHandler(sm, fm, tc.senderSession, tc.inputSNAC)
  245. assert.NoError(t, err)
  246. //
  247. // verify output
  248. //
  249. assert.Equal(t, tc.expectOutput, outputSNAC)
  250. })
  251. }
  252. }
  253. func TestSendAndReceiveClientEvent(t *testing.T) {
  254. cases := []struct {
  255. // name is the unit test name
  256. name string
  257. // blockedState is the response to the sender/recipient block check
  258. blockedState BlockedState
  259. // senderScreenName is the screen name of the user sending the event
  260. senderScreenName string
  261. // inputSNAC is the SNAC sent by the sender client
  262. inputSNAC oscar.SNAC_0x04_0x14_ICBMClientEvent
  263. // expectSNACToClient is the SNAC sent from the server to the
  264. // recipient client
  265. expectSNACToClient XMessage
  266. }{
  267. {
  268. name: "transmit message from sender to recipient",
  269. blockedState: BlockedNo,
  270. senderScreenName: "sender-screen-name",
  271. inputSNAC: oscar.SNAC_0x04_0x14_ICBMClientEvent{
  272. Cookie: [8]byte{1, 2, 3, 4, 5, 6, 7, 8},
  273. ChannelID: 42,
  274. ScreenName: "recipient-screen-name",
  275. Event: 12,
  276. },
  277. expectSNACToClient: XMessage{
  278. snacFrame: oscar.SnacFrame{
  279. FoodGroup: oscar.ICBM,
  280. SubGroup: oscar.ICBMClientEvent,
  281. },
  282. snacOut: oscar.SNAC_0x04_0x14_ICBMClientEvent{
  283. Cookie: [8]byte{1, 2, 3, 4, 5, 6, 7, 8},
  284. ChannelID: 42,
  285. ScreenName: "sender-screen-name",
  286. Event: 12,
  287. },
  288. },
  289. },
  290. {
  291. name: "don't transmit message from sender to recipient because sender has blocked recipient",
  292. blockedState: BlockedA,
  293. senderScreenName: "sender-screen-name",
  294. inputSNAC: oscar.SNAC_0x04_0x14_ICBMClientEvent{
  295. ScreenName: "recipient-screen-name",
  296. },
  297. },
  298. }
  299. for _, tc := range cases {
  300. t.Run(tc.name, func(t *testing.T) {
  301. //
  302. // initialize dependencies
  303. //
  304. fm := NewMockFeedbagManager(t)
  305. fm.EXPECT().
  306. Blocked(tc.senderScreenName, tc.inputSNAC.ScreenName).
  307. Return(tc.blockedState, nil).
  308. Maybe()
  309. sm := NewMockSessionManager(t)
  310. if tc.blockedState == BlockedNo {
  311. sm.EXPECT().
  312. SendToScreenName(tc.inputSNAC.ScreenName, tc.expectSNACToClient)
  313. }
  314. //
  315. // send input SNAC
  316. //
  317. senderSession := &Session{
  318. ScreenName: tc.senderScreenName,
  319. }
  320. svc := ICBMService{}
  321. assert.NoError(t, svc.ClientEventHandler(sm, fm, senderSession, tc.inputSNAC))
  322. })
  323. }
  324. }
  325. func TestSendAndReceiveEvilRequest(t *testing.T) {
  326. defaultSess := &Session{}
  327. cases := []struct {
  328. // name is the unit test name
  329. name string
  330. // blockedState is the response to the sender/recipient block check
  331. blockedState BlockedState
  332. // recipRetrieveErr is the error returned by the recipient session
  333. // lookup
  334. recipRetrieveErr error
  335. // senderScreenName is the session name of the user sending the IM
  336. senderSession *Session
  337. // recipientScreenName is the screen name of the user receiving the IM
  338. recipientScreenName string
  339. // recipientBuddies is a list of the recipient's buddies that get
  340. // updated warning level
  341. recipientBuddies []string
  342. broadcastMessage XMessage
  343. // inputSNAC is the SNAC sent by the sender client
  344. inputSNAC oscar.SNAC_0x04_0x08_ICBMEvilRequest
  345. // expectSNACToClient is the SNAC sent from the server to the
  346. // recipient client
  347. expectSNACToClient XMessage
  348. expectOutput XMessage
  349. }{
  350. {
  351. name: "transmit anonymous warning from sender to recipient",
  352. blockedState: BlockedNo,
  353. senderSession: &Session{
  354. ScreenName: "sender-screen-name",
  355. },
  356. recipientScreenName: "recipient-screen-name",
  357. broadcastMessage: XMessage{
  358. snacFrame: oscar.SnacFrame{
  359. FoodGroup: oscar.BUDDY,
  360. SubGroup: oscar.BuddyArrived,
  361. },
  362. snacOut: oscar.SNAC_0x03_0x0A_BuddyArrived{
  363. TLVUserInfo: oscar.TLVUserInfo{
  364. ScreenName: "recipient-screen-name",
  365. WarningLevel: evilDeltaAnon,
  366. TLVBlock: oscar.TLVBlock{
  367. TLVList: defaultSess.GetUserInfo(),
  368. },
  369. },
  370. },
  371. },
  372. recipientBuddies: []string{"buddy1", "buddy2"},
  373. inputSNAC: oscar.SNAC_0x04_0x08_ICBMEvilRequest{
  374. SendAs: 1, // make it anonymous
  375. ScreenName: "recipient-screen-name",
  376. },
  377. expectSNACToClient: XMessage{
  378. snacFrame: oscar.SnacFrame{
  379. FoodGroup: oscar.OSERVICE,
  380. SubGroup: oscar.OServiceEvilNotification,
  381. },
  382. snacOut: oscar.SNAC_0x01_0x10_OServiceEvilNotificationAnon{
  383. NewEvil: evilDeltaAnon,
  384. },
  385. },
  386. expectOutput: XMessage{
  387. snacFrame: oscar.SnacFrame{
  388. FoodGroup: oscar.ICBM,
  389. SubGroup: oscar.ICBMEvilReply,
  390. },
  391. snacOut: oscar.SNAC_0x04_0x09_ICBMEvilReply{
  392. EvilDeltaApplied: 30,
  393. UpdatedEvilValue: 30,
  394. },
  395. },
  396. },
  397. {
  398. name: "transmit non-anonymous warning from sender to recipient",
  399. blockedState: BlockedNo,
  400. senderSession: &Session{
  401. ScreenName: "sender-screen-name",
  402. },
  403. recipientScreenName: "recipient-screen-name",
  404. recipientBuddies: []string{"buddy1", "buddy2"},
  405. broadcastMessage: XMessage{
  406. snacFrame: oscar.SnacFrame{
  407. FoodGroup: oscar.BUDDY,
  408. SubGroup: oscar.BuddyArrived,
  409. },
  410. snacOut: oscar.SNAC_0x03_0x0A_BuddyArrived{
  411. TLVUserInfo: oscar.TLVUserInfo{
  412. ScreenName: "recipient-screen-name",
  413. WarningLevel: evilDelta,
  414. TLVBlock: oscar.TLVBlock{
  415. TLVList: defaultSess.GetUserInfo(),
  416. },
  417. },
  418. },
  419. },
  420. inputSNAC: oscar.SNAC_0x04_0x08_ICBMEvilRequest{
  421. SendAs: 0, // make it identified
  422. ScreenName: "recipient-screen-name",
  423. },
  424. expectSNACToClient: XMessage{
  425. snacFrame: oscar.SnacFrame{
  426. FoodGroup: oscar.OSERVICE,
  427. SubGroup: oscar.OServiceEvilNotification,
  428. },
  429. snacOut: oscar.SNAC_0x01_0x10_OServiceEvilNotification{
  430. NewEvil: evilDelta,
  431. TLVUserInfo: oscar.TLVUserInfo{
  432. ScreenName: "sender-screen-name",
  433. WarningLevel: 100,
  434. },
  435. },
  436. },
  437. expectOutput: XMessage{
  438. snacFrame: oscar.SnacFrame{
  439. FoodGroup: oscar.ICBM,
  440. SubGroup: oscar.ICBMEvilReply,
  441. },
  442. snacOut: oscar.SNAC_0x04_0x09_ICBMEvilReply{
  443. EvilDeltaApplied: 100,
  444. UpdatedEvilValue: 100,
  445. },
  446. },
  447. },
  448. {
  449. name: "don't transmit non-anonymous warning from sender to recipient because sender has blocked recipient",
  450. blockedState: BlockedA,
  451. senderSession: &Session{
  452. ScreenName: "sender-screen-name",
  453. },
  454. recipientScreenName: "recipient-screen-name",
  455. recipientBuddies: []string{"buddy1", "buddy2"},
  456. inputSNAC: oscar.SNAC_0x04_0x08_ICBMEvilRequest{
  457. SendAs: 0, // make it identified
  458. ScreenName: "recipient-screen-name",
  459. },
  460. expectOutput: XMessage{
  461. snacFrame: oscar.SnacFrame{
  462. FoodGroup: oscar.ICBM,
  463. SubGroup: oscar.ICBMErr,
  464. },
  465. snacOut: oscar.SnacError{
  466. Code: ErrorCodeNotLoggedOn,
  467. },
  468. },
  469. },
  470. {
  471. name: "don't transmit non-anonymous warning from sender to recipient because recipient has blocked sender",
  472. blockedState: BlockedB,
  473. senderSession: &Session{
  474. ScreenName: "sender-screen-name",
  475. },
  476. recipientScreenName: "recipient-screen-name",
  477. recipientBuddies: []string{"buddy1", "buddy2"},
  478. inputSNAC: oscar.SNAC_0x04_0x08_ICBMEvilRequest{
  479. SendAs: 0, // make it identified
  480. ScreenName: "recipient-screen-name",
  481. },
  482. expectOutput: XMessage{
  483. snacFrame: oscar.SnacFrame{
  484. FoodGroup: oscar.ICBM,
  485. SubGroup: oscar.ICBMErr,
  486. },
  487. snacOut: oscar.SnacError{
  488. Code: ErrorCodeNotLoggedOn,
  489. },
  490. },
  491. },
  492. {
  493. name: "don't let users block themselves",
  494. senderSession: &Session{
  495. ScreenName: "sender-screen-name",
  496. },
  497. recipientScreenName: "sender-screen-name",
  498. inputSNAC: oscar.SNAC_0x04_0x08_ICBMEvilRequest{
  499. SendAs: 0, // make it identified
  500. ScreenName: "sender-screen-name",
  501. },
  502. expectOutput: XMessage{
  503. snacFrame: oscar.SnacFrame{
  504. FoodGroup: oscar.ICBM,
  505. SubGroup: oscar.ICBMErr,
  506. },
  507. snacOut: oscar.SnacError{
  508. Code: ErrorCodeNotSupportedByHost,
  509. },
  510. },
  511. },
  512. }
  513. for _, tc := range cases {
  514. t.Run(tc.name, func(t *testing.T) {
  515. //
  516. // initialize dependencies
  517. //
  518. fm := NewMockFeedbagManager(t)
  519. fm.EXPECT().
  520. Blocked(tc.senderSession.ScreenName, tc.recipientScreenName).
  521. Return(tc.blockedState, nil).
  522. Maybe()
  523. fm.EXPECT().
  524. InterestedUsers(tc.recipientScreenName).
  525. Return(tc.recipientBuddies, nil).
  526. Maybe()
  527. recipSess := &Session{
  528. ScreenName: tc.recipientScreenName,
  529. Warning: 0,
  530. }
  531. sm := NewMockSessionManager(t)
  532. sm.EXPECT().
  533. RetrieveByScreenName(tc.recipientScreenName).
  534. Return(recipSess, tc.recipRetrieveErr).
  535. Maybe()
  536. sm.EXPECT().
  537. SendToScreenName(tc.recipientScreenName, tc.expectSNACToClient).
  538. Maybe()
  539. sm.EXPECT().
  540. BroadcastToScreenNames(tc.recipientBuddies, tc.broadcastMessage).
  541. Maybe()
  542. //
  543. // send input SNAC
  544. //
  545. senderSession := &Session{
  546. ScreenName: tc.senderSession.ScreenName,
  547. }
  548. svc := ICBMService{}
  549. outputSNAC, err := svc.EvilRequestHandler(sm, fm, senderSession, tc.inputSNAC)
  550. assert.NoError(t, err)
  551. assert.Equal(t, tc.expectOutput, outputSNAC)
  552. })
  553. }
  554. }
  555. func TestICBMRouter_RouteICBM(t *testing.T) {
  556. cases := []struct {
  557. // name is the unit test name
  558. name string
  559. // input is the request payload
  560. input XMessage
  561. // output is the response payload
  562. output *XMessage
  563. // handlerErr is the mocked handler error response
  564. handlerErr error
  565. // expectErr is the expected error returned by the router
  566. expectErr error
  567. }{
  568. {
  569. name: "receive ICBMAddParameters SNAC, return no response",
  570. input: XMessage{
  571. snacFrame: oscar.SnacFrame{
  572. FoodGroup: oscar.ICBM,
  573. SubGroup: oscar.ICBMAddParameters,
  574. },
  575. snacOut: oscar.SNAC_0x04_0x02_ICBMAddParameters{
  576. Channel: 1,
  577. },
  578. },
  579. output: nil,
  580. },
  581. {
  582. name: "receive ICBMParameterQuery, return ICBMParameterReply",
  583. input: XMessage{
  584. snacFrame: oscar.SnacFrame{
  585. FoodGroup: oscar.ICBM,
  586. SubGroup: oscar.ICBMParameterQuery,
  587. },
  588. snacOut: struct{}{}, // empty SNAC
  589. },
  590. output: &XMessage{
  591. snacFrame: oscar.SnacFrame{
  592. FoodGroup: oscar.ICBM,
  593. SubGroup: oscar.ICBMParameterReply,
  594. },
  595. snacOut: oscar.SNAC_0x04_0x05_ICBMParameterReply{
  596. MaxSlots: 100,
  597. },
  598. },
  599. },
  600. {
  601. name: "receive ICBMChannelMsgToHost, return ICBMHostAck",
  602. input: XMessage{
  603. snacFrame: oscar.SnacFrame{
  604. FoodGroup: oscar.ICBM,
  605. SubGroup: oscar.ICBMChannelMsgToHost,
  606. },
  607. snacOut: oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  608. ScreenName: "recipient-screen-name",
  609. },
  610. },
  611. output: &XMessage{
  612. snacFrame: oscar.SnacFrame{
  613. FoodGroup: oscar.ICBM,
  614. SubGroup: oscar.ICBMHostAck,
  615. },
  616. snacOut: oscar.SNAC_0x04_0x0C_ICBMHostAck{
  617. ChannelID: 4,
  618. },
  619. },
  620. },
  621. {
  622. name: "receive ICBMChannelMsgToHost, return no reply",
  623. input: XMessage{
  624. snacFrame: oscar.SnacFrame{
  625. FoodGroup: oscar.ICBM,
  626. SubGroup: oscar.ICBMChannelMsgToHost,
  627. },
  628. snacOut: oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  629. ScreenName: "recipient-screen-name",
  630. },
  631. },
  632. output: nil,
  633. },
  634. {
  635. name: "receive ICBMEvilRequest, return ICBMEvilReply",
  636. input: XMessage{
  637. snacFrame: oscar.SnacFrame{
  638. FoodGroup: oscar.ICBM,
  639. SubGroup: oscar.ICBMEvilRequest,
  640. },
  641. snacOut: oscar.SNAC_0x04_0x08_ICBMEvilRequest{
  642. ScreenName: "recipient-screen-name",
  643. },
  644. },
  645. output: &XMessage{
  646. snacFrame: oscar.SnacFrame{
  647. FoodGroup: oscar.ICBM,
  648. SubGroup: oscar.ICBMEvilReply,
  649. },
  650. snacOut: oscar.SNAC_0x04_0x09_ICBMEvilReply{
  651. EvilDeltaApplied: 100,
  652. },
  653. },
  654. },
  655. {
  656. name: "receive ICBMClientErr, return no response",
  657. input: XMessage{
  658. snacFrame: oscar.SnacFrame{
  659. FoodGroup: oscar.ICBM,
  660. SubGroup: oscar.ICBMClientErr,
  661. },
  662. snacOut: oscar.SNAC_0x04_0x0B_ICBMClientErr{
  663. Code: 4,
  664. },
  665. },
  666. output: nil,
  667. },
  668. {
  669. name: "receive ICBMClientEvent, return no response",
  670. input: XMessage{
  671. snacFrame: oscar.SnacFrame{
  672. FoodGroup: oscar.ICBM,
  673. SubGroup: oscar.ICBMClientEvent,
  674. },
  675. snacOut: oscar.SNAC_0x04_0x14_ICBMClientEvent{
  676. ScreenName: "recipient-screen-name",
  677. },
  678. },
  679. output: nil,
  680. },
  681. {
  682. name: "receive ICBMMissedCalls, expect ErrUnsupportedSubGroup",
  683. input: XMessage{
  684. snacFrame: oscar.SnacFrame{
  685. FoodGroup: oscar.ICBM,
  686. SubGroup: oscar.ICBMMissedCalls,
  687. },
  688. snacOut: struct{}{}, // empty SNAC
  689. },
  690. output: nil,
  691. expectErr: ErrUnsupportedSubGroup,
  692. },
  693. }
  694. for _, tc := range cases {
  695. t.Run(tc.name, func(t *testing.T) {
  696. svc := NewMockICBMHandler(t)
  697. svc.EXPECT().
  698. ChannelMsgToHostHandler(mock.Anything, mock.Anything, mock.Anything, tc.input.snacOut).
  699. Return(tc.output, tc.handlerErr).
  700. Maybe()
  701. svc.EXPECT().
  702. ClientEventHandler(mock.Anything, mock.Anything, mock.Anything, tc.input.snacOut).
  703. Return(tc.handlerErr).
  704. Maybe()
  705. if tc.output != nil {
  706. svc.EXPECT().
  707. EvilRequestHandler(mock.Anything, mock.Anything, mock.Anything, tc.input.snacOut).
  708. Return(*tc.output, tc.handlerErr).
  709. Maybe()
  710. svc.EXPECT().
  711. ParameterQueryHandler().
  712. Return(*tc.output).
  713. Maybe()
  714. }
  715. router := ICBMRouter{
  716. ICBMHandler: svc,
  717. }
  718. bufIn := &bytes.Buffer{}
  719. assert.NoError(t, oscar.Marshal(tc.input.snacOut, bufIn))
  720. bufOut := &bytes.Buffer{}
  721. seq := uint32(1)
  722. err := router.RouteICBM(nil, nil, nil, tc.input.snacFrame, bufIn, bufOut, &seq)
  723. assert.ErrorIs(t, err, tc.expectErr)
  724. if tc.expectErr != nil {
  725. return
  726. }
  727. if tc.output == nil {
  728. // make sure no response was sent
  729. assert.Empty(t, bufOut.Bytes())
  730. return
  731. }
  732. // make sure the sequence number was incremented
  733. assert.Equal(t, uint32(2), seq)
  734. // verify the FLAP frame
  735. flap := oscar.FlapFrame{}
  736. assert.NoError(t, oscar.Unmarshal(&flap, bufOut))
  737. assert.Equal(t, uint16(1), flap.Sequence)
  738. // verify the SNAC frame
  739. snacFrame := oscar.SnacFrame{}
  740. assert.NoError(t, oscar.Unmarshal(&snacFrame, bufOut))
  741. assert.Equal(t, tc.output.snacFrame, snacFrame)
  742. // verify the SNAC message
  743. snacBuf := &bytes.Buffer{}
  744. assert.NoError(t, oscar.Marshal(tc.output.snacOut, snacBuf))
  745. assert.Equal(t, snacBuf.Bytes(), bufOut.Bytes())
  746. })
  747. }
  748. }