session_test.go 8.0 KB

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