cmd_server_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. package toc
  2. import (
  3. "context"
  4. "log/slog"
  5. "sync"
  6. "testing"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/mk6i/retro-aim-server/state"
  9. "github.com/mk6i/retro-aim-server/wire"
  10. )
  11. func TestOSCARProxy_RecvBOS_ChatIn(t *testing.T) {
  12. cases := []struct {
  13. // name is the unit test name
  14. name string
  15. // me is the TOC user session
  16. me *state.Session
  17. // chatID is the chat ID
  18. chatID int
  19. // givenMsg is the incoming SNAC
  20. givenMsg wire.SNACMessage
  21. // wantCmd is the expected TOC response
  22. wantCmd []byte
  23. }{
  24. {
  25. name: "send chat message",
  26. me: newTestSession("me"),
  27. chatID: 0,
  28. givenMsg: wire.SNACMessage{
  29. Body: wire.SNAC_0x0E_0x06_ChatChannelMsgToClient{
  30. TLVRestBlock: wire.TLVRestBlock{
  31. TLVList: wire.TLVList{
  32. wire.NewTLVBE(wire.ChatTLVSenderInformation, wire.TLVUserInfo{
  33. ScreenName: "them",
  34. }),
  35. wire.NewTLVBE(wire.ChatTLVMessageInfo, wire.TLVRestBlock{
  36. TLVList: wire.TLVList{
  37. wire.NewTLVBE(wire.ChatTLVMessageInfoText, "<p>hello world!</p>"),
  38. },
  39. }),
  40. },
  41. },
  42. },
  43. },
  44. wantCmd: []byte("CHAT_IN:0:them:F:<p>hello world!</p>"),
  45. },
  46. }
  47. for _, tc := range cases {
  48. t.Run(tc.name, func(t *testing.T) {
  49. ctx, cancel := context.WithCancel(context.Background())
  50. svc := OSCARProxy{
  51. Logger: slog.Default(),
  52. }
  53. ch := make(chan []byte)
  54. wg := &sync.WaitGroup{}
  55. wg.Add(1)
  56. go func() {
  57. defer wg.Done()
  58. svc.RecvChat(ctx, tc.me, tc.chatID, ch)
  59. }()
  60. status := tc.me.RelayMessage(tc.givenMsg)
  61. assert.Equal(t, state.SessSendOK, status)
  62. gotCmd := <-ch
  63. assert.Equal(t, string(tc.wantCmd), string(gotCmd))
  64. cancel()
  65. wg.Wait()
  66. })
  67. }
  68. }
  69. func TestOSCARProxy_RecvBOS_ChatUpdateBuddyArrived(t *testing.T) {
  70. cases := []struct {
  71. // name is the unit test name
  72. name string
  73. // me is the TOC user session
  74. me *state.Session
  75. // chatID is the chat ID
  76. chatID int
  77. // givenMsg is the incoming SNAC
  78. givenMsg wire.SNACMessage
  79. // wantCmd is the expected TOC response
  80. wantCmd []byte
  81. }{
  82. {
  83. name: "send chat participant arrival",
  84. me: newTestSession("me"),
  85. givenMsg: wire.SNACMessage{
  86. Body: wire.SNAC_0x0E_0x03_ChatUsersJoined{
  87. Users: []wire.TLVUserInfo{
  88. {ScreenName: "user1"},
  89. {ScreenName: "user2"},
  90. },
  91. },
  92. },
  93. wantCmd: []byte("CHAT_UPDATE_BUDDY:0:T:user1:user2"),
  94. },
  95. }
  96. for _, tc := range cases {
  97. t.Run(tc.name, func(t *testing.T) {
  98. ctx, cancel := context.WithCancel(context.Background())
  99. svc := OSCARProxy{
  100. Logger: slog.Default(),
  101. }
  102. ch := make(chan []byte)
  103. wg := &sync.WaitGroup{}
  104. wg.Add(1)
  105. go func() {
  106. defer wg.Done()
  107. svc.RecvChat(ctx, tc.me, tc.chatID, ch)
  108. }()
  109. status := tc.me.RelayMessage(tc.givenMsg)
  110. assert.Equal(t, state.SessSendOK, status)
  111. gotCmd := <-ch
  112. assert.Equal(t, string(tc.wantCmd), string(gotCmd))
  113. cancel()
  114. wg.Wait()
  115. })
  116. }
  117. }
  118. func TestOSCARProxy_RecvBOS_ChatUpdateBuddyLeft(t *testing.T) {
  119. cases := []struct {
  120. // name is the unit test name
  121. name string
  122. // me is the TOC user session
  123. me *state.Session
  124. // chatID is the chat ID
  125. chatID int
  126. // givenMsg is the incoming SNAC
  127. givenMsg wire.SNACMessage
  128. // wantCmd is the expected TOC response
  129. wantCmd []byte
  130. }{
  131. {
  132. name: "send chat participant departure",
  133. me: newTestSession("me"),
  134. givenMsg: wire.SNACMessage{
  135. Body: wire.SNAC_0x0E_0x04_ChatUsersLeft{
  136. Users: []wire.TLVUserInfo{
  137. {ScreenName: "user1"},
  138. {ScreenName: "user2"},
  139. },
  140. },
  141. },
  142. wantCmd: []byte("CHAT_UPDATE_BUDDY:0:F:user1:user2"),
  143. },
  144. }
  145. for _, tc := range cases {
  146. t.Run(tc.name, func(t *testing.T) {
  147. ctx, cancel := context.WithCancel(context.Background())
  148. svc := OSCARProxy{
  149. Logger: slog.Default(),
  150. }
  151. ch := make(chan []byte)
  152. wg := &sync.WaitGroup{}
  153. wg.Add(1)
  154. go func() {
  155. defer wg.Done()
  156. svc.RecvChat(ctx, tc.me, tc.chatID, ch)
  157. }()
  158. status := tc.me.RelayMessage(tc.givenMsg)
  159. assert.Equal(t, state.SessSendOK, status)
  160. gotCmd := <-ch
  161. assert.Equal(t, string(tc.wantCmd), string(gotCmd))
  162. cancel()
  163. wg.Wait()
  164. })
  165. }
  166. }
  167. func TestOSCARProxy_RecvBOS_Eviled(t *testing.T) {
  168. cases := []struct {
  169. // name is the unit test name
  170. name string
  171. // me is the TOC user session
  172. me *state.Session
  173. // givenMsg is the incoming SNAC
  174. givenMsg wire.SNACMessage
  175. // chatRegistry is the chat registry for the current session
  176. chatRegistry *ChatRegistry
  177. // wantCmd is the expected TOC response
  178. wantCmd []byte
  179. }{
  180. {
  181. name: "anonymous warning - 10%",
  182. me: newTestSession("me"),
  183. givenMsg: wire.SNACMessage{
  184. Body: wire.SNAC_0x01_0x10_OServiceEvilNotification{
  185. NewEvil: 100,
  186. },
  187. },
  188. wantCmd: []byte("EVILED:10:"),
  189. },
  190. {
  191. name: "normal warning - 10%",
  192. me: newTestSession("me"),
  193. givenMsg: wire.SNACMessage{
  194. Body: wire.SNAC_0x01_0x10_OServiceEvilNotification{
  195. NewEvil: 100,
  196. Snitcher: &struct {
  197. wire.TLVUserInfo
  198. }{
  199. TLVUserInfo: wire.TLVUserInfo{
  200. ScreenName: "them",
  201. },
  202. },
  203. },
  204. },
  205. wantCmd: []byte("EVILED:10:them"),
  206. },
  207. }
  208. for _, tc := range cases {
  209. t.Run(tc.name, func(t *testing.T) {
  210. ctx, cancel := context.WithCancel(context.Background())
  211. svc := OSCARProxy{
  212. Logger: slog.Default(),
  213. }
  214. ch := make(chan []byte)
  215. wg := &sync.WaitGroup{}
  216. wg.Add(1)
  217. go func() {
  218. defer wg.Done()
  219. err := svc.RecvBOS(ctx, tc.me, tc.chatRegistry, ch)
  220. assert.NoError(t, err)
  221. }()
  222. status := tc.me.RelayMessage(tc.givenMsg)
  223. assert.Equal(t, state.SessSendOK, status)
  224. gotCmd := <-ch
  225. assert.Equal(t, string(tc.wantCmd), string(gotCmd))
  226. cancel()
  227. wg.Wait()
  228. })
  229. }
  230. }
  231. func TestOSCARProxy_RecvBOS_IMIn(t *testing.T) {
  232. cases := []struct {
  233. // name is the unit test name
  234. name string
  235. // me is the TOC user session
  236. me *state.Session
  237. // givenMsg is the incoming SNAC
  238. givenMsg wire.SNACMessage
  239. // chatRegistry is the chat registry for the current session
  240. chatRegistry *ChatRegistry
  241. // wantCmd is the expected TOC response
  242. wantCmd []byte
  243. }{
  244. {
  245. name: "send IM",
  246. me: newTestSession("me"),
  247. givenMsg: wire.SNACMessage{
  248. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  249. ChannelID: wire.ICBMChannelIM,
  250. TLVUserInfo: wire.TLVUserInfo{
  251. ScreenName: "them",
  252. },
  253. TLVRestBlock: wire.TLVRestBlock{
  254. TLVList: wire.TLVList{
  255. wire.NewTLVBE(wire.ICBMTLVAOLIMData, []wire.ICBMCh1Fragment{
  256. {
  257. ID: 0x5,
  258. Version: 0x1,
  259. Payload: []uint8{0x1, 0x1, 0x2},
  260. },
  261. {
  262. ID: 0x1,
  263. Version: 0x1,
  264. Payload: []uint8{
  265. 0x0, 0x0, // charset
  266. 0x0, 0x0, // lang
  267. 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!',
  268. },
  269. },
  270. }),
  271. },
  272. },
  273. },
  274. },
  275. wantCmd: []byte("IM_IN:them:F:hello world!"),
  276. },
  277. {
  278. name: "send IM - auto-response",
  279. me: newTestSession("me"),
  280. givenMsg: wire.SNACMessage{
  281. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  282. ChannelID: wire.ICBMChannelIM,
  283. TLVUserInfo: wire.TLVUserInfo{
  284. ScreenName: "them",
  285. },
  286. TLVRestBlock: wire.TLVRestBlock{
  287. TLVList: wire.TLVList{
  288. wire.NewTLVBE(wire.ICBMTLVAutoResponse, []byte{}),
  289. wire.NewTLVBE(wire.ICBMTLVAOLIMData, []wire.ICBMCh1Fragment{
  290. {
  291. ID: 0x5,
  292. Version: 0x1,
  293. Payload: []uint8{0x1, 0x1, 0x2},
  294. },
  295. {
  296. ID: 0x1,
  297. Version: 0x1,
  298. Payload: []uint8{
  299. 0x0, 0x0, // charset
  300. 0x0, 0x0, // lang
  301. 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!',
  302. },
  303. },
  304. }),
  305. },
  306. },
  307. },
  308. },
  309. wantCmd: []byte("IM_IN:them:T:hello world!"),
  310. },
  311. {
  312. name: "send chat invitation",
  313. me: newTestSession("me"),
  314. givenMsg: wire.SNACMessage{
  315. Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
  316. ChannelID: wire.ICBMChannelRendezvous,
  317. TLVUserInfo: wire.TLVUserInfo{
  318. ScreenName: "them",
  319. },
  320. TLVRestBlock: wire.TLVRestBlock{
  321. TLVList: wire.TLVList{
  322. wire.NewTLVBE(wire.ICBMTLVData, []wire.ICBMCh2Fragment{
  323. {
  324. TLVRestBlock: wire.TLVRestBlock{
  325. TLVList: wire.TLVList{
  326. wire.NewTLVBE(wire.ICBMRdvTLVTagsInvitation, "join my chat!"),
  327. wire.NewTLVBE(wire.ICBMRdvTLVTagsSvcData, wire.ICBMRoomInfo{
  328. Cookie: "a-b-the room",
  329. }),
  330. },
  331. },
  332. },
  333. }),
  334. },
  335. },
  336. },
  337. },
  338. chatRegistry: NewChatRegistry(),
  339. wantCmd: []byte("CHAT_INVITE:the room:0:them:join my chat!"),
  340. },
  341. }
  342. for _, tc := range cases {
  343. t.Run(tc.name, func(t *testing.T) {
  344. ctx, cancel := context.WithCancel(context.Background())
  345. svc := OSCARProxy{
  346. Logger: slog.Default(),
  347. }
  348. ch := make(chan []byte)
  349. wg := &sync.WaitGroup{}
  350. wg.Add(1)
  351. go func() {
  352. defer wg.Done()
  353. err := svc.RecvBOS(ctx, tc.me, tc.chatRegistry, ch)
  354. assert.NoError(t, err)
  355. }()
  356. status := tc.me.RelayMessage(tc.givenMsg)
  357. assert.Equal(t, state.SessSendOK, status)
  358. gotCmd := <-ch
  359. assert.Equal(t, string(tc.wantCmd), string(gotCmd))
  360. cancel()
  361. wg.Wait()
  362. })
  363. }
  364. }
  365. func TestOSCARProxy_RecvBOS_UpdateBuddyArrival(t *testing.T) {
  366. cases := []struct {
  367. // name is the unit test name
  368. name string
  369. // me is the TOC user session
  370. me *state.Session
  371. // givenMsg is the incoming SNAC
  372. givenMsg wire.SNACMessage
  373. // wantCmd is the expected TOC response
  374. wantCmd []byte
  375. }{
  376. {
  377. name: "send buddy arrival - buddy online",
  378. me: newTestSession("me"),
  379. givenMsg: wire.SNACMessage{
  380. Body: wire.SNAC_0x03_0x0B_BuddyArrived{
  381. TLVUserInfo: wire.TLVUserInfo{
  382. ScreenName: "me",
  383. WarningLevel: 0,
  384. TLVBlock: wire.TLVBlock{
  385. TLVList: wire.TLVList{
  386. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1234)),
  387. wire.NewTLVBE(wire.OServiceUserInfoIdleTime, uint16(5678)),
  388. },
  389. },
  390. },
  391. },
  392. },
  393. wantCmd: []byte("UPDATE_BUDDY:me:T:0:1234:5678: O "),
  394. },
  395. {
  396. name: "send buddy arrival - buddy warned 10%",
  397. me: newTestSession("me"),
  398. givenMsg: wire.SNACMessage{
  399. Body: wire.SNAC_0x03_0x0B_BuddyArrived{
  400. TLVUserInfo: wire.TLVUserInfo{
  401. ScreenName: "me",
  402. WarningLevel: 100,
  403. TLVBlock: wire.TLVBlock{
  404. TLVList: wire.TLVList{
  405. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1234)),
  406. wire.NewTLVBE(wire.OServiceUserInfoIdleTime, uint16(5678)),
  407. },
  408. },
  409. },
  410. },
  411. },
  412. wantCmd: []byte("UPDATE_BUDDY:me:T:10:1234:5678: O "),
  413. },
  414. {
  415. name: "send buddy arrival - buddy away",
  416. me: newTestSession("me"),
  417. givenMsg: wire.SNACMessage{
  418. Body: wire.SNAC_0x03_0x0B_BuddyArrived{
  419. TLVUserInfo: wire.TLVUserInfo{
  420. ScreenName: "me",
  421. WarningLevel: 0,
  422. TLVBlock: wire.TLVBlock{
  423. TLVList: wire.TLVList{
  424. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1234)),
  425. wire.NewTLVBE(wire.OServiceUserInfoIdleTime, uint16(5678)),
  426. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, wire.OServiceUserFlagUnavailable),
  427. },
  428. },
  429. },
  430. },
  431. },
  432. wantCmd: []byte("UPDATE_BUDDY:me:T:0:1234:5678: OU"),
  433. },
  434. }
  435. for _, tc := range cases {
  436. t.Run(tc.name, func(t *testing.T) {
  437. ctx, cancel := context.WithCancel(context.Background())
  438. svc := OSCARProxy{
  439. Logger: slog.Default(),
  440. }
  441. ch := make(chan []byte)
  442. wg := &sync.WaitGroup{}
  443. wg.Add(1)
  444. go func() {
  445. defer wg.Done()
  446. err := svc.RecvBOS(ctx, tc.me, nil, ch)
  447. assert.NoError(t, err)
  448. }()
  449. status := tc.me.RelayMessage(tc.givenMsg)
  450. assert.Equal(t, state.SessSendOK, status)
  451. gotCmd := <-ch
  452. assert.Equal(t, string(tc.wantCmd), string(gotCmd))
  453. cancel()
  454. wg.Wait()
  455. })
  456. }
  457. }
  458. func TestOSCARProxy_RecvBOS_UpdateBuddyDeparted(t *testing.T) {
  459. cases := []struct {
  460. // name is the unit test name
  461. name string
  462. // me is the TOC user session
  463. me *state.Session
  464. // givenMsg is the incoming SNAC
  465. givenMsg wire.SNACMessage
  466. // wantCmd is the expected TOC response
  467. wantCmd []byte
  468. }{
  469. {
  470. name: "send buddy departure",
  471. me: newTestSession("me"),
  472. givenMsg: wire.SNACMessage{
  473. Body: wire.SNAC_0x03_0x0C_BuddyDeparted{
  474. TLVUserInfo: wire.TLVUserInfo{
  475. ScreenName: "me",
  476. },
  477. },
  478. },
  479. wantCmd: []byte("UPDATE_BUDDY:me:F:0:0:0: "),
  480. },
  481. }
  482. for _, tc := range cases {
  483. t.Run(tc.name, func(t *testing.T) {
  484. ctx, cancel := context.WithCancel(context.Background())
  485. svc := OSCARProxy{
  486. Logger: slog.Default(),
  487. }
  488. ch := make(chan []byte)
  489. wg := &sync.WaitGroup{}
  490. wg.Add(1)
  491. go func() {
  492. defer wg.Done()
  493. err := svc.RecvBOS(ctx, tc.me, nil, ch)
  494. assert.NoError(t, err)
  495. }()
  496. status := tc.me.RelayMessage(tc.givenMsg)
  497. assert.Equal(t, state.SessSendOK, status)
  498. gotCmd := <-ch
  499. assert.Equal(t, string(tc.wantCmd), string(gotCmd))
  500. cancel()
  501. wg.Wait()
  502. })
  503. }
  504. }
  505. func TestOSCARProxy_RecvBOS_Signout(t *testing.T) {
  506. }