session_test.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. package state
  2. import (
  3. "sync"
  4. "testing"
  5. "time"
  6. "github.com/mk6i/retro-aim-server/wire"
  7. "github.com/stretchr/testify/assert"
  8. )
  9. func TestSession_SetAndGetAwayMessage(t *testing.T) {
  10. s := NewSession()
  11. assert.Empty(t, s.AwayMessage())
  12. msg := "here's my message"
  13. s.SetAwayMessage(msg)
  14. assert.Equal(t, msg, s.AwayMessage())
  15. }
  16. func TestSession_IncrementAndGetWarning(t *testing.T) {
  17. s := NewSession()
  18. assert.Zero(t, s.Warning())
  19. s.IncrementWarning(1)
  20. s.IncrementWarning(2)
  21. assert.Equal(t, uint16(3), s.Warning())
  22. }
  23. func TestSession_SetAndGetInvisible(t *testing.T) {
  24. s := NewSession()
  25. assert.False(t, s.Invisible())
  26. s.SetUserStatusBitmask(wire.OServiceUserStatusInvisible)
  27. assert.True(t, s.Invisible())
  28. }
  29. func TestSession_SetAndGetScreenName(t *testing.T) {
  30. s := NewSession()
  31. assert.Empty(t, s.IdentScreenName())
  32. sn := NewIdentScreenName("user-screen-name")
  33. s.SetIdentScreenName(sn)
  34. assert.Equal(t, sn, s.IdentScreenName())
  35. }
  36. func TestSession_SetAndGetChatRoomCookie(t *testing.T) {
  37. s := NewSession()
  38. assert.Empty(t, s.ChatRoomCookie())
  39. sn := "the-chat-cookie"
  40. s.SetChatRoomCookie(sn)
  41. assert.Equal(t, sn, s.ChatRoomCookie())
  42. }
  43. func TestSession_SetAndGetUIN(t *testing.T) {
  44. s := NewSession()
  45. assert.Empty(t, s.UIN())
  46. uin := uint32(100003)
  47. s.SetUIN(uin)
  48. assert.Equal(t, uin, s.UIN())
  49. }
  50. func TestSession_SetAndGetClientID(t *testing.T) {
  51. s := NewSession()
  52. assert.Empty(t, s.ClientID())
  53. clientID := "AIM Client ID"
  54. s.SetClientID(clientID)
  55. assert.Equal(t, clientID, s.ClientID())
  56. }
  57. func TestSession_TLVUserInfo(t *testing.T) {
  58. tests := []struct {
  59. name string
  60. givenSessionFn func() *Session
  61. want wire.TLVUserInfo
  62. }{
  63. {
  64. name: "user is active and visible",
  65. givenSessionFn: func() *Session {
  66. s := NewSession()
  67. s.SetSignonTime(time.Unix(1, 0))
  68. s.SetIdentScreenName(NewIdentScreenName("xXAIMUSERXx"))
  69. s.SetDisplayScreenName("xXAIMUSERXx")
  70. s.IncrementWarning(10)
  71. s.SetUserInfoFlag(wire.OServiceUserFlagOSCARFree)
  72. return s
  73. },
  74. want: wire.TLVUserInfo{
  75. ScreenName: "xXAIMUSERXx",
  76. WarningLevel: 10,
  77. TLVBlock: wire.TLVBlock{
  78. TLVList: wire.TLVList{
  79. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  80. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
  81. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  82. },
  83. },
  84. },
  85. },
  86. {
  87. name: "user is on ICQ",
  88. givenSessionFn: func() *Session {
  89. s := NewSession()
  90. s.SetSignonTime(time.Unix(1, 0))
  91. s.SetIdentScreenName(NewIdentScreenName("1000003"))
  92. s.SetDisplayScreenName("1000003")
  93. s.SetUserInfoFlag(wire.OServiceUserFlagICQ)
  94. return s
  95. },
  96. want: wire.TLVUserInfo{
  97. ScreenName: "1000003",
  98. TLVBlock: wire.TLVBlock{
  99. TLVList: wire.TLVList{
  100. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  101. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, wire.OServiceUserFlagOSCARFree|wire.OServiceUserFlagICQ),
  102. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  103. wire.NewTLVBE(wire.OServiceUserInfoICQDC, wire.ICQDCInfo{}),
  104. },
  105. },
  106. },
  107. },
  108. {
  109. name: "user has away message set",
  110. givenSessionFn: func() *Session {
  111. s := NewSession()
  112. s.SetSignonTime(time.Unix(1, 0))
  113. s.SetAwayMessage("here's my away message")
  114. return s
  115. },
  116. want: wire.TLVUserInfo{
  117. TLVBlock: wire.TLVBlock{
  118. TLVList: wire.TLVList{
  119. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  120. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x30)),
  121. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  122. },
  123. },
  124. },
  125. },
  126. {
  127. name: "user is invisible",
  128. givenSessionFn: func() *Session {
  129. s := NewSession()
  130. s.SetSignonTime(time.Unix(1, 0))
  131. s.SetUserStatusBitmask(wire.OServiceUserStatusInvisible)
  132. return s
  133. },
  134. want: wire.TLVUserInfo{
  135. TLVBlock: wire.TLVBlock{
  136. TLVList: wire.TLVList{
  137. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  138. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
  139. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0100)),
  140. },
  141. },
  142. },
  143. },
  144. {
  145. name: "user is idle",
  146. givenSessionFn: func() *Session {
  147. s := NewSession()
  148. // sign on at t=0m
  149. timeBegin := time.Unix(0, 0)
  150. s.SetSignonTime(timeBegin)
  151. // set idle for 1m at t=+5m (ergo user idled @ t=+4m)
  152. timeIdle := timeBegin.Add(5 * time.Minute)
  153. s.nowFn = func() time.Time { return timeIdle }
  154. s.SetIdle(1 * time.Minute)
  155. // now it's t=+10m, ergo idle time should be t10-t4=6m
  156. timeNow := timeBegin.Add(10 * time.Minute)
  157. s.nowFn = func() time.Time { return timeNow }
  158. return s
  159. },
  160. want: wire.TLVUserInfo{
  161. TLVBlock: wire.TLVBlock{
  162. TLVList: wire.TLVList{
  163. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(0)),
  164. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
  165. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  166. wire.NewTLVBE(wire.OServiceUserInfoIdleTime, uint16(6)),
  167. },
  168. },
  169. },
  170. },
  171. {
  172. name: "user goes idle then returns",
  173. givenSessionFn: func() *Session {
  174. s := NewSession()
  175. s.SetSignonTime(time.Unix(1, 0))
  176. s.SetIdle(1 * time.Second)
  177. s.UnsetIdle()
  178. return s
  179. },
  180. want: wire.TLVUserInfo{
  181. TLVBlock: wire.TLVBlock{
  182. TLVList: wire.TLVList{
  183. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  184. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
  185. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  186. },
  187. },
  188. },
  189. },
  190. {
  191. name: "user has capabilities",
  192. givenSessionFn: func() *Session {
  193. s := NewSession()
  194. s.SetSignonTime(time.Unix(1, 0))
  195. s.SetCaps([][16]byte{
  196. {
  197. // chat: "748F2420-6287-11D1-8222-444553540000"
  198. 0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
  199. 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00,
  200. },
  201. {
  202. // chat2: "748F2420-6287-11D1-8222-444553540000"
  203. 0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
  204. 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01,
  205. },
  206. })
  207. return s
  208. },
  209. want: wire.TLVUserInfo{
  210. TLVBlock: wire.TLVBlock{
  211. TLVList: wire.TLVList{
  212. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  213. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
  214. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  215. wire.NewTLVBE(wire.OServiceUserInfoOscarCaps, []byte{
  216. // chat: "748F2420-6287-11D1-8222-444553540000"
  217. 0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
  218. 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00,
  219. // chat: "748F2420-6287-11D1-8222-444553540000"
  220. 0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
  221. 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01,
  222. }),
  223. },
  224. },
  225. },
  226. },
  227. {
  228. name: "user has buddy icon",
  229. givenSessionFn: func() *Session {
  230. s := NewSession()
  231. s.SetSignonTime(time.Unix(1, 0))
  232. return s
  233. },
  234. want: wire.TLVUserInfo{
  235. WarningLevel: 0,
  236. TLVBlock: wire.TLVBlock{
  237. TLVList: wire.TLVList{
  238. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  239. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
  240. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  241. },
  242. },
  243. },
  244. },
  245. }
  246. for _, tt := range tests {
  247. t.Run(tt.name, func(t *testing.T) {
  248. s := tt.givenSessionFn()
  249. assert.Equal(t, tt.want, s.TLVUserInfo())
  250. })
  251. }
  252. }
  253. func TestSession_SendAndRecvMessage_ExpectSessSendOK(t *testing.T) {
  254. s := NewSession()
  255. msg := wire.SNACMessage{
  256. Frame: wire.SNACFrame{
  257. FoodGroup: wire.ICBM,
  258. },
  259. }
  260. var wg sync.WaitGroup
  261. wg.Add(1)
  262. go func() {
  263. defer wg.Done()
  264. defer s.Close()
  265. status := s.RelayMessage(msg)
  266. assert.Equal(t, SessSendOK, status)
  267. }()
  268. loop:
  269. for {
  270. select {
  271. case m := <-s.ReceiveMessage():
  272. assert.Equal(t, msg, m)
  273. case <-s.Closed():
  274. break loop
  275. }
  276. }
  277. wg.Wait()
  278. }
  279. func TestSession_SendMessage_SessSendClosed(t *testing.T) {
  280. s := Session{
  281. msgCh: make(chan wire.SNACMessage, 1),
  282. stopCh: make(chan struct{}),
  283. }
  284. s.Close()
  285. if res := s.RelayMessage(wire.SNACMessage{}); res != SessSendClosed {
  286. t.Fatalf("expected SessSendClosed, got %+v", res)
  287. }
  288. }
  289. func TestSession_SendMessage_SessQueueFull(t *testing.T) {
  290. bufSize := 10
  291. s := Session{
  292. msgCh: make(chan wire.SNACMessage, bufSize),
  293. stopCh: make(chan struct{}),
  294. }
  295. for i := 0; i < bufSize; i++ {
  296. assert.Equal(t, SessSendOK, s.RelayMessage(wire.SNACMessage{}))
  297. }
  298. assert.Equal(t, SessQueueFull, s.RelayMessage(wire.SNACMessage{}))
  299. }
  300. func TestSession_Close_Twice(t *testing.T) {
  301. s := Session{
  302. stopCh: make(chan struct{}),
  303. }
  304. s.Close()
  305. s.Close() // make sure close is idempotent
  306. if !s.closed {
  307. t.Fatal("expected session to be closed")
  308. }
  309. select {
  310. case <-s.Closed():
  311. case <-time.After(1 * time.Second):
  312. t.Fatalf("channel is not closed")
  313. }
  314. }
  315. func TestSession_Close(t *testing.T) {
  316. s := NewSession()
  317. select {
  318. case <-s.Closed():
  319. assert.Fail(t, "channel is closed")
  320. default:
  321. // channel is open by default
  322. }
  323. s.Close()
  324. <-s.Closed()
  325. }