session_test.go 9.1 KB

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