session_test.go 7.7 KB

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