session_test.go 8.7 KB

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