buddy_test.go 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320
  1. package foodgroup
  2. import (
  3. "context"
  4. "testing"
  5. "github.com/stretchr/testify/assert"
  6. "github.com/stretchr/testify/mock"
  7. "github.com/mk6i/open-oscar-server/state"
  8. "github.com/mk6i/open-oscar-server/wire"
  9. )
  10. func TestBuddyService_RightsQuery(t *testing.T) {
  11. svc := NewBuddyService(nil, nil, nil, nil, nil, newMockContactPreAuthorizer(t))
  12. want := wire.SNACMessage{
  13. Frame: wire.SNACFrame{
  14. FoodGroup: wire.Buddy,
  15. SubGroup: wire.BuddyRightsReply,
  16. RequestID: 1234,
  17. },
  18. Body: wire.SNAC_0x03_0x03_BuddyRightsReply{
  19. TLVRestBlock: wire.TLVRestBlock{
  20. TLVList: wire.TLVList{
  21. wire.NewTLVBE(wire.BuddyTLVTagsParmMaxBuddies, uint16(100)),
  22. wire.NewTLVBE(wire.BuddyTLVTagsParmMaxWatchers, uint16(100)),
  23. wire.NewTLVBE(wire.BuddyTLVTagsParmMaxIcqBroad, uint16(100)),
  24. wire.NewTLVBE(wire.BuddyTLVTagsParmMaxTempBuddies, uint16(100)),
  25. },
  26. },
  27. },
  28. }
  29. have := svc.RightsQuery(context.TODO(), wire.SNACFrame{RequestID: 1234})
  30. assert.Equal(t, want, have)
  31. }
  32. func TestBuddyService_AddBuddies(t *testing.T) {
  33. tests := []struct {
  34. // name is the name of the test
  35. name string
  36. // instance is the client session
  37. instance *state.SessionInstance
  38. // bodyIn is the input SNAC
  39. bodyIn wire.SNAC_0x03_0x04_BuddyAddBuddies
  40. // mockParams is the list of params sent to mocks that satisfy this
  41. // method's dependencies
  42. mockParams mockParams
  43. // wantSNAC is the expected SNAC returned when buddies are rejected
  44. wantSNAC *wire.SNACMessage
  45. // wantErr is the expected error
  46. wantErr error
  47. }{
  48. {
  49. name: "add 2 buddies, sign-on complete",
  50. instance: newTestInstance("user_screen_name", sessOptSignonComplete),
  51. bodyIn: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  52. Buddies: []struct {
  53. ScreenName string `oscar:"len_prefix=uint8"`
  54. }{
  55. {
  56. ScreenName: "buddy_1_online",
  57. },
  58. {
  59. ScreenName: "buddy_2_offline",
  60. },
  61. },
  62. },
  63. mockParams: mockParams{
  64. clientSideBuddyListManagerParams: clientSideBuddyListManagerParams{
  65. addBuddyParams: addBuddyParams{
  66. {
  67. me: state.NewIdentScreenName("user_screen_name"),
  68. them: state.NewIdentScreenName("buddy_1_online"),
  69. },
  70. {
  71. me: state.NewIdentScreenName("user_screen_name"),
  72. them: state.NewIdentScreenName("buddy_2_offline"),
  73. },
  74. },
  75. },
  76. buddyBroadcasterParams: buddyBroadcasterParams{
  77. broadcastVisibilityParams: broadcastVisibilityParams{
  78. {
  79. from: state.NewIdentScreenName("user_screen_name"),
  80. filter: []state.IdentScreenName{
  81. state.NewIdentScreenName("buddy_1_online"),
  82. state.NewIdentScreenName("buddy_2_offline"),
  83. },
  84. },
  85. },
  86. },
  87. },
  88. },
  89. {
  90. name: "add 2 buddies, sign-on not complete",
  91. instance: newTestInstance("user_screen_name"),
  92. bodyIn: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  93. Buddies: []struct {
  94. ScreenName string `oscar:"len_prefix=uint8"`
  95. }{
  96. {
  97. ScreenName: "buddy_1_online",
  98. },
  99. {
  100. ScreenName: "buddy_2_offline",
  101. },
  102. },
  103. },
  104. mockParams: mockParams{
  105. clientSideBuddyListManagerParams: clientSideBuddyListManagerParams{
  106. addBuddyParams: addBuddyParams{
  107. {
  108. me: state.NewIdentScreenName("user_screen_name"),
  109. them: state.NewIdentScreenName("buddy_1_online"),
  110. },
  111. {
  112. me: state.NewIdentScreenName("user_screen_name"),
  113. them: state.NewIdentScreenName("buddy_2_offline"),
  114. },
  115. },
  116. },
  117. },
  118. },
  119. {
  120. name: "UIN blocked by pre-authorization",
  121. instance: newTestInstance("100002", sessOptSignonComplete),
  122. bodyIn: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  123. Buddies: []struct {
  124. ScreenName string `oscar:"len_prefix=uint8"`
  125. }{
  126. {ScreenName: "100001"},
  127. },
  128. },
  129. mockParams: mockParams{
  130. contactPreAuthorizerParams: contactPreAuthorizerParams{
  131. requiresAuthorizationParams: requiresAuthorizationParams{
  132. {
  133. owner: state.NewIdentScreenName("100001"),
  134. requester: state.NewIdentScreenName("100002"),
  135. result: true,
  136. },
  137. },
  138. },
  139. },
  140. wantSNAC: &wire.SNACMessage{
  141. Frame: wire.SNACFrame{
  142. FoodGroup: wire.Buddy,
  143. SubGroup: wire.BuddyRejectNotification,
  144. RequestID: 42,
  145. },
  146. Body: wire.SNAC_0x03_0x0A_BuddyRejectNotification{
  147. Buddies: []struct {
  148. ScreenName string `oscar:"len_prefix=uint8"`
  149. }{
  150. {ScreenName: "100001"},
  151. },
  152. },
  153. },
  154. },
  155. {
  156. name: "AIM session rejects ICQ UIN on client-side buddy path",
  157. instance: newTestInstance("aimuser", sessOptSignonComplete),
  158. bodyIn: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  159. Buddies: []struct {
  160. ScreenName string `oscar:"len_prefix=uint8"`
  161. }{
  162. {ScreenName: "100001"},
  163. },
  164. },
  165. wantSNAC: &wire.SNACMessage{
  166. Frame: wire.SNACFrame{
  167. FoodGroup: wire.Buddy,
  168. SubGroup: wire.BuddyRejectNotification,
  169. RequestID: 42,
  170. },
  171. Body: wire.SNAC_0x03_0x0A_BuddyRejectNotification{
  172. Buddies: []struct {
  173. ScreenName string `oscar:"len_prefix=uint8"`
  174. }{
  175. {ScreenName: "100001"},
  176. },
  177. },
  178. },
  179. },
  180. {
  181. name: "AIM session rejects ICQ UIN but adds non-UIN buddy in same batch",
  182. instance: newTestInstance("aimuser", sessOptSignonComplete),
  183. bodyIn: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  184. Buddies: []struct {
  185. ScreenName string `oscar:"len_prefix=uint8"`
  186. }{
  187. {ScreenName: "100001"},
  188. {ScreenName: "buddyalice"},
  189. },
  190. },
  191. mockParams: mockParams{
  192. clientSideBuddyListManagerParams: clientSideBuddyListManagerParams{
  193. addBuddyParams: addBuddyParams{
  194. {
  195. me: state.NewIdentScreenName("aimuser"),
  196. them: state.NewIdentScreenName("buddyalice"),
  197. },
  198. },
  199. },
  200. buddyBroadcasterParams: buddyBroadcasterParams{
  201. broadcastVisibilityParams: broadcastVisibilityParams{
  202. {
  203. from: state.NewIdentScreenName("aimuser"),
  204. filter: []state.IdentScreenName{state.NewIdentScreenName("buddyalice")},
  205. },
  206. },
  207. },
  208. },
  209. wantSNAC: &wire.SNACMessage{
  210. Frame: wire.SNACFrame{
  211. FoodGroup: wire.Buddy,
  212. SubGroup: wire.BuddyRejectNotification,
  213. RequestID: 42,
  214. },
  215. Body: wire.SNAC_0x03_0x0A_BuddyRejectNotification{
  216. Buddies: []struct {
  217. ScreenName string `oscar:"len_prefix=uint8"`
  218. }{
  219. {ScreenName: "100001"},
  220. },
  221. },
  222. },
  223. },
  224. {
  225. name: "non-UIN skips pre-auth check",
  226. instance: newTestInstance("100002", sessOptSignonComplete),
  227. bodyIn: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  228. Buddies: []struct {
  229. ScreenName string `oscar:"len_prefix=uint8"`
  230. }{
  231. {ScreenName: "somebody"},
  232. },
  233. },
  234. mockParams: mockParams{
  235. clientSideBuddyListManagerParams: clientSideBuddyListManagerParams{
  236. addBuddyParams: addBuddyParams{
  237. {
  238. me: state.NewIdentScreenName("100002"),
  239. them: state.NewIdentScreenName("somebody"),
  240. },
  241. },
  242. },
  243. buddyBroadcasterParams: buddyBroadcasterParams{
  244. broadcastVisibilityParams: broadcastVisibilityParams{
  245. {
  246. from: state.NewIdentScreenName("100002"),
  247. filter: []state.IdentScreenName{state.NewIdentScreenName("somebody")},
  248. },
  249. },
  250. },
  251. },
  252. },
  253. {
  254. name: "partial batch, UIN blocked",
  255. instance: newTestInstance("100002", sessOptSignonComplete),
  256. bodyIn: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  257. Buddies: []struct {
  258. ScreenName string `oscar:"len_prefix=uint8"`
  259. }{
  260. {ScreenName: "100001"},
  261. {ScreenName: "100003"},
  262. },
  263. },
  264. mockParams: mockParams{
  265. contactPreAuthorizerParams: contactPreAuthorizerParams{
  266. requiresAuthorizationParams: requiresAuthorizationParams{
  267. {
  268. owner: state.NewIdentScreenName("100001"),
  269. requester: state.NewIdentScreenName("100002"),
  270. result: true,
  271. },
  272. {
  273. owner: state.NewIdentScreenName("100003"),
  274. requester: state.NewIdentScreenName("100002"),
  275. result: false,
  276. },
  277. },
  278. },
  279. clientSideBuddyListManagerParams: clientSideBuddyListManagerParams{
  280. addBuddyParams: addBuddyParams{
  281. {
  282. me: state.NewIdentScreenName("100002"),
  283. them: state.NewIdentScreenName("100003"),
  284. },
  285. },
  286. },
  287. buddyBroadcasterParams: buddyBroadcasterParams{
  288. broadcastVisibilityParams: broadcastVisibilityParams{
  289. {
  290. from: state.NewIdentScreenName("100002"),
  291. filter: []state.IdentScreenName{state.NewIdentScreenName("100003")},
  292. },
  293. },
  294. },
  295. },
  296. wantSNAC: &wire.SNACMessage{
  297. Frame: wire.SNACFrame{
  298. FoodGroup: wire.Buddy,
  299. SubGroup: wire.BuddyRejectNotification,
  300. RequestID: 42,
  301. },
  302. Body: wire.SNAC_0x03_0x0A_BuddyRejectNotification{
  303. Buddies: []struct {
  304. ScreenName string `oscar:"len_prefix=uint8"`
  305. }{
  306. {ScreenName: "100001"},
  307. },
  308. },
  309. },
  310. },
  311. }
  312. for _, tt := range tests {
  313. t.Run(tt.name, func(t *testing.T) {
  314. clientSideBuddyListManager := newMockClientSideBuddyListManager(t)
  315. for _, params := range tt.mockParams.addBuddyParams {
  316. clientSideBuddyListManager.EXPECT().
  317. AddBuddy(matchContext(), params.me, params.them).
  318. Return(params.err)
  319. }
  320. mockBuddyBroadcaster := newMockbuddyBroadcaster(t)
  321. for _, params := range tt.mockParams.broadcastVisibilityParams {
  322. mockBuddyBroadcaster.EXPECT().
  323. BroadcastVisibility(matchContext(), matchSession(params.from), params.filter, true).
  324. Return(params.err)
  325. }
  326. contactPreAuth := newMockContactPreAuthorizer(t)
  327. for _, params := range tt.mockParams.requiresAuthorizationParams {
  328. contactPreAuth.EXPECT().RequiresAuthorization(matchContext(), params.owner, params.requester).Return(params.result, params.err)
  329. }
  330. svc := BuddyService{
  331. clientSideBuddyListManager: clientSideBuddyListManager,
  332. buddyBroadcaster: mockBuddyBroadcaster,
  333. contactPreAuthorizer: contactPreAuth,
  334. }
  335. rejectSNAC, haveErr := svc.AddBuddies(context.Background(), tt.instance, wire.SNACFrame{RequestID: 42}, tt.bodyIn)
  336. assert.Equal(t, tt.wantSNAC, rejectSNAC)
  337. assert.ErrorIs(t, haveErr, tt.wantErr)
  338. assert.True(t, tt.instance.ContactsInit(), "AddBuddies marks client-side buddy list loaded")
  339. })
  340. }
  341. }
  342. func TestBuddyService_DelBuddies(t *testing.T) {
  343. tests := []struct {
  344. // name is the name of the test
  345. name string
  346. // instance is the client session
  347. instance *state.SessionInstance
  348. // bodyIn is the input SNAC
  349. bodyIn wire.SNAC_0x03_0x05_BuddyDelBuddies
  350. // mockParams is the list of params sent to mocks that satisfy this
  351. // method's dependencies
  352. mockParams mockParams
  353. // wantErr is the expected error
  354. wantErr error
  355. }{
  356. {
  357. name: "delete 2 buddies",
  358. instance: newTestInstance("user_screen_name", sessOptSignonComplete),
  359. bodyIn: wire.SNAC_0x03_0x05_BuddyDelBuddies{
  360. Buddies: []struct {
  361. ScreenName string `oscar:"len_prefix=uint8"`
  362. }{
  363. {
  364. ScreenName: "buddy_1_online",
  365. },
  366. {
  367. ScreenName: "buddy_2_offline",
  368. },
  369. },
  370. },
  371. mockParams: mockParams{
  372. buddyBroadcasterParams: buddyBroadcasterParams{
  373. broadcastVisibilityParams: broadcastVisibilityParams{
  374. {
  375. from: state.NewIdentScreenName("user_screen_name"),
  376. filter: []state.IdentScreenName{
  377. state.NewIdentScreenName("buddy_1_online"),
  378. state.NewIdentScreenName("buddy_2_offline"),
  379. },
  380. },
  381. },
  382. },
  383. clientSideBuddyListManagerParams: clientSideBuddyListManagerParams{
  384. deleteBuddyParams: deleteBuddyParams{
  385. {
  386. me: state.NewIdentScreenName("user_screen_name"),
  387. them: state.NewIdentScreenName("buddy_1_online"),
  388. },
  389. {
  390. me: state.NewIdentScreenName("user_screen_name"),
  391. them: state.NewIdentScreenName("buddy_2_offline"),
  392. },
  393. },
  394. },
  395. },
  396. },
  397. }
  398. for _, tt := range tests {
  399. t.Run(tt.name, func(t *testing.T) {
  400. mockBuddyBroadcaster := newMockbuddyBroadcaster(t)
  401. for _, params := range tt.mockParams.broadcastVisibilityParams {
  402. mockBuddyBroadcaster.EXPECT().
  403. BroadcastVisibility(matchContext(), matchSession(params.from), params.filter, true).
  404. Return(params.err)
  405. }
  406. clientSideBuddyListManager := newMockClientSideBuddyListManager(t)
  407. for _, params := range tt.mockParams.deleteBuddyParams {
  408. clientSideBuddyListManager.EXPECT().
  409. RemoveBuddy(matchContext(), params.me, params.them).
  410. Return(params.err)
  411. }
  412. svc := BuddyService{
  413. buddyBroadcaster: mockBuddyBroadcaster,
  414. clientSideBuddyListManager: clientSideBuddyListManager,
  415. }
  416. assert.ErrorIs(t, tt.wantErr, svc.DelBuddies(context.Background(), tt.instance, tt.bodyIn))
  417. })
  418. }
  419. }
  420. func TestBuddyService_AddTempBuddies(t *testing.T) {
  421. tests := []struct {
  422. // name is the name of the test
  423. name string
  424. // instance is the client session
  425. instance *state.SessionInstance
  426. // bodyIn is the input SNAC
  427. bodyIn wire.SNAC_0x03_0x0F_BuddyAddTempBuddies
  428. // mockParams is the list of params sent to mocks that satisfy this
  429. // method's dependencies
  430. mockParams mockParams
  431. // wantErr is the expected error
  432. wantErr error
  433. }{
  434. {
  435. name: "add 2 buddies, sign-on complete",
  436. instance: newTestInstance("user_screen_name", sessOptSignonComplete),
  437. bodyIn: wire.SNAC_0x03_0x0F_BuddyAddTempBuddies{
  438. Buddies: []struct {
  439. ScreenName string `oscar:"len_prefix=uint8"`
  440. }{
  441. {
  442. ScreenName: "buddy_1_online",
  443. },
  444. {
  445. ScreenName: "buddy_2_offline",
  446. },
  447. },
  448. },
  449. mockParams: mockParams{
  450. clientSideBuddyListManagerParams: clientSideBuddyListManagerParams{
  451. addBuddyParams: addBuddyParams{
  452. {
  453. me: state.NewIdentScreenName("user_screen_name"),
  454. them: state.NewIdentScreenName("buddy_1_online"),
  455. },
  456. {
  457. me: state.NewIdentScreenName("user_screen_name"),
  458. them: state.NewIdentScreenName("buddy_2_offline"),
  459. },
  460. },
  461. },
  462. buddyBroadcasterParams: buddyBroadcasterParams{
  463. broadcastVisibilityParams: broadcastVisibilityParams{
  464. {
  465. from: state.NewIdentScreenName("user_screen_name"),
  466. filter: []state.IdentScreenName{
  467. state.NewIdentScreenName("buddy_1_online"),
  468. state.NewIdentScreenName("buddy_2_offline"),
  469. },
  470. },
  471. },
  472. },
  473. },
  474. },
  475. {
  476. name: "add 2 buddies, sign-on not complete",
  477. instance: newTestInstance("user_screen_name"),
  478. bodyIn: wire.SNAC_0x03_0x0F_BuddyAddTempBuddies{
  479. Buddies: []struct {
  480. ScreenName string `oscar:"len_prefix=uint8"`
  481. }{
  482. {
  483. ScreenName: "buddy_1_online",
  484. },
  485. {
  486. ScreenName: "buddy_2_offline",
  487. },
  488. },
  489. },
  490. mockParams: mockParams{
  491. clientSideBuddyListManagerParams: clientSideBuddyListManagerParams{
  492. addBuddyParams: addBuddyParams{
  493. {
  494. me: state.NewIdentScreenName("user_screen_name"),
  495. them: state.NewIdentScreenName("buddy_1_online"),
  496. },
  497. {
  498. me: state.NewIdentScreenName("user_screen_name"),
  499. them: state.NewIdentScreenName("buddy_2_offline"),
  500. },
  501. },
  502. },
  503. },
  504. },
  505. }
  506. for _, tt := range tests {
  507. t.Run(tt.name, func(t *testing.T) {
  508. clientSideBuddyListManager := newMockClientSideBuddyListManager(t)
  509. for _, params := range tt.mockParams.addBuddyParams {
  510. clientSideBuddyListManager.EXPECT().
  511. AddBuddy(matchContext(), params.me, params.them).
  512. Return(params.err)
  513. }
  514. mockBuddyBroadcaster := newMockbuddyBroadcaster(t)
  515. for _, params := range tt.mockParams.broadcastVisibilityParams {
  516. mockBuddyBroadcaster.EXPECT().
  517. BroadcastVisibility(matchContext(), matchSession(params.from), params.filter, true).
  518. Return(params.err)
  519. }
  520. contactPreAuth := newMockContactPreAuthorizer(t)
  521. for _, params := range tt.mockParams.requiresAuthorizationParams {
  522. contactPreAuth.EXPECT().RequiresAuthorization(matchContext(), params.owner, params.requester).Return(params.result, params.err)
  523. }
  524. svc := BuddyService{
  525. clientSideBuddyListManager: clientSideBuddyListManager,
  526. buddyBroadcaster: mockBuddyBroadcaster,
  527. contactPreAuthorizer: contactPreAuth,
  528. }
  529. rejectSNAC, haveErr := svc.AddTempBuddies(context.Background(), tt.instance, wire.SNACFrame{RequestID: 42}, tt.bodyIn)
  530. assert.Nil(t, rejectSNAC)
  531. assert.ErrorIs(t, haveErr, tt.wantErr)
  532. })
  533. }
  534. }
  535. func TestBuddyService_DelTempBuddies(t *testing.T) {
  536. tests := []struct {
  537. // name is the name of the test
  538. name string
  539. // instance is the client session
  540. instance *state.SessionInstance
  541. // bodyIn is the input SNAC
  542. bodyIn wire.SNAC_0x03_0x10_BuddyDelTempBuddies
  543. // mockParams is the list of params sent to mocks that satisfy this
  544. // method's dependencies
  545. mockParams mockParams
  546. // wantErr is the expected error
  547. wantErr error
  548. }{
  549. {
  550. name: "delete 2 buddies",
  551. instance: newTestInstance("user_screen_name", sessOptSignonComplete),
  552. bodyIn: wire.SNAC_0x03_0x10_BuddyDelTempBuddies{
  553. Buddies: []struct {
  554. ScreenName string `oscar:"len_prefix=uint8"`
  555. }{
  556. {
  557. ScreenName: "buddy_1_online",
  558. },
  559. {
  560. ScreenName: "buddy_2_offline",
  561. },
  562. },
  563. },
  564. mockParams: mockParams{
  565. buddyBroadcasterParams: buddyBroadcasterParams{
  566. broadcastVisibilityParams: broadcastVisibilityParams{
  567. {
  568. from: state.NewIdentScreenName("user_screen_name"),
  569. filter: []state.IdentScreenName{
  570. state.NewIdentScreenName("buddy_1_online"),
  571. state.NewIdentScreenName("buddy_2_offline"),
  572. },
  573. },
  574. },
  575. },
  576. clientSideBuddyListManagerParams: clientSideBuddyListManagerParams{
  577. deleteBuddyParams: deleteBuddyParams{
  578. {
  579. me: state.NewIdentScreenName("user_screen_name"),
  580. them: state.NewIdentScreenName("buddy_1_online"),
  581. },
  582. {
  583. me: state.NewIdentScreenName("user_screen_name"),
  584. them: state.NewIdentScreenName("buddy_2_offline"),
  585. },
  586. },
  587. },
  588. },
  589. },
  590. }
  591. for _, tt := range tests {
  592. t.Run(tt.name, func(t *testing.T) {
  593. mockBuddyBroadcaster := newMockbuddyBroadcaster(t)
  594. for _, params := range tt.mockParams.broadcastVisibilityParams {
  595. mockBuddyBroadcaster.EXPECT().
  596. BroadcastVisibility(matchContext(), matchSession(params.from), params.filter, true).
  597. Return(params.err)
  598. }
  599. clientSideBuddyListManager := newMockClientSideBuddyListManager(t)
  600. for _, params := range tt.mockParams.deleteBuddyParams {
  601. clientSideBuddyListManager.EXPECT().
  602. RemoveBuddy(matchContext(), params.me, params.them).
  603. Return(params.err)
  604. }
  605. svc := BuddyService{
  606. buddyBroadcaster: mockBuddyBroadcaster,
  607. clientSideBuddyListManager: clientSideBuddyListManager,
  608. }
  609. assert.ErrorIs(t, tt.wantErr, svc.DelTempBuddies(context.Background(), tt.instance, tt.bodyIn))
  610. })
  611. }
  612. }
  613. func TestBuddyNotifier_BroadcastBuddyArrived(t *testing.T) {
  614. cases := []struct {
  615. // name is the unit test name
  616. name string
  617. // screenName is the user screen name
  618. screenName state.IdentScreenName
  619. // userInfo is the user info passed to BroadcastBuddyArrived
  620. userInfo wire.TLVUserInfo
  621. // mockParams is the list of params sent to mocks that satisfy this
  622. // method's dependencies
  623. mockParams mockParams
  624. }{
  625. {
  626. name: "happy path",
  627. screenName: state.NewIdentScreenName("me"),
  628. userInfo: wire.TLVUserInfo{ScreenName: "me"},
  629. mockParams: mockParams{
  630. relationshipFetcherParams: relationshipFetcherParams{
  631. allRelationshipsParams: allRelationshipsParams{
  632. {
  633. screenName: state.NewIdentScreenName("me"),
  634. filter: nil,
  635. result: []state.Relationship{
  636. {
  637. User: state.NewIdentScreenName("friend1-visible"),
  638. BlocksYou: false,
  639. YouBlock: false,
  640. IsOnYourList: true,
  641. IsOnTheirList: true,
  642. },
  643. {
  644. User: state.NewIdentScreenName("friend2-visible"),
  645. BlocksYou: false,
  646. YouBlock: false,
  647. IsOnYourList: true,
  648. IsOnTheirList: true,
  649. },
  650. {
  651. User: state.NewIdentScreenName("friend3-you-block"),
  652. BlocksYou: false,
  653. YouBlock: true,
  654. IsOnYourList: true,
  655. IsOnTheirList: true,
  656. },
  657. {
  658. User: state.NewIdentScreenName("friend4-blocks-you"),
  659. BlocksYou: true,
  660. YouBlock: false,
  661. IsOnYourList: true,
  662. IsOnTheirList: true,
  663. },
  664. {
  665. User: state.NewIdentScreenName("friend5-not-on-list"),
  666. BlocksYou: false,
  667. YouBlock: false,
  668. IsOnYourList: true,
  669. IsOnTheirList: false,
  670. },
  671. },
  672. },
  673. },
  674. },
  675. messageRelayerParams: messageRelayerParams{
  676. relayToScreenNamesParams: relayToScreenNamesParams{
  677. {
  678. screenNames: []state.IdentScreenName{
  679. state.NewIdentScreenName("friend1-visible"),
  680. state.NewIdentScreenName("friend2-visible"),
  681. },
  682. message: wire.SNACMessage{
  683. Frame: wire.SNACFrame{
  684. FoodGroup: wire.Buddy,
  685. SubGroup: wire.BuddyArrived,
  686. RequestID: wire.ReqIDFromServer,
  687. },
  688. Body: wire.SNAC_0x03_0x0B_BuddyArrived{
  689. TLVUserInfo: wire.TLVUserInfo{ScreenName: "me"},
  690. },
  691. },
  692. },
  693. },
  694. },
  695. },
  696. },
  697. {
  698. name: "user invisible, don't send notification",
  699. screenName: state.NewIdentScreenName("me"),
  700. userInfo: wire.TLVUserInfo{
  701. ScreenName: "me",
  702. TLVBlock: wire.TLVBlock{
  703. TLVList: wire.TLVList{
  704. wire.NewTLVBE(wire.OServiceUserInfoStatus, wire.OServiceUserStatusInvisible),
  705. },
  706. },
  707. },
  708. mockParams: mockParams{
  709. relationshipFetcherParams: relationshipFetcherParams{
  710. allRelationshipsParams: allRelationshipsParams{}, // don't look up relationships
  711. },
  712. messageRelayerParams: messageRelayerParams{
  713. relayToScreenNamesParams: relayToScreenNamesParams{}, // don't send notification
  714. },
  715. },
  716. },
  717. }
  718. for _, tc := range cases {
  719. t.Run(tc.name, func(t *testing.T) {
  720. relationshipFetcher := newMockRelationshipFetcher(t)
  721. for _, params := range tc.mockParams.allRelationshipsParams {
  722. relationshipFetcher.EXPECT().
  723. AllRelationships(matchContext(), params.screenName, params.filter).
  724. Return(params.result, params.err)
  725. }
  726. messageRelayer := newMockMessageRelayer(t)
  727. for _, params := range tc.mockParams.relayToScreenNamesParams {
  728. messageRelayer.EXPECT().
  729. RelayToScreenNames(matchContext(), params.screenNames, params.message)
  730. }
  731. svc := buddyNotifier{
  732. relationshipFetcher: relationshipFetcher,
  733. messageRelayer: messageRelayer,
  734. }
  735. err := svc.BroadcastBuddyArrived(context.Background(), tc.screenName, tc.userInfo)
  736. assert.NoError(t, err)
  737. })
  738. }
  739. }
  740. func TestBuddyService_BroadcastDeparture(t *testing.T) {
  741. cases := []struct {
  742. // name is the unit test name
  743. name string
  744. // screenName is the user that departed
  745. screenName state.IdentScreenName
  746. // mockParams is the list of params sent to mocks that satisfy this
  747. // method's dependencies
  748. mockParams mockParams
  749. }{
  750. {
  751. name: "happy path",
  752. screenName: state.NewIdentScreenName("me"),
  753. mockParams: mockParams{
  754. relationshipFetcherParams: relationshipFetcherParams{
  755. allRelationshipsParams: allRelationshipsParams{
  756. {
  757. screenName: state.NewIdentScreenName("me"),
  758. filter: nil,
  759. result: []state.Relationship{
  760. {
  761. User: state.NewIdentScreenName("friend1-visible"),
  762. BlocksYou: false,
  763. YouBlock: false,
  764. IsOnYourList: true,
  765. IsOnTheirList: true,
  766. },
  767. {
  768. User: state.NewIdentScreenName("friend2-visible"),
  769. BlocksYou: false,
  770. YouBlock: false,
  771. IsOnYourList: true,
  772. IsOnTheirList: true,
  773. },
  774. {
  775. User: state.NewIdentScreenName("friend3-you-block"),
  776. BlocksYou: false,
  777. YouBlock: true,
  778. IsOnYourList: true,
  779. IsOnTheirList: true,
  780. },
  781. {
  782. User: state.NewIdentScreenName("friend4-blocks-you"),
  783. BlocksYou: true,
  784. YouBlock: false,
  785. IsOnYourList: true,
  786. IsOnTheirList: true,
  787. },
  788. {
  789. User: state.NewIdentScreenName("friend5-not-on-list"),
  790. BlocksYou: false,
  791. YouBlock: false,
  792. IsOnYourList: true,
  793. IsOnTheirList: false,
  794. },
  795. },
  796. },
  797. },
  798. },
  799. messageRelayerParams: messageRelayerParams{
  800. relayToScreenNamesParams: relayToScreenNamesParams{
  801. {
  802. screenNames: []state.IdentScreenName{
  803. state.NewIdentScreenName("friend1-visible"),
  804. state.NewIdentScreenName("friend2-visible"),
  805. },
  806. message: wire.SNACMessage{
  807. Frame: wire.SNACFrame{
  808. FoodGroup: wire.Buddy,
  809. SubGroup: wire.BuddyDeparted,
  810. RequestID: wire.ReqIDFromServer,
  811. },
  812. Body: wire.SNAC_0x03_0x0C_BuddyDeparted{
  813. TLVUserInfo: wire.TLVUserInfo{
  814. ScreenName: "me",
  815. WarningLevel: 0,
  816. TLVBlock: wire.TLVBlock{
  817. TLVList: wire.TLVList{
  818. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0)),
  819. },
  820. },
  821. },
  822. },
  823. },
  824. },
  825. },
  826. },
  827. },
  828. },
  829. }
  830. for _, tc := range cases {
  831. t.Run(tc.name, func(t *testing.T) {
  832. relationshipFetcher := newMockRelationshipFetcher(t)
  833. for _, params := range tc.mockParams.allRelationshipsParams {
  834. relationshipFetcher.EXPECT().
  835. AllRelationships(matchContext(), params.screenName, params.filter).
  836. Return(params.result, params.err)
  837. }
  838. messageRelayer := newMockMessageRelayer(t)
  839. for _, params := range tc.mockParams.relayToScreenNamesParams {
  840. messageRelayer.EXPECT().
  841. RelayToScreenNames(matchContext(), params.screenNames, params.message)
  842. }
  843. svc := buddyNotifier{
  844. relationshipFetcher: relationshipFetcher,
  845. messageRelayer: messageRelayer,
  846. }
  847. err := svc.BroadcastBuddyDeparted(context.Background(), tc.screenName)
  848. assert.NoError(t, err)
  849. })
  850. }
  851. }
  852. func Test_buddyNotifier_BroadcastVisibility(t *testing.T) {
  853. cases := []struct {
  854. // name is the unit test name
  855. name string
  856. // instance is the session of the user
  857. instance *state.SessionInstance
  858. // filter limits specific users that can be notified
  859. filter []state.IdentScreenName
  860. // doSendDepartures indicates whether departure messages should be sent
  861. doSendDepartures bool
  862. // mockParams is the list of params sent to mocks that satisfy this
  863. // method's dependencies
  864. mockParams mockParams
  865. }{
  866. {
  867. name: "happy path",
  868. instance: newTestInstance("me"),
  869. mockParams: mockParams{
  870. relationshipFetcherParams: relationshipFetcherParams{
  871. allRelationshipsParams: allRelationshipsParams{
  872. {
  873. screenName: state.NewIdentScreenName("me"),
  874. filter: nil,
  875. result: []state.Relationship{
  876. {
  877. User: state.NewIdentScreenName("friend1-blocks-you"),
  878. BlocksYou: true,
  879. YouBlock: false,
  880. IsOnYourList: true,
  881. IsOnTheirList: true,
  882. },
  883. {
  884. User: state.NewIdentScreenName("friend2-visible-on-their-list"),
  885. BlocksYou: false,
  886. YouBlock: false,
  887. IsOnYourList: false,
  888. IsOnTheirList: true,
  889. },
  890. {
  891. User: state.NewIdentScreenName("friend3-visible-on-your-list"),
  892. BlocksYou: false,
  893. YouBlock: false,
  894. IsOnYourList: true,
  895. IsOnTheirList: false,
  896. },
  897. {
  898. User: state.NewIdentScreenName("friend4-visible-on-both-lists"),
  899. BlocksYou: false,
  900. YouBlock: false,
  901. IsOnYourList: true,
  902. IsOnTheirList: true,
  903. },
  904. {
  905. User: state.NewIdentScreenName("friend5-blocked-on-their-list"),
  906. BlocksYou: false,
  907. YouBlock: true,
  908. IsOnYourList: false,
  909. IsOnTheirList: true,
  910. },
  911. {
  912. User: state.NewIdentScreenName("friend6-blocked-on-your-list"),
  913. BlocksYou: false,
  914. YouBlock: true,
  915. IsOnYourList: true,
  916. IsOnTheirList: false,
  917. },
  918. {
  919. User: state.NewIdentScreenName("friend7-blocked-on-both-lists"),
  920. BlocksYou: false,
  921. YouBlock: true,
  922. IsOnYourList: true,
  923. IsOnTheirList: true,
  924. },
  925. {
  926. User: state.NewIdentScreenName("friend7-visible-offline"),
  927. BlocksYou: false,
  928. YouBlock: false,
  929. IsOnYourList: true,
  930. IsOnTheirList: true,
  931. },
  932. },
  933. },
  934. },
  935. },
  936. messageRelayerParams: messageRelayerParams{
  937. relayToScreenNameParams: relayToScreenNameParams{
  938. {
  939. screenName: state.NewIdentScreenName("friend2-visible-on-their-list"),
  940. message: newBuddyArrivedNotif("me"),
  941. },
  942. {
  943. screenName: state.NewIdentScreenName("me"),
  944. message: newBuddyArrivedNotif("friend3-visible-on-your-list"),
  945. },
  946. {
  947. screenName: state.NewIdentScreenName("friend4-visible-on-both-lists"),
  948. message: newBuddyArrivedNotif("me"),
  949. },
  950. {
  951. screenName: state.NewIdentScreenName("me"),
  952. message: newBuddyArrivedNotif("friend4-visible-on-both-lists"),
  953. },
  954. {
  955. screenName: state.NewIdentScreenName("friend5-blocked-on-their-list"),
  956. message: newBuddyDepartedNotif("me"),
  957. },
  958. {
  959. screenName: state.NewIdentScreenName("me"),
  960. message: newBuddyDepartedNotif("friend6-blocked-on-your-list"),
  961. },
  962. {
  963. screenName: state.NewIdentScreenName("me"),
  964. message: newBuddyDepartedNotif("friend7-blocked-on-both-lists"),
  965. },
  966. {
  967. screenName: state.NewIdentScreenName("friend7-blocked-on-both-lists"),
  968. message: newBuddyDepartedNotif("me"),
  969. },
  970. },
  971. },
  972. sessionRetrieverParams: sessionRetrieverParams{
  973. retrieveSessionParams: retrieveSessionParams{
  974. {
  975. screenName: state.NewIdentScreenName("friend2-visible-on-their-list"),
  976. result: newTestInstance("friend2-visible-on-their-list").Session(),
  977. },
  978. {
  979. screenName: state.NewIdentScreenName("friend3-visible-on-your-list"),
  980. result: newTestInstance("friend3-visible-on-your-list").Session(),
  981. },
  982. {
  983. screenName: state.NewIdentScreenName("friend4-visible-on-both-lists"),
  984. result: newTestInstance("friend4-visible-on-both-lists").Session(),
  985. },
  986. {
  987. screenName: state.NewIdentScreenName("friend5-blocked-on-their-list"),
  988. result: newTestInstance("friend5-blocked-on-their-list").Session(),
  989. },
  990. {
  991. screenName: state.NewIdentScreenName("friend6-blocked-on-your-list"),
  992. result: newTestInstance("friend6-blocked-on-your-list").Session(),
  993. },
  994. {
  995. screenName: state.NewIdentScreenName("friend7-blocked-on-both-lists"),
  996. result: newTestInstance("friend7-blocked-on-both-lists").Session(),
  997. },
  998. {
  999. screenName: state.NewIdentScreenName("friend7-visible-offline"),
  1000. result: nil,
  1001. },
  1002. },
  1003. },
  1004. },
  1005. doSendDepartures: true,
  1006. },
  1007. {
  1008. name: "user invisible, don't send notification to buddies",
  1009. instance: newTestInstance("me", sessOptInvisible),
  1010. mockParams: mockParams{
  1011. relationshipFetcherParams: relationshipFetcherParams{
  1012. allRelationshipsParams: allRelationshipsParams{
  1013. {
  1014. screenName: state.NewIdentScreenName("me"),
  1015. filter: nil,
  1016. result: []state.Relationship{
  1017. {
  1018. User: state.NewIdentScreenName("friend1-blocks-you"),
  1019. BlocksYou: true,
  1020. YouBlock: false,
  1021. IsOnYourList: true,
  1022. IsOnTheirList: true,
  1023. },
  1024. {
  1025. User: state.NewIdentScreenName("friend2-visible-on-their-list"),
  1026. BlocksYou: false,
  1027. YouBlock: false,
  1028. IsOnYourList: false,
  1029. IsOnTheirList: true,
  1030. },
  1031. {
  1032. User: state.NewIdentScreenName("friend3-visible-on-your-list"),
  1033. BlocksYou: false,
  1034. YouBlock: false,
  1035. IsOnYourList: true,
  1036. IsOnTheirList: false,
  1037. },
  1038. {
  1039. User: state.NewIdentScreenName("friend4-visible-on-both-lists"),
  1040. BlocksYou: false,
  1041. YouBlock: false,
  1042. IsOnYourList: true,
  1043. IsOnTheirList: true,
  1044. },
  1045. {
  1046. User: state.NewIdentScreenName("friend5-blocked-on-their-list"),
  1047. BlocksYou: false,
  1048. YouBlock: true,
  1049. IsOnYourList: false,
  1050. IsOnTheirList: true,
  1051. },
  1052. {
  1053. User: state.NewIdentScreenName("friend6-blocked-on-your-list"),
  1054. BlocksYou: false,
  1055. YouBlock: true,
  1056. IsOnYourList: true,
  1057. IsOnTheirList: false,
  1058. },
  1059. {
  1060. User: state.NewIdentScreenName("friend7-blocked-on-both-lists"),
  1061. BlocksYou: false,
  1062. YouBlock: true,
  1063. IsOnYourList: true,
  1064. IsOnTheirList: true,
  1065. },
  1066. {
  1067. User: state.NewIdentScreenName("friend7-visible-offline"),
  1068. BlocksYou: false,
  1069. YouBlock: false,
  1070. IsOnYourList: true,
  1071. IsOnTheirList: true,
  1072. },
  1073. },
  1074. },
  1075. },
  1076. },
  1077. messageRelayerParams: messageRelayerParams{
  1078. relayToScreenNameParams: relayToScreenNameParams{
  1079. {
  1080. screenName: state.NewIdentScreenName("me"),
  1081. message: newBuddyArrivedNotif("friend3-visible-on-your-list"),
  1082. },
  1083. {
  1084. screenName: state.NewIdentScreenName("me"),
  1085. message: newBuddyArrivedNotif("friend4-visible-on-both-lists"),
  1086. },
  1087. {
  1088. screenName: state.NewIdentScreenName("friend5-blocked-on-their-list"),
  1089. message: newBuddyDepartedNotif("me"),
  1090. },
  1091. {
  1092. screenName: state.NewIdentScreenName("me"),
  1093. message: newBuddyDepartedNotif("friend6-blocked-on-your-list"),
  1094. },
  1095. {
  1096. screenName: state.NewIdentScreenName("me"),
  1097. message: newBuddyDepartedNotif("friend7-blocked-on-both-lists"),
  1098. },
  1099. {
  1100. screenName: state.NewIdentScreenName("friend7-blocked-on-both-lists"),
  1101. message: newBuddyDepartedNotif("me"),
  1102. },
  1103. },
  1104. },
  1105. sessionRetrieverParams: sessionRetrieverParams{
  1106. retrieveSessionParams: retrieveSessionParams{
  1107. {
  1108. screenName: state.NewIdentScreenName("friend2-visible-on-their-list"),
  1109. result: newTestInstance("friend2-visible-on-their-list").Session(),
  1110. },
  1111. {
  1112. screenName: state.NewIdentScreenName("friend3-visible-on-your-list"),
  1113. result: newTestInstance("friend3-visible-on-your-list").Session(),
  1114. },
  1115. {
  1116. screenName: state.NewIdentScreenName("friend4-visible-on-both-lists"),
  1117. result: newTestInstance("friend4-visible-on-both-lists").Session(),
  1118. },
  1119. {
  1120. screenName: state.NewIdentScreenName("friend5-blocked-on-their-list"),
  1121. result: newTestInstance("friend5-blocked-on-their-list").Session(),
  1122. },
  1123. {
  1124. screenName: state.NewIdentScreenName("friend6-blocked-on-your-list"),
  1125. result: newTestInstance("friend6-blocked-on-your-list").Session(),
  1126. },
  1127. {
  1128. screenName: state.NewIdentScreenName("friend7-blocked-on-both-lists"),
  1129. result: newTestInstance("friend7-blocked-on-both-lists").Session(),
  1130. },
  1131. {
  1132. screenName: state.NewIdentScreenName("friend7-visible-offline"),
  1133. result: nil,
  1134. },
  1135. },
  1136. },
  1137. },
  1138. doSendDepartures: true,
  1139. },
  1140. {
  1141. name: "don't send departure notifications",
  1142. instance: newTestInstance("me"),
  1143. mockParams: mockParams{
  1144. relationshipFetcherParams: relationshipFetcherParams{
  1145. allRelationshipsParams: allRelationshipsParams{
  1146. {
  1147. screenName: state.NewIdentScreenName("me"),
  1148. filter: nil,
  1149. result: []state.Relationship{
  1150. {
  1151. User: state.NewIdentScreenName("friend2-visible-on-their-list"),
  1152. BlocksYou: false,
  1153. YouBlock: false,
  1154. IsOnYourList: false,
  1155. IsOnTheirList: true,
  1156. },
  1157. {
  1158. User: state.NewIdentScreenName("friend3-visible-on-your-list"),
  1159. BlocksYou: false,
  1160. YouBlock: false,
  1161. IsOnYourList: true,
  1162. IsOnTheirList: false,
  1163. },
  1164. {
  1165. User: state.NewIdentScreenName("friend4-visible-on-both-lists"),
  1166. BlocksYou: false,
  1167. YouBlock: false,
  1168. IsOnYourList: true,
  1169. IsOnTheirList: true,
  1170. },
  1171. {
  1172. User: state.NewIdentScreenName("friend7-visible-offline"),
  1173. BlocksYou: false,
  1174. YouBlock: false,
  1175. IsOnYourList: true,
  1176. IsOnTheirList: true,
  1177. },
  1178. },
  1179. },
  1180. },
  1181. },
  1182. messageRelayerParams: messageRelayerParams{
  1183. relayToScreenNameParams: relayToScreenNameParams{
  1184. {
  1185. screenName: state.NewIdentScreenName("friend2-visible-on-their-list"),
  1186. message: newBuddyArrivedNotif("me"),
  1187. },
  1188. {
  1189. screenName: state.NewIdentScreenName("me"),
  1190. message: newBuddyArrivedNotif("friend3-visible-on-your-list"),
  1191. },
  1192. {
  1193. screenName: state.NewIdentScreenName("friend4-visible-on-both-lists"),
  1194. message: newBuddyArrivedNotif("me"),
  1195. },
  1196. {
  1197. screenName: state.NewIdentScreenName("me"),
  1198. message: newBuddyArrivedNotif("friend4-visible-on-both-lists"),
  1199. },
  1200. },
  1201. },
  1202. sessionRetrieverParams: sessionRetrieverParams{
  1203. retrieveSessionParams: retrieveSessionParams{
  1204. {
  1205. screenName: state.NewIdentScreenName("friend2-visible-on-their-list"),
  1206. result: newTestInstance("friend2-visible-on-their-list").Session(),
  1207. },
  1208. {
  1209. screenName: state.NewIdentScreenName("friend3-visible-on-your-list"),
  1210. result: newTestInstance("friend3-visible-on-your-list").Session(),
  1211. },
  1212. {
  1213. screenName: state.NewIdentScreenName("friend4-visible-on-both-lists"),
  1214. result: newTestInstance("friend4-visible-on-both-lists").Session(),
  1215. },
  1216. {
  1217. screenName: state.NewIdentScreenName("friend7-visible-offline"),
  1218. result: nil,
  1219. },
  1220. },
  1221. },
  1222. },
  1223. doSendDepartures: false,
  1224. },
  1225. }
  1226. for _, tc := range cases {
  1227. t.Run(tc.name, func(t *testing.T) {
  1228. relationshipFetcher := newMockRelationshipFetcher(t)
  1229. for _, params := range tc.mockParams.allRelationshipsParams {
  1230. relationshipFetcher.EXPECT().
  1231. AllRelationships(matchContext(), params.screenName, params.filter).
  1232. Return(params.result, params.err)
  1233. }
  1234. messageRelayer := newMockMessageRelayer(t)
  1235. for _, params := range tc.mockParams.relayToScreenNameParams {
  1236. messageRelayer.EXPECT().
  1237. RelayToScreenName(matchContext(), params.screenName, mock.MatchedBy(func(message wire.SNACMessage) bool {
  1238. return params.message.Frame == message.Frame &&
  1239. params.message.Body.(func(any) bool)(message.Body)
  1240. }))
  1241. }
  1242. sessionRetriever := newMockSessionRetriever(t)
  1243. for _, params := range tc.mockParams.retrieveSessionParams {
  1244. sessionRetriever.EXPECT().
  1245. RetrieveSession(params.screenName).
  1246. Return(params.result)
  1247. }
  1248. svc := buddyNotifier{
  1249. relationshipFetcher: relationshipFetcher,
  1250. messageRelayer: messageRelayer,
  1251. sessionRetriever: sessionRetriever,
  1252. }
  1253. err := svc.BroadcastVisibility(context.Background(), tc.instance, tc.filter, tc.doSendDepartures)
  1254. assert.NoError(t, err)
  1255. })
  1256. }
  1257. }
  1258. func newBuddyDepartedNotif(screenName state.DisplayScreenName) wire.SNACMessage {
  1259. return wire.SNACMessage{
  1260. Frame: wire.SNACFrame{
  1261. FoodGroup: wire.Buddy,
  1262. SubGroup: wire.BuddyDeparted,
  1263. RequestID: wire.ReqIDFromServer,
  1264. },
  1265. Body: func(val any) bool {
  1266. snac, ok := val.(wire.SNAC_0x03_0x0C_BuddyDeparted)
  1267. if !ok {
  1268. return false
  1269. }
  1270. return snac.ScreenName == screenName.String()
  1271. },
  1272. }
  1273. }
  1274. func newBuddyArrivedNotif(screenName state.DisplayScreenName) wire.SNACMessage {
  1275. return wire.SNACMessage{
  1276. Frame: wire.SNACFrame{
  1277. FoodGroup: wire.Buddy,
  1278. SubGroup: wire.BuddyArrived,
  1279. RequestID: wire.ReqIDFromServer,
  1280. },
  1281. Body: func(val any) bool {
  1282. snac, ok := val.(wire.SNAC_0x03_0x0B_BuddyArrived)
  1283. if !ok {
  1284. return false
  1285. }
  1286. return snac.ScreenName == screenName.String() && len(snac.TLVList) > 0
  1287. },
  1288. }
  1289. }