icbm_test.go 120 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912
  1. package foodgroup
  2. import (
  3. "context"
  4. "errors"
  5. "io"
  6. "log/slog"
  7. "net"
  8. "net/netip"
  9. "sync"
  10. "testing"
  11. "time"
  12. "github.com/mk6i/open-oscar-server/state"
  13. "github.com/mk6i/open-oscar-server/wire"
  14. "github.com/stretchr/testify/assert"
  15. "github.com/stretchr/testify/mock"
  16. )
  17. func TestICBMService_ChannelMsgToHost(t *testing.T) {
  18. cases := []struct {
  19. // name is the unit test name
  20. name string
  21. // instance is the session of the user sending the message
  22. instance *state.SessionInstance
  23. // inputSNAC is the SNAC frame sent from the server to the recipient
  24. // client
  25. inputSNAC wire.SNACMessage
  26. // expectOutput is the expected return SNAC value.
  27. expectOutput *wire.SNACMessage
  28. // wantErr is the expected error (nil for success)
  29. wantErr error
  30. // mockParams is the list of params sent to mocks that satisfy this
  31. // method's dependencies
  32. mockParams mockParams
  33. // expectForwardICQAuthEvents indicates whether ChannelMsgToHost should
  34. // delegate ICQ channel-4 auth events to forwardICQAuthEvents.
  35. expectForwardICQAuthEvents bool
  36. // expectForwardRecipient is the expected recipient passed to
  37. // forwardICQAuthEvents when expectForwardICQAuthEvents is true.
  38. expectForwardRecipient state.IdentScreenName
  39. // expectForwardAuthMsg is the expected auth message passed to
  40. // forwardICQAuthEvents when expectForwardICQAuthEvents is true.
  41. expectForwardAuthMsg wire.ICBMCh4Message
  42. // timeNow returns the current time
  43. timeNow func() time.Time
  44. }{
  45. {
  46. name: "transmit message from sender to recipient, ack message back to sender",
  47. instance: newTestInstance("sender-screen-name", sessOptWarning(10), sessOptWantTypingEvents),
  48. mockParams: mockParams{
  49. relationshipFetcherParams: relationshipFetcherParams{
  50. relationshipParams: relationshipParams{
  51. {
  52. me: state.NewIdentScreenName("sender-screen-name"),
  53. them: state.NewIdentScreenName("recipient-screen-name"),
  54. result: state.Relationship{
  55. User: state.NewIdentScreenName("recipient-screen-name"),
  56. BlocksYou: false,
  57. YouBlock: false,
  58. IsOnTheirList: false,
  59. IsOnYourList: false,
  60. },
  61. },
  62. },
  63. },
  64. sessionRetrieverParams: sessionRetrieverParams{
  65. retrieveSessionParams{
  66. {
  67. screenName: state.NewIdentScreenName("recipient-screen-name"),
  68. result: newTestInstance("recipient-screen-name", sessOptWarning(20), sessOptSignonComplete).Session(),
  69. },
  70. },
  71. },
  72. messageRelayerParams: messageRelayerParams{
  73. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  74. {
  75. screenName: state.NewIdentScreenName("recipient-screen-name"),
  76. message: wire.SNACMessage{
  77. Frame: wire.SNACFrame{
  78. FoodGroup: wire.ICBM,
  79. SubGroup: wire.ICBMChannelMsgToClient,
  80. RequestID: wire.ReqIDFromServer,
  81. },
  82. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  83. ChannelID: wire.ICBMChannelIM,
  84. TLVUserInfo: newTestInstance("sender-screen-name", sessOptWarning(10)).Session().TLVUserInfo(),
  85. TLVRestBlock: wire.TLVRestBlock{
  86. TLVList: wire.TLVList{
  87. {
  88. Tag: wire.ICBMTLVData,
  89. Value: []byte{1, 2, 3, 4},
  90. },
  91. {
  92. Tag: wire.ICBMTLVWantEvents,
  93. Value: []byte{},
  94. },
  95. },
  96. },
  97. },
  98. },
  99. },
  100. },
  101. },
  102. },
  103. inputSNAC: wire.SNACMessage{
  104. Frame: wire.SNACFrame{
  105. RequestID: 1234,
  106. },
  107. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  108. ChannelID: wire.ICBMChannelIM,
  109. ScreenName: "recipient-screen-name",
  110. TLVRestBlock: wire.TLVRestBlock{
  111. TLVList: wire.TLVList{
  112. {
  113. Tag: wire.ICBMTLVRequestHostAck,
  114. Value: []byte{},
  115. },
  116. {
  117. Tag: wire.ICBMTLVData,
  118. Value: []byte{1, 2, 3, 4},
  119. },
  120. },
  121. },
  122. },
  123. },
  124. expectOutput: &wire.SNACMessage{
  125. Frame: wire.SNACFrame{
  126. FoodGroup: wire.ICBM,
  127. SubGroup: wire.ICBMHostAck,
  128. RequestID: 1234,
  129. },
  130. Body: wire.SNAC_0x04_0x0C_ICBMHostAck{
  131. ChannelID: wire.ICBMChannelIM,
  132. ScreenName: "recipient-screen-name",
  133. },
  134. },
  135. },
  136. {
  137. name: "transmit message from sender to recipient, don't ack message back to sender",
  138. instance: newTestInstance("sender-screen-name", sessOptWarning(10), sessOptWantTypingEvents),
  139. mockParams: mockParams{
  140. relationshipFetcherParams: relationshipFetcherParams{
  141. relationshipParams: relationshipParams{
  142. {
  143. me: state.NewIdentScreenName("sender-screen-name"),
  144. them: state.NewIdentScreenName("recipient-screen-name"),
  145. result: state.Relationship{
  146. User: state.NewIdentScreenName("recipient-screen-name"),
  147. BlocksYou: false,
  148. YouBlock: false,
  149. IsOnTheirList: false,
  150. IsOnYourList: false,
  151. },
  152. },
  153. },
  154. },
  155. sessionRetrieverParams: sessionRetrieverParams{
  156. retrieveSessionParams{
  157. {
  158. screenName: state.NewIdentScreenName("recipient-screen-name"),
  159. result: newTestInstance("recipient-screen-name", sessOptWarning(20), sessOptSignonComplete).Session(),
  160. },
  161. },
  162. },
  163. messageRelayerParams: messageRelayerParams{
  164. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  165. {
  166. screenName: state.NewIdentScreenName("recipient-screen-name"),
  167. message: wire.SNACMessage{
  168. Frame: wire.SNACFrame{
  169. FoodGroup: wire.ICBM,
  170. SubGroup: wire.ICBMChannelMsgToClient,
  171. RequestID: wire.ReqIDFromServer,
  172. },
  173. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  174. ChannelID: wire.ICBMChannelIM,
  175. TLVUserInfo: newTestInstance("sender-screen-name", sessOptWarning(10)).Session().TLVUserInfo(),
  176. TLVRestBlock: wire.TLVRestBlock{
  177. TLVList: wire.TLVList{
  178. {
  179. Tag: wire.ICBMTLVData,
  180. Value: []byte{1, 2, 3, 4},
  181. },
  182. {
  183. Tag: wire.ICBMTLVWantEvents,
  184. Value: []byte{},
  185. },
  186. },
  187. },
  188. },
  189. },
  190. },
  191. },
  192. },
  193. },
  194. inputSNAC: wire.SNACMessage{
  195. Frame: wire.SNACFrame{
  196. RequestID: 1234,
  197. },
  198. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  199. ChannelID: wire.ICBMChannelIM,
  200. ScreenName: "recipient-screen-name",
  201. TLVRestBlock: wire.TLVRestBlock{
  202. TLVList: wire.TLVList{
  203. {
  204. Tag: wire.ICBMTLVData,
  205. Value: []byte{1, 2, 3, 4},
  206. },
  207. },
  208. },
  209. },
  210. },
  211. expectOutput: nil,
  212. },
  213. {
  214. name: "transmit message from sender to recipient, don't ack message back to sender, don't want typing events",
  215. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  216. mockParams: mockParams{
  217. relationshipFetcherParams: relationshipFetcherParams{
  218. relationshipParams: relationshipParams{
  219. {
  220. me: state.NewIdentScreenName("sender-screen-name"),
  221. them: state.NewIdentScreenName("recipient-screen-name"),
  222. result: state.Relationship{
  223. User: state.NewIdentScreenName("recipient-screen-name"),
  224. BlocksYou: false,
  225. YouBlock: false,
  226. IsOnTheirList: false,
  227. IsOnYourList: false,
  228. },
  229. },
  230. },
  231. },
  232. sessionRetrieverParams: sessionRetrieverParams{
  233. retrieveSessionParams{
  234. {
  235. screenName: state.NewIdentScreenName("recipient-screen-name"),
  236. result: newTestInstance("recipient-screen-name", sessOptWarning(20), sessOptSignonComplete).Session(),
  237. },
  238. },
  239. },
  240. messageRelayerParams: messageRelayerParams{
  241. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  242. {
  243. screenName: state.NewIdentScreenName("recipient-screen-name"),
  244. message: wire.SNACMessage{
  245. Frame: wire.SNACFrame{
  246. FoodGroup: wire.ICBM,
  247. SubGroup: wire.ICBMChannelMsgToClient,
  248. RequestID: wire.ReqIDFromServer,
  249. },
  250. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  251. ChannelID: wire.ICBMChannelIM,
  252. TLVUserInfo: newTestInstance("sender-screen-name", sessOptWarning(10)).Session().TLVUserInfo(),
  253. TLVRestBlock: wire.TLVRestBlock{
  254. TLVList: wire.TLVList{
  255. {
  256. Tag: wire.ICBMTLVData,
  257. Value: []byte{1, 2, 3, 4},
  258. },
  259. },
  260. },
  261. },
  262. },
  263. },
  264. },
  265. },
  266. },
  267. inputSNAC: wire.SNACMessage{
  268. Frame: wire.SNACFrame{
  269. RequestID: 1234,
  270. },
  271. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  272. ChannelID: wire.ICBMChannelIM,
  273. ScreenName: "recipient-screen-name",
  274. TLVRestBlock: wire.TLVRestBlock{
  275. TLVList: wire.TLVList{
  276. {
  277. Tag: wire.ICBMTLVData,
  278. Value: []byte{1, 2, 3, 4},
  279. },
  280. },
  281. },
  282. },
  283. },
  284. expectOutput: nil,
  285. },
  286. {
  287. name: "strip store directive from message relayed to online recipient",
  288. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  289. mockParams: mockParams{
  290. relationshipFetcherParams: relationshipFetcherParams{
  291. relationshipParams: relationshipParams{
  292. {
  293. me: state.NewIdentScreenName("sender-screen-name"),
  294. them: state.NewIdentScreenName("recipient-screen-name"),
  295. result: state.Relationship{
  296. User: state.NewIdentScreenName("recipient-screen-name"),
  297. BlocksYou: false,
  298. YouBlock: false,
  299. IsOnTheirList: false,
  300. IsOnYourList: false,
  301. },
  302. },
  303. },
  304. },
  305. sessionRetrieverParams: sessionRetrieverParams{
  306. retrieveSessionParams{
  307. {
  308. screenName: state.NewIdentScreenName("recipient-screen-name"),
  309. result: newTestInstance("recipient-screen-name", sessOptWarning(20), sessOptSignonComplete).Session(),
  310. },
  311. },
  312. },
  313. messageRelayerParams: messageRelayerParams{
  314. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  315. {
  316. screenName: state.NewIdentScreenName("recipient-screen-name"),
  317. message: wire.SNACMessage{
  318. Frame: wire.SNACFrame{
  319. FoodGroup: wire.ICBM,
  320. SubGroup: wire.ICBMChannelMsgToClient,
  321. RequestID: wire.ReqIDFromServer,
  322. },
  323. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  324. ChannelID: wire.ICBMChannelIM,
  325. TLVUserInfo: newTestInstance("sender-screen-name", sessOptWarning(10)).Session().TLVUserInfo(),
  326. TLVRestBlock: wire.TLVRestBlock{
  327. TLVList: wire.TLVList{
  328. wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3, 4}),
  329. },
  330. },
  331. },
  332. },
  333. },
  334. },
  335. },
  336. },
  337. inputSNAC: wire.SNACMessage{
  338. Frame: wire.SNACFrame{
  339. RequestID: 1234,
  340. },
  341. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  342. ChannelID: wire.ICBMChannelIM,
  343. ScreenName: "recipient-screen-name",
  344. TLVRestBlock: wire.TLVRestBlock{
  345. TLVList: wire.TLVList{
  346. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  347. wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3, 4}),
  348. },
  349. },
  350. },
  351. },
  352. expectOutput: nil,
  353. },
  354. {
  355. name: "don't transmit message from sender to recipient because sender has blocked recipient",
  356. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  357. mockParams: mockParams{
  358. relationshipFetcherParams: relationshipFetcherParams{
  359. relationshipParams: relationshipParams{
  360. {
  361. me: state.NewIdentScreenName("sender-screen-name"),
  362. them: state.NewIdentScreenName("recipient-screen-name"),
  363. result: state.Relationship{
  364. User: state.NewIdentScreenName("recipient-screen-name"),
  365. BlocksYou: false,
  366. YouBlock: true,
  367. IsOnTheirList: false,
  368. IsOnYourList: false,
  369. },
  370. },
  371. },
  372. },
  373. sessionRetrieverParams: sessionRetrieverParams{
  374. retrieveSessionParams{},
  375. },
  376. messageRelayerParams: messageRelayerParams{
  377. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{},
  378. },
  379. },
  380. inputSNAC: wire.SNACMessage{
  381. Frame: wire.SNACFrame{
  382. RequestID: 1234,
  383. },
  384. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  385. ChannelID: wire.ICBMChannelIM,
  386. ScreenName: "recipient-screen-name",
  387. TLVRestBlock: wire.TLVRestBlock{
  388. TLVList: wire.TLVList{
  389. {
  390. Tag: wire.ICBMTLVRequestHostAck,
  391. Value: []byte{},
  392. },
  393. },
  394. },
  395. },
  396. },
  397. expectOutput: &wire.SNACMessage{
  398. Frame: wire.SNACFrame{
  399. FoodGroup: wire.ICBM,
  400. SubGroup: wire.ICBMErr,
  401. RequestID: 1234,
  402. },
  403. Body: wire.SNACError{
  404. Code: wire.ErrorCodeInLocalPermitDeny,
  405. },
  406. },
  407. },
  408. {
  409. name: "don't transmit message from sender to recipient because recipient has blocked sender",
  410. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  411. mockParams: mockParams{
  412. relationshipFetcherParams: relationshipFetcherParams{
  413. relationshipParams: relationshipParams{
  414. {
  415. me: state.NewIdentScreenName("sender-screen-name"),
  416. them: state.NewIdentScreenName("recipient-screen-name"),
  417. result: state.Relationship{
  418. User: state.NewIdentScreenName("recipient-screen-name"),
  419. BlocksYou: true,
  420. YouBlock: false,
  421. IsOnTheirList: false,
  422. IsOnYourList: false,
  423. },
  424. },
  425. },
  426. },
  427. sessionRetrieverParams: sessionRetrieverParams{
  428. retrieveSessionParams{},
  429. },
  430. messageRelayerParams: messageRelayerParams{
  431. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{},
  432. },
  433. },
  434. inputSNAC: wire.SNACMessage{
  435. Frame: wire.SNACFrame{
  436. RequestID: 1234,
  437. },
  438. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  439. ChannelID: wire.ICBMChannelIM,
  440. ScreenName: "recipient-screen-name",
  441. TLVRestBlock: wire.TLVRestBlock{
  442. TLVList: wire.TLVList{
  443. {
  444. Tag: wire.ICBMTLVRequestHostAck,
  445. Value: []byte{},
  446. },
  447. },
  448. },
  449. },
  450. },
  451. expectOutput: &wire.SNACMessage{
  452. Frame: wire.SNACFrame{
  453. FoodGroup: wire.ICBM,
  454. SubGroup: wire.ICBMErr,
  455. RequestID: 1234,
  456. },
  457. Body: wire.SNACError{
  458. Code: wire.ErrorCodeNotLoggedOn,
  459. },
  460. },
  461. },
  462. {
  463. name: "don't transmit message from sender to recipient because recipient doesn't exist",
  464. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  465. mockParams: mockParams{
  466. relationshipFetcherParams: relationshipFetcherParams{
  467. relationshipParams: relationshipParams{
  468. {
  469. me: state.NewIdentScreenName("sender-screen-name"),
  470. them: state.NewIdentScreenName("recipient-screen-name"),
  471. result: state.Relationship{
  472. User: state.NewIdentScreenName("recipient-screen-name"),
  473. BlocksYou: false,
  474. YouBlock: false,
  475. IsOnTheirList: false,
  476. IsOnYourList: false,
  477. },
  478. },
  479. },
  480. },
  481. sessionRetrieverParams: sessionRetrieverParams{
  482. retrieveSessionParams{
  483. {
  484. screenName: state.NewIdentScreenName("recipient-screen-name"),
  485. result: nil,
  486. },
  487. },
  488. },
  489. messageRelayerParams: messageRelayerParams{
  490. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{},
  491. },
  492. },
  493. inputSNAC: wire.SNACMessage{
  494. Frame: wire.SNACFrame{
  495. RequestID: 1234,
  496. },
  497. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  498. ChannelID: wire.ICBMChannelIM,
  499. ScreenName: "recipient-screen-name",
  500. TLVRestBlock: wire.TLVRestBlock{
  501. TLVList: wire.TLVList{
  502. {
  503. Tag: wire.ICBMTLVRequestHostAck,
  504. Value: []byte{},
  505. },
  506. },
  507. },
  508. },
  509. },
  510. expectOutput: &wire.SNACMessage{
  511. Frame: wire.SNACFrame{
  512. FoodGroup: wire.ICBM,
  513. SubGroup: wire.ICBMErr,
  514. RequestID: 1234,
  515. },
  516. Body: wire.SNACError{
  517. Code: wire.ErrorCodeNotLoggedOn,
  518. },
  519. },
  520. },
  521. {
  522. name: "send offline message to ICQ recipient",
  523. instance: newTestInstance("11111111", sessOptUIN(11111111)),
  524. inputSNAC: wire.SNACMessage{
  525. Frame: wire.SNACFrame{
  526. RequestID: 1234,
  527. },
  528. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  529. ChannelID: wire.ICBMChannelIM,
  530. ScreenName: "22222222",
  531. TLVRestBlock: wire.TLVRestBlock{
  532. TLVList: wire.TLVList{
  533. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  534. },
  535. },
  536. },
  537. },
  538. expectOutput: nil,
  539. timeNow: func() time.Time {
  540. return time.Date(2020, time.August, 1, 0, 0, 0, 0, time.UTC)
  541. },
  542. mockParams: mockParams{
  543. relationshipFetcherParams: relationshipFetcherParams{
  544. relationshipParams: relationshipParams{
  545. {
  546. me: state.NewIdentScreenName("11111111"),
  547. them: state.NewIdentScreenName("22222222"),
  548. result: state.Relationship{
  549. User: state.NewIdentScreenName("22222222"),
  550. BlocksYou: false,
  551. YouBlock: false,
  552. IsOnTheirList: false,
  553. IsOnYourList: false,
  554. },
  555. },
  556. },
  557. },
  558. sessionRetrieverParams: sessionRetrieverParams{
  559. retrieveSessionParams{
  560. {
  561. screenName: state.NewIdentScreenName("22222222"),
  562. result: nil,
  563. },
  564. },
  565. },
  566. messageRelayerParams: messageRelayerParams{
  567. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{},
  568. },
  569. offlineMessageManagerParams: offlineMessageManagerParams{
  570. saveMessageParams: saveMessageParams{
  571. {
  572. offlineMessageIn: state.OfflineMessage{
  573. Message: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  574. ChannelID: wire.ICBMChannelIM,
  575. ScreenName: "22222222",
  576. TLVRestBlock: wire.TLVRestBlock{
  577. TLVList: wire.TLVList{
  578. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  579. },
  580. },
  581. },
  582. Recipient: state.NewIdentScreenName("22222222"),
  583. Sender: state.NewIdentScreenName("11111111"),
  584. Sent: time.Date(2020, time.August, 1, 0, 0, 0, 0, time.UTC),
  585. },
  586. },
  587. },
  588. },
  589. feedbagManagerParams: feedbagManagerParams{
  590. feedbagParams: feedbagParams{
  591. {
  592. screenName: state.NewIdentScreenName("22222222"),
  593. results: []wire.FeedbagItem{},
  594. },
  595. },
  596. },
  597. },
  598. },
  599. {
  600. name: "send offline message to recipient with accept offline IM flag set",
  601. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  602. inputSNAC: wire.SNACMessage{
  603. Frame: wire.SNACFrame{
  604. RequestID: 1234,
  605. },
  606. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  607. ChannelID: wire.ICBMChannelIM,
  608. ScreenName: "recipient-screen-name",
  609. TLVRestBlock: wire.TLVRestBlock{
  610. TLVList: wire.TLVList{
  611. wire.NewTLVBE(wire.ICBMTLVRequestHostAck, []byte{}),
  612. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  613. wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3, 4}),
  614. },
  615. },
  616. },
  617. },
  618. expectOutput: &wire.SNACMessage{
  619. Frame: wire.SNACFrame{
  620. FoodGroup: wire.ICBM,
  621. SubGroup: wire.ICBMHostAck,
  622. RequestID: 1234,
  623. },
  624. Body: wire.SNAC_0x04_0x0C_ICBMHostAck{
  625. ChannelID: wire.ICBMChannelIM,
  626. ScreenName: "recipient-screen-name",
  627. },
  628. },
  629. timeNow: func() time.Time {
  630. return time.Date(2020, time.August, 1, 0, 0, 0, 0, time.UTC)
  631. },
  632. mockParams: mockParams{
  633. relationshipFetcherParams: relationshipFetcherParams{
  634. relationshipParams: relationshipParams{
  635. {
  636. me: state.NewIdentScreenName("sender-screen-name"),
  637. them: state.NewIdentScreenName("recipient-screen-name"),
  638. result: state.Relationship{
  639. User: state.NewIdentScreenName("recipient-screen-name"),
  640. BlocksYou: false,
  641. YouBlock: false,
  642. IsOnTheirList: false,
  643. IsOnYourList: false,
  644. },
  645. },
  646. },
  647. },
  648. sessionRetrieverParams: sessionRetrieverParams{
  649. retrieveSessionParams{
  650. {
  651. screenName: state.NewIdentScreenName("recipient-screen-name"),
  652. result: nil,
  653. },
  654. },
  655. },
  656. messageRelayerParams: messageRelayerParams{
  657. relayToScreenNameParams: relayToScreenNameParams{},
  658. },
  659. offlineMessageManagerParams: offlineMessageManagerParams{
  660. saveMessageParams: saveMessageParams{
  661. {
  662. offlineMessageIn: state.OfflineMessage{
  663. Message: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  664. ChannelID: wire.ICBMChannelIM,
  665. ScreenName: "recipient-screen-name",
  666. TLVRestBlock: wire.TLVRestBlock{
  667. TLVList: wire.TLVList{
  668. wire.NewTLVBE(wire.ICBMTLVRequestHostAck, []byte{}),
  669. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  670. wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3, 4}),
  671. },
  672. },
  673. },
  674. Recipient: state.NewIdentScreenName("recipient-screen-name"),
  675. Sender: state.NewIdentScreenName("sender-screen-name"),
  676. Sent: time.Date(2020, time.August, 1, 0, 0, 0, 0, time.UTC),
  677. },
  678. countOut: 1,
  679. },
  680. },
  681. },
  682. feedbagManagerParams: feedbagManagerParams{
  683. feedbagParams: feedbagParams{
  684. {
  685. screenName: state.NewIdentScreenName("recipient-screen-name"),
  686. results: []wire.FeedbagItem{
  687. {
  688. ClassID: wire.FeedbagClassIdBuddyPrefs,
  689. TLVLBlock: wire.TLVLBlock{
  690. TLVList: wire.TLVList{
  691. {Tag: wire.FeedbagAttributesBuddyPrefsValid, Value: []byte{0, 0, 24, 64}},
  692. {Tag: wire.FeedbagAttributesBuddyPrefs, Value: []byte{0, 0, 24, 64}},
  693. {Tag: wire.FeedbagAttributesBuddyPrefs2Valid, Value: []byte{0, 0, 17}},
  694. {Tag: wire.FeedbagAttributesBuddyPrefs2, Value: []byte{0, 0, 17}},
  695. },
  696. },
  697. },
  698. },
  699. },
  700. },
  701. },
  702. },
  703. },
  704. {
  705. name: "reject offline message when recipient has opted out of offline messages",
  706. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  707. inputSNAC: wire.SNACMessage{
  708. Frame: wire.SNACFrame{
  709. RequestID: 1234,
  710. },
  711. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  712. ChannelID: wire.ICBMChannelIM,
  713. ScreenName: "recipient-screen-name",
  714. TLVRestBlock: wire.TLVRestBlock{
  715. TLVList: wire.TLVList{
  716. wire.NewTLVBE(wire.ICBMTLVRequestHostAck, []byte{}),
  717. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  718. wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3, 4}),
  719. },
  720. },
  721. },
  722. },
  723. expectOutput: &wire.SNACMessage{
  724. Frame: wire.SNACFrame{
  725. FoodGroup: wire.ICBM,
  726. SubGroup: wire.ICBMErr,
  727. RequestID: 1234,
  728. },
  729. Body: wire.SNACError{
  730. Code: wire.ErrorCodeNotLoggedOn,
  731. },
  732. },
  733. mockParams: mockParams{
  734. relationshipFetcherParams: relationshipFetcherParams{
  735. relationshipParams: relationshipParams{
  736. {
  737. me: state.NewIdentScreenName("sender-screen-name"),
  738. them: state.NewIdentScreenName("recipient-screen-name"),
  739. result: state.Relationship{
  740. User: state.NewIdentScreenName("recipient-screen-name"),
  741. BlocksYou: false,
  742. YouBlock: false,
  743. IsOnTheirList: false,
  744. IsOnYourList: false,
  745. },
  746. },
  747. },
  748. },
  749. sessionRetrieverParams: sessionRetrieverParams{
  750. retrieveSessionParams{
  751. {
  752. screenName: state.NewIdentScreenName("recipient-screen-name"),
  753. result: nil,
  754. },
  755. },
  756. },
  757. messageRelayerParams: messageRelayerParams{
  758. relayToScreenNameParams: relayToScreenNameParams{},
  759. },
  760. offlineMessageManagerParams: offlineMessageManagerParams{
  761. saveMessageParams: saveMessageParams{},
  762. },
  763. feedbagManagerParams: feedbagManagerParams{
  764. feedbagParams: feedbagParams{
  765. {
  766. screenName: state.NewIdentScreenName("recipient-screen-name"),
  767. results: []wire.FeedbagItem{
  768. {
  769. ClassID: wire.FeedbagClassIdBuddyPrefs,
  770. TLVLBlock: wire.TLVLBlock{
  771. TLVList: wire.TLVList{
  772. {Tag: wire.FeedbagAttributesBuddyPrefsValid, Value: []byte{0, 0, 24, 64}},
  773. {Tag: wire.FeedbagAttributesBuddyPrefs, Value: []byte{0, 0, 24, 64}},
  774. {Tag: wire.FeedbagAttributesBuddyPrefs2Valid, Value: []byte{0, 0, 17}},
  775. {Tag: wire.FeedbagAttributesBuddyPrefs2, Value: []byte{0, 0, 1}},
  776. },
  777. },
  778. },
  779. },
  780. },
  781. },
  782. },
  783. },
  784. },
  785. {
  786. name: "send offline message when offline IM preference is not valid",
  787. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  788. inputSNAC: wire.SNACMessage{
  789. Frame: wire.SNACFrame{
  790. RequestID: 1234,
  791. },
  792. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  793. ChannelID: wire.ICBMChannelIM,
  794. ScreenName: "recipient-screen-name",
  795. TLVRestBlock: wire.TLVRestBlock{
  796. TLVList: wire.TLVList{
  797. wire.NewTLVBE(wire.ICBMTLVRequestHostAck, []byte{}),
  798. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  799. wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3, 4}),
  800. },
  801. },
  802. },
  803. },
  804. expectOutput: &wire.SNACMessage{
  805. Frame: wire.SNACFrame{
  806. FoodGroup: wire.ICBM,
  807. SubGroup: wire.ICBMHostAck,
  808. RequestID: 1234,
  809. },
  810. Body: wire.SNAC_0x04_0x0C_ICBMHostAck{
  811. ChannelID: wire.ICBMChannelIM,
  812. ScreenName: "recipient-screen-name",
  813. },
  814. },
  815. timeNow: func() time.Time {
  816. return time.Date(2020, time.August, 1, 0, 0, 0, 0, time.UTC)
  817. },
  818. mockParams: mockParams{
  819. relationshipFetcherParams: relationshipFetcherParams{
  820. relationshipParams: relationshipParams{
  821. {
  822. me: state.NewIdentScreenName("sender-screen-name"),
  823. them: state.NewIdentScreenName("recipient-screen-name"),
  824. result: state.Relationship{
  825. User: state.NewIdentScreenName("recipient-screen-name"),
  826. BlocksYou: false,
  827. YouBlock: false,
  828. IsOnTheirList: false,
  829. IsOnYourList: false,
  830. },
  831. },
  832. },
  833. },
  834. sessionRetrieverParams: sessionRetrieverParams{
  835. retrieveSessionParams{
  836. {
  837. screenName: state.NewIdentScreenName("recipient-screen-name"),
  838. result: nil,
  839. },
  840. },
  841. },
  842. messageRelayerParams: messageRelayerParams{
  843. relayToScreenNameParams: relayToScreenNameParams{},
  844. },
  845. offlineMessageManagerParams: offlineMessageManagerParams{
  846. saveMessageParams: saveMessageParams{
  847. {
  848. offlineMessageIn: state.OfflineMessage{
  849. Message: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  850. ChannelID: wire.ICBMChannelIM,
  851. ScreenName: "recipient-screen-name",
  852. TLVRestBlock: wire.TLVRestBlock{
  853. TLVList: wire.TLVList{
  854. wire.NewTLVBE(wire.ICBMTLVRequestHostAck, []byte{}),
  855. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  856. wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3, 4}),
  857. },
  858. },
  859. },
  860. Recipient: state.NewIdentScreenName("recipient-screen-name"),
  861. Sender: state.NewIdentScreenName("sender-screen-name"),
  862. Sent: time.Date(2020, time.August, 1, 0, 0, 0, 0, time.UTC),
  863. },
  864. countOut: 1,
  865. },
  866. },
  867. },
  868. feedbagManagerParams: feedbagManagerParams{
  869. feedbagParams: feedbagParams{
  870. {
  871. screenName: state.NewIdentScreenName("recipient-screen-name"),
  872. results: []wire.FeedbagItem{
  873. {
  874. ClassID: wire.FeedbagClassIdBuddyPrefs,
  875. TLVLBlock: wire.TLVLBlock{
  876. TLVList: wire.TLVList{
  877. {Tag: wire.FeedbagAttributesBuddyPrefsValid, Value: []byte{0, 0, 24, 64}},
  878. {Tag: wire.FeedbagAttributesBuddyPrefs, Value: []byte{0, 0, 24, 64}},
  879. {Tag: wire.FeedbagAttributesBuddyPrefs2Valid, Value: []byte{0, 0, 7}},
  880. {Tag: wire.FeedbagAttributesBuddyPrefs2, Value: []byte{0, 0, 17}},
  881. },
  882. },
  883. },
  884. },
  885. },
  886. },
  887. },
  888. },
  889. },
  890. {
  891. name: "send rendezvous request for file transfer, expect IP TLV override",
  892. instance: newTestInstance("sender-screen-name", sessOptWarning(10),
  893. sessRemoteAddr(netip.AddrPortFrom(netip.MustParseAddr("129.168.0.1"), 0))),
  894. mockParams: mockParams{
  895. relationshipFetcherParams: relationshipFetcherParams{
  896. relationshipParams: relationshipParams{
  897. {
  898. me: state.NewIdentScreenName("sender-screen-name"),
  899. them: state.NewIdentScreenName("recipient-screen-name"),
  900. result: state.Relationship{
  901. User: state.NewIdentScreenName("recipient-screen-name"),
  902. BlocksYou: false,
  903. YouBlock: false,
  904. IsOnTheirList: false,
  905. IsOnYourList: false,
  906. },
  907. },
  908. },
  909. },
  910. sessionRetrieverParams: sessionRetrieverParams{
  911. retrieveSessionParams{
  912. {
  913. screenName: state.NewIdentScreenName("recipient-screen-name"),
  914. result: newTestInstance("recipient-screen-name", sessOptWarning(20), sessOptSignonComplete).Session(),
  915. },
  916. },
  917. },
  918. messageRelayerParams: messageRelayerParams{
  919. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  920. {
  921. screenName: state.NewIdentScreenName("recipient-screen-name"),
  922. message: wire.SNACMessage{
  923. Frame: wire.SNACFrame{
  924. FoodGroup: wire.ICBM,
  925. SubGroup: wire.ICBMChannelMsgToClient,
  926. RequestID: wire.ReqIDFromServer,
  927. },
  928. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  929. ChannelID: wire.ICBMChannelRendezvous,
  930. TLVUserInfo: newTestInstance("sender-screen-name", sessOptWarning(10)).Session().TLVUserInfo(),
  931. TLVRestBlock: wire.TLVRestBlock{
  932. TLVList: wire.TLVList{
  933. wire.NewTLVBE(wire.ICBMTLVData, wire.ICBMCh2Fragment{
  934. Type: wire.ICBMRdvMessagePropose,
  935. Capability: wire.CapFileTransfer,
  936. TLVRestBlock: wire.TLVRestBlock{
  937. TLVList: wire.TLVList{
  938. wire.NewTLVBE(wire.ICBMRdvTLVTagsPort, uint16(4000)),
  939. wire.NewTLVBE(wire.ICBMRdvTLVTagsRequesterIP, net.ParseIP("129.168.0.1").To4()),
  940. wire.NewTLVBE(wire.ICBMRdvTLVTagsVerifiedIP, net.ParseIP("129.168.0.1").To4()),
  941. },
  942. },
  943. }),
  944. },
  945. },
  946. },
  947. },
  948. },
  949. },
  950. },
  951. },
  952. inputSNAC: wire.SNACMessage{
  953. Frame: wire.SNACFrame{
  954. RequestID: 1234,
  955. },
  956. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  957. ChannelID: wire.ICBMChannelRendezvous,
  958. ScreenName: "recipient-screen-name",
  959. TLVRestBlock: wire.TLVRestBlock{
  960. TLVList: wire.TLVList{
  961. wire.NewTLVBE(wire.ICBMTLVData, wire.ICBMCh2Fragment{
  962. Type: wire.ICBMRdvMessagePropose,
  963. Capability: wire.CapFileTransfer,
  964. TLVRestBlock: wire.TLVRestBlock{
  965. TLVList: wire.TLVList{
  966. wire.NewTLVBE(wire.ICBMRdvTLVTagsPort, uint16(4000)),
  967. wire.NewTLVBE(wire.ICBMRdvTLVTagsRequesterIP, net.ParseIP("127.0.0.1").To4()),
  968. },
  969. },
  970. }),
  971. },
  972. },
  973. },
  974. },
  975. expectOutput: nil,
  976. },
  977. {
  978. name: "send rendezvous rejection for file transfer, expect no IP TLV override",
  979. instance: newTestInstance("sender-screen-name", sessOptWarning(10),
  980. sessRemoteAddr(netip.AddrPortFrom(netip.MustParseAddr("129.168.0.1"), 0))),
  981. mockParams: mockParams{
  982. relationshipFetcherParams: relationshipFetcherParams{
  983. relationshipParams: relationshipParams{
  984. {
  985. me: state.NewIdentScreenName("sender-screen-name"),
  986. them: state.NewIdentScreenName("recipient-screen-name"),
  987. result: state.Relationship{
  988. User: state.NewIdentScreenName("recipient-screen-name"),
  989. BlocksYou: false,
  990. YouBlock: false,
  991. IsOnTheirList: false,
  992. IsOnYourList: false,
  993. },
  994. },
  995. },
  996. },
  997. sessionRetrieverParams: sessionRetrieverParams{
  998. retrieveSessionParams{
  999. {
  1000. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1001. result: newTestInstance("recipient-screen-name", sessOptWarning(20), sessOptSignonComplete).Session(),
  1002. },
  1003. },
  1004. },
  1005. messageRelayerParams: messageRelayerParams{
  1006. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  1007. {
  1008. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1009. message: wire.SNACMessage{
  1010. Frame: wire.SNACFrame{
  1011. FoodGroup: wire.ICBM,
  1012. SubGroup: wire.ICBMChannelMsgToClient,
  1013. RequestID: wire.ReqIDFromServer,
  1014. },
  1015. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  1016. ChannelID: wire.ICBMChannelRendezvous,
  1017. TLVUserInfo: newTestInstance("sender-screen-name", sessOptWarning(10)).Session().TLVUserInfo(),
  1018. TLVRestBlock: wire.TLVRestBlock{
  1019. TLVList: wire.TLVList{
  1020. wire.NewTLVBE(wire.ICBMTLVData, wire.ICBMCh2Fragment{
  1021. Type: wire.ICBMRdvMessageCancel,
  1022. Capability: wire.CapFileTransfer,
  1023. }),
  1024. },
  1025. },
  1026. },
  1027. },
  1028. },
  1029. },
  1030. },
  1031. },
  1032. inputSNAC: wire.SNACMessage{
  1033. Frame: wire.SNACFrame{
  1034. RequestID: 1234,
  1035. },
  1036. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1037. ChannelID: wire.ICBMChannelRendezvous,
  1038. ScreenName: "recipient-screen-name",
  1039. TLVRestBlock: wire.TLVRestBlock{
  1040. TLVList: wire.TLVList{
  1041. wire.NewTLVBE(wire.ICBMTLVData, wire.ICBMCh2Fragment{
  1042. Type: wire.ICBMRdvMessageCancel,
  1043. Capability: wire.CapFileTransfer,
  1044. }),
  1045. },
  1046. },
  1047. },
  1048. },
  1049. expectOutput: nil,
  1050. },
  1051. {
  1052. name: "send rendezvous request for file transfer without IP in session, expect no IP TLV override",
  1053. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  1054. mockParams: mockParams{
  1055. relationshipFetcherParams: relationshipFetcherParams{
  1056. relationshipParams: relationshipParams{
  1057. {
  1058. me: state.NewIdentScreenName("sender-screen-name"),
  1059. them: state.NewIdentScreenName("recipient-screen-name"),
  1060. result: state.Relationship{
  1061. User: state.NewIdentScreenName("recipient-screen-name"),
  1062. BlocksYou: false,
  1063. YouBlock: false,
  1064. IsOnTheirList: false,
  1065. IsOnYourList: false,
  1066. },
  1067. },
  1068. },
  1069. },
  1070. sessionRetrieverParams: sessionRetrieverParams{
  1071. retrieveSessionParams{
  1072. {
  1073. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1074. result: newTestInstance("recipient-screen-name", sessOptWarning(20), sessOptSignonComplete).Session(),
  1075. },
  1076. },
  1077. },
  1078. messageRelayerParams: messageRelayerParams{
  1079. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  1080. {
  1081. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1082. message: wire.SNACMessage{
  1083. Frame: wire.SNACFrame{
  1084. FoodGroup: wire.ICBM,
  1085. SubGroup: wire.ICBMChannelMsgToClient,
  1086. RequestID: wire.ReqIDFromServer,
  1087. },
  1088. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  1089. ChannelID: wire.ICBMChannelRendezvous,
  1090. TLVUserInfo: newTestInstance("sender-screen-name", sessOptWarning(10)).Session().TLVUserInfo(),
  1091. TLVRestBlock: wire.TLVRestBlock{
  1092. TLVList: wire.TLVList{
  1093. wire.NewTLVBE(wire.ICBMTLVData, wire.ICBMCh2Fragment{
  1094. Type: wire.ICBMRdvMessagePropose,
  1095. Capability: wire.CapFileTransfer,
  1096. TLVRestBlock: wire.TLVRestBlock{
  1097. TLVList: wire.TLVList{
  1098. wire.NewTLVBE(wire.ICBMRdvTLVTagsPort, uint16(4000)),
  1099. wire.NewTLVBE(wire.ICBMRdvTLVTagsRequesterIP, net.ParseIP("127.0.0.1").To4()),
  1100. },
  1101. },
  1102. }),
  1103. },
  1104. },
  1105. },
  1106. },
  1107. },
  1108. },
  1109. },
  1110. },
  1111. inputSNAC: wire.SNACMessage{
  1112. Frame: wire.SNACFrame{
  1113. RequestID: 1234,
  1114. },
  1115. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1116. ChannelID: wire.ICBMChannelRendezvous,
  1117. ScreenName: "recipient-screen-name",
  1118. TLVRestBlock: wire.TLVRestBlock{
  1119. TLVList: wire.TLVList{
  1120. wire.NewTLVBE(wire.ICBMTLVData, wire.ICBMCh2Fragment{
  1121. Type: wire.ICBMRdvMessagePropose,
  1122. Capability: wire.CapFileTransfer,
  1123. TLVRestBlock: wire.TLVRestBlock{
  1124. TLVList: wire.TLVList{
  1125. wire.NewTLVBE(wire.ICBMRdvTLVTagsPort, uint16(4000)),
  1126. wire.NewTLVBE(wire.ICBMRdvTLVTagsRequesterIP, net.ParseIP("127.0.0.1").To4()),
  1127. },
  1128. },
  1129. }),
  1130. },
  1131. },
  1132. },
  1133. },
  1134. expectOutput: nil,
  1135. },
  1136. {
  1137. name: "relay Xtraz XStatus message",
  1138. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  1139. mockParams: mockParams{
  1140. relationshipFetcherParams: relationshipFetcherParams{
  1141. relationshipParams: relationshipParams{
  1142. {
  1143. me: state.NewIdentScreenName("sender-screen-name"),
  1144. them: state.NewIdentScreenName("recipient-screen-name"),
  1145. result: state.Relationship{
  1146. User: state.NewIdentScreenName("recipient-screen-name"),
  1147. BlocksYou: false,
  1148. YouBlock: false,
  1149. IsOnTheirList: false,
  1150. IsOnYourList: false,
  1151. },
  1152. },
  1153. },
  1154. },
  1155. sessionRetrieverParams: sessionRetrieverParams{
  1156. retrieveSessionParams{
  1157. {
  1158. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1159. result: newTestInstance("recipient-screen-name", sessOptWarning(20), sessOptSignonComplete).Session(),
  1160. },
  1161. },
  1162. },
  1163. messageRelayerParams: messageRelayerParams{
  1164. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  1165. {
  1166. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1167. message: wire.SNACMessage{
  1168. Frame: wire.SNACFrame{
  1169. FoodGroup: wire.ICBM,
  1170. SubGroup: wire.ICBMChannelMsgToClient,
  1171. RequestID: wire.ReqIDFromServer,
  1172. },
  1173. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  1174. ChannelID: wire.ICBMChannelRendezvous,
  1175. TLVUserInfo: newTestInstance("sender-screen-name", sessOptWarning(10)).Session().TLVUserInfo(),
  1176. TLVRestBlock: wire.TLVRestBlock{
  1177. TLVList: wire.TLVList{
  1178. wire.NewTLVBE(wire.ICBMTLVData, wire.ICBMCh2Fragment{
  1179. Type: wire.ICBMRdvMessagePropose,
  1180. Capability: wire.CapXtrazScript,
  1181. TLVRestBlock: wire.TLVRestBlock{
  1182. TLVList: wire.TLVList{
  1183. wire.NewTLVBE(0x2711, []byte("xtraz-xml-payload")),
  1184. },
  1185. },
  1186. }),
  1187. },
  1188. },
  1189. },
  1190. },
  1191. },
  1192. },
  1193. },
  1194. },
  1195. inputSNAC: wire.SNACMessage{
  1196. Frame: wire.SNACFrame{
  1197. RequestID: 1234,
  1198. },
  1199. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1200. ChannelID: wire.ICBMChannelRendezvous,
  1201. ScreenName: "recipient-screen-name",
  1202. TLVRestBlock: wire.TLVRestBlock{
  1203. TLVList: wire.TLVList{
  1204. wire.NewTLVBE(wire.ICBMTLVData, wire.ICBMCh2Fragment{
  1205. Type: wire.ICBMRdvMessagePropose,
  1206. Capability: wire.CapXtrazScript,
  1207. TLVRestBlock: wire.TLVRestBlock{
  1208. TLVList: wire.TLVList{
  1209. wire.NewTLVBE(0x2711, []byte("xtraz-xml-payload")),
  1210. },
  1211. },
  1212. }),
  1213. },
  1214. },
  1215. },
  1216. },
  1217. expectOutput: nil,
  1218. },
  1219. {
  1220. name: "transmit message to recipient with all sessions inactive - use RelayToScreenName",
  1221. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  1222. mockParams: mockParams{
  1223. relationshipFetcherParams: relationshipFetcherParams{
  1224. relationshipParams: relationshipParams{
  1225. {
  1226. me: state.NewIdentScreenName("sender-screen-name"),
  1227. them: state.NewIdentScreenName("recipient-screen-name"),
  1228. result: state.Relationship{
  1229. User: state.NewIdentScreenName("recipient-screen-name"),
  1230. BlocksYou: false,
  1231. YouBlock: false,
  1232. IsOnTheirList: false,
  1233. IsOnYourList: false,
  1234. },
  1235. },
  1236. },
  1237. },
  1238. sessionRetrieverParams: sessionRetrieverParams{
  1239. retrieveSessionParams{
  1240. {
  1241. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1242. result: newTestInstance("recipient-screen-name", sessOptWarning(20), sessOptAllInactive).Session(),
  1243. },
  1244. },
  1245. },
  1246. messageRelayerParams: messageRelayerParams{
  1247. relayToScreenNameParams: relayToScreenNameParams{
  1248. {
  1249. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1250. message: wire.SNACMessage{
  1251. Frame: wire.SNACFrame{
  1252. FoodGroup: wire.ICBM,
  1253. SubGroup: wire.ICBMChannelMsgToClient,
  1254. RequestID: wire.ReqIDFromServer,
  1255. },
  1256. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  1257. ChannelID: wire.ICBMChannelIM,
  1258. TLVUserInfo: newTestInstance("sender-screen-name", sessOptWarning(10)).Session().TLVUserInfo(),
  1259. TLVRestBlock: wire.TLVRestBlock{
  1260. TLVList: wire.TLVList{
  1261. {
  1262. Tag: wire.ICBMTLVData,
  1263. Value: []byte{1, 2, 3, 4},
  1264. },
  1265. },
  1266. },
  1267. },
  1268. },
  1269. },
  1270. },
  1271. },
  1272. },
  1273. inputSNAC: wire.SNACMessage{
  1274. Frame: wire.SNACFrame{
  1275. RequestID: 1234,
  1276. },
  1277. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1278. ChannelID: wire.ICBMChannelIM,
  1279. ScreenName: "recipient-screen-name",
  1280. TLVRestBlock: wire.TLVRestBlock{
  1281. TLVList: wire.TLVList{
  1282. {
  1283. Tag: wire.ICBMTLVData,
  1284. Value: []byte{1, 2, 3, 4},
  1285. },
  1286. },
  1287. },
  1288. },
  1289. },
  1290. expectOutput: nil,
  1291. },
  1292. {
  1293. name: "transmit message to recipient with some active sessions - use RelayToScreenNameActiveOnly",
  1294. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  1295. mockParams: mockParams{
  1296. relationshipFetcherParams: relationshipFetcherParams{
  1297. relationshipParams: relationshipParams{
  1298. {
  1299. me: state.NewIdentScreenName("sender-screen-name"),
  1300. them: state.NewIdentScreenName("recipient-screen-name"),
  1301. result: state.Relationship{
  1302. User: state.NewIdentScreenName("recipient-screen-name"),
  1303. BlocksYou: false,
  1304. YouBlock: false,
  1305. IsOnTheirList: false,
  1306. IsOnYourList: false,
  1307. },
  1308. },
  1309. },
  1310. },
  1311. sessionRetrieverParams: sessionRetrieverParams{
  1312. retrieveSessionParams{
  1313. {
  1314. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1315. result: newTestInstance("recipient-screen-name", sessOptWarning(20), sessOptSomeActive, sessOptSignonComplete).Session(),
  1316. },
  1317. },
  1318. },
  1319. messageRelayerParams: messageRelayerParams{
  1320. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  1321. {
  1322. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1323. message: wire.SNACMessage{
  1324. Frame: wire.SNACFrame{
  1325. FoodGroup: wire.ICBM,
  1326. SubGroup: wire.ICBMChannelMsgToClient,
  1327. RequestID: wire.ReqIDFromServer,
  1328. },
  1329. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  1330. ChannelID: wire.ICBMChannelIM,
  1331. TLVUserInfo: newTestInstance("sender-screen-name", sessOptWarning(10)).Session().TLVUserInfo(),
  1332. TLVRestBlock: wire.TLVRestBlock{
  1333. TLVList: wire.TLVList{
  1334. {
  1335. Tag: wire.ICBMTLVData,
  1336. Value: []byte{1, 2, 3, 4},
  1337. },
  1338. },
  1339. },
  1340. },
  1341. },
  1342. },
  1343. },
  1344. },
  1345. },
  1346. inputSNAC: wire.SNACMessage{
  1347. Frame: wire.SNACFrame{
  1348. RequestID: 1234,
  1349. },
  1350. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1351. ChannelID: wire.ICBMChannelIM,
  1352. ScreenName: "recipient-screen-name",
  1353. TLVRestBlock: wire.TLVRestBlock{
  1354. TLVList: wire.TLVList{
  1355. {
  1356. Tag: wire.ICBMTLVData,
  1357. Value: []byte{1, 2, 3, 4},
  1358. },
  1359. },
  1360. },
  1361. },
  1362. },
  1363. expectOutput: nil,
  1364. },
  1365. {
  1366. name: "transmit message to recipient with closed session - use RelayToScreenName",
  1367. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  1368. mockParams: mockParams{
  1369. relationshipFetcherParams: relationshipFetcherParams{
  1370. relationshipParams: relationshipParams{
  1371. {
  1372. me: state.NewIdentScreenName("sender-screen-name"),
  1373. them: state.NewIdentScreenName("recipient-screen-name"),
  1374. result: state.Relationship{
  1375. User: state.NewIdentScreenName("recipient-screen-name"),
  1376. BlocksYou: false,
  1377. YouBlock: false,
  1378. IsOnTheirList: false,
  1379. IsOnYourList: false,
  1380. },
  1381. },
  1382. },
  1383. },
  1384. sessionRetrieverParams: sessionRetrieverParams{
  1385. retrieveSessionParams{
  1386. {
  1387. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1388. result: newTestInstance("recipient-screen-name", sessOptWarning(20), sessOptClosed).Session(),
  1389. },
  1390. },
  1391. },
  1392. messageRelayerParams: messageRelayerParams{
  1393. relayToScreenNameParams: relayToScreenNameParams{
  1394. {
  1395. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1396. message: wire.SNACMessage{
  1397. Frame: wire.SNACFrame{
  1398. FoodGroup: wire.ICBM,
  1399. SubGroup: wire.ICBMChannelMsgToClient,
  1400. RequestID: wire.ReqIDFromServer,
  1401. },
  1402. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  1403. ChannelID: wire.ICBMChannelIM,
  1404. TLVUserInfo: newTestInstance("sender-screen-name", sessOptWarning(10)).Session().TLVUserInfo(),
  1405. TLVRestBlock: wire.TLVRestBlock{
  1406. TLVList: wire.TLVList{
  1407. {
  1408. Tag: wire.ICBMTLVData,
  1409. Value: []byte{1, 2, 3, 4},
  1410. },
  1411. },
  1412. },
  1413. },
  1414. },
  1415. },
  1416. },
  1417. },
  1418. },
  1419. inputSNAC: wire.SNACMessage{
  1420. Frame: wire.SNACFrame{
  1421. RequestID: 1234,
  1422. },
  1423. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1424. ChannelID: wire.ICBMChannelIM,
  1425. ScreenName: "recipient-screen-name",
  1426. TLVRestBlock: wire.TLVRestBlock{
  1427. TLVList: wire.TLVList{
  1428. {
  1429. Tag: wire.ICBMTLVData,
  1430. Value: []byte{1, 2, 3, 4},
  1431. },
  1432. },
  1433. },
  1434. },
  1435. },
  1436. expectOutput: nil,
  1437. },
  1438. {
  1439. name: "transmit message to recipient with mixed session states - use RelayToScreenNameActiveOnly",
  1440. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  1441. mockParams: mockParams{
  1442. relationshipFetcherParams: relationshipFetcherParams{
  1443. relationshipParams: relationshipParams{
  1444. {
  1445. me: state.NewIdentScreenName("sender-screen-name"),
  1446. them: state.NewIdentScreenName("recipient-screen-name"),
  1447. result: state.Relationship{
  1448. User: state.NewIdentScreenName("recipient-screen-name"),
  1449. BlocksYou: false,
  1450. YouBlock: false,
  1451. IsOnTheirList: false,
  1452. IsOnYourList: false,
  1453. },
  1454. },
  1455. },
  1456. },
  1457. sessionRetrieverParams: sessionRetrieverParams{
  1458. retrieveSessionParams{
  1459. {
  1460. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1461. result: newTestInstance("recipient-screen-name", sessOptWarning(20), sessOptMixedStates, sessOptSignonComplete).Session(),
  1462. },
  1463. },
  1464. },
  1465. messageRelayerParams: messageRelayerParams{
  1466. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  1467. {
  1468. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1469. message: wire.SNACMessage{
  1470. Frame: wire.SNACFrame{
  1471. FoodGroup: wire.ICBM,
  1472. SubGroup: wire.ICBMChannelMsgToClient,
  1473. RequestID: wire.ReqIDFromServer,
  1474. },
  1475. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  1476. ChannelID: wire.ICBMChannelIM,
  1477. TLVUserInfo: newTestInstance("sender-screen-name", sessOptWarning(10)).Session().TLVUserInfo(),
  1478. TLVRestBlock: wire.TLVRestBlock{
  1479. TLVList: wire.TLVList{
  1480. {
  1481. Tag: wire.ICBMTLVData,
  1482. Value: []byte{1, 2, 3, 4},
  1483. },
  1484. },
  1485. },
  1486. },
  1487. },
  1488. },
  1489. },
  1490. },
  1491. },
  1492. inputSNAC: wire.SNACMessage{
  1493. Frame: wire.SNACFrame{
  1494. RequestID: 1234,
  1495. },
  1496. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1497. ChannelID: wire.ICBMChannelIM,
  1498. ScreenName: "recipient-screen-name",
  1499. TLVRestBlock: wire.TLVRestBlock{
  1500. TLVList: wire.TLVList{
  1501. {
  1502. Tag: wire.ICBMTLVData,
  1503. Value: []byte{1, 2, 3, 4},
  1504. },
  1505. },
  1506. },
  1507. },
  1508. },
  1509. expectOutput: nil,
  1510. },
  1511. {
  1512. name: "send offline message when inbox is full, return ICBM error with subcode",
  1513. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  1514. inputSNAC: wire.SNACMessage{
  1515. Frame: wire.SNACFrame{
  1516. RequestID: 1234,
  1517. },
  1518. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1519. ChannelID: wire.ICBMChannelIM,
  1520. ScreenName: "recipient-screen-name",
  1521. TLVRestBlock: wire.TLVRestBlock{
  1522. TLVList: wire.TLVList{
  1523. wire.NewTLVBE(wire.ICBMTLVRequestHostAck, []byte{}),
  1524. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  1525. wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3, 4}),
  1526. },
  1527. },
  1528. },
  1529. },
  1530. expectOutput: &wire.SNACMessage{
  1531. Frame: wire.SNACFrame{
  1532. FoodGroup: wire.ICBM,
  1533. SubGroup: wire.ICBMErr,
  1534. RequestID: 1234,
  1535. },
  1536. Body: wire.SNACError{
  1537. Code: wire.ErrorCodeNotLoggedOn,
  1538. TLVRestBlock: wire.TLVRestBlock{
  1539. TLVList: wire.TLVList{
  1540. wire.NewTLVBE(wire.ErrorTLVErrorSubcode, wire.ICBMSubErrOfflineIMExceedMax),
  1541. },
  1542. },
  1543. },
  1544. },
  1545. wantErr: nil,
  1546. timeNow: func() time.Time {
  1547. return time.Date(2020, time.August, 1, 0, 0, 0, 0, time.UTC)
  1548. },
  1549. mockParams: mockParams{
  1550. relationshipFetcherParams: relationshipFetcherParams{
  1551. relationshipParams: relationshipParams{
  1552. {
  1553. me: state.NewIdentScreenName("sender-screen-name"),
  1554. them: state.NewIdentScreenName("recipient-screen-name"),
  1555. result: state.Relationship{
  1556. User: state.NewIdentScreenName("recipient-screen-name"),
  1557. BlocksYou: false,
  1558. YouBlock: false,
  1559. IsOnTheirList: false,
  1560. IsOnYourList: false,
  1561. },
  1562. },
  1563. },
  1564. },
  1565. sessionRetrieverParams: sessionRetrieverParams{
  1566. retrieveSessionParams{
  1567. {
  1568. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1569. result: nil,
  1570. },
  1571. },
  1572. },
  1573. messageRelayerParams: messageRelayerParams{
  1574. relayToScreenNameParams: relayToScreenNameParams{},
  1575. },
  1576. offlineMessageManagerParams: offlineMessageManagerParams{
  1577. saveMessageParams: saveMessageParams{
  1578. {
  1579. offlineMessageIn: state.OfflineMessage{
  1580. Message: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1581. ChannelID: wire.ICBMChannelIM,
  1582. ScreenName: "recipient-screen-name",
  1583. TLVRestBlock: wire.TLVRestBlock{
  1584. TLVList: wire.TLVList{
  1585. wire.NewTLVBE(wire.ICBMTLVRequestHostAck, []byte{}),
  1586. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  1587. wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3, 4}),
  1588. },
  1589. },
  1590. },
  1591. Recipient: state.NewIdentScreenName("recipient-screen-name"),
  1592. Sender: state.NewIdentScreenName("sender-screen-name"),
  1593. Sent: time.Date(2020, time.August, 1, 0, 0, 0, 0, time.UTC),
  1594. },
  1595. err: state.ErrOfflineInboxFull,
  1596. },
  1597. },
  1598. },
  1599. feedbagManagerParams: feedbagManagerParams{
  1600. feedbagParams: feedbagParams{
  1601. {
  1602. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1603. results: []wire.FeedbagItem{
  1604. {
  1605. ClassID: wire.FeedbagClassIdBuddyPrefs,
  1606. TLVLBlock: wire.TLVLBlock{
  1607. TLVList: wire.TLVList{
  1608. {Tag: wire.FeedbagAttributesBuddyPrefsValid, Value: []byte{0, 0, 24, 64}},
  1609. {Tag: wire.FeedbagAttributesBuddyPrefs, Value: []byte{0, 0, 24, 64}},
  1610. {Tag: wire.FeedbagAttributesBuddyPrefs2Valid, Value: []byte{0, 0, 17}},
  1611. {Tag: wire.FeedbagAttributesBuddyPrefs2, Value: []byte{0, 0, 17}},
  1612. },
  1613. },
  1614. },
  1615. },
  1616. },
  1617. },
  1618. },
  1619. },
  1620. },
  1621. {
  1622. name: "send offline message when SaveMessage returns generic error",
  1623. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  1624. inputSNAC: wire.SNACMessage{
  1625. Frame: wire.SNACFrame{
  1626. RequestID: 1234,
  1627. },
  1628. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1629. ChannelID: wire.ICBMChannelIM,
  1630. ScreenName: "recipient-screen-name",
  1631. TLVRestBlock: wire.TLVRestBlock{
  1632. TLVList: wire.TLVList{
  1633. wire.NewTLVBE(wire.ICBMTLVRequestHostAck, []byte{}),
  1634. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  1635. wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3, 4}),
  1636. },
  1637. },
  1638. },
  1639. },
  1640. expectOutput: nil,
  1641. wantErr: assert.AnError,
  1642. timeNow: func() time.Time {
  1643. return time.Date(2020, time.August, 1, 0, 0, 0, 0, time.UTC)
  1644. },
  1645. mockParams: mockParams{
  1646. relationshipFetcherParams: relationshipFetcherParams{
  1647. relationshipParams: relationshipParams{
  1648. {
  1649. me: state.NewIdentScreenName("sender-screen-name"),
  1650. them: state.NewIdentScreenName("recipient-screen-name"),
  1651. result: state.Relationship{
  1652. User: state.NewIdentScreenName("recipient-screen-name"),
  1653. BlocksYou: false,
  1654. YouBlock: false,
  1655. IsOnTheirList: false,
  1656. IsOnYourList: false,
  1657. },
  1658. },
  1659. },
  1660. },
  1661. sessionRetrieverParams: sessionRetrieverParams{
  1662. retrieveSessionParams{
  1663. {
  1664. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1665. result: nil,
  1666. },
  1667. },
  1668. },
  1669. messageRelayerParams: messageRelayerParams{
  1670. relayToScreenNameParams: relayToScreenNameParams{},
  1671. },
  1672. offlineMessageManagerParams: offlineMessageManagerParams{
  1673. saveMessageParams: saveMessageParams{
  1674. {
  1675. offlineMessageIn: state.OfflineMessage{
  1676. Message: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1677. ChannelID: wire.ICBMChannelIM,
  1678. ScreenName: "recipient-screen-name",
  1679. TLVRestBlock: wire.TLVRestBlock{
  1680. TLVList: wire.TLVList{
  1681. wire.NewTLVBE(wire.ICBMTLVRequestHostAck, []byte{}),
  1682. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  1683. wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3, 4}),
  1684. },
  1685. },
  1686. },
  1687. Recipient: state.NewIdentScreenName("recipient-screen-name"),
  1688. Sender: state.NewIdentScreenName("sender-screen-name"),
  1689. Sent: time.Date(2020, time.August, 1, 0, 0, 0, 0, time.UTC),
  1690. },
  1691. err: assert.AnError,
  1692. },
  1693. },
  1694. },
  1695. feedbagManagerParams: feedbagManagerParams{
  1696. feedbagParams: feedbagParams{
  1697. {
  1698. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1699. results: []wire.FeedbagItem{
  1700. {
  1701. ClassID: wire.FeedbagClassIdBuddyPrefs,
  1702. TLVLBlock: wire.TLVLBlock{
  1703. TLVList: wire.TLVList{
  1704. {Tag: wire.FeedbagAttributesBuddyPrefsValid, Value: []byte{0, 0, 24, 64}},
  1705. {Tag: wire.FeedbagAttributesBuddyPrefs, Value: []byte{0, 0, 24, 64}},
  1706. {Tag: wire.FeedbagAttributesBuddyPrefs2Valid, Value: []byte{0, 0, 17}},
  1707. {Tag: wire.FeedbagAttributesBuddyPrefs2, Value: []byte{0, 0, 17}},
  1708. },
  1709. },
  1710. },
  1711. },
  1712. },
  1713. },
  1714. },
  1715. },
  1716. },
  1717. {
  1718. name: "send offline message when SaveMessage returns ErrNoUser, return ICBM error",
  1719. instance: newTestInstance("sender-screen-name", sessOptWarning(10)),
  1720. inputSNAC: wire.SNACMessage{
  1721. Frame: wire.SNACFrame{
  1722. RequestID: 1234,
  1723. },
  1724. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1725. ChannelID: wire.ICBMChannelIM,
  1726. ScreenName: "recipient-screen-name",
  1727. TLVRestBlock: wire.TLVRestBlock{
  1728. TLVList: wire.TLVList{
  1729. wire.NewTLVBE(wire.ICBMTLVRequestHostAck, []byte{}),
  1730. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  1731. wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3, 4}),
  1732. },
  1733. },
  1734. },
  1735. },
  1736. expectOutput: &wire.SNACMessage{
  1737. Frame: wire.SNACFrame{
  1738. FoodGroup: wire.ICBM,
  1739. SubGroup: wire.ICBMErr,
  1740. RequestID: 1234,
  1741. },
  1742. Body: wire.SNACError{
  1743. Code: wire.ErrorCodeNotLoggedOn,
  1744. },
  1745. },
  1746. wantErr: nil,
  1747. timeNow: func() time.Time {
  1748. return time.Date(2020, time.August, 1, 0, 0, 0, 0, time.UTC)
  1749. },
  1750. mockParams: mockParams{
  1751. relationshipFetcherParams: relationshipFetcherParams{
  1752. relationshipParams: relationshipParams{
  1753. {
  1754. me: state.NewIdentScreenName("sender-screen-name"),
  1755. them: state.NewIdentScreenName("recipient-screen-name"),
  1756. result: state.Relationship{
  1757. User: state.NewIdentScreenName("recipient-screen-name"),
  1758. BlocksYou: false,
  1759. YouBlock: false,
  1760. IsOnTheirList: false,
  1761. IsOnYourList: false,
  1762. },
  1763. },
  1764. },
  1765. },
  1766. sessionRetrieverParams: sessionRetrieverParams{
  1767. retrieveSessionParams{
  1768. {
  1769. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1770. result: nil,
  1771. },
  1772. },
  1773. },
  1774. messageRelayerParams: messageRelayerParams{
  1775. relayToScreenNameParams: relayToScreenNameParams{},
  1776. },
  1777. offlineMessageManagerParams: offlineMessageManagerParams{
  1778. saveMessageParams: saveMessageParams{
  1779. {
  1780. offlineMessageIn: state.OfflineMessage{
  1781. Message: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1782. ChannelID: wire.ICBMChannelIM,
  1783. ScreenName: "recipient-screen-name",
  1784. TLVRestBlock: wire.TLVRestBlock{
  1785. TLVList: wire.TLVList{
  1786. wire.NewTLVBE(wire.ICBMTLVRequestHostAck, []byte{}),
  1787. wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
  1788. wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3, 4}),
  1789. },
  1790. },
  1791. },
  1792. Recipient: state.NewIdentScreenName("recipient-screen-name"),
  1793. Sender: state.NewIdentScreenName("sender-screen-name"),
  1794. Sent: time.Date(2020, time.August, 1, 0, 0, 0, 0, time.UTC),
  1795. },
  1796. err: state.ErrNoUser,
  1797. },
  1798. },
  1799. },
  1800. feedbagManagerParams: feedbagManagerParams{
  1801. feedbagParams: feedbagParams{
  1802. {
  1803. screenName: state.NewIdentScreenName("recipient-screen-name"),
  1804. results: []wire.FeedbagItem{
  1805. {
  1806. ClassID: wire.FeedbagClassIdBuddyPrefs,
  1807. TLVLBlock: wire.TLVLBlock{
  1808. TLVList: wire.TLVList{
  1809. {Tag: wire.FeedbagAttributesBuddyPrefsValid, Value: []byte{0, 0, 24, 64}},
  1810. {Tag: wire.FeedbagAttributesBuddyPrefs, Value: []byte{0, 0, 24, 64}},
  1811. {Tag: wire.FeedbagAttributesBuddyPrefs2Valid, Value: []byte{0, 0, 17}},
  1812. {Tag: wire.FeedbagAttributesBuddyPrefs2, Value: []byte{0, 0, 17}},
  1813. },
  1814. },
  1815. },
  1816. },
  1817. },
  1818. },
  1819. },
  1820. },
  1821. },
  1822. {
  1823. name: "ICQ channel feedbag ch4: auth_ok",
  1824. instance: newTestInstance("100001", sessOptUIN(100001)),
  1825. mockParams: mockParams{
  1826. relationshipFetcherParams: relationshipFetcherParams{
  1827. relationshipParams: relationshipParams{
  1828. {
  1829. me: state.NewIdentScreenName("100001"),
  1830. them: state.NewIdentScreenName("200002"),
  1831. result: state.Relationship{
  1832. User: state.NewIdentScreenName("200002"),
  1833. BlocksYou: false,
  1834. YouBlock: false,
  1835. IsOnTheirList: false,
  1836. IsOnYourList: false,
  1837. },
  1838. },
  1839. },
  1840. },
  1841. sessionRetrieverParams: sessionRetrieverParams{
  1842. retrieveSessionParams{
  1843. {
  1844. screenName: state.NewIdentScreenName("200002"),
  1845. result: newTestInstance("200002", sessOptFeedbagEnabled, sessOptSignonComplete).Session(),
  1846. },
  1847. },
  1848. },
  1849. },
  1850. expectForwardICQAuthEvents: true,
  1851. expectForwardRecipient: state.NewIdentScreenName("200002"),
  1852. expectForwardAuthMsg: wire.ICBMCh4Message{
  1853. UIN: 100001,
  1854. MessageType: wire.ICBMMsgTypeAuthOK,
  1855. },
  1856. inputSNAC: wire.SNACMessage{
  1857. Frame: wire.SNACFrame{},
  1858. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1859. ChannelID: wire.ICBMChannelICQ,
  1860. ScreenName: "200002",
  1861. TLVRestBlock: wire.TLVRestBlock{
  1862. TLVList: wire.TLVList{
  1863. wire.NewTLVLE(wire.ICBMTLVData, wire.ICBMCh4Message{
  1864. UIN: 100001,
  1865. MessageType: wire.ICBMMsgTypeAuthOK,
  1866. }),
  1867. },
  1868. },
  1869. },
  1870. },
  1871. expectOutput: nil,
  1872. wantErr: nil,
  1873. },
  1874. {
  1875. name: "ICQ channel non-feedbag ch4: auth_ok",
  1876. instance: newTestInstance("100001", sessOptUIN(100001)),
  1877. mockParams: mockParams{
  1878. relationshipFetcherParams: relationshipFetcherParams{
  1879. relationshipParams: relationshipParams{
  1880. {
  1881. me: state.NewIdentScreenName("100001"),
  1882. them: state.NewIdentScreenName("200002"),
  1883. result: state.Relationship{
  1884. User: state.NewIdentScreenName("200002"),
  1885. BlocksYou: false,
  1886. YouBlock: false,
  1887. IsOnTheirList: false,
  1888. IsOnYourList: false,
  1889. },
  1890. },
  1891. },
  1892. },
  1893. sessionRetrieverParams: sessionRetrieverParams{
  1894. retrieveSessionParams{
  1895. {
  1896. screenName: state.NewIdentScreenName("200002"),
  1897. result: newTestInstance("200002", sessOptSignonComplete).Session(),
  1898. },
  1899. },
  1900. },
  1901. contactPreAuthorizerParams: contactPreAuthorizerParams{
  1902. recordPreAuthParams: recordPreAuthParams{
  1903. {owner: state.NewIdentScreenName("100001"), buddy: state.NewIdentScreenName("200002")},
  1904. },
  1905. },
  1906. messageRelayerParams: messageRelayerParams{
  1907. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  1908. {
  1909. screenName: state.NewIdentScreenName("200002"),
  1910. message: wire.SNACMessage{
  1911. Frame: wire.SNACFrame{
  1912. FoodGroup: wire.ICBM,
  1913. SubGroup: wire.ICBMChannelMsgToClient,
  1914. RequestID: wire.ReqIDFromServer,
  1915. },
  1916. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  1917. ChannelID: wire.ICBMChannelICQ,
  1918. TLVUserInfo: newTestInstance("100001", sessOptUIN(100001)).Session().TLVUserInfo(),
  1919. TLVRestBlock: wire.TLVRestBlock{
  1920. TLVList: wire.TLVList{
  1921. wire.NewTLVLE(wire.ICBMTLVData, wire.ICBMCh4Message{
  1922. UIN: 100001,
  1923. MessageType: wire.ICBMMsgTypeAuthOK,
  1924. }),
  1925. },
  1926. },
  1927. },
  1928. },
  1929. },
  1930. },
  1931. },
  1932. },
  1933. inputSNAC: wire.SNACMessage{
  1934. Frame: wire.SNACFrame{},
  1935. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1936. ChannelID: wire.ICBMChannelICQ,
  1937. ScreenName: "200002",
  1938. TLVRestBlock: wire.TLVRestBlock{
  1939. TLVList: wire.TLVList{
  1940. wire.NewTLVLE(wire.ICBMTLVData, wire.ICBMCh4Message{
  1941. UIN: 100001,
  1942. MessageType: wire.ICBMMsgTypeAuthOK,
  1943. }),
  1944. },
  1945. },
  1946. },
  1947. },
  1948. expectOutput: nil,
  1949. wantErr: nil,
  1950. },
  1951. {
  1952. name: "ICQ channel feedbag ch4: auth_deny",
  1953. instance: newTestInstance("100001", sessOptUIN(100001)),
  1954. mockParams: mockParams{
  1955. relationshipFetcherParams: relationshipFetcherParams{
  1956. relationshipParams: relationshipParams{
  1957. {
  1958. me: state.NewIdentScreenName("100001"),
  1959. them: state.NewIdentScreenName("200002"),
  1960. result: state.Relationship{
  1961. User: state.NewIdentScreenName("200002"),
  1962. BlocksYou: false,
  1963. YouBlock: false,
  1964. IsOnTheirList: false,
  1965. IsOnYourList: false,
  1966. },
  1967. },
  1968. },
  1969. },
  1970. sessionRetrieverParams: sessionRetrieverParams{
  1971. retrieveSessionParams{
  1972. {
  1973. screenName: state.NewIdentScreenName("200002"),
  1974. result: newTestInstance("200002", sessOptFeedbagEnabled, sessOptSignonComplete).Session(),
  1975. },
  1976. },
  1977. },
  1978. },
  1979. expectForwardICQAuthEvents: true,
  1980. expectForwardRecipient: state.NewIdentScreenName("200002"),
  1981. expectForwardAuthMsg: wire.ICBMCh4Message{
  1982. UIN: 100001,
  1983. MessageType: wire.ICBMMsgTypeAuthDeny,
  1984. Message: "no thanks",
  1985. },
  1986. inputSNAC: wire.SNACMessage{
  1987. Frame: wire.SNACFrame{},
  1988. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1989. ChannelID: wire.ICBMChannelICQ,
  1990. ScreenName: "200002",
  1991. TLVRestBlock: wire.TLVRestBlock{
  1992. TLVList: wire.TLVList{
  1993. wire.NewTLVLE(wire.ICBMTLVData, wire.ICBMCh4Message{
  1994. UIN: 100001,
  1995. MessageType: wire.ICBMMsgTypeAuthDeny,
  1996. Message: "no thanks",
  1997. }),
  1998. },
  1999. },
  2000. },
  2001. },
  2002. expectOutput: nil,
  2003. wantErr: nil,
  2004. },
  2005. {
  2006. name: "ICQ channel feedbag ch4: added",
  2007. instance: newTestInstance("100001", sessOptUIN(100001)),
  2008. mockParams: mockParams{
  2009. relationshipFetcherParams: relationshipFetcherParams{
  2010. relationshipParams: relationshipParams{
  2011. {
  2012. me: state.NewIdentScreenName("100001"),
  2013. them: state.NewIdentScreenName("200002"),
  2014. result: state.Relationship{
  2015. User: state.NewIdentScreenName("200002"),
  2016. BlocksYou: false,
  2017. YouBlock: false,
  2018. IsOnTheirList: false,
  2019. IsOnYourList: false,
  2020. },
  2021. },
  2022. },
  2023. },
  2024. sessionRetrieverParams: sessionRetrieverParams{
  2025. retrieveSessionParams{
  2026. {
  2027. screenName: state.NewIdentScreenName("200002"),
  2028. result: newTestInstance("200002", sessOptFeedbagEnabled, sessOptSignonComplete).Session(),
  2029. },
  2030. },
  2031. },
  2032. },
  2033. expectForwardICQAuthEvents: true,
  2034. expectForwardRecipient: state.NewIdentScreenName("200002"),
  2035. expectForwardAuthMsg: wire.ICBMCh4Message{
  2036. UIN: 100001,
  2037. MessageType: wire.ICBMMsgTypeAdded,
  2038. },
  2039. inputSNAC: wire.SNACMessage{
  2040. Frame: wire.SNACFrame{},
  2041. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  2042. ChannelID: wire.ICBMChannelICQ,
  2043. ScreenName: "200002",
  2044. TLVRestBlock: wire.TLVRestBlock{
  2045. TLVList: wire.TLVList{
  2046. wire.NewTLVLE(wire.ICBMTLVData, wire.ICBMCh4Message{
  2047. UIN: 100001,
  2048. MessageType: wire.ICBMMsgTypeAdded,
  2049. }),
  2050. },
  2051. },
  2052. },
  2053. },
  2054. expectOutput: nil,
  2055. wantErr: nil,
  2056. },
  2057. {
  2058. name: "ICQ channel feedbag ch4: auth_req",
  2059. instance: newTestInstance("100001", sessOptUIN(100001)),
  2060. mockParams: mockParams{
  2061. relationshipFetcherParams: relationshipFetcherParams{
  2062. relationshipParams: relationshipParams{
  2063. {
  2064. me: state.NewIdentScreenName("100001"),
  2065. them: state.NewIdentScreenName("200002"),
  2066. result: state.Relationship{
  2067. User: state.NewIdentScreenName("200002"),
  2068. BlocksYou: false,
  2069. YouBlock: false,
  2070. IsOnTheirList: false,
  2071. IsOnYourList: false,
  2072. },
  2073. },
  2074. },
  2075. },
  2076. sessionRetrieverParams: sessionRetrieverParams{
  2077. retrieveSessionParams{
  2078. {
  2079. screenName: state.NewIdentScreenName("200002"),
  2080. result: newTestInstance("200002", sessOptFeedbagEnabled, sessOptSignonComplete).Session(),
  2081. },
  2082. },
  2083. },
  2084. },
  2085. expectForwardICQAuthEvents: true,
  2086. expectForwardRecipient: state.NewIdentScreenName("200002"),
  2087. expectForwardAuthMsg: wire.ICBMCh4Message{
  2088. UIN: 100001,
  2089. MessageType: wire.ICBMMsgTypeAuthReq,
  2090. Message: "hi",
  2091. },
  2092. inputSNAC: wire.SNACMessage{
  2093. Frame: wire.SNACFrame{},
  2094. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  2095. ChannelID: wire.ICBMChannelICQ,
  2096. ScreenName: "200002",
  2097. TLVRestBlock: wire.TLVRestBlock{
  2098. TLVList: wire.TLVList{
  2099. wire.NewTLVLE(wire.ICBMTLVData, wire.ICBMCh4Message{
  2100. UIN: 100001,
  2101. MessageType: wire.ICBMMsgTypeAuthReq,
  2102. Message: "hi",
  2103. }),
  2104. },
  2105. },
  2106. },
  2107. },
  2108. expectOutput: nil,
  2109. wantErr: nil,
  2110. },
  2111. }
  2112. for _, tc := range cases {
  2113. t.Run(tc.name, func(t *testing.T) {
  2114. discardLogger := slog.New(slog.NewTextHandler(io.Discard, nil))
  2115. relationshipFetcher := newMockRelationshipFetcher(t)
  2116. for _, item := range tc.mockParams.relationshipParams {
  2117. relationshipFetcher.EXPECT().
  2118. Relationship(matchContext(), item.me, item.them).
  2119. Return(item.result, item.err)
  2120. }
  2121. sessionRetriever := newMockSessionRetriever(t)
  2122. for _, item := range tc.mockParams.retrieveSessionParams {
  2123. sessionRetriever.EXPECT().
  2124. RetrieveSession(item.screenName).
  2125. Return(item.result)
  2126. }
  2127. messageRelayer := newMockMessageRelayer(t)
  2128. for _, item := range tc.mockParams.relayToScreenNameParams {
  2129. messageRelayer.EXPECT().
  2130. RelayToScreenName(mock.Anything, item.screenName, item.message)
  2131. }
  2132. for _, item := range tc.mockParams.relayToScreenNameActiveOnlyParams {
  2133. messageRelayer.EXPECT().
  2134. RelayToScreenNameActiveOnly(mock.Anything, item.screenName, item.message)
  2135. }
  2136. offlineMessageManager := newMockOfflineMessageManager(t)
  2137. for _, params := range tc.mockParams.saveMessageParams {
  2138. offlineMessageManager.EXPECT().
  2139. SaveMessage(matchContext(), params.offlineMessageIn).
  2140. Return(params.countOut, params.err)
  2141. }
  2142. feedbagManager := newMockFeedbagManager(t)
  2143. for _, params := range tc.mockParams.feedbagParams {
  2144. feedbagManager.EXPECT().
  2145. Feedbag(matchContext(), params.screenName).
  2146. Return(params.results, params.err)
  2147. }
  2148. for _, params := range tc.mockParams.feedbagUpsertParams {
  2149. feedbagManager.EXPECT().
  2150. FeedbagUpsert(matchContext(), params.screenName, params.items).
  2151. Return(nil)
  2152. }
  2153. buddyBroadcaster := newMockbuddyBroadcaster(t)
  2154. for _, params := range tc.mockParams.broadcastBuddyArrivedParams {
  2155. buddyBroadcaster.EXPECT().
  2156. BroadcastBuddyArrived(matchContext(), state.NewIdentScreenName(params.screenName.String()), mock.Anything).
  2157. Return(params.err)
  2158. }
  2159. for _, params := range tc.mockParams.broadcastVisibilityParams {
  2160. buddyBroadcaster.EXPECT().
  2161. BroadcastVisibility(mock.Anything, matchSession(params.from), params.filter, params.doSendDepartures).
  2162. Return(params.err)
  2163. }
  2164. contactPreAuth := newMockContactPreAuthorizer(t)
  2165. for _, params := range tc.mockParams.recordPreAuthParams {
  2166. contactPreAuth.EXPECT().
  2167. RecordPreAuth(matchContext(), params.owner, params.buddy).
  2168. Return(params.err)
  2169. }
  2170. svc := ICBMService{
  2171. relationshipFetcher: relationshipFetcher,
  2172. messageRelayer: messageRelayer,
  2173. offlineMessageSaver: offlineMessageManager,
  2174. sessionRetriever: sessionRetriever,
  2175. timeNow: tc.timeNow,
  2176. convoTracker: newConvoTracker(),
  2177. feedbagManager: feedbagManager,
  2178. contactPreAuthorizer: contactPreAuth,
  2179. buddyBroadcaster: buddyBroadcaster,
  2180. logger: discardLogger,
  2181. }
  2182. var forwardCalled bool
  2183. svc.forwardICQAuthEvents = func(ctx context.Context, sender state.IdentScreenName, recipient state.IdentScreenName, authMsg wire.ICBMCh4Message) error {
  2184. forwardCalled = true
  2185. assert.Equal(t, tc.instance.IdentScreenName(), sender)
  2186. assert.Equal(t, tc.expectForwardRecipient, recipient)
  2187. assert.Equal(t, tc.expectForwardAuthMsg, authMsg)
  2188. return nil
  2189. }
  2190. outputSNAC, err := svc.ChannelMsgToHost(context.Background(), tc.instance, tc.inputSNAC.Frame,
  2191. tc.inputSNAC.Body.(wire.SNAC_0x04_0x06_ICBMChannelMsgToHost))
  2192. assert.ErrorIs(t, err, tc.wantErr)
  2193. assert.Equal(t, tc.expectOutput, outputSNAC)
  2194. assert.Equal(t, tc.expectForwardICQAuthEvents, forwardCalled)
  2195. })
  2196. }
  2197. }
  2198. func TestICBMService_ClientEvent(t *testing.T) {
  2199. cases := []struct {
  2200. // name is the unit test name
  2201. name string
  2202. // senderScreenName is the screen name of the user sending the event
  2203. senderScreenName state.DisplayScreenName
  2204. // inputSNAC is the SNAC sent by the sender client
  2205. inputSNAC wire.SNACMessage
  2206. // mockParams is the list of params sent to mocks that satisfy this
  2207. // method's dependencies
  2208. mockParams mockParams
  2209. // expectError indicates whether an error is expected
  2210. expectError bool
  2211. }{
  2212. {
  2213. name: "transmit typing event (event=2) from sender to recipient",
  2214. senderScreenName: "sender-screen-name",
  2215. mockParams: mockParams{
  2216. relationshipFetcherParams: relationshipFetcherParams{
  2217. relationshipParams: relationshipParams{
  2218. {
  2219. me: state.NewIdentScreenName("sender-screen-name"),
  2220. them: state.NewIdentScreenName("recipient-screen-name"),
  2221. result: state.Relationship{
  2222. User: state.NewIdentScreenName("recipient-screen-name"),
  2223. BlocksYou: false,
  2224. YouBlock: false,
  2225. IsOnTheirList: false,
  2226. IsOnYourList: false,
  2227. },
  2228. },
  2229. },
  2230. },
  2231. messageRelayerParams: messageRelayerParams{
  2232. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  2233. {
  2234. screenName: state.NewIdentScreenName("recipient-screen-name"),
  2235. message: wire.SNACMessage{
  2236. Frame: wire.SNACFrame{
  2237. FoodGroup: wire.ICBM,
  2238. SubGroup: wire.ICBMClientEvent,
  2239. RequestID: 1234,
  2240. },
  2241. Body: wire.SNAC_0x04_0x14_ICBMClientEvent{
  2242. Cookie: 12345678,
  2243. ChannelID: 42,
  2244. ScreenName: "sender-screen-name",
  2245. Event: 2, // typing
  2246. },
  2247. },
  2248. },
  2249. },
  2250. },
  2251. },
  2252. inputSNAC: wire.SNACMessage{
  2253. Frame: wire.SNACFrame{
  2254. RequestID: 1234,
  2255. },
  2256. Body: wire.SNAC_0x04_0x14_ICBMClientEvent{
  2257. Cookie: 12345678,
  2258. ChannelID: 42,
  2259. ScreenName: "recipient-screen-name",
  2260. Event: 2, // typing
  2261. },
  2262. },
  2263. },
  2264. {
  2265. name: "transmit text typed event (event=1) from sender to recipient",
  2266. senderScreenName: "sender-screen-name",
  2267. mockParams: mockParams{
  2268. relationshipFetcherParams: relationshipFetcherParams{
  2269. relationshipParams: relationshipParams{
  2270. {
  2271. me: state.NewIdentScreenName("sender-screen-name"),
  2272. them: state.NewIdentScreenName("recipient-screen-name"),
  2273. result: state.Relationship{
  2274. User: state.NewIdentScreenName("recipient-screen-name"),
  2275. BlocksYou: false,
  2276. YouBlock: false,
  2277. IsOnTheirList: false,
  2278. IsOnYourList: false,
  2279. },
  2280. },
  2281. },
  2282. },
  2283. messageRelayerParams: messageRelayerParams{
  2284. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  2285. {
  2286. screenName: state.NewIdentScreenName("recipient-screen-name"),
  2287. message: wire.SNACMessage{
  2288. Frame: wire.SNACFrame{
  2289. FoodGroup: wire.ICBM,
  2290. SubGroup: wire.ICBMClientEvent,
  2291. RequestID: 5678,
  2292. },
  2293. Body: wire.SNAC_0x04_0x14_ICBMClientEvent{
  2294. Cookie: 87654321,
  2295. ChannelID: 1,
  2296. ScreenName: "sender-screen-name",
  2297. Event: 1, // text typed
  2298. },
  2299. },
  2300. },
  2301. },
  2302. },
  2303. },
  2304. inputSNAC: wire.SNACMessage{
  2305. Frame: wire.SNACFrame{
  2306. RequestID: 5678,
  2307. },
  2308. Body: wire.SNAC_0x04_0x14_ICBMClientEvent{
  2309. Cookie: 87654321,
  2310. ChannelID: 1,
  2311. ScreenName: "recipient-screen-name",
  2312. Event: 1, // text typed
  2313. },
  2314. },
  2315. },
  2316. {
  2317. name: "transmit stopped typing event (event=0) from sender to recipient",
  2318. senderScreenName: "sender-screen-name",
  2319. mockParams: mockParams{
  2320. relationshipFetcherParams: relationshipFetcherParams{
  2321. relationshipParams: relationshipParams{
  2322. {
  2323. me: state.NewIdentScreenName("sender-screen-name"),
  2324. them: state.NewIdentScreenName("recipient-screen-name"),
  2325. result: state.Relationship{
  2326. User: state.NewIdentScreenName("recipient-screen-name"),
  2327. BlocksYou: false,
  2328. YouBlock: false,
  2329. IsOnTheirList: false,
  2330. IsOnYourList: false,
  2331. },
  2332. },
  2333. },
  2334. },
  2335. messageRelayerParams: messageRelayerParams{
  2336. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{
  2337. {
  2338. screenName: state.NewIdentScreenName("recipient-screen-name"),
  2339. message: wire.SNACMessage{
  2340. Frame: wire.SNACFrame{
  2341. FoodGroup: wire.ICBM,
  2342. SubGroup: wire.ICBMClientEvent,
  2343. RequestID: 9999,
  2344. },
  2345. Body: wire.SNAC_0x04_0x14_ICBMClientEvent{
  2346. Cookie: 98765432,
  2347. ChannelID: 5,
  2348. ScreenName: "sender-screen-name",
  2349. Event: 0, // stopped typing
  2350. },
  2351. },
  2352. },
  2353. },
  2354. },
  2355. },
  2356. inputSNAC: wire.SNACMessage{
  2357. Frame: wire.SNACFrame{
  2358. RequestID: 9999,
  2359. },
  2360. Body: wire.SNAC_0x04_0x14_ICBMClientEvent{
  2361. Cookie: 98765432,
  2362. ChannelID: 5,
  2363. ScreenName: "recipient-screen-name",
  2364. Event: 0, // stopped typing
  2365. },
  2366. },
  2367. },
  2368. {
  2369. name: "don't transmit typing event because sender has blocked recipient",
  2370. senderScreenName: "sender-screen-name",
  2371. mockParams: mockParams{
  2372. relationshipFetcherParams: relationshipFetcherParams{
  2373. relationshipParams: relationshipParams{
  2374. {
  2375. me: state.NewIdentScreenName("sender-screen-name"),
  2376. them: state.NewIdentScreenName("recipient-screen-name"),
  2377. result: state.Relationship{
  2378. User: state.NewIdentScreenName("recipient-screen-name"),
  2379. BlocksYou: false,
  2380. YouBlock: true,
  2381. IsOnTheirList: false,
  2382. IsOnYourList: false,
  2383. },
  2384. },
  2385. },
  2386. },
  2387. messageRelayerParams: messageRelayerParams{
  2388. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{},
  2389. },
  2390. },
  2391. inputSNAC: wire.SNACMessage{
  2392. Frame: wire.SNACFrame{
  2393. RequestID: 1234,
  2394. },
  2395. Body: wire.SNAC_0x04_0x14_ICBMClientEvent{
  2396. Cookie: 12345678,
  2397. ChannelID: 42,
  2398. ScreenName: "recipient-screen-name",
  2399. Event: 2, // typing
  2400. },
  2401. },
  2402. },
  2403. {
  2404. name: "don't transmit typing event because recipient has blocked sender",
  2405. senderScreenName: "sender-screen-name",
  2406. mockParams: mockParams{
  2407. relationshipFetcherParams: relationshipFetcherParams{
  2408. relationshipParams: relationshipParams{
  2409. {
  2410. me: state.NewIdentScreenName("sender-screen-name"),
  2411. them: state.NewIdentScreenName("recipient-screen-name"),
  2412. result: state.Relationship{
  2413. User: state.NewIdentScreenName("recipient-screen-name"),
  2414. BlocksYou: true,
  2415. YouBlock: false,
  2416. IsOnTheirList: false,
  2417. IsOnYourList: false,
  2418. },
  2419. },
  2420. },
  2421. },
  2422. messageRelayerParams: messageRelayerParams{
  2423. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{},
  2424. },
  2425. },
  2426. inputSNAC: wire.SNACMessage{
  2427. Frame: wire.SNACFrame{
  2428. RequestID: 1234,
  2429. },
  2430. Body: wire.SNAC_0x04_0x14_ICBMClientEvent{
  2431. Cookie: 12345678,
  2432. ChannelID: 42,
  2433. ScreenName: "recipient-screen-name",
  2434. Event: 2, // typing
  2435. },
  2436. },
  2437. },
  2438. {
  2439. name: "return error when relationship fetcher fails",
  2440. senderScreenName: "sender-screen-name",
  2441. mockParams: mockParams{
  2442. relationshipFetcherParams: relationshipFetcherParams{
  2443. relationshipParams: relationshipParams{
  2444. {
  2445. me: state.NewIdentScreenName("sender-screen-name"),
  2446. them: state.NewIdentScreenName("recipient-screen-name"),
  2447. result: state.Relationship{},
  2448. err: errors.New("database connection failed"),
  2449. },
  2450. },
  2451. },
  2452. messageRelayerParams: messageRelayerParams{
  2453. relayToScreenNameActiveOnlyParams: relayToScreenNameActiveOnlyParams{},
  2454. },
  2455. },
  2456. inputSNAC: wire.SNACMessage{
  2457. Frame: wire.SNACFrame{
  2458. RequestID: 1234,
  2459. },
  2460. Body: wire.SNAC_0x04_0x14_ICBMClientEvent{
  2461. Cookie: 12345678,
  2462. ChannelID: 42,
  2463. ScreenName: "recipient-screen-name",
  2464. Event: 2, // typing
  2465. },
  2466. },
  2467. expectError: true,
  2468. },
  2469. }
  2470. for _, tc := range cases {
  2471. t.Run(tc.name, func(t *testing.T) {
  2472. relationshipFetcher := newMockRelationshipFetcher(t)
  2473. for _, item := range tc.mockParams.relationshipParams {
  2474. relationshipFetcher.EXPECT().
  2475. Relationship(matchContext(), item.me, item.them).
  2476. Return(item.result, item.err)
  2477. }
  2478. messageRelayer := newMockMessageRelayer(t)
  2479. for _, item := range tc.mockParams.relayToScreenNameActiveOnlyParams {
  2480. messageRelayer.EXPECT().
  2481. RelayToScreenNameActiveOnly(matchContext(), item.screenName, item.message)
  2482. }
  2483. senderSession := newTestInstance(tc.senderScreenName)
  2484. svc := ICBMService{
  2485. relationshipFetcher: relationshipFetcher,
  2486. messageRelayer: messageRelayer,
  2487. }
  2488. err := svc.ClientEvent(context.Background(), senderSession, tc.inputSNAC.Frame,
  2489. tc.inputSNAC.Body.(wire.SNAC_0x04_0x14_ICBMClientEvent))
  2490. if tc.expectError {
  2491. assert.Error(t, err)
  2492. } else {
  2493. assert.NoError(t, err)
  2494. }
  2495. })
  2496. }
  2497. }
  2498. func TestICBMService_EvilRequest(t *testing.T) {
  2499. cases := []struct {
  2500. // name is the unit test name
  2501. name string
  2502. // senderScreenName is the session of the user sending the EvilRequest
  2503. instance *state.SessionInstance
  2504. // msgsReceived is the # of messages received from the warned user
  2505. msgsReceived int
  2506. // inputSNAC is the SNAC sent by the sender client
  2507. inputSNAC wire.SNACMessage
  2508. // expectOutput is the SNAC sent from the server to client
  2509. expectOutput wire.SNACMessage
  2510. // mockParams is the list of params sent to mocks that satisfy this
  2511. // method's dependencies
  2512. mockParams mockParams
  2513. // waitForWarnMsg indicates whether to wait for session warn signal
  2514. waitForWarnMsg bool
  2515. }{
  2516. {
  2517. name: "transmit anonymous warning from sender to recipient",
  2518. instance: newTestInstance("sender-screen-name"),
  2519. msgsReceived: 1,
  2520. inputSNAC: wire.SNACMessage{
  2521. Frame: wire.SNACFrame{
  2522. RequestID: 1234,
  2523. },
  2524. Body: wire.SNAC_0x04_0x08_ICBMEvilRequest{
  2525. SendAs: 1, // make it anonymous
  2526. ScreenName: "recipient-screen-name",
  2527. },
  2528. },
  2529. expectOutput: wire.SNACMessage{
  2530. Frame: wire.SNACFrame{
  2531. FoodGroup: wire.ICBM,
  2532. SubGroup: wire.ICBMEvilReply,
  2533. RequestID: 1234,
  2534. },
  2535. Body: wire.SNAC_0x04_0x09_ICBMEvilReply{
  2536. EvilDeltaApplied: 30,
  2537. UpdatedEvilValue: 30,
  2538. },
  2539. },
  2540. mockParams: mockParams{
  2541. relationshipFetcherParams: relationshipFetcherParams{
  2542. relationshipParams: relationshipParams{
  2543. {
  2544. me: state.NewIdentScreenName("sender-screen-name"),
  2545. them: state.NewIdentScreenName("recipient-screen-name"),
  2546. result: state.Relationship{
  2547. User: state.NewIdentScreenName("recipient-screen-name"),
  2548. BlocksYou: false,
  2549. YouBlock: false,
  2550. IsOnTheirList: false,
  2551. IsOnYourList: false,
  2552. },
  2553. },
  2554. },
  2555. },
  2556. sessionRetrieverParams: sessionRetrieverParams{
  2557. retrieveSessionParams{
  2558. {
  2559. screenName: state.NewIdentScreenName("recipient-screen-name"),
  2560. result: newTestInstance("recipient-screen-name", sessOptCannedSignonTime).Session(),
  2561. },
  2562. },
  2563. },
  2564. messageRelayerParams: messageRelayerParams{
  2565. relayToScreenNameParams: relayToScreenNameParams{
  2566. {
  2567. screenName: state.NewIdentScreenName("recipient-screen-name"),
  2568. message: wire.SNACMessage{
  2569. Frame: wire.SNACFrame{
  2570. FoodGroup: wire.OService,
  2571. SubGroup: wire.OServiceEvilNotification,
  2572. },
  2573. Body: wire.SNAC_0x01_0x10_OServiceEvilNotification{
  2574. NewEvil: evilDeltaAnon,
  2575. },
  2576. },
  2577. },
  2578. },
  2579. },
  2580. },
  2581. waitForWarnMsg: true,
  2582. },
  2583. {
  2584. name: "transmit non-anonymous warning from sender to recipient",
  2585. instance: newTestInstance("sender-screen-name", sessOptWarning(110)),
  2586. msgsReceived: 1,
  2587. inputSNAC: wire.SNACMessage{
  2588. Frame: wire.SNACFrame{
  2589. RequestID: 1234,
  2590. },
  2591. Body: wire.SNAC_0x04_0x08_ICBMEvilRequest{
  2592. SendAs: 0, // make it identified
  2593. ScreenName: "recipient-screen-name",
  2594. },
  2595. },
  2596. expectOutput: wire.SNACMessage{
  2597. Frame: wire.SNACFrame{
  2598. FoodGroup: wire.ICBM,
  2599. SubGroup: wire.ICBMEvilReply,
  2600. RequestID: 1234,
  2601. },
  2602. Body: wire.SNAC_0x04_0x09_ICBMEvilReply{
  2603. EvilDeltaApplied: 100,
  2604. UpdatedEvilValue: 100,
  2605. },
  2606. },
  2607. mockParams: mockParams{
  2608. relationshipFetcherParams: relationshipFetcherParams{
  2609. relationshipParams: relationshipParams{
  2610. {
  2611. me: state.NewIdentScreenName("sender-screen-name"),
  2612. them: state.NewIdentScreenName("recipient-screen-name"),
  2613. result: state.Relationship{
  2614. User: state.NewIdentScreenName("recipient-screen-name"),
  2615. BlocksYou: false,
  2616. YouBlock: false,
  2617. IsOnTheirList: false,
  2618. IsOnYourList: false,
  2619. },
  2620. },
  2621. },
  2622. },
  2623. sessionRetrieverParams: sessionRetrieverParams{
  2624. retrieveSessionParams{
  2625. {
  2626. screenName: state.NewIdentScreenName("recipient-screen-name"),
  2627. result: newTestInstance("recipient-screen-name", sessOptCannedSignonTime).Session(),
  2628. },
  2629. },
  2630. },
  2631. messageRelayerParams: messageRelayerParams{
  2632. relayToScreenNameParams: relayToScreenNameParams{
  2633. {
  2634. screenName: state.NewIdentScreenName("recipient-screen-name"),
  2635. message: wire.SNACMessage{
  2636. Frame: wire.SNACFrame{
  2637. FoodGroup: wire.OService,
  2638. SubGroup: wire.OServiceEvilNotification,
  2639. },
  2640. Body: wire.SNAC_0x01_0x10_OServiceEvilNotification{
  2641. NewEvil: evilDelta,
  2642. Snitcher: &struct {
  2643. wire.TLVUserInfo
  2644. }{
  2645. wire.TLVUserInfo{
  2646. ScreenName: "sender-screen-name",
  2647. WarningLevel: 110,
  2648. },
  2649. },
  2650. },
  2651. },
  2652. },
  2653. },
  2654. },
  2655. },
  2656. waitForWarnMsg: true,
  2657. },
  2658. {
  2659. name: "don't transmit non-anonymous warning from sender to recipient because sender has blocked recipient",
  2660. instance: newTestInstance("sender-screen-name"),
  2661. msgsReceived: 1,
  2662. inputSNAC: wire.SNACMessage{
  2663. Frame: wire.SNACFrame{
  2664. RequestID: 1234,
  2665. },
  2666. Body: wire.SNAC_0x04_0x08_ICBMEvilRequest{
  2667. SendAs: 0, // make it identified
  2668. ScreenName: "recipient-screen-name",
  2669. },
  2670. },
  2671. expectOutput: wire.SNACMessage{
  2672. Frame: wire.SNACFrame{
  2673. FoodGroup: wire.ICBM,
  2674. SubGroup: wire.ICBMErr,
  2675. RequestID: 1234,
  2676. },
  2677. Body: wire.SNACError{
  2678. Code: wire.ErrorCodeNotLoggedOn,
  2679. },
  2680. },
  2681. mockParams: mockParams{
  2682. relationshipFetcherParams: relationshipFetcherParams{
  2683. relationshipParams: relationshipParams{
  2684. {
  2685. me: state.NewIdentScreenName("sender-screen-name"),
  2686. them: state.NewIdentScreenName("recipient-screen-name"),
  2687. result: state.Relationship{
  2688. User: state.NewIdentScreenName("recipient-screen-name"),
  2689. BlocksYou: false,
  2690. YouBlock: true,
  2691. IsOnTheirList: false,
  2692. IsOnYourList: false,
  2693. },
  2694. },
  2695. },
  2696. },
  2697. },
  2698. },
  2699. {
  2700. name: "don't transmit non-anonymous warning from sender to recipient because recipient has blocked sender",
  2701. instance: newTestInstance("sender-screen-name"),
  2702. msgsReceived: 1,
  2703. inputSNAC: wire.SNACMessage{
  2704. Frame: wire.SNACFrame{
  2705. RequestID: 1234,
  2706. },
  2707. Body: wire.SNAC_0x04_0x08_ICBMEvilRequest{
  2708. SendAs: 0, // make it identified
  2709. ScreenName: "recipient-screen-name",
  2710. },
  2711. },
  2712. expectOutput: wire.SNACMessage{
  2713. Frame: wire.SNACFrame{
  2714. FoodGroup: wire.ICBM,
  2715. SubGroup: wire.ICBMErr,
  2716. RequestID: 1234,
  2717. },
  2718. Body: wire.SNACError{
  2719. Code: wire.ErrorCodeNotLoggedOn,
  2720. },
  2721. },
  2722. mockParams: mockParams{
  2723. relationshipFetcherParams: relationshipFetcherParams{
  2724. relationshipParams: relationshipParams{
  2725. {
  2726. me: state.NewIdentScreenName("sender-screen-name"),
  2727. them: state.NewIdentScreenName("recipient-screen-name"),
  2728. result: state.Relationship{
  2729. User: state.NewIdentScreenName("recipient-screen-name"),
  2730. BlocksYou: true,
  2731. YouBlock: false,
  2732. IsOnTheirList: false,
  2733. IsOnYourList: false,
  2734. },
  2735. },
  2736. },
  2737. },
  2738. },
  2739. },
  2740. {
  2741. name: "can't warn bots",
  2742. instance: newTestInstance("sender-screen-name"),
  2743. msgsReceived: 1,
  2744. inputSNAC: wire.SNACMessage{
  2745. Frame: wire.SNACFrame{
  2746. RequestID: 1234,
  2747. },
  2748. Body: wire.SNAC_0x04_0x08_ICBMEvilRequest{
  2749. SendAs: 0, // make it identified
  2750. ScreenName: "recipient-screen-name",
  2751. },
  2752. },
  2753. expectOutput: wire.SNACMessage{
  2754. Frame: wire.SNACFrame{
  2755. FoodGroup: wire.ICBM,
  2756. SubGroup: wire.ICBMErr,
  2757. RequestID: 1234,
  2758. },
  2759. Body: wire.SNACError{
  2760. Code: wire.ErrorCodeRequestDenied,
  2761. },
  2762. },
  2763. mockParams: mockParams{
  2764. relationshipFetcherParams: relationshipFetcherParams{
  2765. relationshipParams: relationshipParams{
  2766. {
  2767. me: state.NewIdentScreenName("sender-screen-name"),
  2768. them: state.NewIdentScreenName("recipient-screen-name"),
  2769. result: state.Relationship{
  2770. User: state.NewIdentScreenName("recipient-screen-name"),
  2771. BlocksYou: false,
  2772. YouBlock: false,
  2773. IsOnTheirList: false,
  2774. IsOnYourList: false,
  2775. },
  2776. },
  2777. },
  2778. },
  2779. sessionRetrieverParams: sessionRetrieverParams{
  2780. retrieveSessionParams{
  2781. {
  2782. screenName: state.NewIdentScreenName("recipient-screen-name"),
  2783. result: newTestInstance("recipient-screen-name", sessOptBot).Session(),
  2784. },
  2785. },
  2786. },
  2787. },
  2788. },
  2789. {
  2790. name: "don't let users warn themselves",
  2791. instance: newTestInstance("sender-screen-name"),
  2792. msgsReceived: 1,
  2793. inputSNAC: wire.SNACMessage{
  2794. Frame: wire.SNACFrame{
  2795. RequestID: 1234,
  2796. },
  2797. Body: wire.SNAC_0x04_0x08_ICBMEvilRequest{
  2798. SendAs: 0, // make it identified
  2799. ScreenName: "sender-screen-name",
  2800. },
  2801. },
  2802. expectOutput: wire.SNACMessage{
  2803. Frame: wire.SNACFrame{
  2804. FoodGroup: wire.ICBM,
  2805. SubGroup: wire.ICBMErr,
  2806. RequestID: 1234,
  2807. },
  2808. Body: wire.SNACError{
  2809. Code: wire.ErrorCodeNotSupportedByHost,
  2810. },
  2811. },
  2812. },
  2813. {
  2814. name: "don't transmit non-anonymous warning from sender to recipient because recipient is offline",
  2815. instance: newTestInstance("sender-screen-name"),
  2816. msgsReceived: 1,
  2817. mockParams: mockParams{
  2818. relationshipFetcherParams: relationshipFetcherParams{
  2819. relationshipParams: relationshipParams{
  2820. {
  2821. me: state.NewIdentScreenName("sender-screen-name"),
  2822. them: state.NewIdentScreenName("recipient-screen-name"),
  2823. result: state.Relationship{
  2824. User: state.NewIdentScreenName("recipient-screen-name"),
  2825. BlocksYou: false,
  2826. YouBlock: false,
  2827. IsOnTheirList: false,
  2828. IsOnYourList: false,
  2829. },
  2830. },
  2831. },
  2832. },
  2833. sessionRetrieverParams: sessionRetrieverParams{
  2834. retrieveSessionParams{
  2835. {
  2836. screenName: state.NewIdentScreenName("recipient-screen-name"),
  2837. result: nil,
  2838. },
  2839. },
  2840. },
  2841. },
  2842. inputSNAC: wire.SNACMessage{
  2843. Frame: wire.SNACFrame{
  2844. RequestID: 1234,
  2845. },
  2846. Body: wire.SNAC_0x04_0x08_ICBMEvilRequest{
  2847. SendAs: 0, // make it identified
  2848. ScreenName: "recipient-screen-name",
  2849. },
  2850. },
  2851. expectOutput: wire.SNACMessage{
  2852. Frame: wire.SNACFrame{
  2853. FoodGroup: wire.ICBM,
  2854. SubGroup: wire.ICBMErr,
  2855. RequestID: 1234,
  2856. },
  2857. Body: wire.SNACError{
  2858. Code: wire.ErrorCodeNotLoggedOn,
  2859. },
  2860. },
  2861. },
  2862. {
  2863. name: "don't transmit anonymous warning from sender to recipient because recipient is offline",
  2864. instance: newTestInstance("sender-screen-name"),
  2865. msgsReceived: 1,
  2866. mockParams: mockParams{
  2867. relationshipFetcherParams: relationshipFetcherParams{
  2868. relationshipParams: relationshipParams{
  2869. {
  2870. me: state.NewIdentScreenName("sender-screen-name"),
  2871. them: state.NewIdentScreenName("recipient-screen-name"),
  2872. result: state.Relationship{
  2873. User: state.NewIdentScreenName("recipient-screen-name"),
  2874. BlocksYou: false,
  2875. YouBlock: false,
  2876. IsOnTheirList: false,
  2877. IsOnYourList: false,
  2878. },
  2879. },
  2880. },
  2881. },
  2882. sessionRetrieverParams: sessionRetrieverParams{
  2883. retrieveSessionParams{
  2884. {
  2885. screenName: state.NewIdentScreenName("recipient-screen-name"),
  2886. result: nil,
  2887. },
  2888. },
  2889. },
  2890. },
  2891. inputSNAC: wire.SNACMessage{
  2892. Frame: wire.SNACFrame{
  2893. RequestID: 1234,
  2894. },
  2895. Body: wire.SNAC_0x04_0x08_ICBMEvilRequest{
  2896. SendAs: 1, // make it anonymous
  2897. ScreenName: "recipient-screen-name",
  2898. },
  2899. },
  2900. expectOutput: wire.SNACMessage{
  2901. Frame: wire.SNACFrame{
  2902. FoodGroup: wire.ICBM,
  2903. SubGroup: wire.ICBMErr,
  2904. RequestID: 1234,
  2905. },
  2906. Body: wire.SNACError{
  2907. Code: wire.ErrorCodeNotLoggedOn,
  2908. },
  2909. },
  2910. },
  2911. }
  2912. for _, tc := range cases {
  2913. t.Run(tc.name, func(t *testing.T) {
  2914. relationshipFetcher := newMockRelationshipFetcher(t)
  2915. for _, item := range tc.mockParams.relationshipParams {
  2916. relationshipFetcher.EXPECT().
  2917. Relationship(matchContext(), item.me, item.them).
  2918. Return(item.result, item.err)
  2919. }
  2920. sessionRetriever := newMockSessionRetriever(t)
  2921. for _, item := range tc.mockParams.retrieveSessionParams {
  2922. sessionRetriever.EXPECT().
  2923. RetrieveSession(item.screenName).
  2924. Return(item.result)
  2925. }
  2926. messageRelayer := newMockMessageRelayer(t)
  2927. for _, item := range tc.mockParams.relayToScreenNameParams {
  2928. messageRelayer.EXPECT().
  2929. RelayToScreenName(mock.Anything, item.screenName, item.message)
  2930. }
  2931. for _, item := range tc.mockParams.relayToScreenNameActiveOnlyParams {
  2932. messageRelayer.EXPECT().
  2933. RelayToScreenNameActiveOnly(mock.Anything, item.screenName, item.message)
  2934. }
  2935. offlineMessageManager := newMockOfflineMessageManager(t)
  2936. for _, params := range tc.mockParams.saveMessageParams {
  2937. offlineMessageManager.EXPECT().
  2938. SaveMessage(matchContext(), params.offlineMessageIn).
  2939. Return(params.countOut, params.err)
  2940. }
  2941. svc := ICBMService{
  2942. relationshipFetcher: relationshipFetcher,
  2943. messageRelayer: messageRelayer,
  2944. offlineMessageSaver: offlineMessageManager,
  2945. sessionRetriever: sessionRetriever,
  2946. convoTracker: newConvoTracker(),
  2947. snacRateLimits: wire.DefaultSNACRateLimits(),
  2948. }
  2949. for i := 0; i < tc.msgsReceived; i++ {
  2950. svc.convoTracker.trackConvo(time.Now(),
  2951. state.NewIdentScreenName(tc.inputSNAC.Body.(wire.SNAC_0x04_0x08_ICBMEvilRequest).ScreenName),
  2952. tc.instance.IdentScreenName())
  2953. }
  2954. var wg sync.WaitGroup
  2955. if tc.waitForWarnMsg {
  2956. wg.Add(1)
  2957. go func() {
  2958. defer wg.Done()
  2959. for _, sess := range tc.mockParams.retrieveSessionParams {
  2960. <-sess.result.WarningCh()
  2961. }
  2962. }()
  2963. }
  2964. outputSNAC, err := svc.EvilRequest(context.Background(), tc.instance, tc.inputSNAC.Frame,
  2965. tc.inputSNAC.Body.(wire.SNAC_0x04_0x08_ICBMEvilRequest))
  2966. assert.NoError(t, err)
  2967. assert.Equal(t, tc.expectOutput, outputSNAC)
  2968. wg.Wait()
  2969. })
  2970. }
  2971. }
  2972. func TestICBMService_ParameterQuery(t *testing.T) {
  2973. svc := NewICBMService(nil, nil, nil, nil, nil, nil, nil, nil, wire.DefaultSNACRateLimits(), slog.Default())
  2974. have := svc.ParameterQuery(context.TODO(), wire.SNACFrame{RequestID: 1234})
  2975. want := wire.SNACMessage{
  2976. Frame: wire.SNACFrame{
  2977. FoodGroup: wire.ICBM,
  2978. SubGroup: wire.ICBMParameterReply,
  2979. RequestID: 1234,
  2980. },
  2981. Body: wire.SNAC_0x04_0x05_ICBMParameterReply{
  2982. MaxSlots: 100,
  2983. ICBMFlags: 3,
  2984. MaxIncomingICBMLen: 512,
  2985. MaxSourceEvil: 999,
  2986. MaxDestinationEvil: 999,
  2987. MinInterICBMInterval: 0,
  2988. },
  2989. }
  2990. assert.Equal(t, want, have)
  2991. }
  2992. func TestICBMService_ClientErr(t *testing.T) {
  2993. instance := newTestInstance("theScreenName")
  2994. inBody := wire.SNAC_0x04_0x0B_ICBMClientErr{
  2995. Cookie: 1234,
  2996. ChannelID: wire.ICBMChannelMIME,
  2997. ScreenName: "recipientScreenName",
  2998. Code: 10,
  2999. ErrInfo: []byte{1, 2, 3, 4},
  3000. }
  3001. expect := wire.SNACMessage{
  3002. Frame: wire.SNACFrame{
  3003. FoodGroup: wire.ICBM,
  3004. SubGroup: wire.ICBMClientErr,
  3005. RequestID: 1234,
  3006. },
  3007. Body: wire.SNAC_0x04_0x0B_ICBMClientErr{
  3008. Cookie: inBody.Cookie,
  3009. ChannelID: inBody.ChannelID,
  3010. ScreenName: instance.DisplayScreenName().String(),
  3011. Code: inBody.Code,
  3012. ErrInfo: inBody.ErrInfo,
  3013. },
  3014. }
  3015. messageRelayer := newMockMessageRelayer(t)
  3016. messageRelayer.EXPECT().
  3017. RelayToScreenName(mock.Anything, state.NewIdentScreenName("recipientScreenName"), expect)
  3018. svc := NewICBMService(nil, messageRelayer, nil, nil, nil, nil, nil, nil, wire.DefaultSNACRateLimits(), slog.Default())
  3019. err := svc.ClientErr(context.Background(), instance, wire.SNACFrame{RequestID: 1234}, inBody)
  3020. assert.NoError(t, err)
  3021. }
  3022. func TestICBMService_OfflineRetrieve(t *testing.T) {
  3023. cases := []struct {
  3024. // name is the unit test name
  3025. name string
  3026. // senderInstance is the session of the user retrieving messages
  3027. senderInstance *state.SessionInstance
  3028. // inputSNAC is the input frame (RequestID checked on reply)
  3029. inputSNAC wire.SNACMessage
  3030. // expectOutput is the expected return SNAC value.
  3031. expectOutput wire.SNACMessage
  3032. // wantErr is the expected error (nil for success)
  3033. wantErr error
  3034. // mockParams is the list of params sent to mocks that satisfy this method's dependencies
  3035. mockParams mockParams
  3036. }{
  3037. {
  3038. name: "relays stored messages and replies",
  3039. senderInstance: newTestInstance("recipient"),
  3040. inputSNAC: wire.SNACMessage{
  3041. Frame: wire.SNACFrame{RequestID: 42},
  3042. },
  3043. expectOutput: wire.SNACMessage{
  3044. Frame: wire.SNACFrame{
  3045. FoodGroup: wire.ICBM,
  3046. SubGroup: wire.ICBMOfflineRetrieveReply,
  3047. RequestID: 42,
  3048. },
  3049. Body: wire.SNAC_0x04_0x17_ICBMOfflineRetrieveReply{},
  3050. },
  3051. wantErr: nil,
  3052. mockParams: mockParams{
  3053. offlineMessageManagerParams: offlineMessageManagerParams{
  3054. retrieveMessagesParams: retrieveMessagesParams{
  3055. {
  3056. recipIn: state.NewIdentScreenName("recipient"),
  3057. messagesOut: []state.OfflineMessage{
  3058. {
  3059. Message: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  3060. Cookie: 1234,
  3061. ChannelID: wire.ICBMChannelIM,
  3062. TLVRestBlock: wire.TLVRestBlock{TLVList: []wire.TLV{wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3})}},
  3063. },
  3064. Recipient: state.NewIdentScreenName("recipient"),
  3065. Sender: state.NewIdentScreenName("sender"),
  3066. Sent: time.Unix(1700000000, 0).UTC(),
  3067. },
  3068. },
  3069. },
  3070. },
  3071. deleteMessagesParams: deleteMessagesParams{
  3072. {
  3073. recipIn: state.NewIdentScreenName("recipient"),
  3074. err: nil,
  3075. },
  3076. },
  3077. },
  3078. messageRelayerParams: messageRelayerParams{
  3079. relayToScreenNameParams: relayToScreenNameParams{
  3080. {
  3081. screenName: state.NewIdentScreenName("recipient"),
  3082. message: wire.SNACMessage{
  3083. Frame: wire.SNACFrame{
  3084. FoodGroup: wire.ICBM,
  3085. SubGroup: wire.ICBMChannelMsgToClient,
  3086. RequestID: wire.ReqIDFromServer,
  3087. },
  3088. Body: func() wire.SNAC_0x04_0x07_ICBMChannelMsgToClient {
  3089. msg := wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  3090. Cookie: 1234,
  3091. ChannelID: wire.ICBMChannelIM,
  3092. TLVUserInfo: wire.TLVUserInfo{
  3093. ScreenName: "sender",
  3094. },
  3095. TLVRestBlock: wire.TLVRestBlock{},
  3096. }
  3097. msg.Append(wire.NewTLVBE(wire.ICBMTLVData, []byte{1, 2, 3}))
  3098. msg.Append(wire.NewTLVBE(wire.ICBMTLVSendTime, uint32(time.Unix(1700000000, 0).UTC().Unix())))
  3099. return msg
  3100. }(),
  3101. },
  3102. },
  3103. },
  3104. },
  3105. },
  3106. },
  3107. {
  3108. name: "no stored messages returns reply without relays or deletes",
  3109. senderInstance: newTestInstance("recipient"),
  3110. inputSNAC: wire.SNACMessage{
  3111. Frame: wire.SNACFrame{RequestID: 55},
  3112. },
  3113. expectOutput: wire.SNACMessage{
  3114. Frame: wire.SNACFrame{
  3115. FoodGroup: wire.ICBM,
  3116. SubGroup: wire.ICBMOfflineRetrieveReply,
  3117. RequestID: 55,
  3118. },
  3119. Body: wire.SNAC_0x04_0x17_ICBMOfflineRetrieveReply{},
  3120. },
  3121. wantErr: nil,
  3122. mockParams: mockParams{
  3123. offlineMessageManagerParams: offlineMessageManagerParams{
  3124. retrieveMessagesParams: retrieveMessagesParams{
  3125. {
  3126. recipIn: state.NewIdentScreenName("recipient"),
  3127. messagesOut: []state.OfflineMessage{},
  3128. err: nil,
  3129. },
  3130. },
  3131. deleteMessagesParams: deleteMessagesParams{},
  3132. },
  3133. messageRelayerParams: messageRelayerParams{
  3134. relayToScreenNameParams: relayToScreenNameParams{},
  3135. },
  3136. },
  3137. },
  3138. {
  3139. name: "delete messages error after relay",
  3140. senderInstance: newTestInstance("recipient"),
  3141. inputSNAC: wire.SNACMessage{
  3142. Frame: wire.SNACFrame{RequestID: 99},
  3143. },
  3144. expectOutput: wire.SNACMessage{},
  3145. wantErr: assert.AnError,
  3146. mockParams: mockParams{
  3147. offlineMessageManagerParams: offlineMessageManagerParams{
  3148. retrieveMessagesParams: retrieveMessagesParams{
  3149. {
  3150. recipIn: state.NewIdentScreenName("recipient"),
  3151. messagesOut: []state.OfflineMessage{
  3152. {
  3153. Message: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  3154. Cookie: 4321,
  3155. ChannelID: wire.ICBMChannelIM,
  3156. TLVRestBlock: wire.TLVRestBlock{TLVList: []wire.TLV{wire.NewTLVBE(wire.ICBMTLVData, []byte{9, 8, 7})}},
  3157. },
  3158. Recipient: state.NewIdentScreenName("recipient"),
  3159. Sender: state.NewIdentScreenName("sender"),
  3160. Sent: time.Unix(1700001234, 0).UTC(),
  3161. },
  3162. },
  3163. },
  3164. },
  3165. deleteMessagesParams: deleteMessagesParams{
  3166. {
  3167. recipIn: state.NewIdentScreenName("recipient"),
  3168. err: assert.AnError,
  3169. },
  3170. },
  3171. },
  3172. messageRelayerParams: messageRelayerParams{
  3173. relayToScreenNameParams: relayToScreenNameParams{
  3174. {
  3175. screenName: state.NewIdentScreenName("recipient"),
  3176. message: wire.SNACMessage{
  3177. Frame: wire.SNACFrame{
  3178. FoodGroup: wire.ICBM,
  3179. SubGroup: wire.ICBMChannelMsgToClient,
  3180. RequestID: wire.ReqIDFromServer,
  3181. },
  3182. Body: func() wire.SNAC_0x04_0x07_ICBMChannelMsgToClient {
  3183. msg := wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  3184. Cookie: 4321,
  3185. ChannelID: wire.ICBMChannelIM,
  3186. TLVUserInfo: wire.TLVUserInfo{
  3187. ScreenName: "sender",
  3188. },
  3189. TLVRestBlock: wire.TLVRestBlock{},
  3190. }
  3191. msg.Append(wire.NewTLVBE(wire.ICBMTLVData, []byte{9, 8, 7}))
  3192. msg.Append(wire.NewTLVBE(wire.ICBMTLVSendTime, uint32(time.Unix(1700001234, 0).UTC().Unix())))
  3193. return msg
  3194. }(),
  3195. },
  3196. },
  3197. },
  3198. },
  3199. },
  3200. },
  3201. {
  3202. name: "propagates retrieve error",
  3203. senderInstance: newTestInstance("recipient"),
  3204. inputSNAC: wire.SNACMessage{
  3205. Frame: wire.SNACFrame{RequestID: 7},
  3206. },
  3207. expectOutput: wire.SNACMessage{},
  3208. wantErr: assert.AnError,
  3209. mockParams: mockParams{
  3210. offlineMessageManagerParams: offlineMessageManagerParams{
  3211. retrieveMessagesParams: retrieveMessagesParams{
  3212. {
  3213. recipIn: state.NewIdentScreenName("recipient"),
  3214. messagesOut: nil,
  3215. err: assert.AnError,
  3216. },
  3217. },
  3218. },
  3219. messageRelayerParams: messageRelayerParams{
  3220. relayToScreenNameParams: relayToScreenNameParams{},
  3221. },
  3222. },
  3223. },
  3224. }
  3225. for _, tc := range cases {
  3226. t.Run(tc.name, func(t *testing.T) {
  3227. offlineMessageManager := newMockOfflineMessageManager(t)
  3228. for _, params := range tc.mockParams.retrieveMessagesParams {
  3229. offlineMessageManager.EXPECT().
  3230. RetrieveMessages(matchContext(), params.recipIn).
  3231. Return(params.messagesOut, params.err)
  3232. }
  3233. for _, params := range tc.mockParams.deleteMessagesParams {
  3234. offlineMessageManager.EXPECT().
  3235. DeleteMessages(matchContext(), params.recipIn).
  3236. Return(params.err)
  3237. }
  3238. messageRelayer := newMockMessageRelayer(t)
  3239. for _, item := range tc.mockParams.relayToScreenNameParams {
  3240. messageRelayer.EXPECT().
  3241. RelayToScreenName(mock.Anything, item.screenName, item.message)
  3242. }
  3243. svc := ICBMService{
  3244. messageRelayer: messageRelayer,
  3245. offlineMessageManager: offlineMessageManager,
  3246. }
  3247. out, err := svc.OfflineRetrieve(context.Background(), tc.senderInstance, tc.inputSNAC.Frame)
  3248. assert.Equal(t, tc.expectOutput, out)
  3249. assert.ErrorIs(t, err, tc.wantErr)
  3250. })
  3251. }
  3252. }
  3253. func TestRingBuffer(t *testing.T) {
  3254. t.Run("new ringBuffer should have zero values", func(t *testing.T) {
  3255. rb := &ringBuffer{}
  3256. // Test val() on empty ringBuffer - should return zero time
  3257. result := rb.val()
  3258. zeroTime := time.Time{}
  3259. assert.Equal(t, zeroTime, result)
  3260. })
  3261. t.Run("val() should return current time", func(t *testing.T) {
  3262. now := time.Now()
  3263. rb := &ringBuffer{
  3264. cur: 1,
  3265. vals: [3]time.Time{
  3266. now.Add(-2 * time.Hour),
  3267. now.Add(-1 * time.Hour),
  3268. now,
  3269. },
  3270. }
  3271. result := rb.val()
  3272. assert.Equal(t, rb.vals[1], result)
  3273. })
  3274. t.Run("set() should store time and advance cursor", func(t *testing.T) {
  3275. rb := &ringBuffer{cur: 0}
  3276. newTime := time.Now()
  3277. // Set the time
  3278. rb.set(newTime)
  3279. // After set, cursor should advance to position 1
  3280. // We can verify this by setting another time and checking that it's stored at position 1
  3281. secondTime := time.Now().Add(time.Hour)
  3282. rb.set(secondTime)
  3283. // Now cursor should be at position 2, so val() should return the time at position 2
  3284. // But since we only set 2 times, position 2 should still be the zero time
  3285. zeroTime := time.Time{}
  3286. assert.Equal(t, zeroTime, rb.val())
  3287. })
  3288. t.Run("set() should wrap around after reaching end of array", func(t *testing.T) {
  3289. rb := &ringBuffer{cur: 2}
  3290. newTime := time.Now()
  3291. // Set the time at position 2
  3292. rb.set(newTime)
  3293. // Cursor should wrap around to position 0
  3294. // We can verify this by setting another time and checking behavior
  3295. rb.set(time.Now().Add(time.Hour))
  3296. // Now cursor should be at position 1, so val() should return the time at position 1
  3297. // But since we only set 2 times, position 1 should still be the zero time
  3298. zeroTime := time.Time{}
  3299. assert.Equal(t, zeroTime, rb.val())
  3300. })
  3301. t.Run("set() should handle multiple insertions correctly", func(t *testing.T) {
  3302. rb := &ringBuffer{}
  3303. // Insert 3 times
  3304. time1 := time.Now()
  3305. time2 := time.Now().Add(time.Hour)
  3306. time3 := time.Now().Add(2 * time.Hour)
  3307. rb.set(time1)
  3308. rb.set(time2)
  3309. rb.set(time3)
  3310. // After 3 insertions, cursor should be at position 0
  3311. // So val() should return the time at position 0
  3312. // This should be time1 since it was the first time set
  3313. assert.Equal(t, time1, rb.val())
  3314. })
  3315. t.Run("set() should overwrite existing values in order", func(t *testing.T) {
  3316. rb := &ringBuffer{}
  3317. // Set 3 times to fill the buffer
  3318. rb.set(time.Now())
  3319. rb.set(time.Now().Add(time.Hour))
  3320. rb.set(time.Now().Add(2 * time.Hour))
  3321. // After 3 sets, cursor is at position 0, val() returns first time
  3322. firstTime := rb.val()
  3323. assert.False(t, firstTime.IsZero())
  3324. // Set a 4th time - should overwrite position 0
  3325. fourthTime := time.Now().Add(3 * time.Hour)
  3326. rb.set(fourthTime)
  3327. // Now cursor is at position 1, val() returns second time
  3328. secondTime := rb.val()
  3329. assert.False(t, secondTime.IsZero())
  3330. // Set a 5th time - should overwrite position 1
  3331. fifthTime := time.Now().Add(4 * time.Hour)
  3332. rb.set(fifthTime)
  3333. // Now cursor is at position 2, val() returns third time
  3334. thirdTime := rb.val()
  3335. assert.False(t, thirdTime.IsZero())
  3336. // Set a 6th time - should overwrite position 2
  3337. sixthTime := time.Now().Add(5 * time.Hour)
  3338. rb.set(sixthTime)
  3339. // Now cursor wraps around to position 0, val() returns fourth time
  3340. assert.Equal(t, fourthTime, rb.val())
  3341. })
  3342. t.Run("val() should return correct time after multiple operations", func(t *testing.T) {
  3343. rb := &ringBuffer{}
  3344. // Insert times and verify val() returns correct current time
  3345. time1 := time.Now()
  3346. time2 := time.Now().Add(time.Hour)
  3347. rb.set(time1)
  3348. rb.set(time2)
  3349. // After 2 sets, cursor is at position 2
  3350. // So val() should return the time at position 2
  3351. // But since we only set 2 times, position 2 should still be the zero time
  3352. zeroTime := time.Time{}
  3353. assert.Equal(t, zeroTime, rb.val())
  3354. // Set one more to wrap around
  3355. time3 := time.Now().Add(2 * time.Hour)
  3356. rb.set(time3)
  3357. // Now cursor is at position 0, so val() should return the time at position 0
  3358. // This should be time1
  3359. assert.Equal(t, time1, rb.val())
  3360. })
  3361. t.Run("ringBuffer should maintain circular behavior over many operations", func(t *testing.T) {
  3362. rb := &ringBuffer{}
  3363. // Perform many operations to test circular behavior
  3364. for i := 0; i < 10; i++ {
  3365. rb.set(time.Now().Add(time.Duration(i) * time.Hour))
  3366. }
  3367. // After 10 operations, cursor should be at position 1 (10 % 3 = 1)
  3368. // So val() should return the time at position 1
  3369. // This should be the 8th time set (at position 1)
  3370. // We can't compare exact times since they're set in a loop, so just verify it's not zero
  3371. assert.False(t, rb.val().IsZero())
  3372. // Set one more to advance cursor to position 2
  3373. rb.set(time.Now().Add(10 * time.Hour))
  3374. // Now cursor is at position 2, so val() should return the time at position 2
  3375. // This should be the 9th time set (at position 2)
  3376. assert.False(t, rb.val().IsZero())
  3377. // Set one more to wrap around to position 0
  3378. rb.set(time.Now().Add(11 * time.Hour))
  3379. // Now cursor is at position 0, so val() should return the time at position 0
  3380. // This should be the 10th time set (at position 0)
  3381. assert.False(t, rb.val().IsZero())
  3382. })
  3383. t.Run("ringBuffer should cycle through all positions correctly", func(t *testing.T) {
  3384. rb := &ringBuffer{}
  3385. // Test cycling through all 3 positions
  3386. times := []time.Time{
  3387. time.Now(),
  3388. time.Now().Add(time.Hour),
  3389. time.Now().Add(2 * time.Hour),
  3390. }
  3391. // Set all 3 times
  3392. for _, t := range times {
  3393. rb.set(t)
  3394. }
  3395. // After 3 sets, cursor should be at position 0
  3396. // So val() should return the time at position 0
  3397. assert.Equal(t, times[0], rb.val())
  3398. // Set one more to advance cursor to position 1
  3399. nextTime := time.Now().Add(3 * time.Hour)
  3400. rb.set(nextTime)
  3401. // Now cursor is at position 1, so val() should return the time at position 1
  3402. // This should be the second time since it was stored at position 1
  3403. assert.Equal(t, times[1], rb.val())
  3404. })
  3405. }
  3406. func TestConvoTracker(t *testing.T) {
  3407. ct := newConvoTracker()
  3408. sender := state.NewIdentScreenName("sender")
  3409. recip := state.NewIdentScreenName("recipient")
  3410. now := time.Now()
  3411. // can't warn until a message is sent
  3412. assert.False(t, ct.trackWarn(now, recip, sender))
  3413. // can warn 1st time
  3414. ct.trackConvo(now, sender, recip)
  3415. assert.True(t, ct.trackWarn(now, recip, sender))
  3416. // can't warn 2nd time until 2nd message is sent
  3417. assert.False(t, ct.trackWarn(now, recip, sender))
  3418. // can warn 2nd time
  3419. now = now.Add(1 * time.Second)
  3420. ct.trackConvo(now, sender, recip)
  3421. assert.True(t, ct.trackWarn(now, recip, sender))
  3422. // can't warn 3rd time until 3rd message is sent
  3423. assert.False(t, ct.trackWarn(now, recip, sender))
  3424. // can warn 3rd time
  3425. now = now.Add(1 * time.Second)
  3426. ct.trackConvo(now, sender, recip)
  3427. assert.True(t, ct.trackWarn(now, recip, sender))
  3428. // can't warn 4th time
  3429. now = now.Add(1 * time.Second)
  3430. ct.trackConvo(now, sender, recip)
  3431. assert.False(t, ct.trackWarn(now, recip, sender))
  3432. // let an hour pass, we should be able to warn again
  3433. now = now.Add(1 * time.Hour)
  3434. ct.trackConvo(now, sender, recip)
  3435. assert.True(t, ct.trackWarn(now, recip, sender))
  3436. }
  3437. func TestICBMService_UpdateWarnLevel(t *testing.T) {
  3438. t.Run("happy path", func(t *testing.T) {
  3439. now := time.Now()
  3440. instance := newTestInstance("screen-name")
  3441. warnCh := make(chan uint16)
  3442. mockBuddyBroadcaster := newMockbuddyBroadcaster(t)
  3443. mockBuddyBroadcaster.EXPECT().
  3444. BroadcastBuddyArrived(mock.Anything, instance.IdentScreenName(), mock.MatchedBy(func(userInfo wire.TLVUserInfo) bool {
  3445. return userInfo.ScreenName == instance.IdentScreenName().String()
  3446. })).
  3447. Run(func(ctx context.Context, screenName state.IdentScreenName, userInfo wire.TLVUserInfo) {
  3448. warnCh <- userInfo.WarningLevel
  3449. }).Return(nil)
  3450. u := &state.User{}
  3451. userManager := newMockUserManager(t)
  3452. userManager.EXPECT().
  3453. User(matchContext(), instance.IdentScreenName()).
  3454. Return(u, nil)
  3455. userManager.EXPECT().
  3456. SetWarnLevel(matchContext(), instance.IdentScreenName(), now, uint16(100)).
  3457. Return(nil)
  3458. userManager.EXPECT().
  3459. SetWarnLevel(matchContext(), instance.IdentScreenName(), now, uint16(50)).
  3460. Return(nil)
  3461. userManager.EXPECT().
  3462. SetWarnLevel(matchContext(), instance.IdentScreenName(), now, uint16(30)).
  3463. Return(nil)
  3464. userManager.EXPECT().
  3465. SetWarnLevel(matchContext(), instance.IdentScreenName(), now, uint16(0)).
  3466. Return(nil)
  3467. svc := ICBMService{
  3468. buddyBroadcaster: mockBuddyBroadcaster,
  3469. interval: 1 * time.Millisecond,
  3470. logger: slog.Default(),
  3471. snacRateLimits: wire.DefaultSNACRateLimits(),
  3472. timeNow: func() time.Time { return now },
  3473. userManager: userManager,
  3474. }
  3475. ctx, cancel := context.WithCancel(context.Background())
  3476. defer cancel()
  3477. var wg sync.WaitGroup
  3478. wg.Add(1)
  3479. go func() {
  3480. defer wg.Done()
  3481. svc.UpdateWarnLevel(ctx, instance) // do a sync test here?
  3482. }()
  3483. ok, _ := instance.Session().ScaleWarningAndRateLimit(100, 3)
  3484. assert.True(t, ok)
  3485. assert.Equal(t, uint16(100), <-warnCh)
  3486. assert.Equal(t, uint16(50), <-warnCh)
  3487. assert.Equal(t, uint16(0), <-warnCh)
  3488. ok, _ = instance.Session().ScaleWarningAndRateLimit(100, 3)
  3489. assert.True(t, ok)
  3490. assert.Equal(t, uint16(100), <-warnCh)
  3491. assert.Equal(t, uint16(50), <-warnCh)
  3492. assert.Equal(t, uint16(0), <-warnCh)
  3493. instance.Session().ScaleWarningAndRateLimit(30, 3)
  3494. assert.Equal(t, uint16(30), <-warnCh)
  3495. assert.Equal(t, uint16(0), <-warnCh)
  3496. cancel()
  3497. wg.Wait()
  3498. })
  3499. }
  3500. func TestICBMService_RestoreWarningLevel(t *testing.T) {
  3501. tests := []struct {
  3502. name string
  3503. lastWarnUpdate time.Duration
  3504. lastWarnLevel uint16
  3505. expectedWarn uint16
  3506. }{
  3507. {
  3508. name: "decays warning when last update is before interval boundary",
  3509. lastWarnUpdate: -15*time.Millisecond - 1*time.Millisecond,
  3510. lastWarnLevel: 250,
  3511. expectedWarn: 100,
  3512. },
  3513. {
  3514. name: "decays warning when last update is after interval boundary",
  3515. lastWarnUpdate: -15*time.Millisecond + 1*time.Millisecond,
  3516. lastWarnLevel: 250,
  3517. expectedWarn: 150,
  3518. },
  3519. {
  3520. name: "decays warning when last update is exactly on interval boundary",
  3521. lastWarnUpdate: -15 * time.Millisecond,
  3522. lastWarnLevel: 250,
  3523. expectedWarn: 100,
  3524. },
  3525. {
  3526. name: "resets warning to zero when time is exactly at decay period",
  3527. lastWarnUpdate: -25 * time.Millisecond,
  3528. lastWarnLevel: 250,
  3529. expectedWarn: 0,
  3530. },
  3531. {
  3532. name: "resets warning to zero when time far decay period",
  3533. lastWarnUpdate: -1 * time.Second,
  3534. lastWarnLevel: 250,
  3535. expectedWarn: 0,
  3536. },
  3537. }
  3538. for _, tt := range tests {
  3539. t.Run(tt.name, func(t *testing.T) {
  3540. now := time.Now()
  3541. instance := newTestInstance("screen-name")
  3542. u := &state.User{
  3543. LastWarnUpdate: now.Add(tt.lastWarnUpdate),
  3544. LastWarnLevel: tt.lastWarnLevel,
  3545. }
  3546. userManager := newMockUserManager(t)
  3547. userManager.EXPECT().
  3548. User(matchContext(), instance.IdentScreenName()).
  3549. Return(u, nil)
  3550. svc := ICBMService{
  3551. logger: slog.Default(),
  3552. interval: 5 * time.Millisecond,
  3553. snacRateLimits: wire.DefaultSNACRateLimits(),
  3554. timeNow: func() time.Time { return now },
  3555. userManager: userManager,
  3556. }
  3557. ctx, cancel := context.WithCancel(context.Background())
  3558. defer cancel()
  3559. statesBefore := instance.RateLimitStates()
  3560. err := svc.RestoreWarningLevel(ctx, instance)
  3561. assert.NoError(t, err)
  3562. assert.Equal(t, tt.expectedWarn, instance.Warning())
  3563. statesAfter := instance.RateLimitStates()
  3564. if tt.expectedWarn > 0 {
  3565. // make sure the rate limits changed
  3566. assert.NotEqual(t, statesBefore, statesAfter)
  3567. } else {
  3568. // make sure the rate limits have been restored
  3569. assert.Equal(t, statesBefore, statesAfter)
  3570. }
  3571. })
  3572. }
  3573. }
  3574. func TestICBMService_RestoreWarningLevel_ErrorCases(t *testing.T) {
  3575. t.Run("user does not exist", func(t *testing.T) {
  3576. instance := newTestInstance("screen-name")
  3577. userManager := newMockUserManager(t)
  3578. userManager.EXPECT().
  3579. User(matchContext(), instance.IdentScreenName()).
  3580. Return(nil, nil)
  3581. svc := ICBMService{
  3582. logger: slog.Default(),
  3583. interval: 5 * time.Millisecond,
  3584. snacRateLimits: wire.DefaultSNACRateLimits(),
  3585. timeNow: time.Now,
  3586. userManager: userManager,
  3587. }
  3588. err := svc.RestoreWarningLevel(context.Background(), instance)
  3589. assert.ErrorIs(t, err, state.ErrNoUser)
  3590. })
  3591. t.Run("user manager returns error", func(t *testing.T) {
  3592. instance := newTestInstance("screen-name")
  3593. expectedErr := errors.New("database connection failed")
  3594. userManager := newMockUserManager(t)
  3595. userManager.EXPECT().
  3596. User(matchContext(), instance.IdentScreenName()).
  3597. Return(nil, expectedErr)
  3598. svc := ICBMService{
  3599. logger: slog.Default(),
  3600. interval: 5 * time.Millisecond,
  3601. snacRateLimits: wire.DefaultSNACRateLimits(),
  3602. timeNow: time.Now,
  3603. userManager: userManager,
  3604. }
  3605. err := svc.RestoreWarningLevel(context.Background(), instance)
  3606. assert.Error(t, err)
  3607. // When user is nil, it returns ErrNoUser regardless of the error
  3608. assert.ErrorIs(t, err, expectedErr)
  3609. })
  3610. t.Run("user exists with zero warning level", func(t *testing.T) {
  3611. instance := newTestInstance("screen-name")
  3612. u := &state.User{
  3613. LastWarnUpdate: time.Now().Add(-10 * time.Millisecond),
  3614. LastWarnLevel: 0, // No warning level
  3615. }
  3616. userManager := newMockUserManager(t)
  3617. userManager.EXPECT().
  3618. User(matchContext(), instance.IdentScreenName()).
  3619. Return(u, nil)
  3620. svc := ICBMService{
  3621. logger: slog.Default(),
  3622. interval: 5 * time.Millisecond,
  3623. snacRateLimits: wire.DefaultSNACRateLimits(),
  3624. timeNow: time.Now,
  3625. userManager: userManager,
  3626. }
  3627. err := svc.RestoreWarningLevel(context.Background(), instance)
  3628. assert.NoError(t, err)
  3629. assert.Equal(t, uint16(0), instance.Warning())
  3630. })
  3631. }
  3632. func Test_calcWarningLevelChange(t *testing.T) {
  3633. t.Run("active warn level, last modified between intervals", func(t *testing.T) {
  3634. now := time.Now()
  3635. interval := 5 * time.Millisecond
  3636. lastWarn := now.Add(-15 * time.Millisecond).Add(-1 * time.Millisecond)
  3637. warnDelta := calcElapsedWarningLevel(lastWarn, now, interval)
  3638. assert.Equal(t, int16(-150), warnDelta)
  3639. })
  3640. t.Run("active warn level, last modified between intervals", func(t *testing.T) {
  3641. now := time.Now()
  3642. interval := 5 * time.Millisecond
  3643. lastWarn := now.Add(-15 * time.Millisecond).Add(1 * time.Millisecond)
  3644. warnDelta := calcElapsedWarningLevel(lastWarn, now, interval)
  3645. assert.Equal(t, int16(-100), warnDelta)
  3646. })
  3647. t.Run("active warn level, last modified exactly on interval", func(t *testing.T) {
  3648. now := time.Now()
  3649. interval := 5 * time.Millisecond
  3650. lastWarn := now.Add(-15 * time.Millisecond)
  3651. warnDelta := calcElapsedWarningLevel(lastWarn, now, interval)
  3652. assert.Equal(t, int16(-150), warnDelta)
  3653. })
  3654. t.Run("resolved warn level", func(t *testing.T) {
  3655. now := time.Now()
  3656. interval := 5 * time.Millisecond
  3657. lastWarn := now.Add(-25 * time.Millisecond)
  3658. warnDelta := calcElapsedWarningLevel(lastWarn, now, interval)
  3659. assert.Equal(t, int16(-250), warnDelta)
  3660. })
  3661. t.Run("resolved warn level - time past exceeds maximum window", func(t *testing.T) {
  3662. now := time.Now()
  3663. interval := 5 * time.Millisecond
  3664. lastWarn := now.Add(-200 * time.Millisecond)
  3665. warnDelta := calcElapsedWarningLevel(lastWarn, now, interval)
  3666. assert.Equal(t, int16(-2000), warnDelta)
  3667. })
  3668. }
  3669. func Test_calcRefreshInterval(t *testing.T) {
  3670. t.Run("active warn level, last modified between intervals", func(t *testing.T) {
  3671. now := time.Now()
  3672. interval := 5 * time.Millisecond
  3673. lastWarn := now.Add(-15 * time.Millisecond).Add(-1 * time.Millisecond)
  3674. newInterval := timeTillNextInterval(lastWarn, now, interval)
  3675. assert.Equal(t, 4*time.Millisecond, newInterval)
  3676. })
  3677. t.Run("active warn level, last modified between intervals", func(t *testing.T) {
  3678. now := time.Now()
  3679. interval := 5 * time.Millisecond
  3680. lastWarn := now.Add(-15 * time.Millisecond).Add(1 * time.Millisecond)
  3681. newInterval := timeTillNextInterval(lastWarn, now, interval)
  3682. assert.Equal(t, 1*time.Millisecond, newInterval)
  3683. })
  3684. t.Run("active warn level, last modified exactly on interval", func(t *testing.T) {
  3685. now := time.Now()
  3686. interval := 5 * time.Millisecond
  3687. lastWarn := now.Add(-15 * time.Millisecond)
  3688. newInterval := timeTillNextInterval(lastWarn, now, interval)
  3689. assert.Equal(t, 5*time.Millisecond, newInterval)
  3690. })
  3691. t.Run("resolved warn level", func(t *testing.T) {
  3692. now := time.Now()
  3693. interval := 5 * time.Millisecond
  3694. lastWarn := now.Add(-25 * time.Millisecond)
  3695. newInterval := timeTillNextInterval(lastWarn, now, interval)
  3696. assert.Equal(t, interval, newInterval)
  3697. })
  3698. t.Run("resolved warn level - time past exceeds maximum window", func(t *testing.T) {
  3699. now := time.Now()
  3700. interval := 5 * time.Millisecond
  3701. lastWarn := now.Add(-200 * time.Millisecond)
  3702. newInterval := timeTillNextInterval(lastWarn, now, interval)
  3703. assert.Equal(t, interval, newInterval)
  3704. })
  3705. }
  3706. func TestStripHTML(t *testing.T) {
  3707. tests := []struct {
  3708. name string
  3709. input []byte
  3710. expected []byte
  3711. }{
  3712. {
  3713. name: "simple HTML tags",
  3714. input: []byte("<HTML><BODY>Hello World</BODY></HTML>"),
  3715. expected: []byte("Hello World"),
  3716. },
  3717. {
  3718. name: "BR tag to newline",
  3719. input: []byte("Line 1<BR>Line 2"),
  3720. expected: []byte("Line 1\nLine 2"),
  3721. },
  3722. {
  3723. name: "lowercase br tag",
  3724. input: []byte("Line 1<br>Line 2"),
  3725. expected: []byte("Line 1\nLine 2"),
  3726. },
  3727. {
  3728. name: "self-closing br tag",
  3729. input: []byte("Line 1<br/>Line 2"),
  3730. expected: []byte("Line 1\nLine 2"),
  3731. },
  3732. {
  3733. name: "HTML entities",
  3734. input: []byte("&lt;test&gt; &amp; &quot;quoted&quot;"),
  3735. expected: []byte("<test> & \"quoted\""),
  3736. },
  3737. {
  3738. name: "FONT tags with attributes",
  3739. input: []byte(`<FONT FACE="Arial" SIZE="3">Text</FONT>`),
  3740. expected: []byte("Text"),
  3741. },
  3742. {
  3743. name: "mixed formatting",
  3744. input: []byte("<HTML><BODY><B>Bold</B> <I>Italic</I></BODY></HTML>"),
  3745. expected: []byte("Bold Italic"),
  3746. },
  3747. {
  3748. name: "plain text unchanged",
  3749. input: []byte("Hello World"),
  3750. expected: []byte("Hello World"),
  3751. },
  3752. {
  3753. name: "empty input",
  3754. input: []byte(""),
  3755. expected: []byte(""),
  3756. },
  3757. {
  3758. name: "nbsp entity",
  3759. input: []byte("Hello&nbsp;World"),
  3760. expected: []byte("Hello\u00a0World"),
  3761. },
  3762. {
  3763. name: "realistic HTML message",
  3764. input: []byte(`<HTML><BODY dir="ltr"><FONT color="#000000" size="2" face="Arial">Hello</FONT></BODY></HTML>`),
  3765. expected: []byte("Hello"),
  3766. },
  3767. {
  3768. name: "apos entity",
  3769. input: []byte("it&apos;s working"),
  3770. expected: []byte("it's working"),
  3771. },
  3772. }
  3773. for _, tt := range tests {
  3774. t.Run(tt.name, func(t *testing.T) {
  3775. result := stripHTML(tt.input)
  3776. assert.Equal(t, tt.expected, result)
  3777. })
  3778. }
  3779. }