session_test.go 7.6 KB

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