cmd_client_test.go 94 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516
  1. package toc
  2. import (
  3. "context"
  4. "encoding/hex"
  5. "io"
  6. "log/slog"
  7. "testing"
  8. "github.com/google/uuid"
  9. "github.com/stretchr/testify/assert"
  10. "github.com/stretchr/testify/mock"
  11. "github.com/mk6i/retro-aim-server/state"
  12. "github.com/mk6i/retro-aim-server/wire"
  13. )
  14. func TestOSCARProxy_AddBuddy(t *testing.T) {
  15. cases := []struct {
  16. // name is the unit test name
  17. name string
  18. // me is the TOC user session
  19. me *state.Session
  20. // givenCmd is the TOC command
  21. givenCmd []byte
  22. // wantMsg is the expected TOC response
  23. wantMsg string
  24. // mockParams is the list of params sent to mocks that satisfy this
  25. // method's dependencies
  26. mockParams mockParams
  27. }{
  28. {
  29. name: "successfully add buddies",
  30. me: newTestSession("me"),
  31. givenCmd: []byte("toc_add_buddy friend1 friend2 friend3"),
  32. mockParams: mockParams{
  33. buddyParams: buddyParams{
  34. addBuddiesParams: addBuddiesParams{
  35. {
  36. me: state.NewIdentScreenName("me"),
  37. inBody: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  38. Buddies: []struct {
  39. ScreenName string `oscar:"len_prefix=uint8"`
  40. }{
  41. {ScreenName: "friend1"},
  42. {ScreenName: "friend2"},
  43. {ScreenName: "friend3"},
  44. },
  45. },
  46. },
  47. },
  48. },
  49. },
  50. },
  51. {
  52. name: "add buddies, receive error from buddy service",
  53. me: newTestSession("me"),
  54. givenCmd: []byte("toc_add_buddy friend1"),
  55. mockParams: mockParams{
  56. buddyParams: buddyParams{
  57. addBuddiesParams: addBuddiesParams{
  58. {
  59. me: state.NewIdentScreenName("me"),
  60. inBody: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  61. Buddies: []struct {
  62. ScreenName string `oscar:"len_prefix=uint8"`
  63. }{
  64. {ScreenName: "friend1"},
  65. },
  66. },
  67. err: io.EOF,
  68. },
  69. },
  70. },
  71. },
  72. wantMsg: cmdInternalSvcErr,
  73. },
  74. {
  75. name: "bad command",
  76. givenCmd: []byte(`toc_add_buddy_bad`),
  77. wantMsg: cmdInternalSvcErr,
  78. },
  79. }
  80. for _, tc := range cases {
  81. t.Run(tc.name, func(t *testing.T) {
  82. ctx := context.Background()
  83. buddySvc := newMockBuddyService(t)
  84. for _, params := range tc.mockParams.addBuddiesParams {
  85. buddySvc.EXPECT().
  86. AddBuddies(ctx, matchSession(params.me), params.inBody).
  87. Return(params.err)
  88. }
  89. svc := OSCARProxy{
  90. Logger: slog.Default(),
  91. BuddyService: buddySvc,
  92. }
  93. msg := svc.AddBuddy(ctx, tc.me, tc.givenCmd)
  94. assert.Equal(t, tc.wantMsg, msg)
  95. })
  96. }
  97. }
  98. func TestOSCARProxy_AddPermit(t *testing.T) {
  99. cases := []struct {
  100. // name is the unit test name
  101. name string
  102. // me is the TOC user session
  103. me *state.Session
  104. // givenCmd is the TOC command
  105. givenCmd []byte
  106. // wantMsg is the expected TOC response
  107. wantMsg string
  108. // mockParams is the list of params sent to mocks that satisfy this
  109. // method's dependencies
  110. mockParams mockParams
  111. }{
  112. {
  113. name: "successfully permit buddies",
  114. me: newTestSession("me"),
  115. givenCmd: []byte("toc_add_permit friend1 friend2 friend3"),
  116. mockParams: mockParams{
  117. permitDenyParams: permitDenyParams{
  118. addPermListEntriesParams: addPermListEntriesParams{
  119. {
  120. me: state.NewIdentScreenName("me"),
  121. body: wire.SNAC_0x09_0x05_PermitDenyAddPermListEntries{
  122. Users: []struct {
  123. ScreenName string `oscar:"len_prefix=uint8"`
  124. }{
  125. {ScreenName: "friend1"},
  126. {ScreenName: "friend2"},
  127. {ScreenName: "friend3"},
  128. },
  129. },
  130. },
  131. },
  132. },
  133. },
  134. },
  135. {
  136. name: "permit buddies, receive error from buddy service",
  137. me: newTestSession("me"),
  138. givenCmd: []byte("toc_add_permit friend1"),
  139. mockParams: mockParams{
  140. permitDenyParams: permitDenyParams{
  141. addPermListEntriesParams: addPermListEntriesParams{
  142. {
  143. me: state.NewIdentScreenName("me"),
  144. body: wire.SNAC_0x09_0x05_PermitDenyAddPermListEntries{
  145. Users: []struct {
  146. ScreenName string `oscar:"len_prefix=uint8"`
  147. }{
  148. {ScreenName: "friend1"},
  149. },
  150. },
  151. err: io.EOF,
  152. },
  153. },
  154. },
  155. },
  156. wantMsg: cmdInternalSvcErr,
  157. },
  158. {
  159. name: "bad command",
  160. givenCmd: []byte(`toc_remove_buddy_bad`),
  161. wantMsg: cmdInternalSvcErr,
  162. },
  163. }
  164. for _, tc := range cases {
  165. t.Run(tc.name, func(t *testing.T) {
  166. ctx := context.Background()
  167. pdSvc := newMockPermitDenyService(t)
  168. for _, params := range tc.mockParams.addPermListEntriesParams {
  169. pdSvc.EXPECT().
  170. AddPermListEntries(ctx, matchSession(params.me), params.body).
  171. Return(params.err)
  172. }
  173. svc := OSCARProxy{
  174. Logger: slog.Default(),
  175. PermitDenyService: pdSvc,
  176. }
  177. msg := svc.AddPermit(ctx, tc.me, tc.givenCmd)
  178. assert.Equal(t, tc.wantMsg, msg)
  179. })
  180. }
  181. }
  182. func TestOSCARProxy_AddDeny(t *testing.T) {
  183. cases := []struct {
  184. // name is the unit test name
  185. name string
  186. // me is the TOC user session
  187. me *state.Session
  188. // givenCmd is the TOC command
  189. givenCmd []byte
  190. // wantMsg is the expected TOC response
  191. wantMsg string
  192. // mockParams is the list of params sent to mocks that satisfy this
  193. // method's dependencies
  194. mockParams mockParams
  195. }{
  196. {
  197. name: "successfully deny buddies",
  198. me: newTestSession("me"),
  199. givenCmd: []byte("toc_add_deny friend1 friend2 friend3"),
  200. mockParams: mockParams{
  201. permitDenyParams: permitDenyParams{
  202. addDenyListEntriesParams: addDenyListEntriesParams{
  203. {
  204. me: state.NewIdentScreenName("me"),
  205. body: wire.SNAC_0x09_0x07_PermitDenyAddDenyListEntries{
  206. Users: []struct {
  207. ScreenName string `oscar:"len_prefix=uint8"`
  208. }{
  209. {ScreenName: "friend1"},
  210. {ScreenName: "friend2"},
  211. {ScreenName: "friend3"},
  212. },
  213. },
  214. },
  215. },
  216. },
  217. },
  218. },
  219. {
  220. name: "deny buddies, receive error from buddy service",
  221. me: newTestSession("me"),
  222. givenCmd: []byte("toc_add_deny friend1"),
  223. mockParams: mockParams{
  224. permitDenyParams: permitDenyParams{
  225. addDenyListEntriesParams: addDenyListEntriesParams{
  226. {
  227. me: state.NewIdentScreenName("me"),
  228. body: wire.SNAC_0x09_0x07_PermitDenyAddDenyListEntries{
  229. Users: []struct {
  230. ScreenName string `oscar:"len_prefix=uint8"`
  231. }{
  232. {ScreenName: "friend1"},
  233. },
  234. },
  235. err: io.EOF,
  236. },
  237. },
  238. },
  239. },
  240. wantMsg: cmdInternalSvcErr,
  241. },
  242. {
  243. name: "bad command",
  244. givenCmd: []byte(`toc_add_deny_bad`),
  245. wantMsg: cmdInternalSvcErr,
  246. },
  247. }
  248. for _, tc := range cases {
  249. t.Run(tc.name, func(t *testing.T) {
  250. ctx := context.Background()
  251. pdSvc := newMockPermitDenyService(t)
  252. for _, params := range tc.mockParams.addDenyListEntriesParams {
  253. pdSvc.EXPECT().
  254. AddDenyListEntries(ctx, matchSession(params.me), params.body).
  255. Return(params.err)
  256. }
  257. svc := OSCARProxy{
  258. Logger: slog.Default(),
  259. PermitDenyService: pdSvc,
  260. }
  261. msg := svc.AddDeny(ctx, tc.me, tc.givenCmd)
  262. assert.Equal(t, tc.wantMsg, msg)
  263. })
  264. }
  265. }
  266. func TestOSCARProxy_ChatAccept(t *testing.T) {
  267. fnNewChatNavParams := func(err error) chatNavParams {
  268. ret := chatNavParams{
  269. requestRoomInfoParams: requestRoomInfoParams{
  270. {
  271. inBody: wire.SNAC_0x0D_0x04_ChatNavRequestRoomInfo{
  272. Cookie: "the-cookie",
  273. Exchange: 4,
  274. InstanceNumber: 0,
  275. },
  276. },
  277. },
  278. }
  279. if err != nil {
  280. ret.requestRoomInfoParams[0].err = err
  281. } else {
  282. ret.requestRoomInfoParams[0].msg = wire.SNACMessage{
  283. Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
  284. TLVRestBlock: wire.TLVRestBlock{
  285. TLVList: wire.TLVList{
  286. wire.NewTLVBE(wire.ChatNavTLVRoomInfo, wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
  287. Cookie: "the-cookie",
  288. TLVBlock: wire.TLVBlock{
  289. TLVList: wire.TLVList{
  290. wire.NewTLVBE(wire.ChatRoomTLVRoomName, "cool room"),
  291. },
  292. },
  293. }),
  294. },
  295. },
  296. },
  297. }
  298. }
  299. return ret
  300. }
  301. fnNewOServiceBOSParams := func(err error) oServiceParams {
  302. ret := oServiceParams{
  303. serviceRequestParams: serviceRequestParams{
  304. {
  305. me: state.NewIdentScreenName("me"),
  306. bodyIn: wire.SNAC_0x01_0x04_OServiceServiceRequest{
  307. FoodGroup: wire.Chat,
  308. TLVRestBlock: wire.TLVRestBlock{
  309. TLVList: wire.TLVList{
  310. wire.NewTLVBE(0x01, wire.SNAC_0x01_0x04_TLVRoomInfo{
  311. Cookie: "the-cookie",
  312. }),
  313. },
  314. },
  315. },
  316. },
  317. },
  318. }
  319. if err != nil {
  320. ret.serviceRequestParams[0].err = err
  321. } else {
  322. ret.serviceRequestParams[0].msg = wire.SNACMessage{
  323. Body: wire.SNAC_0x01_0x05_OServiceServiceResponse{
  324. TLVRestBlock: wire.TLVRestBlock{
  325. TLVList: wire.TLVList{
  326. wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, "chat-auth-cookie"),
  327. },
  328. },
  329. },
  330. }
  331. }
  332. return ret
  333. }
  334. fnNewAuthParams := func(err error) authParams {
  335. ret := authParams{
  336. registerChatSessionParams: registerChatSessionParams{
  337. {
  338. authCookie: []byte("chat-auth-cookie"),
  339. },
  340. },
  341. }
  342. if err != nil {
  343. ret.registerChatSessionParams[0].err = err
  344. } else {
  345. ret.registerChatSessionParams[0].sess = newTestSession("me-chat")
  346. }
  347. return ret
  348. }
  349. fnNewOServiceChatParams := func(err error) oServiceParams {
  350. ret := oServiceParams{
  351. clientOnlineParams: clientOnlineParams{
  352. {
  353. body: wire.SNAC_0x01_0x02_OServiceClientOnline{},
  354. me: state.NewIdentScreenName("me-chat"),
  355. },
  356. },
  357. }
  358. if err != nil {
  359. ret.clientOnlineParams[0].err = err
  360. }
  361. return ret
  362. }
  363. cases := []struct {
  364. // name is the unit test name
  365. name string
  366. // me is the TOC user session
  367. me *state.Session
  368. // givenCmd is the TOC command
  369. givenCmd []byte
  370. // givenChatRegistry is the chat registry passed to the function
  371. givenChatRegistry *ChatRegistry
  372. // wantMsg is the expected TOC response
  373. wantMsg string
  374. // wantChatID is the expected chat ID
  375. wantChatID int
  376. // mockParams is the list of params sent to mocks that satisfy this
  377. // method's dependencies
  378. mockParams mockParams
  379. }{
  380. {
  381. name: "successfully accept chat",
  382. me: newTestSession("me"),
  383. givenCmd: []byte(`toc_chat_accept 0`),
  384. givenChatRegistry: func() *ChatRegistry {
  385. reg := NewChatRegistry()
  386. reg.Add(wire.ICBMRoomInfo{
  387. Cookie: "the-cookie",
  388. Exchange: 4,
  389. Instance: 0,
  390. })
  391. return reg
  392. }(),
  393. mockParams: mockParams{
  394. chatNavParams: fnNewChatNavParams(nil),
  395. oServiceBOSParams: fnNewOServiceBOSParams(nil),
  396. authParams: fnNewAuthParams(nil),
  397. oServiceChatParams: fnNewOServiceChatParams(nil),
  398. },
  399. wantMsg: "CHAT_JOIN:0:cool room",
  400. },
  401. {
  402. name: "accept chat, receive error from chat oservice svc",
  403. me: newTestSession("me"),
  404. givenCmd: []byte(`toc_chat_accept 0`),
  405. givenChatRegistry: func() *ChatRegistry {
  406. reg := NewChatRegistry()
  407. reg.Add(wire.ICBMRoomInfo{
  408. Cookie: "the-cookie",
  409. Exchange: 4,
  410. Instance: 0,
  411. })
  412. return reg
  413. }(),
  414. mockParams: mockParams{
  415. chatNavParams: fnNewChatNavParams(nil),
  416. oServiceBOSParams: fnNewOServiceBOSParams(nil),
  417. authParams: fnNewAuthParams(nil),
  418. oServiceChatParams: fnNewOServiceChatParams(io.EOF),
  419. },
  420. wantMsg: cmdInternalSvcErr,
  421. },
  422. {
  423. name: "accept chat, receive error from auth svc",
  424. me: newTestSession("me"),
  425. givenCmd: []byte(`toc_chat_accept 0`),
  426. givenChatRegistry: func() *ChatRegistry {
  427. reg := NewChatRegistry()
  428. reg.Add(wire.ICBMRoomInfo{
  429. Cookie: "the-cookie",
  430. Exchange: 4,
  431. Instance: 0,
  432. })
  433. return reg
  434. }(),
  435. mockParams: mockParams{
  436. chatNavParams: fnNewChatNavParams(nil),
  437. oServiceBOSParams: fnNewOServiceBOSParams(nil),
  438. authParams: fnNewAuthParams(io.EOF),
  439. },
  440. wantMsg: cmdInternalSvcErr,
  441. },
  442. {
  443. name: "accept chat, receive error from BOS oservice svc",
  444. me: newTestSession("me"),
  445. givenCmd: []byte(`toc_chat_accept 0`),
  446. givenChatRegistry: func() *ChatRegistry {
  447. reg := NewChatRegistry()
  448. reg.Add(wire.ICBMRoomInfo{
  449. Cookie: "the-cookie",
  450. Exchange: 4,
  451. Instance: 0,
  452. })
  453. return reg
  454. }(),
  455. mockParams: mockParams{
  456. chatNavParams: fnNewChatNavParams(nil),
  457. oServiceBOSParams: fnNewOServiceBOSParams(io.EOF),
  458. },
  459. wantMsg: cmdInternalSvcErr,
  460. },
  461. {
  462. name: "accept chat, receive error from chat nav svc",
  463. me: newTestSession("me"),
  464. givenCmd: []byte(`toc_chat_accept 0`),
  465. givenChatRegistry: func() *ChatRegistry {
  466. reg := NewChatRegistry()
  467. reg.Add(wire.ICBMRoomInfo{
  468. Cookie: "the-cookie",
  469. Exchange: 4,
  470. Instance: 0,
  471. })
  472. return reg
  473. }(),
  474. mockParams: mockParams{
  475. chatNavParams: fnNewChatNavParams(io.EOF),
  476. },
  477. wantMsg: cmdInternalSvcErr,
  478. },
  479. {
  480. name: "chat doesn't exist",
  481. givenCmd: []byte(`toc_chat_accept 0`),
  482. givenChatRegistry: NewChatRegistry(),
  483. wantMsg: cmdInternalSvcErr,
  484. },
  485. {
  486. name: "bad command",
  487. givenCmd: []byte(`toc_chat_accept`),
  488. wantMsg: cmdInternalSvcErr,
  489. },
  490. {
  491. name: "bad exchange number",
  492. givenCmd: []byte(`toc_chat_accept four`),
  493. wantMsg: cmdInternalSvcErr,
  494. },
  495. }
  496. for _, tc := range cases {
  497. t.Run(tc.name, func(t *testing.T) {
  498. ctx := context.Background()
  499. chatNavSvc := newMockChatNavService(t)
  500. for _, params := range tc.mockParams.requestRoomInfoParams {
  501. chatNavSvc.EXPECT().
  502. RequestRoomInfo(ctx, wire.SNACFrame{}, params.inBody).
  503. Return(params.msg, params.err)
  504. }
  505. bosOServiceSvc := newMockOServiceService(t)
  506. for _, params := range tc.mockParams.oServiceBOSParams.serviceRequestParams {
  507. bosOServiceSvc.EXPECT().
  508. ServiceRequest(ctx, matchSession(params.me), wire.SNACFrame{}, params.bodyIn).
  509. Return(params.msg, params.err)
  510. }
  511. chatOServiceSvc := newMockOServiceService(t)
  512. for _, params := range tc.mockParams.oServiceChatParams.clientOnlineParams {
  513. chatOServiceSvc.EXPECT().
  514. ClientOnline(ctx, params.body, matchSession(params.me)).
  515. Return(params.err)
  516. }
  517. authSvc := newMockAuthService(t)
  518. for _, params := range tc.mockParams.authParams.registerChatSessionParams {
  519. authSvc.EXPECT().
  520. RegisterChatSession(ctx, params.authCookie).
  521. Return(params.sess, params.err)
  522. }
  523. svc := OSCARProxy{
  524. AuthService: authSvc,
  525. ChatNavService: chatNavSvc,
  526. Logger: slog.Default(),
  527. OServiceServiceBOS: bosOServiceSvc,
  528. OServiceServiceChat: chatOServiceSvc,
  529. }
  530. chatID, msg := svc.ChatAccept(ctx, tc.me, tc.givenChatRegistry, tc.givenCmd)
  531. assert.Equal(t, tc.wantMsg, msg)
  532. assert.Equal(t, tc.wantChatID, chatID)
  533. })
  534. }
  535. }
  536. func TestOSCARProxy_ChatInvite(t *testing.T) {
  537. cases := []struct {
  538. // name is the unit test name
  539. name string
  540. // me is the TOC user session
  541. me *state.Session
  542. // givenCmd is the TOC command
  543. givenCmd []byte
  544. // givenChatRegistry is the chat registry passed to the function
  545. givenChatRegistry *ChatRegistry
  546. // wantMsg is the expected TOC response
  547. wantMsg string
  548. // mockParams is the list of params sent to mocks that satisfy this
  549. // method's dependencies
  550. mockParams mockParams
  551. }{
  552. {
  553. name: "successfully send chat invitation",
  554. me: newTestSession("me"),
  555. givenCmd: []byte(`toc_chat_invite 0 "join my chat!" friend1`),
  556. givenChatRegistry: func() *ChatRegistry {
  557. reg := NewChatRegistry()
  558. reg.Add(wire.ICBMRoomInfo{
  559. Exchange: 4,
  560. Cookie: "the-cookie",
  561. Instance: 0,
  562. })
  563. return reg
  564. }(),
  565. mockParams: mockParams{
  566. icbmParams: icbmParams{
  567. channelMsgToHostParamsICBM: channelMsgToHostParamsICBM{
  568. {
  569. sender: state.NewIdentScreenName("me"),
  570. inBody: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  571. ChannelID: wire.ICBMChannelRendezvous,
  572. ScreenName: "friend1",
  573. TLVRestBlock: wire.TLVRestBlock{
  574. TLVList: wire.TLVList{
  575. wire.NewTLVBE(0x05, wire.ICBMCh2Fragment{
  576. Type: 0,
  577. Capability: capChat,
  578. TLVRestBlock: wire.TLVRestBlock{
  579. TLVList: wire.TLVList{
  580. wire.NewTLVBE(10, uint16(1)),
  581. wire.NewTLVBE(12, "join my chat!"),
  582. wire.NewTLVBE(13, "us-ascii"),
  583. wire.NewTLVBE(14, "en"),
  584. wire.NewTLVBE(10001, wire.ICBMRoomInfo{
  585. Exchange: 4,
  586. Cookie: "the-cookie",
  587. Instance: 0,
  588. }),
  589. },
  590. },
  591. }),
  592. },
  593. },
  594. },
  595. },
  596. },
  597. },
  598. },
  599. },
  600. {
  601. name: "send chat invitation, receive error from ICBM svc",
  602. me: newTestSession("me"),
  603. givenCmd: []byte(`toc_chat_invite 0 "join my chat!" friend1`),
  604. givenChatRegistry: func() *ChatRegistry {
  605. reg := NewChatRegistry()
  606. reg.Add(wire.ICBMRoomInfo{
  607. Exchange: 4,
  608. Cookie: "the-cookie",
  609. Instance: 0,
  610. })
  611. return reg
  612. }(),
  613. mockParams: mockParams{
  614. icbmParams: icbmParams{
  615. channelMsgToHostParamsICBM: channelMsgToHostParamsICBM{
  616. {
  617. sender: state.NewIdentScreenName("me"),
  618. inBody: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  619. ChannelID: wire.ICBMChannelRendezvous,
  620. ScreenName: "friend1",
  621. TLVRestBlock: wire.TLVRestBlock{
  622. TLVList: wire.TLVList{
  623. wire.NewTLVBE(0x05, wire.ICBMCh2Fragment{
  624. Type: 0,
  625. Capability: capChat,
  626. TLVRestBlock: wire.TLVRestBlock{
  627. TLVList: wire.TLVList{
  628. wire.NewTLVBE(10, uint16(1)),
  629. wire.NewTLVBE(12, "join my chat!"),
  630. wire.NewTLVBE(13, "us-ascii"),
  631. wire.NewTLVBE(14, "en"),
  632. wire.NewTLVBE(10001, wire.ICBMRoomInfo{
  633. Exchange: 4,
  634. Cookie: "the-cookie",
  635. Instance: 0,
  636. }),
  637. },
  638. },
  639. }),
  640. },
  641. },
  642. },
  643. err: io.EOF,
  644. },
  645. },
  646. },
  647. },
  648. wantMsg: cmdInternalSvcErr,
  649. },
  650. {
  651. name: "send chat invitation to non-existent room",
  652. me: newTestSession("me"),
  653. givenCmd: []byte(`toc_chat_invite 0 "join my chat!" friend1`),
  654. givenChatRegistry: NewChatRegistry(),
  655. wantMsg: cmdInternalSvcErr,
  656. },
  657. {
  658. name: "bad chat room ID",
  659. givenCmd: []byte(`toc_chat_invite zero "join my chat!" friend1`),
  660. wantMsg: cmdInternalSvcErr,
  661. },
  662. {
  663. name: "bad command",
  664. givenCmd: []byte(`toc_chat_invite`),
  665. wantMsg: cmdInternalSvcErr,
  666. },
  667. }
  668. for _, tc := range cases {
  669. t.Run(tc.name, func(t *testing.T) {
  670. ctx := context.Background()
  671. icbmSvc := newMockICBMService(t)
  672. for _, params := range tc.mockParams.channelMsgToHostParamsICBM {
  673. icbmSvc.EXPECT().
  674. ChannelMsgToHost(ctx, matchSession(params.sender), wire.SNACFrame{}, params.inBody).
  675. Return(nil, params.err)
  676. }
  677. svc := OSCARProxy{
  678. Logger: slog.Default(),
  679. ICBMService: icbmSvc,
  680. }
  681. msg := svc.ChatInvite(ctx, tc.me, tc.givenChatRegistry, tc.givenCmd)
  682. assert.Equal(t, tc.wantMsg, msg)
  683. })
  684. }
  685. }
  686. func TestOSCARProxy_ChatJoin(t *testing.T) {
  687. fnNewChatNavParams := func(err error) chatNavParams {
  688. ret := chatNavParams{
  689. createRoomParams: createRoomParams{
  690. {
  691. me: state.NewIdentScreenName("me"),
  692. inBody: wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
  693. Exchange: 4,
  694. Cookie: "create",
  695. TLVBlock: wire.TLVBlock{
  696. TLVList: wire.TLVList{
  697. wire.NewTLVBE(wire.ChatRoomTLVRoomName, "cool room"),
  698. },
  699. },
  700. },
  701. },
  702. },
  703. }
  704. if err != nil {
  705. ret.createRoomParams[0].err = err
  706. } else {
  707. ret.createRoomParams[0].msg = wire.SNACMessage{
  708. Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
  709. TLVRestBlock: wire.TLVRestBlock{
  710. TLVList: wire.TLVList{
  711. wire.NewTLVBE(wire.ChatNavTLVRoomInfo, wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
  712. Cookie: "the-cookie",
  713. }),
  714. },
  715. },
  716. },
  717. }
  718. }
  719. return ret
  720. }
  721. fnNewOServiceBOSParams := func(err error) oServiceParams {
  722. ret := oServiceParams{
  723. serviceRequestParams: serviceRequestParams{
  724. {
  725. me: state.NewIdentScreenName("me"),
  726. bodyIn: wire.SNAC_0x01_0x04_OServiceServiceRequest{
  727. FoodGroup: wire.Chat,
  728. TLVRestBlock: wire.TLVRestBlock{
  729. TLVList: wire.TLVList{
  730. wire.NewTLVBE(0x01, wire.SNAC_0x01_0x04_TLVRoomInfo{
  731. Cookie: "the-cookie",
  732. }),
  733. },
  734. },
  735. },
  736. },
  737. },
  738. }
  739. if err != nil {
  740. ret.serviceRequestParams[0].err = err
  741. } else {
  742. ret.serviceRequestParams[0].msg = wire.SNACMessage{
  743. Body: wire.SNAC_0x01_0x05_OServiceServiceResponse{
  744. TLVRestBlock: wire.TLVRestBlock{
  745. TLVList: wire.TLVList{
  746. wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, "chat-auth-cookie"),
  747. },
  748. },
  749. },
  750. }
  751. }
  752. return ret
  753. }
  754. fnNewAuthParams := func(err error) authParams {
  755. ret := authParams{
  756. registerChatSessionParams: registerChatSessionParams{
  757. {
  758. authCookie: []byte("chat-auth-cookie"),
  759. },
  760. },
  761. }
  762. if err != nil {
  763. ret.registerChatSessionParams[0].err = err
  764. } else {
  765. ret.registerChatSessionParams[0].sess = newTestSession("me-chat")
  766. }
  767. return ret
  768. }
  769. fnNewOServiceChatParams := func(err error) oServiceParams {
  770. ret := oServiceParams{
  771. clientOnlineParams: clientOnlineParams{
  772. {
  773. body: wire.SNAC_0x01_0x02_OServiceClientOnline{},
  774. me: state.NewIdentScreenName("me-chat"),
  775. },
  776. },
  777. }
  778. if err != nil {
  779. ret.clientOnlineParams[0].err = err
  780. }
  781. return ret
  782. }
  783. cases := []struct {
  784. // name is the unit test name
  785. name string
  786. // me is the TOC user session
  787. me *state.Session
  788. // givenCmd is the TOC command
  789. givenCmd []byte
  790. // givenChatRegistry is the chat registry passed to the function
  791. givenChatRegistry *ChatRegistry
  792. // wantMsg is the expected TOC response
  793. wantMsg string
  794. // wantChatID is the expected chat ID
  795. wantChatID int
  796. // mockParams is the list of params sent to mocks that satisfy this
  797. // method's dependencies
  798. mockParams mockParams
  799. }{
  800. {
  801. name: "successfully join chat",
  802. me: newTestSession("me"),
  803. givenCmd: []byte(`toc_chat_join 4 "cool room"`),
  804. givenChatRegistry: NewChatRegistry(),
  805. mockParams: mockParams{
  806. chatNavParams: fnNewChatNavParams(nil),
  807. oServiceBOSParams: fnNewOServiceBOSParams(nil),
  808. authParams: fnNewAuthParams(nil),
  809. oServiceChatParams: fnNewOServiceChatParams(nil),
  810. },
  811. wantMsg: "CHAT_JOIN:0:cool room",
  812. },
  813. {
  814. name: "join chat, receive error from chat oservice svc",
  815. me: newTestSession("me"),
  816. givenCmd: []byte(`toc_chat_join 4 "cool room"`),
  817. givenChatRegistry: NewChatRegistry(),
  818. mockParams: mockParams{
  819. chatNavParams: fnNewChatNavParams(nil),
  820. oServiceBOSParams: fnNewOServiceBOSParams(nil),
  821. authParams: fnNewAuthParams(nil),
  822. oServiceChatParams: fnNewOServiceChatParams(io.EOF),
  823. },
  824. wantMsg: cmdInternalSvcErr,
  825. },
  826. {
  827. name: "join chat, receive error from auth svc",
  828. me: newTestSession("me"),
  829. givenCmd: []byte(`toc_chat_join 4 "cool room"`),
  830. givenChatRegistry: NewChatRegistry(),
  831. mockParams: mockParams{
  832. chatNavParams: fnNewChatNavParams(nil),
  833. oServiceBOSParams: fnNewOServiceBOSParams(nil),
  834. authParams: fnNewAuthParams(io.EOF),
  835. },
  836. wantMsg: cmdInternalSvcErr,
  837. },
  838. {
  839. name: "join chat, receive error from BOS oservice svc",
  840. me: newTestSession("me"),
  841. givenCmd: []byte(`toc_chat_join 4 "cool room"`),
  842. givenChatRegistry: NewChatRegistry(),
  843. mockParams: mockParams{
  844. chatNavParams: fnNewChatNavParams(nil),
  845. oServiceBOSParams: fnNewOServiceBOSParams(io.EOF),
  846. },
  847. wantMsg: cmdInternalSvcErr,
  848. },
  849. {
  850. name: "join chat, receive error from chat nav svc",
  851. me: newTestSession("me"),
  852. givenCmd: []byte(`toc_chat_join 4 "cool room"`),
  853. givenChatRegistry: NewChatRegistry(),
  854. mockParams: mockParams{
  855. chatNavParams: fnNewChatNavParams(io.EOF),
  856. },
  857. wantMsg: cmdInternalSvcErr,
  858. },
  859. {
  860. name: "bad command",
  861. givenCmd: []byte(`toc_chat_join`),
  862. wantMsg: cmdInternalSvcErr,
  863. },
  864. {
  865. name: "bad exchange number",
  866. givenCmd: []byte(`toc_chat_join four "cool room"`),
  867. wantMsg: cmdInternalSvcErr,
  868. },
  869. }
  870. for _, tc := range cases {
  871. t.Run(tc.name, func(t *testing.T) {
  872. ctx := context.Background()
  873. chatNavSvc := newMockChatNavService(t)
  874. for _, params := range tc.mockParams.createRoomParams {
  875. chatNavSvc.EXPECT().
  876. CreateRoom(ctx, matchSession(params.me), wire.SNACFrame{}, params.inBody).
  877. Return(params.msg, params.err)
  878. }
  879. bosOServiceSvc := newMockOServiceService(t)
  880. for _, params := range tc.mockParams.oServiceBOSParams.serviceRequestParams {
  881. bosOServiceSvc.EXPECT().
  882. ServiceRequest(ctx, matchSession(params.me), wire.SNACFrame{}, params.bodyIn).
  883. Return(params.msg, params.err)
  884. }
  885. chatOServiceSvc := newMockOServiceService(t)
  886. for _, params := range tc.mockParams.oServiceChatParams.clientOnlineParams {
  887. chatOServiceSvc.EXPECT().
  888. ClientOnline(ctx, params.body, matchSession(params.me)).
  889. Return(params.err)
  890. }
  891. authSvc := newMockAuthService(t)
  892. for _, params := range tc.mockParams.authParams.registerChatSessionParams {
  893. authSvc.EXPECT().
  894. RegisterChatSession(ctx, params.authCookie).
  895. Return(params.sess, params.err)
  896. }
  897. svc := OSCARProxy{
  898. AuthService: authSvc,
  899. ChatNavService: chatNavSvc,
  900. Logger: slog.Default(),
  901. OServiceServiceBOS: bosOServiceSvc,
  902. OServiceServiceChat: chatOServiceSvc,
  903. }
  904. chatID, msg := svc.ChatJoin(ctx, tc.me, tc.givenChatRegistry, tc.givenCmd)
  905. assert.Equal(t, tc.wantMsg, msg)
  906. assert.Equal(t, tc.wantChatID, chatID)
  907. })
  908. }
  909. }
  910. func TestOSCARProxy_ChatLeave(t *testing.T) {
  911. cases := []struct {
  912. // name is the unit test name
  913. name string
  914. // me is the TOC user session
  915. me *state.Session
  916. // givenCmd is the TOC command
  917. givenCmd []byte
  918. // givenChatRegistry is the chat registry passed to the function
  919. givenChatRegistry *ChatRegistry
  920. // wantMsg is the expected TOC response
  921. wantMsg string
  922. // mockParams is the list of params sent to mocks that satisfy this
  923. // method's dependencies
  924. mockParams mockParams
  925. }{
  926. {
  927. name: "successfully leave chat",
  928. me: newTestSession("me"),
  929. givenCmd: []byte(`toc_chat_leave 0`),
  930. givenChatRegistry: func() *ChatRegistry {
  931. reg := NewChatRegistry()
  932. reg.RegisterSess(0, newTestSession("me"))
  933. return reg
  934. }(),
  935. mockParams: mockParams{
  936. authParams: authParams{
  937. signoutChatParams: signoutChatParams{
  938. {
  939. me: state.NewIdentScreenName("me"),
  940. },
  941. },
  942. },
  943. },
  944. wantMsg: "CHAT_LEFT:0",
  945. },
  946. {
  947. name: "chat room ID with invalid format",
  948. givenCmd: []byte(`toc_chat_leave zero`),
  949. wantMsg: cmdInternalSvcErr,
  950. },
  951. {
  952. name: "missing chat session",
  953. givenCmd: []byte(`toc_chat_leave 0`),
  954. givenChatRegistry: NewChatRegistry(),
  955. wantMsg: cmdInternalSvcErr,
  956. },
  957. {
  958. name: "bad command",
  959. givenCmd: []byte(`toc_chat_leave`),
  960. wantMsg: cmdInternalSvcErr,
  961. },
  962. }
  963. for _, tc := range cases {
  964. t.Run(tc.name, func(t *testing.T) {
  965. ctx := context.Background()
  966. authSvc := newMockAuthService(t)
  967. for _, params := range tc.mockParams.signoutChatParams {
  968. authSvc.EXPECT().SignoutChat(ctx, matchSession(params.me))
  969. }
  970. svc := OSCARProxy{
  971. Logger: slog.Default(),
  972. AuthService: authSvc,
  973. }
  974. msg := svc.ChatLeave(ctx, tc.givenChatRegistry, tc.givenCmd)
  975. assert.Equal(t, tc.wantMsg, msg)
  976. })
  977. }
  978. }
  979. func TestOSCARProxy_ChatSend(t *testing.T) {
  980. cases := []struct {
  981. // name is the unit test name
  982. name string
  983. // me is the TOC user session
  984. me *state.Session
  985. // givenCmd is the TOC command
  986. givenCmd []byte
  987. // givenChatRegistry is the chat registry passed to the function
  988. givenChatRegistry *ChatRegistry
  989. // wantMsg is the expected TOC response
  990. wantMsg string
  991. // mockParams is the list of params sent to mocks that satisfy this
  992. // method's dependencies
  993. mockParams mockParams
  994. }{
  995. {
  996. name: "successfully send chat message",
  997. me: newTestSession("me"),
  998. givenCmd: []byte(`toc_chat_send 0 "Hello world!"`),
  999. givenChatRegistry: func() *ChatRegistry {
  1000. reg := NewChatRegistry()
  1001. reg.RegisterSess(0, newTestSession("me"))
  1002. return reg
  1003. }(),
  1004. mockParams: mockParams{
  1005. chatParams: chatParams{
  1006. channelMsgToHostParamsChat: channelMsgToHostParamsChat{
  1007. {
  1008. sender: state.NewIdentScreenName("me"),
  1009. inBody: wire.SNAC_0x0E_0x05_ChatChannelMsgToHost{
  1010. Channel: wire.ICBMChannelMIME,
  1011. TLVRestBlock: wire.TLVRestBlock{
  1012. TLVList: wire.TLVList{
  1013. wire.NewTLVBE(wire.ChatTLVEnableReflectionFlag, uint8(1)),
  1014. wire.NewTLVBE(wire.ChatTLVSenderInformation, newTestSession("me").TLVUserInfo()),
  1015. wire.NewTLVBE(wire.ChatTLVPublicWhisperFlag, []byte{}),
  1016. wire.NewTLVBE(wire.ChatTLVMessageInfo, wire.TLVRestBlock{
  1017. TLVList: wire.TLVList{
  1018. wire.NewTLVBE(wire.ChatTLVMessageInfoText, "Hello world!"),
  1019. },
  1020. }),
  1021. },
  1022. },
  1023. },
  1024. result: &wire.SNACMessage{
  1025. Body: wire.SNAC_0x0E_0x06_ChatChannelMsgToClient{
  1026. Channel: wire.ICBMChannelMIME,
  1027. TLVRestBlock: wire.TLVRestBlock{
  1028. TLVList: wire.TLVList{
  1029. wire.NewTLVBE(wire.ChatTLVSenderInformation,
  1030. newTestSession("me").TLVUserInfo()),
  1031. wire.NewTLVBE(wire.ChatTLVPublicWhisperFlag, []byte{}),
  1032. wire.NewTLVBE(wire.ChatTLVMessageInfo, wire.TLVRestBlock{
  1033. TLVList: wire.TLVList{
  1034. wire.NewTLVBE(wire.ChatTLVMessageInfoText, "Hello world!"),
  1035. },
  1036. }),
  1037. },
  1038. },
  1039. },
  1040. },
  1041. },
  1042. },
  1043. },
  1044. },
  1045. wantMsg: "CHAT_IN:0:me:F:Hello world!",
  1046. },
  1047. {
  1048. name: "send chat message, receive error from chat svc",
  1049. me: newTestSession("me"),
  1050. givenCmd: []byte(`toc_chat_send 0 "Hello world!"`),
  1051. givenChatRegistry: func() *ChatRegistry {
  1052. reg := NewChatRegistry()
  1053. reg.RegisterSess(0, newTestSession("me"))
  1054. return reg
  1055. }(),
  1056. mockParams: mockParams{
  1057. chatParams: chatParams{
  1058. channelMsgToHostParamsChat: channelMsgToHostParamsChat{
  1059. {
  1060. sender: state.NewIdentScreenName("me"),
  1061. inBody: wire.SNAC_0x0E_0x05_ChatChannelMsgToHost{
  1062. Channel: wire.ICBMChannelMIME,
  1063. TLVRestBlock: wire.TLVRestBlock{
  1064. TLVList: wire.TLVList{
  1065. wire.NewTLVBE(wire.ChatTLVEnableReflectionFlag, uint8(1)),
  1066. wire.NewTLVBE(wire.ChatTLVSenderInformation, newTestSession("me").TLVUserInfo()),
  1067. wire.NewTLVBE(wire.ChatTLVPublicWhisperFlag, []byte{}),
  1068. wire.NewTLVBE(wire.ChatTLVMessageInfo, wire.TLVRestBlock{
  1069. TLVList: wire.TLVList{
  1070. wire.NewTLVBE(wire.ChatTLVMessageInfoText, "Hello world!"),
  1071. },
  1072. }),
  1073. },
  1074. },
  1075. },
  1076. err: io.EOF,
  1077. },
  1078. },
  1079. },
  1080. },
  1081. wantMsg: cmdInternalSvcErr,
  1082. },
  1083. {
  1084. name: "send chat message, receive nil response from chat svc",
  1085. me: newTestSession("me"),
  1086. givenCmd: []byte(`toc_chat_send 0 "Hello world!"`),
  1087. givenChatRegistry: func() *ChatRegistry {
  1088. reg := NewChatRegistry()
  1089. reg.RegisterSess(0, newTestSession("me"))
  1090. return reg
  1091. }(),
  1092. mockParams: mockParams{
  1093. chatParams: chatParams{
  1094. channelMsgToHostParamsChat: channelMsgToHostParamsChat{
  1095. {
  1096. sender: state.NewIdentScreenName("me"),
  1097. inBody: wire.SNAC_0x0E_0x05_ChatChannelMsgToHost{
  1098. Channel: wire.ICBMChannelMIME,
  1099. TLVRestBlock: wire.TLVRestBlock{
  1100. TLVList: wire.TLVList{
  1101. wire.NewTLVBE(wire.ChatTLVEnableReflectionFlag, uint8(1)),
  1102. wire.NewTLVBE(wire.ChatTLVSenderInformation, newTestSession("me").TLVUserInfo()),
  1103. wire.NewTLVBE(wire.ChatTLVPublicWhisperFlag, []byte{}),
  1104. wire.NewTLVBE(wire.ChatTLVMessageInfo, wire.TLVRestBlock{
  1105. TLVList: wire.TLVList{
  1106. wire.NewTLVBE(wire.ChatTLVMessageInfoText, "Hello world!"),
  1107. },
  1108. }),
  1109. },
  1110. },
  1111. },
  1112. result: nil,
  1113. },
  1114. },
  1115. },
  1116. },
  1117. wantMsg: cmdInternalSvcErr,
  1118. },
  1119. {
  1120. name: "send chat message, receive unexpected response from chat svc",
  1121. me: newTestSession("me"),
  1122. givenCmd: []byte(`toc_chat_send 0 "Hello world!"`),
  1123. givenChatRegistry: func() *ChatRegistry {
  1124. reg := NewChatRegistry()
  1125. reg.RegisterSess(0, newTestSession("me"))
  1126. return reg
  1127. }(),
  1128. mockParams: mockParams{
  1129. chatParams: chatParams{
  1130. channelMsgToHostParamsChat: channelMsgToHostParamsChat{
  1131. {
  1132. sender: state.NewIdentScreenName("me"),
  1133. inBody: wire.SNAC_0x0E_0x05_ChatChannelMsgToHost{
  1134. Channel: wire.ICBMChannelMIME,
  1135. TLVRestBlock: wire.TLVRestBlock{
  1136. TLVList: wire.TLVList{
  1137. wire.NewTLVBE(wire.ChatTLVEnableReflectionFlag, uint8(1)),
  1138. wire.NewTLVBE(wire.ChatTLVSenderInformation, newTestSession("me").TLVUserInfo()),
  1139. wire.NewTLVBE(wire.ChatTLVPublicWhisperFlag, []byte{}),
  1140. wire.NewTLVBE(wire.ChatTLVMessageInfo, wire.TLVRestBlock{
  1141. TLVList: wire.TLVList{
  1142. wire.NewTLVBE(wire.ChatTLVMessageInfoText, "Hello world!"),
  1143. },
  1144. }),
  1145. },
  1146. },
  1147. },
  1148. result: &wire.SNACMessage{
  1149. Body: wire.SNACError{},
  1150. },
  1151. },
  1152. },
  1153. },
  1154. },
  1155. wantMsg: cmdInternalSvcErr,
  1156. },
  1157. {
  1158. name: "chat room ID with invalid format",
  1159. givenCmd: []byte(`toc_chat_send zero "Hello world!"`),
  1160. wantMsg: cmdInternalSvcErr,
  1161. },
  1162. {
  1163. name: "missing chat session",
  1164. givenCmd: []byte(`toc_chat_send 0 "Hello world!"`),
  1165. givenChatRegistry: NewChatRegistry(),
  1166. wantMsg: cmdInternalSvcErr,
  1167. },
  1168. {
  1169. name: "bad command",
  1170. givenCmd: []byte(`toc_chat_send`),
  1171. wantMsg: cmdInternalSvcErr,
  1172. },
  1173. }
  1174. for _, tc := range cases {
  1175. t.Run(tc.name, func(t *testing.T) {
  1176. ctx := context.Background()
  1177. chatSvc := newMockChatService(t)
  1178. for _, params := range tc.mockParams.channelMsgToHostParamsChat {
  1179. chatSvc.EXPECT().
  1180. ChannelMsgToHost(ctx, matchSession(params.sender), wire.SNACFrame{}, params.inBody).
  1181. Return(params.result, params.err)
  1182. }
  1183. svc := OSCARProxy{
  1184. Logger: slog.Default(),
  1185. ChatService: chatSvc,
  1186. }
  1187. msg := svc.ChatSend(ctx, tc.givenChatRegistry, tc.givenCmd)
  1188. assert.Equal(t, tc.wantMsg, msg)
  1189. })
  1190. }
  1191. }
  1192. func TestOSCARProxy_Evil(t *testing.T) {
  1193. cases := []struct {
  1194. // name is the unit test name
  1195. name string
  1196. // me is the TOC user session
  1197. me *state.Session
  1198. // givenCmd is the TOC command
  1199. givenCmd []byte
  1200. // wantMsg is the expected TOC response
  1201. wantMsg string
  1202. // mockParams is the list of params sent to mocks that satisfy this
  1203. // method's dependencies
  1204. mockParams mockParams
  1205. }{
  1206. {
  1207. name: "successfully warn normally",
  1208. me: newTestSession("me"),
  1209. givenCmd: []byte(`toc_evil them norm`),
  1210. mockParams: mockParams{
  1211. icbmParams: icbmParams{
  1212. evilRequestParams: evilRequestParams{
  1213. {
  1214. me: state.NewIdentScreenName("me"),
  1215. inBody: wire.SNAC_0x04_0x08_ICBMEvilRequest{
  1216. SendAs: 0,
  1217. ScreenName: "them",
  1218. },
  1219. msg: wire.SNACMessage{
  1220. Body: wire.SNAC_0x04_0x09_ICBMEvilReply{},
  1221. },
  1222. },
  1223. },
  1224. },
  1225. },
  1226. },
  1227. {
  1228. name: "successfully warn anonymously",
  1229. me: newTestSession("me"),
  1230. givenCmd: []byte(`toc_evil them anon`),
  1231. mockParams: mockParams{
  1232. icbmParams: icbmParams{
  1233. evilRequestParams: evilRequestParams{
  1234. {
  1235. me: state.NewIdentScreenName("me"),
  1236. inBody: wire.SNAC_0x04_0x08_ICBMEvilRequest{
  1237. SendAs: 1,
  1238. ScreenName: "them",
  1239. },
  1240. msg: wire.SNACMessage{
  1241. Body: wire.SNAC_0x04_0x09_ICBMEvilReply{},
  1242. },
  1243. },
  1244. },
  1245. },
  1246. },
  1247. },
  1248. {
  1249. name: "warn, receive error from ICBM service",
  1250. me: newTestSession("me"),
  1251. givenCmd: []byte(`toc_evil them anon`),
  1252. mockParams: mockParams{
  1253. icbmParams: icbmParams{
  1254. evilRequestParams: evilRequestParams{
  1255. {
  1256. me: state.NewIdentScreenName("me"),
  1257. inBody: wire.SNAC_0x04_0x08_ICBMEvilRequest{
  1258. SendAs: 1,
  1259. ScreenName: "them",
  1260. },
  1261. err: io.EOF,
  1262. },
  1263. },
  1264. },
  1265. },
  1266. wantMsg: cmdInternalSvcErr,
  1267. },
  1268. {
  1269. name: "warn, receive snac err",
  1270. me: newTestSession("me"),
  1271. givenCmd: []byte(`toc_evil them anon`),
  1272. mockParams: mockParams{
  1273. icbmParams: icbmParams{
  1274. evilRequestParams: evilRequestParams{
  1275. {
  1276. me: state.NewIdentScreenName("me"),
  1277. inBody: wire.SNAC_0x04_0x08_ICBMEvilRequest{
  1278. SendAs: 1,
  1279. ScreenName: "them",
  1280. },
  1281. msg: wire.SNACMessage{
  1282. Body: wire.SNACError{},
  1283. },
  1284. },
  1285. },
  1286. },
  1287. },
  1288. },
  1289. {
  1290. name: "warn, ICBM svc returns unexpected snac type",
  1291. me: newTestSession("me"),
  1292. givenCmd: []byte(`toc_evil them anon`),
  1293. mockParams: mockParams{
  1294. icbmParams: icbmParams{
  1295. evilRequestParams: evilRequestParams{
  1296. {
  1297. me: state.NewIdentScreenName("me"),
  1298. inBody: wire.SNAC_0x04_0x08_ICBMEvilRequest{
  1299. SendAs: 1,
  1300. ScreenName: "them",
  1301. },
  1302. msg: wire.SNACMessage{
  1303. Body: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{},
  1304. },
  1305. },
  1306. },
  1307. },
  1308. },
  1309. wantMsg: cmdInternalSvcErr,
  1310. },
  1311. {
  1312. name: "warn with incorrect type",
  1313. me: newTestSession("me"),
  1314. givenCmd: []byte(`toc_evil them blah`),
  1315. wantMsg: cmdInternalSvcErr,
  1316. },
  1317. {
  1318. name: "bad command",
  1319. givenCmd: []byte(`toc_evil`),
  1320. wantMsg: cmdInternalSvcErr,
  1321. },
  1322. }
  1323. for _, tc := range cases {
  1324. t.Run(tc.name, func(t *testing.T) {
  1325. ctx := context.Background()
  1326. icbmSvc := newMockICBMService(t)
  1327. for _, params := range tc.mockParams.evilRequestParams {
  1328. icbmSvc.EXPECT().
  1329. EvilRequest(ctx, matchSession(params.me), wire.SNACFrame{}, params.inBody).
  1330. Return(params.msg, params.err)
  1331. }
  1332. svc := OSCARProxy{
  1333. Logger: slog.Default(),
  1334. ICBMService: icbmSvc,
  1335. }
  1336. msg := svc.Evil(ctx, tc.me, tc.givenCmd)
  1337. assert.Equal(t, tc.wantMsg, msg)
  1338. })
  1339. }
  1340. }
  1341. func TestOSCARProxy_GetDirSearchURL(t *testing.T) {
  1342. cases := []struct {
  1343. // name is the unit test name
  1344. name string
  1345. // me is the TOC user session
  1346. me *state.Session
  1347. // givenCmd is the TOC command
  1348. givenCmd []byte
  1349. // wantMsg is the expected TOC response
  1350. wantMsg string
  1351. // mockParams is the list of params sent to mocks that satisfy this
  1352. // method's dependencies
  1353. mockParams mockParams
  1354. }{
  1355. {
  1356. name: "successfully request user info",
  1357. me: newTestSession("me"),
  1358. givenCmd: []byte(`toc_dir_search "first name":"middle name":"last name":"maiden name":"city":"state":"country":"email"`),
  1359. mockParams: mockParams{
  1360. cookieBakerParams: cookieBakerParams{
  1361. issueParams: issueParams{
  1362. {
  1363. data: []byte("me"),
  1364. returnData: []byte("monster"),
  1365. },
  1366. },
  1367. },
  1368. },
  1369. wantMsg: "GOTO_URL:search results:dir_search?city=city&cookie=6d6f6e73746572&country=country&email=email&first_name=first+name&last_name=last+name&maiden_name=maiden+name&middle_name=middle+name&state=state",
  1370. },
  1371. {
  1372. name: "successfully request user info by keywords",
  1373. me: newTestSession("me"),
  1374. givenCmd: []byte(`toc_dir_search ::::::::::"searchkw"`),
  1375. mockParams: mockParams{
  1376. cookieBakerParams: cookieBakerParams{
  1377. issueParams: issueParams{
  1378. {
  1379. data: []byte("me"),
  1380. returnData: []byte("monster"),
  1381. },
  1382. },
  1383. },
  1384. },
  1385. wantMsg: "GOTO_URL:search results:dir_search?cookie=6d6f6e73746572&keyword=searchkw",
  1386. },
  1387. {
  1388. name: "request user info with too many params",
  1389. me: newTestSession("me"),
  1390. givenCmd: []byte(`toc_dir_search ::::::::::::::::::::"searchkw"`),
  1391. wantMsg: cmdInternalSvcErr,
  1392. },
  1393. {
  1394. name: "request user info, get cookie issue error",
  1395. me: newTestSession("me"),
  1396. givenCmd: []byte(`toc_dir_search them`),
  1397. mockParams: mockParams{
  1398. cookieBakerParams: cookieBakerParams{
  1399. issueParams: issueParams{
  1400. {
  1401. data: []byte("me"),
  1402. returnErr: io.EOF,
  1403. },
  1404. },
  1405. },
  1406. },
  1407. wantMsg: cmdInternalSvcErr,
  1408. },
  1409. {
  1410. name: "bad command",
  1411. givenCmd: []byte(`toc_dir_search`),
  1412. wantMsg: cmdInternalSvcErr,
  1413. },
  1414. }
  1415. for _, tc := range cases {
  1416. t.Run(tc.name, func(t *testing.T) {
  1417. ctx := context.Background()
  1418. cookieBaker := newMockCookieBaker(t)
  1419. for _, params := range tc.mockParams.issueParams {
  1420. cookieBaker.EXPECT().
  1421. Issue(params.data).
  1422. Return(params.returnData, params.returnErr)
  1423. }
  1424. svc := OSCARProxy{
  1425. Logger: slog.Default(),
  1426. CookieBaker: cookieBaker,
  1427. }
  1428. msg := svc.GetDirSearchURL(ctx, tc.me, tc.givenCmd)
  1429. assert.Equal(t, tc.wantMsg, msg)
  1430. })
  1431. }
  1432. }
  1433. func TestOSCARProxy_GetDirURL(t *testing.T) {
  1434. cases := []struct {
  1435. // name is the unit test name
  1436. name string
  1437. // me is the TOC user session
  1438. me *state.Session
  1439. // givenCmd is the TOC command
  1440. givenCmd []byte
  1441. // wantMsg is the expected TOC response
  1442. wantMsg string
  1443. // mockParams is the list of params sent to mocks that satisfy this
  1444. // method's dependencies
  1445. mockParams mockParams
  1446. }{
  1447. {
  1448. name: "successfully request user dir info",
  1449. me: newTestSession("me"),
  1450. givenCmd: []byte(`toc_get_dir them`),
  1451. mockParams: mockParams{
  1452. cookieBakerParams: cookieBakerParams{
  1453. issueParams: issueParams{
  1454. {
  1455. data: []byte("me"),
  1456. returnData: []byte("monster"),
  1457. },
  1458. },
  1459. },
  1460. },
  1461. wantMsg: "GOTO_URL:directory info:dir_info?cookie=6d6f6e73746572&user=them",
  1462. },
  1463. {
  1464. name: "request user info, get cookie issue error",
  1465. me: newTestSession("me"),
  1466. givenCmd: []byte(`toc_get_dir them`),
  1467. mockParams: mockParams{
  1468. cookieBakerParams: cookieBakerParams{
  1469. issueParams: issueParams{
  1470. {
  1471. data: []byte("me"),
  1472. returnErr: io.EOF,
  1473. },
  1474. },
  1475. },
  1476. },
  1477. wantMsg: cmdInternalSvcErr,
  1478. },
  1479. {
  1480. name: "bad command",
  1481. givenCmd: []byte(`toc_get_dir`),
  1482. wantMsg: cmdInternalSvcErr,
  1483. },
  1484. }
  1485. for _, tc := range cases {
  1486. t.Run(tc.name, func(t *testing.T) {
  1487. ctx := context.Background()
  1488. cookieBaker := newMockCookieBaker(t)
  1489. for _, params := range tc.mockParams.issueParams {
  1490. cookieBaker.EXPECT().
  1491. Issue(params.data).
  1492. Return(params.returnData, params.returnErr)
  1493. }
  1494. svc := OSCARProxy{
  1495. Logger: slog.Default(),
  1496. CookieBaker: cookieBaker,
  1497. }
  1498. msg := svc.GetDirURL(ctx, tc.me, tc.givenCmd)
  1499. assert.Equal(t, tc.wantMsg, msg)
  1500. })
  1501. }
  1502. }
  1503. func TestOSCARProxy_GetInfoURL(t *testing.T) {
  1504. cases := []struct {
  1505. // name is the unit test name
  1506. name string
  1507. // me is the TOC user session
  1508. me *state.Session
  1509. // givenCmd is the TOC command
  1510. givenCmd []byte
  1511. // wantMsg is the expected TOC response
  1512. wantMsg string
  1513. // mockParams is the list of params sent to mocks that satisfy this
  1514. // method's dependencies
  1515. mockParams mockParams
  1516. }{
  1517. {
  1518. name: "successfully request user info",
  1519. me: newTestSession("me"),
  1520. givenCmd: []byte(`toc_get_info them`),
  1521. mockParams: mockParams{
  1522. cookieBakerParams: cookieBakerParams{
  1523. issueParams: issueParams{
  1524. {
  1525. data: []byte("me"),
  1526. returnData: []byte("monster"),
  1527. },
  1528. },
  1529. },
  1530. },
  1531. wantMsg: "GOTO_URL:profile:info?cookie=6d6f6e73746572&from=me&user=them",
  1532. },
  1533. {
  1534. name: "request user info, get cookie issue error",
  1535. me: newTestSession("me"),
  1536. givenCmd: []byte(`toc_get_info them`),
  1537. mockParams: mockParams{
  1538. cookieBakerParams: cookieBakerParams{
  1539. issueParams: issueParams{
  1540. {
  1541. data: []byte("me"),
  1542. returnErr: io.EOF,
  1543. },
  1544. },
  1545. },
  1546. },
  1547. wantMsg: cmdInternalSvcErr,
  1548. },
  1549. {
  1550. name: "bad command",
  1551. givenCmd: []byte(`toc_get_info`),
  1552. wantMsg: cmdInternalSvcErr,
  1553. },
  1554. }
  1555. for _, tc := range cases {
  1556. t.Run(tc.name, func(t *testing.T) {
  1557. ctx := context.Background()
  1558. cookieBaker := newMockCookieBaker(t)
  1559. for _, params := range tc.mockParams.issueParams {
  1560. cookieBaker.EXPECT().
  1561. Issue(params.data).
  1562. Return(params.returnData, params.returnErr)
  1563. }
  1564. svc := OSCARProxy{
  1565. Logger: slog.Default(),
  1566. CookieBaker: cookieBaker,
  1567. }
  1568. msg := svc.GetInfoURL(ctx, tc.me, tc.givenCmd)
  1569. assert.Equal(t, tc.wantMsg, msg)
  1570. })
  1571. }
  1572. }
  1573. func TestOSCARProxy_InitDone(t *testing.T) {
  1574. cases := []struct {
  1575. // name is the unit test name
  1576. name string
  1577. // me is the TOC user session
  1578. me *state.Session
  1579. // givenCmd is the TOC command
  1580. givenCmd []byte
  1581. // wantMsg is the expected TOC response
  1582. wantMsg string
  1583. // mockParams is the list of params sent to mocks that satisfy this
  1584. // method's dependencies
  1585. mockParams mockParams
  1586. }{
  1587. {
  1588. name: "successfully initialize connection",
  1589. me: newTestSession("me"),
  1590. givenCmd: []byte(`toc_init_done`),
  1591. mockParams: mockParams{
  1592. oServiceBOSParams: oServiceParams{
  1593. clientOnlineParams: clientOnlineParams{
  1594. {
  1595. me: state.NewIdentScreenName("me"),
  1596. body: wire.SNAC_0x01_0x02_OServiceClientOnline{},
  1597. },
  1598. },
  1599. },
  1600. },
  1601. },
  1602. {
  1603. name: "initialize connection, receive err from BOS oservice svc",
  1604. me: newTestSession("me"),
  1605. givenCmd: []byte(`toc_init_done`),
  1606. mockParams: mockParams{
  1607. oServiceBOSParams: oServiceParams{
  1608. clientOnlineParams: clientOnlineParams{
  1609. {
  1610. me: state.NewIdentScreenName("me"),
  1611. body: wire.SNAC_0x01_0x02_OServiceClientOnline{},
  1612. err: io.EOF,
  1613. },
  1614. },
  1615. },
  1616. },
  1617. wantMsg: cmdInternalSvcErr,
  1618. },
  1619. {
  1620. name: "bad command",
  1621. givenCmd: []byte(`toc_init_done_diff`),
  1622. wantMsg: cmdInternalSvcErr,
  1623. },
  1624. }
  1625. for _, tc := range cases {
  1626. t.Run(tc.name, func(t *testing.T) {
  1627. ctx := context.Background()
  1628. oSvc := newMockOServiceService(t)
  1629. for _, params := range tc.mockParams.oServiceBOSParams.clientOnlineParams {
  1630. oSvc.EXPECT().
  1631. ClientOnline(ctx, params.body, matchSession(params.me)).
  1632. Return(params.err)
  1633. }
  1634. svc := OSCARProxy{
  1635. Logger: slog.Default(),
  1636. OServiceServiceBOS: oSvc,
  1637. }
  1638. msg := svc.InitDone(ctx, tc.me, tc.givenCmd)
  1639. assert.Equal(t, tc.wantMsg, msg)
  1640. })
  1641. }
  1642. }
  1643. func TestOSCARProxy_RemoveBuddy(t *testing.T) {
  1644. cases := []struct {
  1645. // name is the unit test name
  1646. name string
  1647. // me is the TOC user session
  1648. me *state.Session
  1649. // givenCmd is the TOC command
  1650. givenCmd []byte
  1651. // wantMsg is the expected TOC response
  1652. wantMsg string
  1653. // mockParams is the list of params sent to mocks that satisfy this
  1654. // method's dependencies
  1655. mockParams mockParams
  1656. }{
  1657. {
  1658. name: "successfully remove buddies",
  1659. me: newTestSession("me"),
  1660. givenCmd: []byte("toc_remove_buddy friend1 friend2 friend3"),
  1661. mockParams: mockParams{
  1662. buddyParams: buddyParams{
  1663. delBuddiesParams: delBuddiesParams{
  1664. {
  1665. me: state.NewIdentScreenName("me"),
  1666. inBody: wire.SNAC_0x03_0x05_BuddyDelBuddies{
  1667. Buddies: []struct {
  1668. ScreenName string `oscar:"len_prefix=uint8"`
  1669. }{
  1670. {ScreenName: "friend1"},
  1671. {ScreenName: "friend2"},
  1672. {ScreenName: "friend3"},
  1673. },
  1674. },
  1675. },
  1676. },
  1677. },
  1678. },
  1679. },
  1680. {
  1681. name: "remove buddies, receive error from buddy service",
  1682. me: newTestSession("me"),
  1683. givenCmd: []byte("toc_remove_buddy friend1"),
  1684. mockParams: mockParams{
  1685. buddyParams: buddyParams{
  1686. delBuddiesParams: delBuddiesParams{
  1687. {
  1688. me: state.NewIdentScreenName("me"),
  1689. inBody: wire.SNAC_0x03_0x05_BuddyDelBuddies{
  1690. Buddies: []struct {
  1691. ScreenName string `oscar:"len_prefix=uint8"`
  1692. }{
  1693. {ScreenName: "friend1"},
  1694. },
  1695. },
  1696. err: io.EOF,
  1697. },
  1698. },
  1699. },
  1700. },
  1701. wantMsg: cmdInternalSvcErr,
  1702. },
  1703. {
  1704. name: "bad command",
  1705. givenCmd: []byte(`toc_remove_buddy_bad`),
  1706. wantMsg: cmdInternalSvcErr,
  1707. },
  1708. }
  1709. for _, tc := range cases {
  1710. t.Run(tc.name, func(t *testing.T) {
  1711. ctx := context.Background()
  1712. buddySvc := newMockBuddyService(t)
  1713. for _, params := range tc.mockParams.delBuddiesParams {
  1714. buddySvc.EXPECT().
  1715. DelBuddies(ctx, matchSession(params.me), params.inBody).
  1716. Return(params.err)
  1717. }
  1718. svc := OSCARProxy{
  1719. Logger: slog.Default(),
  1720. BuddyService: buddySvc,
  1721. }
  1722. msg := svc.RemoveBuddy(ctx, tc.me, tc.givenCmd)
  1723. assert.Equal(t, tc.wantMsg, msg)
  1724. })
  1725. }
  1726. }
  1727. func TestOSCARProxy_SendIM(t *testing.T) {
  1728. cases := []struct {
  1729. // name is the unit test name
  1730. name string
  1731. // me is the TOC user session
  1732. me *state.Session
  1733. // givenCmd is the TOC command
  1734. givenCmd []byte
  1735. // wantMsg is the expected TOC response
  1736. wantMsg string
  1737. // mockParams is the list of params sent to mocks that satisfy this
  1738. // method's dependencies
  1739. mockParams mockParams
  1740. }{
  1741. {
  1742. name: "successfully send instant message",
  1743. me: newTestSession("me"),
  1744. givenCmd: []byte(`toc_send_im chattingChuck "hello world!"`),
  1745. mockParams: mockParams{
  1746. icbmParams: icbmParams{
  1747. channelMsgToHostParamsICBM: channelMsgToHostParamsICBM{
  1748. {
  1749. sender: state.NewIdentScreenName("me"),
  1750. inBody: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1751. ChannelID: wire.ICBMChannelIM,
  1752. ScreenName: "chattingChuck",
  1753. TLVRestBlock: wire.TLVRestBlock{
  1754. TLVList: wire.TLVList{
  1755. wire.NewTLVBE(wire.ICBMTLVAOLIMData, []wire.ICBMCh1Fragment{
  1756. {
  1757. ID: 5,
  1758. Version: 1,
  1759. Payload: []byte{1, 1, 2},
  1760. },
  1761. {
  1762. ID: 1,
  1763. Version: 1,
  1764. Payload: []byte{
  1765. 0x00, 0x00,
  1766. 0x00, 0x00,
  1767. 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!',
  1768. },
  1769. },
  1770. }),
  1771. },
  1772. },
  1773. },
  1774. },
  1775. },
  1776. },
  1777. },
  1778. },
  1779. {
  1780. name: "successfully auto-reply send instant message",
  1781. me: newTestSession("me"),
  1782. givenCmd: []byte(`toc_send_im chattingChuck "hello world!" auto`),
  1783. mockParams: mockParams{
  1784. icbmParams: icbmParams{
  1785. channelMsgToHostParamsICBM: channelMsgToHostParamsICBM{
  1786. {
  1787. sender: state.NewIdentScreenName("me"),
  1788. inBody: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1789. ChannelID: wire.ICBMChannelIM,
  1790. ScreenName: "chattingChuck",
  1791. TLVRestBlock: wire.TLVRestBlock{
  1792. TLVList: wire.TLVList{
  1793. wire.NewTLVBE(wire.ICBMTLVAOLIMData, []wire.ICBMCh1Fragment{
  1794. {
  1795. ID: 5,
  1796. Version: 1,
  1797. Payload: []byte{1, 1, 2},
  1798. },
  1799. {
  1800. ID: 1,
  1801. Version: 1,
  1802. Payload: []byte{
  1803. 0x00, 0x00,
  1804. 0x00, 0x00,
  1805. 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!',
  1806. },
  1807. },
  1808. }),
  1809. wire.NewTLVBE(wire.ICBMTLVAutoResponse, []byte{}),
  1810. },
  1811. },
  1812. },
  1813. },
  1814. },
  1815. },
  1816. },
  1817. },
  1818. {
  1819. name: "send instant message, receive error from ICBM service",
  1820. me: newTestSession("me"),
  1821. givenCmd: []byte(`toc_send_im chattingChuck "hello world!"`),
  1822. mockParams: mockParams{
  1823. icbmParams: icbmParams{
  1824. channelMsgToHostParamsICBM: channelMsgToHostParamsICBM{
  1825. {
  1826. sender: state.NewIdentScreenName("me"),
  1827. inBody: wire.SNAC_0x04_0x06_ICBMChannelMsgToHost{
  1828. ChannelID: wire.ICBMChannelIM,
  1829. ScreenName: "chattingChuck",
  1830. TLVRestBlock: wire.TLVRestBlock{
  1831. TLVList: wire.TLVList{
  1832. wire.NewTLVBE(wire.ICBMTLVAOLIMData, []wire.ICBMCh1Fragment{
  1833. {
  1834. ID: 5,
  1835. Version: 1,
  1836. Payload: []byte{1, 1, 2},
  1837. },
  1838. {
  1839. ID: 1,
  1840. Version: 1,
  1841. Payload: []byte{
  1842. 0x00, 0x00,
  1843. 0x00, 0x00,
  1844. 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!',
  1845. },
  1846. },
  1847. }),
  1848. },
  1849. },
  1850. },
  1851. err: io.EOF,
  1852. },
  1853. },
  1854. },
  1855. },
  1856. wantMsg: cmdInternalSvcErr,
  1857. },
  1858. {
  1859. name: "bad command",
  1860. givenCmd: []byte(`toc_send_im`),
  1861. wantMsg: cmdInternalSvcErr,
  1862. },
  1863. }
  1864. for _, tc := range cases {
  1865. t.Run(tc.name, func(t *testing.T) {
  1866. ctx := context.Background()
  1867. icbmSvc := newMockICBMService(t)
  1868. for _, params := range tc.mockParams.channelMsgToHostParamsICBM {
  1869. icbmSvc.EXPECT().
  1870. ChannelMsgToHost(ctx, matchSession(params.sender), params.inFrame, params.inBody).
  1871. Return(params.result, params.err)
  1872. }
  1873. svc := OSCARProxy{
  1874. Logger: slog.Default(),
  1875. ICBMService: icbmSvc,
  1876. }
  1877. msg := svc.SendIM(ctx, tc.me, tc.givenCmd)
  1878. assert.Equal(t, tc.wantMsg, msg)
  1879. })
  1880. }
  1881. }
  1882. func TestOSCARProxy_SetAway(t *testing.T) {
  1883. cases := []struct {
  1884. // name is the unit test name
  1885. name string
  1886. // me is the TOC user session
  1887. me *state.Session
  1888. // givenCmd is the TOC command
  1889. givenCmd []byte
  1890. // wantMsg is the expected TOC response
  1891. wantMsg string
  1892. // mockParams is the list of params sent to mocks that satisfy this
  1893. // method's dependencies
  1894. mockParams mockParams
  1895. }{
  1896. {
  1897. name: "successfully set away with message",
  1898. me: newTestSession("me"),
  1899. givenCmd: []byte(`toc_set_away "I'm away from my computer right now."`),
  1900. mockParams: mockParams{
  1901. locateParams: locateParams{
  1902. setInfoParams: setInfoParams{
  1903. {
  1904. me: state.NewIdentScreenName("me"),
  1905. inBody: wire.SNAC_0x02_0x04_LocateSetInfo{
  1906. TLVRestBlock: wire.TLVRestBlock{
  1907. TLVList: wire.TLVList{
  1908. wire.NewTLVBE(wire.LocateTLVTagsInfoUnavailableData, "I'm away from my computer right now."),
  1909. },
  1910. },
  1911. },
  1912. },
  1913. },
  1914. },
  1915. },
  1916. },
  1917. {
  1918. name: "successfully set away without message",
  1919. me: newTestSession("me"),
  1920. givenCmd: []byte(`toc_set_away`),
  1921. mockParams: mockParams{
  1922. locateParams: locateParams{
  1923. setInfoParams: setInfoParams{
  1924. {
  1925. me: state.NewIdentScreenName("me"),
  1926. inBody: wire.SNAC_0x02_0x04_LocateSetInfo{
  1927. TLVRestBlock: wire.TLVRestBlock{
  1928. TLVList: wire.TLVList{
  1929. wire.NewTLVBE(wire.LocateTLVTagsInfoUnavailableData, ""),
  1930. },
  1931. },
  1932. },
  1933. },
  1934. },
  1935. },
  1936. },
  1937. },
  1938. {
  1939. name: "set away message, receive error from locate service",
  1940. me: newTestSession("me"),
  1941. givenCmd: []byte(`toc_set_away "I'm away from my computer right now."`),
  1942. mockParams: mockParams{
  1943. locateParams: locateParams{
  1944. setInfoParams: setInfoParams{
  1945. {
  1946. me: state.NewIdentScreenName("me"),
  1947. inBody: wire.SNAC_0x02_0x04_LocateSetInfo{
  1948. TLVRestBlock: wire.TLVRestBlock{
  1949. TLVList: wire.TLVList{
  1950. wire.NewTLVBE(wire.LocateTLVTagsInfoUnavailableData, "I'm away from my computer right now."),
  1951. },
  1952. },
  1953. },
  1954. err: io.EOF,
  1955. },
  1956. },
  1957. },
  1958. },
  1959. wantMsg: cmdInternalSvcErr,
  1960. },
  1961. {
  1962. name: "bad command",
  1963. givenCmd: []byte(`toc_set_away_bad`),
  1964. wantMsg: cmdInternalSvcErr,
  1965. },
  1966. }
  1967. for _, tc := range cases {
  1968. t.Run(tc.name, func(t *testing.T) {
  1969. ctx := context.Background()
  1970. locateSvc := newMockLocateService(t)
  1971. for _, params := range tc.mockParams.setInfoParams {
  1972. locateSvc.EXPECT().
  1973. SetInfo(ctx, matchSession(params.me), params.inBody).
  1974. Return(params.err)
  1975. }
  1976. svc := OSCARProxy{
  1977. Logger: slog.Default(),
  1978. LocateService: locateSvc,
  1979. }
  1980. msg := svc.SetAway(ctx, tc.me, tc.givenCmd)
  1981. assert.Equal(t, tc.wantMsg, msg)
  1982. })
  1983. }
  1984. }
  1985. func TestOSCARProxy_SetCaps(t *testing.T) {
  1986. cases := []struct {
  1987. // name is the unit test name
  1988. name string
  1989. // me is the TOC user session
  1990. me *state.Session
  1991. // givenCmd is the TOC command
  1992. givenCmd []byte
  1993. // wantMsg is the expected TOC response
  1994. wantMsg string
  1995. // mockParams is the list of params sent to mocks that satisfy this
  1996. // method's dependencies
  1997. mockParams mockParams
  1998. }{
  1999. {
  2000. name: "successfully set capabilities",
  2001. me: newTestSession("me"),
  2002. givenCmd: []byte(`toc_set_caps 09460000-4C7F-11D1-8222-444553540000 09460001-4C7F-11D1-8222-444553540000`),
  2003. mockParams: mockParams{
  2004. locateParams: locateParams{
  2005. setInfoParams: setInfoParams{
  2006. {
  2007. me: state.NewIdentScreenName("me"),
  2008. inBody: wire.SNAC_0x02_0x04_LocateSetInfo{
  2009. TLVRestBlock: wire.TLVRestBlock{
  2010. TLVList: wire.TLVList{
  2011. wire.NewTLVBE(wire.LocateTLVTagsInfoCapabilities, []uuid.UUID{
  2012. uuid.MustParse("09460000-4C7F-11D1-8222-444553540000"),
  2013. uuid.MustParse("09460001-4C7F-11D1-8222-444553540000"),
  2014. capChat,
  2015. }),
  2016. },
  2017. },
  2018. },
  2019. },
  2020. },
  2021. },
  2022. },
  2023. },
  2024. {
  2025. name: "set capability, receive error from locate service",
  2026. me: newTestSession("me"),
  2027. givenCmd: []byte(`toc_set_caps 09460000-4C7F-11D1-8222-444553540000`),
  2028. mockParams: mockParams{
  2029. locateParams: locateParams{
  2030. setInfoParams: setInfoParams{
  2031. {
  2032. me: state.NewIdentScreenName("me"),
  2033. inBody: wire.SNAC_0x02_0x04_LocateSetInfo{
  2034. TLVRestBlock: wire.TLVRestBlock{
  2035. TLVList: wire.TLVList{
  2036. wire.NewTLVBE(wire.LocateTLVTagsInfoCapabilities, []uuid.UUID{
  2037. uuid.MustParse("09460000-4C7F-11D1-8222-444553540000"),
  2038. capChat,
  2039. }),
  2040. },
  2041. },
  2042. },
  2043. err: io.EOF,
  2044. },
  2045. },
  2046. },
  2047. },
  2048. wantMsg: cmdInternalSvcErr,
  2049. },
  2050. {
  2051. name: "set malformed capability UUID",
  2052. me: newTestSession("me"),
  2053. givenCmd: []byte(`toc_set_caps 09460000-`),
  2054. wantMsg: cmdInternalSvcErr,
  2055. },
  2056. {
  2057. name: "bad command",
  2058. givenCmd: []byte(`toc_set_caps_bad`),
  2059. wantMsg: cmdInternalSvcErr,
  2060. },
  2061. }
  2062. for _, tc := range cases {
  2063. t.Run(tc.name, func(t *testing.T) {
  2064. ctx := context.Background()
  2065. locateSvc := newMockLocateService(t)
  2066. for _, params := range tc.mockParams.setInfoParams {
  2067. locateSvc.EXPECT().
  2068. SetInfo(ctx, matchSession(params.me), params.inBody).
  2069. Return(params.err)
  2070. }
  2071. svc := OSCARProxy{
  2072. Logger: slog.Default(),
  2073. LocateService: locateSvc,
  2074. }
  2075. msg := svc.SetCaps(ctx, tc.me, tc.givenCmd)
  2076. assert.Equal(t, tc.wantMsg, msg)
  2077. })
  2078. }
  2079. }
  2080. func TestOSCARProxy_SetConfig(t *testing.T) {
  2081. cases := []struct {
  2082. // name is the unit test name
  2083. name string
  2084. // me is the TOC user session
  2085. me *state.Session
  2086. // givenCmd is the TOC command
  2087. givenCmd []byte
  2088. // wantMsg is the expected TOC response
  2089. wantMsg string
  2090. // mockParams is the list of params sent to mocks that satisfy this
  2091. // method's dependencies
  2092. mockParams mockParams
  2093. }{
  2094. {
  2095. name: "successfully set permit all config",
  2096. me: newTestSession("me"),
  2097. givenCmd: []byte("toc_set_config {m 1\ng Buddies\nb friend1\nb friend2\n}\n"),
  2098. mockParams: mockParams{
  2099. permitDenyParams: permitDenyParams{
  2100. // confusingly, setting "permit all" mode requires adding a
  2101. // deny list entry
  2102. addDenyListEntriesParams: addDenyListEntriesParams{
  2103. {
  2104. me: state.NewIdentScreenName("me"),
  2105. body: wire.SNAC_0x09_0x07_PermitDenyAddDenyListEntries{
  2106. Users: []struct {
  2107. ScreenName string `oscar:"len_prefix=uint8"`
  2108. }{
  2109. {ScreenName: "me"},
  2110. },
  2111. },
  2112. },
  2113. },
  2114. },
  2115. buddyParams: buddyParams{
  2116. addBuddiesParams: addBuddiesParams{
  2117. {
  2118. me: state.NewIdentScreenName("me"),
  2119. inBody: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  2120. Buddies: []struct {
  2121. ScreenName string `oscar:"len_prefix=uint8"`
  2122. }{
  2123. {ScreenName: "friend1"},
  2124. {ScreenName: "friend2"},
  2125. },
  2126. },
  2127. },
  2128. },
  2129. },
  2130. tocConfigParams: tocConfigParams{
  2131. setTOCConfigParams: setTOCConfigParams{
  2132. {
  2133. user: state.NewIdentScreenName("me"),
  2134. config: "m 1\ng Buddies\nb friend1\nb friend2",
  2135. },
  2136. },
  2137. },
  2138. },
  2139. },
  2140. {
  2141. name: "set permit all config, receive err from config store svc",
  2142. me: newTestSession("me"),
  2143. givenCmd: []byte("toc_set_config {m 1\ng Buddies\nb friend1\n}\n"),
  2144. mockParams: mockParams{
  2145. permitDenyParams: permitDenyParams{
  2146. // confusingly, setting "permit all" mode requires adding a
  2147. // deny list entry
  2148. addDenyListEntriesParams: addDenyListEntriesParams{
  2149. {
  2150. me: state.NewIdentScreenName("me"),
  2151. body: wire.SNAC_0x09_0x07_PermitDenyAddDenyListEntries{
  2152. Users: []struct {
  2153. ScreenName string `oscar:"len_prefix=uint8"`
  2154. }{
  2155. {ScreenName: "me"},
  2156. },
  2157. },
  2158. },
  2159. },
  2160. },
  2161. buddyParams: buddyParams{
  2162. addBuddiesParams: addBuddiesParams{
  2163. {
  2164. me: state.NewIdentScreenName("me"),
  2165. inBody: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  2166. Buddies: []struct {
  2167. ScreenName string `oscar:"len_prefix=uint8"`
  2168. }{
  2169. {ScreenName: "friend1"},
  2170. },
  2171. },
  2172. },
  2173. },
  2174. },
  2175. tocConfigParams: tocConfigParams{
  2176. setTOCConfigParams: setTOCConfigParams{
  2177. {
  2178. user: state.NewIdentScreenName("me"),
  2179. config: "m 1\ng Buddies\nb friend1",
  2180. err: io.EOF,
  2181. },
  2182. },
  2183. },
  2184. },
  2185. wantMsg: cmdInternalSvcErr,
  2186. },
  2187. {
  2188. name: "set permit all config, receive err from buddy svc",
  2189. me: newTestSession("me"),
  2190. givenCmd: []byte("toc_set_config {m 1\ng Buddies\nb friend1\n}\n"),
  2191. mockParams: mockParams{
  2192. permitDenyParams: permitDenyParams{
  2193. // confusingly, setting "permit all" mode requires adding a
  2194. // deny list entry
  2195. addDenyListEntriesParams: addDenyListEntriesParams{
  2196. {
  2197. me: state.NewIdentScreenName("me"),
  2198. body: wire.SNAC_0x09_0x07_PermitDenyAddDenyListEntries{
  2199. Users: []struct {
  2200. ScreenName string `oscar:"len_prefix=uint8"`
  2201. }{
  2202. {ScreenName: "me"},
  2203. },
  2204. },
  2205. },
  2206. },
  2207. },
  2208. buddyParams: buddyParams{
  2209. addBuddiesParams: addBuddiesParams{
  2210. {
  2211. me: state.NewIdentScreenName("me"),
  2212. inBody: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  2213. Buddies: []struct {
  2214. ScreenName string `oscar:"len_prefix=uint8"`
  2215. }{
  2216. {ScreenName: "friend1"},
  2217. },
  2218. },
  2219. err: io.EOF,
  2220. },
  2221. },
  2222. },
  2223. },
  2224. wantMsg: cmdInternalSvcErr,
  2225. },
  2226. {
  2227. name: "set permit all config, receive err from permit-deny svc",
  2228. me: newTestSession("me"),
  2229. givenCmd: []byte("toc_set_config {m 1\ng Buddies\nb friend1\nb friend2\n}\n"),
  2230. mockParams: mockParams{
  2231. permitDenyParams: permitDenyParams{
  2232. // confusingly, setting "permit all" mode requires adding a
  2233. // deny list entry
  2234. addDenyListEntriesParams: addDenyListEntriesParams{
  2235. {
  2236. me: state.NewIdentScreenName("me"),
  2237. body: wire.SNAC_0x09_0x07_PermitDenyAddDenyListEntries{
  2238. Users: []struct {
  2239. ScreenName string `oscar:"len_prefix=uint8"`
  2240. }{
  2241. {ScreenName: "me"},
  2242. },
  2243. },
  2244. err: io.EOF,
  2245. },
  2246. },
  2247. },
  2248. },
  2249. wantMsg: cmdInternalSvcErr,
  2250. },
  2251. {
  2252. name: "successfully set deny all config",
  2253. me: newTestSession("me"),
  2254. givenCmd: []byte("toc_set_config {m 2\ng Buddies\nb friend1\nb friend2\n}\n"),
  2255. mockParams: mockParams{
  2256. permitDenyParams: permitDenyParams{
  2257. // confusingly, setting "deny all" mode requires adding a
  2258. // permit list entry
  2259. addPermListEntriesParams: addPermListEntriesParams{
  2260. {
  2261. me: state.NewIdentScreenName("me"),
  2262. body: wire.SNAC_0x09_0x05_PermitDenyAddPermListEntries{
  2263. Users: []struct {
  2264. ScreenName string `oscar:"len_prefix=uint8"`
  2265. }{
  2266. {ScreenName: "me"},
  2267. },
  2268. },
  2269. },
  2270. },
  2271. },
  2272. buddyParams: buddyParams{
  2273. addBuddiesParams: addBuddiesParams{
  2274. {
  2275. me: state.NewIdentScreenName("me"),
  2276. inBody: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  2277. Buddies: []struct {
  2278. ScreenName string `oscar:"len_prefix=uint8"`
  2279. }{
  2280. {ScreenName: "friend1"},
  2281. {ScreenName: "friend2"},
  2282. },
  2283. },
  2284. },
  2285. },
  2286. },
  2287. tocConfigParams: tocConfigParams{
  2288. setTOCConfigParams: setTOCConfigParams{
  2289. {
  2290. user: state.NewIdentScreenName("me"),
  2291. config: "m 2\ng Buddies\nb friend1\nb friend2",
  2292. },
  2293. },
  2294. },
  2295. },
  2296. },
  2297. {
  2298. name: "set deny all config, receive err from permit-deny svc",
  2299. me: newTestSession("me"),
  2300. givenCmd: []byte("toc_set_config {m 2\ng Buddies\nb friend1\nb friend2\n}\n"),
  2301. mockParams: mockParams{
  2302. permitDenyParams: permitDenyParams{
  2303. // confusingly, setting "deny all" mode requires adding a
  2304. // permit list entry
  2305. addPermListEntriesParams: addPermListEntriesParams{
  2306. {
  2307. me: state.NewIdentScreenName("me"),
  2308. body: wire.SNAC_0x09_0x05_PermitDenyAddPermListEntries{
  2309. Users: []struct {
  2310. ScreenName string `oscar:"len_prefix=uint8"`
  2311. }{
  2312. {ScreenName: "me"},
  2313. },
  2314. },
  2315. err: io.EOF,
  2316. },
  2317. },
  2318. },
  2319. },
  2320. wantMsg: cmdInternalSvcErr,
  2321. },
  2322. {
  2323. name: "successfully set permit some config",
  2324. me: newTestSession("me"),
  2325. givenCmd: []byte("toc_set_config {m 3\np friend3\np friend4\n\ng Buddies\nb friend1\nb friend2\n}\n"),
  2326. mockParams: mockParams{
  2327. permitDenyParams: permitDenyParams{
  2328. addPermListEntriesParams: addPermListEntriesParams{
  2329. {
  2330. me: state.NewIdentScreenName("me"),
  2331. body: wire.SNAC_0x09_0x05_PermitDenyAddPermListEntries{
  2332. Users: []struct {
  2333. ScreenName string `oscar:"len_prefix=uint8"`
  2334. }{
  2335. {ScreenName: "friend3"},
  2336. {ScreenName: "friend4"},
  2337. },
  2338. },
  2339. },
  2340. },
  2341. },
  2342. buddyParams: buddyParams{
  2343. addBuddiesParams: addBuddiesParams{
  2344. {
  2345. me: state.NewIdentScreenName("me"),
  2346. inBody: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  2347. Buddies: []struct {
  2348. ScreenName string `oscar:"len_prefix=uint8"`
  2349. }{
  2350. {ScreenName: "friend1"},
  2351. {ScreenName: "friend2"},
  2352. },
  2353. },
  2354. },
  2355. },
  2356. },
  2357. tocConfigParams: tocConfigParams{
  2358. setTOCConfigParams: setTOCConfigParams{
  2359. {
  2360. user: state.NewIdentScreenName("me"),
  2361. config: "m 3\np friend3\np friend4\n\ng Buddies\nb friend1\nb friend2",
  2362. },
  2363. },
  2364. },
  2365. },
  2366. },
  2367. {
  2368. name: "set permit some config, receive err from permit-deny svc",
  2369. me: newTestSession("me"),
  2370. givenCmd: []byte("toc_set_config {m 3\np friend3\np friend4\n\ng Buddies\nb friend1\nb friend2\n}\n"),
  2371. mockParams: mockParams{
  2372. permitDenyParams: permitDenyParams{
  2373. addPermListEntriesParams: addPermListEntriesParams{
  2374. {
  2375. me: state.NewIdentScreenName("me"),
  2376. body: wire.SNAC_0x09_0x05_PermitDenyAddPermListEntries{
  2377. Users: []struct {
  2378. ScreenName string `oscar:"len_prefix=uint8"`
  2379. }{
  2380. {ScreenName: "friend3"},
  2381. {ScreenName: "friend4"},
  2382. },
  2383. },
  2384. err: io.EOF,
  2385. },
  2386. },
  2387. },
  2388. },
  2389. wantMsg: cmdInternalSvcErr,
  2390. },
  2391. {
  2392. name: "successfully set deny some config",
  2393. me: newTestSession("me"),
  2394. givenCmd: []byte("toc_set_config {m 4\nd friend3\nd friend4\n\ng Buddies\nb friend1\nb friend2\n}\n"),
  2395. mockParams: mockParams{
  2396. permitDenyParams: permitDenyParams{
  2397. addDenyListEntriesParams: addDenyListEntriesParams{
  2398. {
  2399. me: state.NewIdentScreenName("me"),
  2400. body: wire.SNAC_0x09_0x07_PermitDenyAddDenyListEntries{
  2401. Users: []struct {
  2402. ScreenName string `oscar:"len_prefix=uint8"`
  2403. }{
  2404. {ScreenName: "friend3"},
  2405. {ScreenName: "friend4"},
  2406. },
  2407. },
  2408. },
  2409. },
  2410. },
  2411. buddyParams: buddyParams{
  2412. addBuddiesParams: addBuddiesParams{
  2413. {
  2414. me: state.NewIdentScreenName("me"),
  2415. inBody: wire.SNAC_0x03_0x04_BuddyAddBuddies{
  2416. Buddies: []struct {
  2417. ScreenName string `oscar:"len_prefix=uint8"`
  2418. }{
  2419. {ScreenName: "friend1"},
  2420. {ScreenName: "friend2"},
  2421. },
  2422. },
  2423. },
  2424. },
  2425. },
  2426. tocConfigParams: tocConfigParams{
  2427. setTOCConfigParams: setTOCConfigParams{
  2428. {
  2429. user: state.NewIdentScreenName("me"),
  2430. config: "m 4\nd friend3\nd friend4\n\ng Buddies\nb friend1\nb friend2",
  2431. },
  2432. },
  2433. },
  2434. },
  2435. },
  2436. {
  2437. name: "set deny some config, receive err from permit-deny svc",
  2438. me: newTestSession("me"),
  2439. givenCmd: []byte("toc_set_config {m 4\nd friend3\nd friend4\n\ng Buddies\nb friend1\nb friend2\n}\n"),
  2440. mockParams: mockParams{
  2441. permitDenyParams: permitDenyParams{
  2442. addDenyListEntriesParams: addDenyListEntriesParams{
  2443. {
  2444. me: state.NewIdentScreenName("me"),
  2445. body: wire.SNAC_0x09_0x07_PermitDenyAddDenyListEntries{
  2446. Users: []struct {
  2447. ScreenName string `oscar:"len_prefix=uint8"`
  2448. }{
  2449. {ScreenName: "friend3"},
  2450. {ScreenName: "friend4"},
  2451. },
  2452. },
  2453. err: io.EOF,
  2454. },
  2455. },
  2456. },
  2457. },
  2458. wantMsg: cmdInternalSvcErr,
  2459. },
  2460. {
  2461. name: "set unknown PD mode",
  2462. me: newTestSession("me"),
  2463. givenCmd: []byte("toc_set_config {m 5\nd friend3\nd friend4\n\ng Buddies\nb friend1\nb friend2\n}\n"),
  2464. wantMsg: cmdInternalSvcErr,
  2465. },
  2466. {
  2467. name: "bad command",
  2468. givenCmd: []byte(`toc_chat_leave`),
  2469. wantMsg: cmdInternalSvcErr,
  2470. },
  2471. }
  2472. for _, tc := range cases {
  2473. t.Run(tc.name, func(t *testing.T) {
  2474. ctx := context.Background()
  2475. pdSvc := newMockPermitDenyService(t)
  2476. for _, params := range tc.mockParams.addDenyListEntriesParams {
  2477. pdSvc.EXPECT().
  2478. AddDenyListEntries(ctx, matchSession(params.me), params.body).
  2479. Return(params.err)
  2480. }
  2481. for _, params := range tc.mockParams.addPermListEntriesParams {
  2482. pdSvc.EXPECT().
  2483. AddPermListEntries(ctx, matchSession(params.me), params.body).
  2484. Return(params.err)
  2485. }
  2486. buddySvc := newMockBuddyService(t)
  2487. for _, params := range tc.mockParams.addBuddiesParams {
  2488. buddySvc.EXPECT().
  2489. AddBuddies(ctx, matchSession(params.me), params.inBody).
  2490. Return(params.err)
  2491. }
  2492. tocConfigSvc := newMockTOCConfigStore(t)
  2493. for _, params := range tc.mockParams.setTOCConfigParams {
  2494. tocConfigSvc.EXPECT().
  2495. SetTOCConfig(params.user, params.config).
  2496. Return(params.err)
  2497. }
  2498. svc := OSCARProxy{
  2499. BuddyService: buddySvc,
  2500. Logger: slog.Default(),
  2501. PermitDenyService: pdSvc,
  2502. TOCConfigStore: tocConfigSvc,
  2503. }
  2504. msg := svc.SetConfig(ctx, tc.me, tc.givenCmd)
  2505. assert.Equal(t, tc.wantMsg, msg)
  2506. })
  2507. }
  2508. }
  2509. func TestOSCARProxy_SetDir(t *testing.T) {
  2510. cases := []struct {
  2511. // name is the unit test name
  2512. name string
  2513. // me is the TOC user session
  2514. me *state.Session
  2515. // givenCmd is the TOC command
  2516. givenCmd []byte
  2517. // wantMsg is the expected TOC response
  2518. wantMsg string
  2519. // mockParams is the list of params sent to mocks that satisfy this
  2520. // method's dependencies
  2521. mockParams mockParams
  2522. }{
  2523. {
  2524. name: "successfully set directory info with quoted fields",
  2525. me: newTestSession("me"),
  2526. givenCmd: []byte(`toc_set_dir "first name":"middle name":"last name":"maiden name":"city":"state":"country":"email":"allow web searches"`),
  2527. mockParams: mockParams{
  2528. locateParams: locateParams{
  2529. setDirInfoParams: setDirInfoParams{
  2530. {
  2531. me: state.NewIdentScreenName("me"),
  2532. inBody: wire.SNAC_0x02_0x09_LocateSetDirInfo{
  2533. TLVRestBlock: wire.TLVRestBlock{
  2534. TLVList: wire.TLVList{
  2535. wire.NewTLVBE(wire.ODirTLVFirstName, "first name"),
  2536. wire.NewTLVBE(wire.ODirTLVMiddleName, "middle name"),
  2537. wire.NewTLVBE(wire.ODirTLVLastName, "last name"),
  2538. wire.NewTLVBE(wire.ODirTLVMaidenName, "maiden name"),
  2539. wire.NewTLVBE(wire.ODirTLVCountry, "country"),
  2540. wire.NewTLVBE(wire.ODirTLVState, "state"),
  2541. wire.NewTLVBE(wire.ODirTLVCity, "city"),
  2542. },
  2543. },
  2544. },
  2545. },
  2546. },
  2547. },
  2548. },
  2549. },
  2550. {
  2551. name: "successfully set directory info with some blank fields",
  2552. me: newTestSession("me"),
  2553. givenCmd: []byte(`toc_set_dir "first name"::"last name"::"city":"state":"country":"email":"allow web searches"`),
  2554. mockParams: mockParams{
  2555. locateParams: locateParams{
  2556. setDirInfoParams: setDirInfoParams{
  2557. {
  2558. me: state.NewIdentScreenName("me"),
  2559. inBody: wire.SNAC_0x02_0x09_LocateSetDirInfo{
  2560. TLVRestBlock: wire.TLVRestBlock{
  2561. TLVList: wire.TLVList{
  2562. wire.NewTLVBE(wire.ODirTLVFirstName, "first name"),
  2563. wire.NewTLVBE(wire.ODirTLVMiddleName, ""),
  2564. wire.NewTLVBE(wire.ODirTLVLastName, "last name"),
  2565. wire.NewTLVBE(wire.ODirTLVMaidenName, ""),
  2566. wire.NewTLVBE(wire.ODirTLVCountry, "country"),
  2567. wire.NewTLVBE(wire.ODirTLVState, "state"),
  2568. wire.NewTLVBE(wire.ODirTLVCity, "city"),
  2569. },
  2570. },
  2571. },
  2572. },
  2573. },
  2574. },
  2575. },
  2576. },
  2577. {
  2578. name: "successfully set directory info with last two fields absent",
  2579. me: newTestSession("me"),
  2580. givenCmd: []byte(`toc_set_dir "first name"::"last name"::"city":"state":"country"`),
  2581. mockParams: mockParams{
  2582. locateParams: locateParams{
  2583. setDirInfoParams: setDirInfoParams{
  2584. {
  2585. me: state.NewIdentScreenName("me"),
  2586. inBody: wire.SNAC_0x02_0x09_LocateSetDirInfo{
  2587. TLVRestBlock: wire.TLVRestBlock{
  2588. TLVList: wire.TLVList{
  2589. wire.NewTLVBE(wire.ODirTLVFirstName, "first name"),
  2590. wire.NewTLVBE(wire.ODirTLVMiddleName, ""),
  2591. wire.NewTLVBE(wire.ODirTLVLastName, "last name"),
  2592. wire.NewTLVBE(wire.ODirTLVMaidenName, ""),
  2593. wire.NewTLVBE(wire.ODirTLVCountry, "country"),
  2594. wire.NewTLVBE(wire.ODirTLVState, "state"),
  2595. wire.NewTLVBE(wire.ODirTLVCity, "city"),
  2596. },
  2597. },
  2598. },
  2599. },
  2600. },
  2601. },
  2602. },
  2603. },
  2604. {
  2605. name: "set directory info, receive error from locate svc",
  2606. me: newTestSession("me"),
  2607. givenCmd: []byte(`toc_set_dir "first name":"middle name":"last name":"maiden name":"city":"state":"country":"email":"allow web searches"`),
  2608. mockParams: mockParams{
  2609. locateParams: locateParams{
  2610. setDirInfoParams: setDirInfoParams{
  2611. {
  2612. me: state.NewIdentScreenName("me"),
  2613. inBody: wire.SNAC_0x02_0x09_LocateSetDirInfo{
  2614. TLVRestBlock: wire.TLVRestBlock{
  2615. TLVList: wire.TLVList{
  2616. wire.NewTLVBE(wire.ODirTLVFirstName, "first name"),
  2617. wire.NewTLVBE(wire.ODirTLVMiddleName, "middle name"),
  2618. wire.NewTLVBE(wire.ODirTLVLastName, "last name"),
  2619. wire.NewTLVBE(wire.ODirTLVMaidenName, "maiden name"),
  2620. wire.NewTLVBE(wire.ODirTLVCountry, "country"),
  2621. wire.NewTLVBE(wire.ODirTLVState, "state"),
  2622. wire.NewTLVBE(wire.ODirTLVCity, "city"),
  2623. },
  2624. },
  2625. },
  2626. err: io.EOF,
  2627. },
  2628. },
  2629. },
  2630. },
  2631. wantMsg: cmdInternalSvcErr,
  2632. },
  2633. {
  2634. name: "set directory with too many fields present",
  2635. me: newTestSession("me"),
  2636. givenCmd: []byte(`toc_set_dir "first name"::"last name"::"city":"state":"country":"email":"allow web searches":"extra":"extra"`),
  2637. wantMsg: cmdInternalSvcErr,
  2638. },
  2639. {
  2640. name: "bad command",
  2641. givenCmd: []byte(`toc_set_dir`),
  2642. wantMsg: cmdInternalSvcErr,
  2643. },
  2644. }
  2645. for _, tc := range cases {
  2646. t.Run(tc.name, func(t *testing.T) {
  2647. ctx := context.Background()
  2648. locateSvc := newMockLocateService(t)
  2649. for _, params := range tc.mockParams.setDirInfoParams {
  2650. locateSvc.EXPECT().
  2651. SetDirInfo(ctx, matchSession(params.me), wire.SNACFrame{}, params.inBody).
  2652. Return(params.msg, params.err)
  2653. }
  2654. svc := OSCARProxy{
  2655. Logger: slog.Default(),
  2656. LocateService: locateSvc,
  2657. }
  2658. msg := svc.SetDir(ctx, tc.me, tc.givenCmd)
  2659. assert.Equal(t, tc.wantMsg, msg)
  2660. })
  2661. }
  2662. }
  2663. func TestOSCARProxy_SetIdle(t *testing.T) {
  2664. cases := []struct {
  2665. // name is the unit test name
  2666. name string
  2667. // me is the TOC user session
  2668. me *state.Session
  2669. // givenCmd is the TOC command
  2670. givenCmd []byte
  2671. // wantMsg is the expected TOC response
  2672. wantMsg string
  2673. // mockParams is the list of params sent to mocks that satisfy this
  2674. // method's dependencies
  2675. mockParams mockParams
  2676. }{
  2677. {
  2678. name: "successfully set idle status",
  2679. me: newTestSession("me"),
  2680. givenCmd: []byte(`toc_set_idle 10`),
  2681. mockParams: mockParams{
  2682. oServiceBOSParams: oServiceParams{
  2683. idleNotificationParams: idleNotificationParams{
  2684. {
  2685. me: state.NewIdentScreenName("me"),
  2686. bodyIn: wire.SNAC_0x01_0x11_OServiceIdleNotification{
  2687. IdleTime: uint32(10),
  2688. },
  2689. },
  2690. },
  2691. },
  2692. },
  2693. },
  2694. {
  2695. name: "set idle status, receive err from BOS oservice svc",
  2696. me: newTestSession("me"),
  2697. givenCmd: []byte(`toc_set_idle 10`),
  2698. mockParams: mockParams{
  2699. oServiceBOSParams: oServiceParams{
  2700. idleNotificationParams: idleNotificationParams{
  2701. {
  2702. me: state.NewIdentScreenName("me"),
  2703. bodyIn: wire.SNAC_0x01_0x11_OServiceIdleNotification{
  2704. IdleTime: uint32(10),
  2705. },
  2706. err: io.EOF,
  2707. },
  2708. },
  2709. },
  2710. },
  2711. wantMsg: cmdInternalSvcErr,
  2712. },
  2713. {
  2714. name: "bad secs param",
  2715. givenCmd: []byte(`toc_set_idle zero`),
  2716. wantMsg: cmdInternalSvcErr,
  2717. },
  2718. {
  2719. name: "bad command",
  2720. givenCmd: []byte(`toc_set_idle`),
  2721. wantMsg: cmdInternalSvcErr,
  2722. },
  2723. }
  2724. for _, tc := range cases {
  2725. t.Run(tc.name, func(t *testing.T) {
  2726. ctx := context.Background()
  2727. oServiceSvc := newMockOServiceService(t)
  2728. for _, params := range tc.mockParams.oServiceBOSParams.idleNotificationParams {
  2729. oServiceSvc.EXPECT().
  2730. IdleNotification(ctx, matchSession(params.me), params.bodyIn).
  2731. Return(params.err)
  2732. }
  2733. svc := OSCARProxy{
  2734. Logger: slog.Default(),
  2735. OServiceServiceBOS: oServiceSvc,
  2736. }
  2737. msg := svc.SetIdle(ctx, tc.me, tc.givenCmd)
  2738. assert.Equal(t, tc.wantMsg, msg)
  2739. })
  2740. }
  2741. }
  2742. func TestOSCARProxy_SetInfo(t *testing.T) {
  2743. cases := []struct {
  2744. // name is the unit test name
  2745. name string
  2746. // me is the TOC user session
  2747. me *state.Session
  2748. // givenCmd is the TOC command
  2749. givenCmd []byte
  2750. // wantMsg is the expected TOC response
  2751. wantMsg string
  2752. // mockParams is the list of params sent to mocks that satisfy this
  2753. // method's dependencies
  2754. mockParams mockParams
  2755. }{
  2756. {
  2757. name: "successfully set profile",
  2758. me: newTestSession("me"),
  2759. givenCmd: []byte(`toc_set_info "my profile!"`),
  2760. mockParams: mockParams{
  2761. locateParams: locateParams{
  2762. setInfoParams: setInfoParams{
  2763. {
  2764. me: state.NewIdentScreenName("me"),
  2765. inBody: wire.SNAC_0x02_0x04_LocateSetInfo{
  2766. TLVRestBlock: wire.TLVRestBlock{
  2767. TLVList: wire.TLVList{
  2768. wire.NewTLVBE(wire.LocateTLVTagsInfoSigData, "my profile!"),
  2769. },
  2770. },
  2771. },
  2772. },
  2773. },
  2774. },
  2775. },
  2776. },
  2777. {
  2778. name: "set profile, receive error from locate svc",
  2779. me: newTestSession("me"),
  2780. givenCmd: []byte(`toc_set_info "my profile!"`),
  2781. mockParams: mockParams{
  2782. locateParams: locateParams{
  2783. setInfoParams: setInfoParams{
  2784. {
  2785. me: state.NewIdentScreenName("me"),
  2786. inBody: wire.SNAC_0x02_0x04_LocateSetInfo{
  2787. TLVRestBlock: wire.TLVRestBlock{
  2788. TLVList: wire.TLVList{
  2789. wire.NewTLVBE(wire.LocateTLVTagsInfoSigData, "my profile!"),
  2790. },
  2791. },
  2792. },
  2793. err: io.EOF,
  2794. },
  2795. },
  2796. },
  2797. },
  2798. wantMsg: cmdInternalSvcErr,
  2799. },
  2800. {
  2801. name: "bad command",
  2802. givenCmd: []byte(`toc_set_info`),
  2803. wantMsg: cmdInternalSvcErr,
  2804. },
  2805. }
  2806. for _, tc := range cases {
  2807. t.Run(tc.name, func(t *testing.T) {
  2808. ctx := context.Background()
  2809. locateSvc := newMockLocateService(t)
  2810. for _, params := range tc.mockParams.setInfoParams {
  2811. locateSvc.EXPECT().
  2812. SetInfo(ctx, matchSession(params.me), params.inBody).
  2813. Return(params.err)
  2814. }
  2815. svc := OSCARProxy{
  2816. Logger: slog.Default(),
  2817. LocateService: locateSvc,
  2818. }
  2819. msg := svc.SetInfo(ctx, tc.me, tc.givenCmd)
  2820. assert.Equal(t, tc.wantMsg, msg)
  2821. })
  2822. }
  2823. }
  2824. func TestOSCARProxy_Signon(t *testing.T) {
  2825. roastedPass := wire.RoastTOCPassword([]byte("thepass"))
  2826. cases := []struct {
  2827. // name is the unit test name
  2828. name string
  2829. // me is the TOC user session
  2830. me *state.Session
  2831. // givenCmd is the TOC command
  2832. givenCmd []byte
  2833. // wantMsg is the expected TOC response
  2834. wantMsg []string
  2835. // mockParams is the list of params sent to mocks that satisfy this
  2836. // method's dependencies
  2837. mockParams mockParams
  2838. }{
  2839. {
  2840. name: "successfully login",
  2841. me: newTestSession("me", func(session *state.Session) {
  2842. session.SetCaps([][16]byte{capChat})
  2843. }),
  2844. givenCmd: []byte(`toc_signon "" "" me "xx` + hex.EncodeToString(roastedPass) + `"`),
  2845. mockParams: mockParams{
  2846. authParams: authParams{
  2847. flapLoginParams: flapLoginParams{
  2848. {
  2849. frame: wire.FLAPSignonFrame{
  2850. TLVRestBlock: wire.TLVRestBlock{
  2851. TLVList: wire.TLVList{
  2852. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "me"),
  2853. wire.NewTLVBE(wire.LoginTLVTagsRoastedTOCPassword, roastedPass),
  2854. },
  2855. },
  2856. },
  2857. newUserFn: state.NewStubUser,
  2858. tlv: wire.TLVRestBlock{
  2859. TLVList: wire.TLVList{
  2860. wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, []byte("thecookie")),
  2861. },
  2862. },
  2863. },
  2864. },
  2865. registerBOSSessionParams: registerBOSSessionParams{
  2866. {
  2867. authCookie: []byte("thecookie"),
  2868. sess: newTestSession("me"),
  2869. },
  2870. },
  2871. },
  2872. buddyListRegistryParams: buddyListRegistryParams{
  2873. registerBuddyListParams: registerBuddyListParams{
  2874. {
  2875. user: state.NewIdentScreenName("me"),
  2876. },
  2877. },
  2878. },
  2879. tocConfigParams: tocConfigParams{
  2880. userParams: userParams{
  2881. {
  2882. screenName: state.NewIdentScreenName("me"),
  2883. returnedUser: &state.User{
  2884. TOCConfig: "my-toc-config",
  2885. },
  2886. },
  2887. },
  2888. },
  2889. },
  2890. wantMsg: []string{"SIGN_ON:TOC1.0", "CONFIG:my-toc-config"},
  2891. },
  2892. {
  2893. name: "login, receive error from auth svc FLAP login",
  2894. givenCmd: []byte(`toc_signon "" "" me "xx` + hex.EncodeToString(roastedPass) + `"`),
  2895. mockParams: mockParams{
  2896. authParams: authParams{
  2897. flapLoginParams: flapLoginParams{
  2898. {
  2899. frame: wire.FLAPSignonFrame{
  2900. TLVRestBlock: wire.TLVRestBlock{
  2901. TLVList: wire.TLVList{
  2902. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "me"),
  2903. wire.NewTLVBE(wire.LoginTLVTagsRoastedTOCPassword, roastedPass),
  2904. },
  2905. },
  2906. },
  2907. newUserFn: state.NewStubUser,
  2908. err: io.EOF,
  2909. },
  2910. },
  2911. },
  2912. },
  2913. wantMsg: []string{string(cmdInternalSvcErr)},
  2914. },
  2915. {
  2916. name: "login, receive error from auth svc registration",
  2917. givenCmd: []byte(`toc_signon "" "" me "xx` + hex.EncodeToString(roastedPass) + `"`),
  2918. mockParams: mockParams{
  2919. authParams: authParams{
  2920. flapLoginParams: flapLoginParams{
  2921. {
  2922. frame: wire.FLAPSignonFrame{
  2923. TLVRestBlock: wire.TLVRestBlock{
  2924. TLVList: wire.TLVList{
  2925. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "me"),
  2926. wire.NewTLVBE(wire.LoginTLVTagsRoastedTOCPassword, roastedPass),
  2927. },
  2928. },
  2929. },
  2930. newUserFn: state.NewStubUser,
  2931. tlv: wire.TLVRestBlock{
  2932. TLVList: wire.TLVList{
  2933. wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, []byte("thecookie")),
  2934. },
  2935. },
  2936. },
  2937. },
  2938. registerBOSSessionParams: registerBOSSessionParams{
  2939. {
  2940. authCookie: []byte("thecookie"),
  2941. err: io.EOF,
  2942. },
  2943. },
  2944. },
  2945. },
  2946. wantMsg: []string{string(cmdInternalSvcErr)},
  2947. },
  2948. {
  2949. name: "login, receive error from buddy list registry",
  2950. givenCmd: []byte(`toc_signon "" "" me "xx` + hex.EncodeToString(roastedPass) + `"`),
  2951. mockParams: mockParams{
  2952. authParams: authParams{
  2953. flapLoginParams: flapLoginParams{
  2954. {
  2955. frame: wire.FLAPSignonFrame{
  2956. TLVRestBlock: wire.TLVRestBlock{
  2957. TLVList: wire.TLVList{
  2958. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "me"),
  2959. wire.NewTLVBE(wire.LoginTLVTagsRoastedTOCPassword, roastedPass),
  2960. },
  2961. },
  2962. },
  2963. newUserFn: state.NewStubUser,
  2964. tlv: wire.TLVRestBlock{
  2965. TLVList: wire.TLVList{
  2966. wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, []byte("thecookie")),
  2967. },
  2968. },
  2969. },
  2970. },
  2971. registerBOSSessionParams: registerBOSSessionParams{
  2972. {
  2973. authCookie: []byte("thecookie"),
  2974. sess: newTestSession("me"),
  2975. },
  2976. },
  2977. },
  2978. buddyListRegistryParams: buddyListRegistryParams{
  2979. registerBuddyListParams: registerBuddyListParams{
  2980. {
  2981. user: state.NewIdentScreenName("me"),
  2982. err: io.EOF,
  2983. },
  2984. },
  2985. },
  2986. },
  2987. wantMsg: []string{string(cmdInternalSvcErr)},
  2988. },
  2989. {
  2990. name: "login, receive error from TOC config store",
  2991. givenCmd: []byte(`toc_signon "" "" me "xx` + hex.EncodeToString(roastedPass) + `"`),
  2992. mockParams: mockParams{
  2993. authParams: authParams{
  2994. flapLoginParams: flapLoginParams{
  2995. {
  2996. frame: wire.FLAPSignonFrame{
  2997. TLVRestBlock: wire.TLVRestBlock{
  2998. TLVList: wire.TLVList{
  2999. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "me"),
  3000. wire.NewTLVBE(wire.LoginTLVTagsRoastedTOCPassword, roastedPass),
  3001. },
  3002. },
  3003. },
  3004. newUserFn: state.NewStubUser,
  3005. tlv: wire.TLVRestBlock{
  3006. TLVList: wire.TLVList{
  3007. wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, []byte("thecookie")),
  3008. },
  3009. },
  3010. },
  3011. },
  3012. registerBOSSessionParams: registerBOSSessionParams{
  3013. {
  3014. authCookie: []byte("thecookie"),
  3015. sess: newTestSession("me"),
  3016. },
  3017. },
  3018. },
  3019. buddyListRegistryParams: buddyListRegistryParams{
  3020. registerBuddyListParams: registerBuddyListParams{
  3021. {
  3022. user: state.NewIdentScreenName("me"),
  3023. },
  3024. },
  3025. },
  3026. tocConfigParams: tocConfigParams{
  3027. userParams: userParams{
  3028. {
  3029. screenName: state.NewIdentScreenName("me"),
  3030. err: io.EOF,
  3031. },
  3032. },
  3033. },
  3034. },
  3035. wantMsg: []string{string(cmdInternalSvcErr)},
  3036. },
  3037. {
  3038. name: "login, user not found after login",
  3039. givenCmd: []byte(`toc_signon "" "" me "xx` + hex.EncodeToString(roastedPass) + `"`),
  3040. mockParams: mockParams{
  3041. authParams: authParams{
  3042. flapLoginParams: flapLoginParams{
  3043. {
  3044. frame: wire.FLAPSignonFrame{
  3045. TLVRestBlock: wire.TLVRestBlock{
  3046. TLVList: wire.TLVList{
  3047. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "me"),
  3048. wire.NewTLVBE(wire.LoginTLVTagsRoastedTOCPassword, roastedPass),
  3049. },
  3050. },
  3051. },
  3052. newUserFn: state.NewStubUser,
  3053. tlv: wire.TLVRestBlock{
  3054. TLVList: wire.TLVList{
  3055. wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, []byte("thecookie")),
  3056. },
  3057. },
  3058. },
  3059. },
  3060. registerBOSSessionParams: registerBOSSessionParams{
  3061. {
  3062. authCookie: []byte("thecookie"),
  3063. sess: newTestSession("me"),
  3064. },
  3065. },
  3066. },
  3067. buddyListRegistryParams: buddyListRegistryParams{
  3068. registerBuddyListParams: registerBuddyListParams{
  3069. {
  3070. user: state.NewIdentScreenName("me"),
  3071. },
  3072. },
  3073. },
  3074. tocConfigParams: tocConfigParams{
  3075. userParams: userParams{
  3076. {
  3077. screenName: state.NewIdentScreenName("me"),
  3078. returnedUser: nil,
  3079. },
  3080. },
  3081. },
  3082. },
  3083. wantMsg: []string{string(cmdInternalSvcErr)},
  3084. },
  3085. {
  3086. name: "login with bad credentials",
  3087. givenCmd: []byte(`toc_signon "" "" me "xx` + hex.EncodeToString(roastedPass) + `"`),
  3088. mockParams: mockParams{
  3089. authParams: authParams{
  3090. flapLoginParams: flapLoginParams{
  3091. {
  3092. frame: wire.FLAPSignonFrame{
  3093. TLVRestBlock: wire.TLVRestBlock{
  3094. TLVList: wire.TLVList{
  3095. wire.NewTLVBE(wire.LoginTLVTagsScreenName, "me"),
  3096. wire.NewTLVBE(wire.LoginTLVTagsRoastedTOCPassword, roastedPass),
  3097. },
  3098. },
  3099. },
  3100. newUserFn: state.NewStubUser,
  3101. tlv: wire.TLVRestBlock{
  3102. TLVList: wire.TLVList{
  3103. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidUsernameOrPassword),
  3104. },
  3105. },
  3106. },
  3107. },
  3108. },
  3109. },
  3110. wantMsg: []string{"ERROR:980"},
  3111. },
  3112. {
  3113. name: "bad command",
  3114. givenCmd: []byte(`toc_init_done_diff`),
  3115. wantMsg: []string{string(cmdInternalSvcErr)},
  3116. },
  3117. }
  3118. for _, tc := range cases {
  3119. t.Run(tc.name, func(t *testing.T) {
  3120. ctx := context.Background()
  3121. authSvc := newMockAuthService(t)
  3122. for _, params := range tc.mockParams.flapLoginParams {
  3123. authSvc.EXPECT().
  3124. FLAPLogin(params.frame, mock.Anything).
  3125. Return(params.tlv, params.err)
  3126. }
  3127. for _, params := range tc.mockParams.registerBOSSessionParams {
  3128. authSvc.EXPECT().
  3129. RegisterBOSSession(ctx, params.authCookie).
  3130. Return(params.sess, params.err)
  3131. }
  3132. buddyRegistry := newMockBuddyListRegistry(t)
  3133. for _, params := range tc.mockParams.registerBuddyListParams {
  3134. buddyRegistry.EXPECT().
  3135. RegisterBuddyList(params.user).
  3136. Return(params.err)
  3137. }
  3138. tocCfg := newMockTOCConfigStore(t)
  3139. for _, params := range tc.mockParams.userParams {
  3140. tocCfg.EXPECT().
  3141. User(params.screenName).
  3142. Return(params.returnedUser, params.err)
  3143. }
  3144. svc := OSCARProxy{
  3145. AuthService: authSvc,
  3146. BuddyListRegistry: buddyRegistry,
  3147. Logger: slog.Default(),
  3148. TOCConfigStore: tocCfg,
  3149. }
  3150. sess, msg := svc.Signon(ctx, tc.givenCmd)
  3151. assert.Equal(t, tc.wantMsg, msg)
  3152. if tc.me == nil {
  3153. assert.Nil(t, sess)
  3154. } else if assert.NotNil(t, sess) {
  3155. assert.Equal(t, tc.me.IdentScreenName(), sess.IdentScreenName())
  3156. assert.Equal(t, tc.me.Caps(), sess.Caps())
  3157. }
  3158. })
  3159. }
  3160. }
  3161. func TestOSCARProxy_Signout(t *testing.T) {
  3162. cases := []struct {
  3163. // name is the unit test name
  3164. name string
  3165. // me is the TOC user session
  3166. me *state.Session
  3167. // mockParams is the list of params sent to mocks that satisfy this
  3168. // method's dependencies
  3169. mockParams mockParams
  3170. }{
  3171. {
  3172. name: "successfully sign out",
  3173. me: newTestSession("me"),
  3174. mockParams: mockParams{
  3175. buddyParams: buddyParams{
  3176. broadcastBuddyDepartedParams: broadcastBuddyDepartedParams{
  3177. {
  3178. me: state.NewIdentScreenName("me"),
  3179. },
  3180. },
  3181. },
  3182. buddyListRegistryParams: buddyListRegistryParams{
  3183. unregisterBuddyListParams: unregisterBuddyListParams{
  3184. {
  3185. user: state.NewIdentScreenName("me"),
  3186. },
  3187. },
  3188. },
  3189. authParams: authParams{
  3190. signoutParams: signoutParams{
  3191. {
  3192. me: state.NewIdentScreenName("me"),
  3193. },
  3194. },
  3195. },
  3196. },
  3197. },
  3198. {
  3199. name: "sign out, receive error from buddy service",
  3200. me: newTestSession("me"),
  3201. mockParams: mockParams{
  3202. buddyParams: buddyParams{
  3203. broadcastBuddyDepartedParams: broadcastBuddyDepartedParams{
  3204. {
  3205. me: state.NewIdentScreenName("me"),
  3206. err: io.EOF,
  3207. },
  3208. },
  3209. },
  3210. buddyListRegistryParams: buddyListRegistryParams{
  3211. unregisterBuddyListParams: unregisterBuddyListParams{
  3212. {
  3213. user: state.NewIdentScreenName("me"),
  3214. },
  3215. },
  3216. },
  3217. authParams: authParams{
  3218. signoutParams: signoutParams{
  3219. {
  3220. me: state.NewIdentScreenName("me"),
  3221. },
  3222. },
  3223. },
  3224. },
  3225. },
  3226. {
  3227. name: "sign out, receive error from buddy list registry",
  3228. me: newTestSession("me"),
  3229. mockParams: mockParams{
  3230. buddyParams: buddyParams{
  3231. broadcastBuddyDepartedParams: broadcastBuddyDepartedParams{
  3232. {
  3233. me: state.NewIdentScreenName("me"),
  3234. },
  3235. },
  3236. },
  3237. buddyListRegistryParams: buddyListRegistryParams{
  3238. unregisterBuddyListParams: unregisterBuddyListParams{
  3239. {
  3240. user: state.NewIdentScreenName("me"),
  3241. err: io.EOF,
  3242. },
  3243. },
  3244. },
  3245. authParams: authParams{
  3246. signoutParams: signoutParams{
  3247. {
  3248. me: state.NewIdentScreenName("me"),
  3249. },
  3250. },
  3251. },
  3252. },
  3253. },
  3254. }
  3255. for _, tc := range cases {
  3256. t.Run(tc.name, func(t *testing.T) {
  3257. ctx := context.Background()
  3258. buddySvc := newMockBuddyService(t)
  3259. for _, params := range tc.mockParams.broadcastBuddyDepartedParams {
  3260. buddySvc.EXPECT().
  3261. BroadcastBuddyDeparted(ctx, matchSession(params.me)).
  3262. Return(params.err)
  3263. }
  3264. buddyListSvc := newMockBuddyListRegistry(t)
  3265. for _, params := range tc.mockParams.unregisterBuddyListParams {
  3266. buddyListSvc.EXPECT().
  3267. UnregisterBuddyList(params.user).
  3268. Return(params.err)
  3269. }
  3270. authSvc := newMockAuthService(t)
  3271. for _, params := range tc.mockParams.signoutParams {
  3272. authSvc.EXPECT().Signout(ctx, matchSession(params.me))
  3273. }
  3274. svc := OSCARProxy{
  3275. AuthService: authSvc,
  3276. BuddyListRegistry: buddyListSvc,
  3277. BuddyService: buddySvc,
  3278. Logger: slog.Default(),
  3279. }
  3280. svc.Signout(ctx, tc.me)
  3281. })
  3282. }
  3283. }
  3284. func Test_parseArgs(t *testing.T) {
  3285. type testCase struct {
  3286. name string
  3287. givenPayload string
  3288. givenCmd string
  3289. givenArgs []*string
  3290. wantVarArgs []string
  3291. wantArgs []string
  3292. wantErrMsg string
  3293. }
  3294. tests := []testCase{
  3295. {
  3296. name: "no positional args or varargs",
  3297. givenPayload: `toc_chat_invite`,
  3298. givenCmd: "toc_chat_invite",
  3299. givenArgs: nil,
  3300. wantVarArgs: []string{},
  3301. },
  3302. {
  3303. name: "positional args with varargs",
  3304. givenPayload: `toc_chat_invite 1234 "Join me!" user1 user2 user3`,
  3305. givenCmd: "toc_chat_invite",
  3306. givenArgs: []*string{new(string), new(string)},
  3307. wantVarArgs: []string{"user1", "user2", "user3"},
  3308. wantArgs: []string{"1234", "Join me!"},
  3309. },
  3310. {
  3311. name: "nil positional argument placeholders should get skipped",
  3312. givenPayload: `toc_chat_invite 1234 "Join me!" user1 user2 user3`,
  3313. givenCmd: "toc_chat_invite",
  3314. givenArgs: []*string{nil, nil}, // still 2 placeholders, both nil
  3315. wantVarArgs: []string{"user1", "user2", "user3"},
  3316. wantArgs: []string{"", ""},
  3317. },
  3318. {
  3319. name: "positional args with no varargs",
  3320. givenPayload: `toc_chat_invite 1234 "Join me!"`,
  3321. givenCmd: "toc_chat_invite",
  3322. givenArgs: []*string{new(string), new(string)}, // roomID + msg
  3323. wantVarArgs: []string{},
  3324. wantArgs: []string{"1234", "Join me!"},
  3325. },
  3326. {
  3327. name: "varargs only",
  3328. givenPayload: `toc_chat_invite user1 user2 user3`,
  3329. givenCmd: "toc_chat_invite",
  3330. givenArgs: nil,
  3331. wantVarArgs: []string{"user1", "user2", "user3"},
  3332. },
  3333. {
  3334. name: "command mismatch",
  3335. givenPayload: `toc_chat_invite user1 user2 user3`,
  3336. givenCmd: "toc_chat_accept",
  3337. givenArgs: nil,
  3338. wantVarArgs: []string{},
  3339. wantErrMsg: "mismatch",
  3340. },
  3341. {
  3342. name: "too many positional arg placeholders",
  3343. givenPayload: `toc_chat_invite`,
  3344. givenCmd: "toc_chat_invite",
  3345. givenArgs: []*string{new(string), new(string)},
  3346. wantVarArgs: []string{},
  3347. wantErrMsg: "command contains fewer arguments than expected",
  3348. },
  3349. {
  3350. name: "CSV parser error",
  3351. givenPayload: ``,
  3352. givenCmd: "toc_chat_invite",
  3353. givenArgs: []*string{nil},
  3354. wantVarArgs: []string{},
  3355. wantErrMsg: "CSV reader error",
  3356. },
  3357. }
  3358. for _, tt := range tests {
  3359. t.Run(tt.name, func(t *testing.T) {
  3360. varArgs, err := parseArgs([]byte(tt.givenPayload), tt.givenCmd, tt.givenArgs...)
  3361. if tt.wantErrMsg != "" {
  3362. assert.ErrorContains(t, err, tt.wantErrMsg)
  3363. return
  3364. }
  3365. assert.NoError(t, err)
  3366. // verify the placeholder pointers got populated
  3367. for i, want := range tt.wantArgs {
  3368. if want == "" {
  3369. assert.Nil(t, tt.givenArgs[i])
  3370. } else {
  3371. got := *tt.givenArgs[i]
  3372. assert.Equal(t, want, got)
  3373. }
  3374. }
  3375. // verify we have the same varargs
  3376. assert.Equal(t, tt.wantVarArgs, varArgs)
  3377. assert.Equal(t, len(tt.wantArgs), len(tt.givenArgs))
  3378. })
  3379. }
  3380. }