session_manager_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. package state
  2. import (
  3. "context"
  4. "log/slog"
  5. "sync"
  6. "testing"
  7. "github.com/mk6i/retro-aim-server/wire"
  8. "github.com/stretchr/testify/assert"
  9. )
  10. func TestInMemorySessionManager_AddSession(t *testing.T) {
  11. sm := NewInMemorySessionManager(slog.Default())
  12. ctx := context.Background()
  13. sess1, err := sm.AddSession(ctx, "user-screen-name")
  14. assert.NoError(t, err)
  15. go func() {
  16. <-sess1.Closed()
  17. sm.RemoveSession(sess1)
  18. }()
  19. sess2, err := sm.AddSession(ctx, "user-screen-name")
  20. assert.NoError(t, err)
  21. assert.NotSame(t, sess1, sess2)
  22. assert.Contains(t, sm.AllSessions(), sess2)
  23. }
  24. func TestInMemorySessionManager_AddSession_Timeout(t *testing.T) {
  25. sm := NewInMemorySessionManager(slog.Default())
  26. ctx, cancel := context.WithCancel(context.Background())
  27. sess1, err := sm.AddSession(ctx, "user-screen-name")
  28. assert.NoError(t, err)
  29. go func() {
  30. <-sess1.Closed()
  31. cancel()
  32. }()
  33. sess2, err := sm.AddSession(ctx, "user-screen-name")
  34. assert.Nil(t, sess2)
  35. assert.ErrorIs(t, err, context.Canceled)
  36. }
  37. func TestInMemorySessionManager_AddSession_SessionConflict(t *testing.T) {
  38. sm := NewInMemorySessionManager(slog.Default())
  39. ctx := context.Background()
  40. sess1, err := sm.AddSession(ctx, "user-screen-name")
  41. assert.NoError(t, err)
  42. go func() {
  43. <-sess1.Closed()
  44. rec, ok := sm.store[NewIdentScreenName("user-screen-name")]
  45. if assert.True(t, ok) {
  46. close(rec.removed)
  47. }
  48. }()
  49. sess2, err := sm.AddSession(ctx, "user-screen-name")
  50. assert.Nil(t, sess2)
  51. assert.ErrorIs(t, err, errSessConflict)
  52. }
  53. func TestInMemorySessionManager_Remove_Existing(t *testing.T) {
  54. sm := NewInMemorySessionManager(slog.Default())
  55. user1Old, err := sm.AddSession(context.Background(), "user-screen-name-1")
  56. assert.NoError(t, err)
  57. sm.RemoveSession(user1Old)
  58. user1New, err := sm.AddSession(context.Background(), "user-screen-name-1")
  59. assert.NoError(t, err)
  60. user2, err := sm.AddSession(context.Background(), "user-screen-name-2")
  61. assert.NoError(t, err)
  62. sm.RemoveSession(user1New)
  63. if assert.Len(t, sm.AllSessions(), 1) {
  64. assert.NotContains(t, sm.AllSessions(), user1Old)
  65. assert.NotContains(t, sm.AllSessions(), user1New)
  66. assert.Contains(t, sm.AllSessions(), user2)
  67. }
  68. }
  69. func TestInMemorySessionManager_Remove_MissingSameScreenName(t *testing.T) {
  70. sm := NewInMemorySessionManager(slog.Default())
  71. user1Old, err := sm.AddSession(context.Background(), "user-screen-name-1")
  72. assert.NoError(t, err)
  73. sm.RemoveSession(user1Old)
  74. user1New, err := sm.AddSession(context.Background(), "user-screen-name-1")
  75. assert.NoError(t, err)
  76. user2, err := sm.AddSession(context.Background(), "user-screen-name-2")
  77. assert.NoError(t, err)
  78. sm.RemoveSession(user1Old)
  79. if assert.Len(t, sm.AllSessions(), 2) {
  80. assert.NotContains(t, sm.AllSessions(), user1Old)
  81. assert.Contains(t, sm.AllSessions(), user1New)
  82. assert.Contains(t, sm.AllSessions(), user2)
  83. }
  84. }
  85. func TestInMemorySessionManager_Empty(t *testing.T) {
  86. tests := []struct {
  87. name string
  88. given []DisplayScreenName
  89. want bool
  90. }{
  91. {
  92. name: "session manager is not empty",
  93. given: []DisplayScreenName{
  94. "user-screen-name-1",
  95. },
  96. want: false,
  97. },
  98. {
  99. name: "session manager is empty",
  100. given: []DisplayScreenName{},
  101. want: true,
  102. },
  103. }
  104. for _, tt := range tests {
  105. t.Run(tt.name, func(t *testing.T) {
  106. sm := NewInMemorySessionManager(slog.Default())
  107. for _, screenName := range tt.given {
  108. _, err := sm.AddSession(context.Background(), screenName)
  109. assert.NoError(t, err)
  110. }
  111. have := sm.Empty()
  112. assert.Equal(t, tt.want, have)
  113. })
  114. }
  115. }
  116. func TestInMemorySessionManager_Retrieve(t *testing.T) {
  117. tests := []struct {
  118. name string
  119. given []DisplayScreenName
  120. lookupScreenName IdentScreenName
  121. wantScreenName IdentScreenName
  122. }{
  123. {
  124. name: "lookup finds match",
  125. given: []DisplayScreenName{
  126. "user-screen-name-1",
  127. "user-screen-name-2",
  128. },
  129. lookupScreenName: NewIdentScreenName("user-screen-name-2"),
  130. wantScreenName: NewIdentScreenName("user-screen-name-2"),
  131. },
  132. {
  133. name: "lookup does not find match",
  134. given: []DisplayScreenName{},
  135. lookupScreenName: NewIdentScreenName("user-screen-name-3"),
  136. wantScreenName: NewIdentScreenName(""),
  137. },
  138. }
  139. for _, tt := range tests {
  140. t.Run(tt.name, func(t *testing.T) {
  141. sm := NewInMemorySessionManager(slog.Default())
  142. for _, screenName := range tt.given {
  143. _, err := sm.AddSession(context.Background(), screenName)
  144. assert.NoError(t, err)
  145. }
  146. have := sm.RetrieveSession(tt.lookupScreenName)
  147. if have == nil {
  148. assert.Empty(t, tt.wantScreenName)
  149. } else {
  150. assert.Equal(t, tt.wantScreenName, have.IdentScreenName())
  151. }
  152. })
  153. }
  154. }
  155. func TestInMemorySessionManager_RelayToScreenNames(t *testing.T) {
  156. sm := NewInMemorySessionManager(slog.Default())
  157. user1, err := sm.AddSession(context.Background(), "user-screen-name-1")
  158. assert.NoError(t, err)
  159. user2, err := sm.AddSession(context.Background(), "user-screen-name-2")
  160. assert.NoError(t, err)
  161. user3, err := sm.AddSession(context.Background(), "user-screen-name-3")
  162. assert.NoError(t, err)
  163. want := wire.SNACMessage{Frame: wire.SNACFrame{FoodGroup: wire.ICBM}}
  164. recips := []IdentScreenName{
  165. NewIdentScreenName("user-screen-name-1"),
  166. NewIdentScreenName("user-screen-name-2"),
  167. }
  168. sm.RelayToScreenNames(context.Background(), recips, want)
  169. select {
  170. case have := <-user1.ReceiveMessage():
  171. assert.Equal(t, want, have)
  172. }
  173. select {
  174. case have := <-user2.ReceiveMessage():
  175. assert.Equal(t, want, have)
  176. }
  177. select {
  178. case <-user3.ReceiveMessage():
  179. assert.Fail(t, "user 3 should not receive a message")
  180. default:
  181. }
  182. }
  183. func TestInMemorySessionManager_Broadcast(t *testing.T) {
  184. sm := NewInMemorySessionManager(slog.Default())
  185. user1, err := sm.AddSession(context.Background(), "user-screen-name-1")
  186. assert.NoError(t, err)
  187. user2, err := sm.AddSession(context.Background(), "user-screen-name-2")
  188. assert.NoError(t, err)
  189. want := wire.SNACMessage{Frame: wire.SNACFrame{FoodGroup: wire.ICBM}}
  190. sm.RelayToAll(context.Background(), want)
  191. select {
  192. case have := <-user1.ReceiveMessage():
  193. assert.Equal(t, want, have)
  194. }
  195. select {
  196. case have := <-user2.ReceiveMessage():
  197. assert.Equal(t, want, have)
  198. }
  199. }
  200. func TestInMemorySessionManager_Broadcast_SkipClosedSession(t *testing.T) {
  201. sm := NewInMemorySessionManager(slog.Default())
  202. user1, err := sm.AddSession(context.Background(), "user-screen-name-1")
  203. assert.NoError(t, err)
  204. user2, err := sm.AddSession(context.Background(), "user-screen-name-2")
  205. assert.NoError(t, err)
  206. user2.Close()
  207. want := wire.SNACMessage{Frame: wire.SNACFrame{FoodGroup: wire.ICBM}}
  208. sm.RelayToAll(context.Background(), want)
  209. select {
  210. case have := <-user1.ReceiveMessage():
  211. assert.Equal(t, want, have)
  212. }
  213. select {
  214. case <-user2.ReceiveMessage():
  215. assert.Fail(t, "user 2 should not receive a message")
  216. default:
  217. }
  218. }
  219. func TestInMemorySessionManager_RelayToScreenName_SessionExists(t *testing.T) {
  220. sm := NewInMemorySessionManager(slog.Default())
  221. user1, err := sm.AddSession(context.Background(), "user-screen-name-1")
  222. assert.NoError(t, err)
  223. user2, err := sm.AddSession(context.Background(), "user-screen-name-2")
  224. assert.NoError(t, err)
  225. want := wire.SNACMessage{Frame: wire.SNACFrame{FoodGroup: wire.ICBM}}
  226. recip := NewIdentScreenName("user-screen-name-1")
  227. sm.RelayToScreenName(context.Background(), recip, want)
  228. select {
  229. case have := <-user1.ReceiveMessage():
  230. assert.Equal(t, want, have)
  231. }
  232. select {
  233. case <-user2.ReceiveMessage():
  234. assert.Fail(t, "user 2 should not receive a message")
  235. default:
  236. }
  237. }
  238. func TestInMemorySessionManager_RelayToScreenName_SessionNotExist(t *testing.T) {
  239. sm := NewInMemorySessionManager(slog.Default())
  240. user1, err := sm.AddSession(context.Background(), "user-screen-name-1")
  241. assert.NoError(t, err)
  242. want := wire.SNACMessage{Frame: wire.SNACFrame{FoodGroup: wire.ICBM}}
  243. recip := NewIdentScreenName("user-screen-name-2")
  244. sm.RelayToScreenName(context.Background(), recip, want)
  245. select {
  246. case <-user1.ReceiveMessage():
  247. assert.Fail(t, "user 1 should not receive a message")
  248. default:
  249. }
  250. }
  251. func TestInMemorySessionManager_RelayToScreenName_SkipFullSession(t *testing.T) {
  252. sm := NewInMemorySessionManager(slog.Default())
  253. user1, err := sm.AddSession(context.Background(), "user-screen-name-1")
  254. assert.NoError(t, err)
  255. msg := wire.SNACMessage{Frame: wire.SNACFrame{FoodGroup: wire.ICBM}}
  256. wantCount := 0
  257. for {
  258. if user1.RelayMessage(msg) == SessQueueFull {
  259. break
  260. }
  261. wantCount++
  262. }
  263. recip := NewIdentScreenName("user-screen-name-1")
  264. sm.RelayToScreenName(context.Background(), recip, msg)
  265. haveCount := 0
  266. loop:
  267. for {
  268. select {
  269. case <-user1.ReceiveMessage():
  270. haveCount++
  271. default:
  272. break loop
  273. }
  274. }
  275. assert.Equal(t, wantCount, haveCount)
  276. }
  277. func TestInMemoryChatSessionManager_RelayToAllExcept_HappyPath(t *testing.T) {
  278. sm := NewInMemoryChatSessionManager(slog.Default())
  279. cookie := "the-cookie"
  280. user1, err := sm.AddSession(context.Background(), cookie, "user-screen-name-1")
  281. assert.NoError(t, err)
  282. user2, err := sm.AddSession(context.Background(), cookie, "user-screen-name-2")
  283. assert.NoError(t, err)
  284. user3, err := sm.AddSession(context.Background(), cookie, "user-screen-name-3")
  285. assert.NoError(t, err)
  286. want := wire.SNACMessage{Frame: wire.SNACFrame{FoodGroup: wire.ICBM}}
  287. sm.RelayToAllExcept(context.Background(), cookie, user2.IdentScreenName(), want)
  288. select {
  289. case have := <-user1.ReceiveMessage():
  290. assert.Equal(t, want, have)
  291. }
  292. select {
  293. case <-user2.ReceiveMessage():
  294. assert.Fail(t, "user 2 should not receive a message")
  295. default:
  296. }
  297. select {
  298. case have := <-user3.ReceiveMessage():
  299. assert.Equal(t, want, have)
  300. }
  301. }
  302. func TestInMemoryChatSessionManager_AllSessions_RoomExists(t *testing.T) {
  303. sm := NewInMemoryChatSessionManager(slog.Default())
  304. user1, err := sm.AddSession(context.Background(), "the-cookie", "user-screen-name-1")
  305. assert.NoError(t, err)
  306. user2, err := sm.AddSession(context.Background(), "the-cookie", "user-screen-name-2")
  307. assert.NoError(t, err)
  308. sessions := sm.AllSessions("the-cookie")
  309. assert.Len(t, sessions, 2)
  310. lookup := make(map[*Session]bool)
  311. for _, session := range sessions {
  312. lookup[session] = true
  313. }
  314. assert.True(t, lookup[user1])
  315. assert.True(t, lookup[user2])
  316. }
  317. func TestInMemoryChatSessionManager_RelayToScreenName_SessionAndChatRoomExist(t *testing.T) {
  318. sm := NewInMemoryChatSessionManager(slog.Default())
  319. user1, err := sm.AddSession(context.Background(), "chat-room-1", "user-screen-name-1")
  320. assert.NoError(t, err)
  321. user2, err := sm.AddSession(context.Background(), "chat-room-1", "user-screen-name-2")
  322. assert.NoError(t, err)
  323. want := wire.SNACMessage{Frame: wire.SNACFrame{FoodGroup: wire.ICBM}}
  324. recip := NewIdentScreenName("user-screen-name-1")
  325. sm.RelayToScreenName(context.Background(), "chat-room-1", recip, want)
  326. select {
  327. case have := <-user1.ReceiveMessage():
  328. assert.Equal(t, want, have)
  329. }
  330. select {
  331. case <-user2.ReceiveMessage():
  332. assert.Fail(t, "user 2 should not receive a message")
  333. default:
  334. }
  335. }
  336. func TestInMemoryChatSessionManager_RemoveSession(t *testing.T) {
  337. sm := NewInMemoryChatSessionManager(slog.Default())
  338. user1, err := sm.AddSession(context.Background(), "chat-room-1", "user-screen-name-1")
  339. assert.NoError(t, err)
  340. user2, err := sm.AddSession(context.Background(), "chat-room-1", "user-screen-name-2")
  341. assert.NoError(t, err)
  342. assert.Len(t, sm.AllSessions("chat-room-1"), 2)
  343. sm.RemoveSession(user1)
  344. sm.RemoveSession(user2)
  345. assert.Empty(t, sm.AllSessions("chat-room-1"))
  346. }
  347. func TestInMemoryChatSessionManager_RemoveSession_DoubleLogin(t *testing.T) {
  348. sm := NewInMemoryChatSessionManager(slog.Default())
  349. chatSess1, err := sm.AddSession(context.Background(), "chat-room-1", "user-screen-name-1")
  350. assert.NoError(t, err)
  351. wg := &sync.WaitGroup{}
  352. wg.Add(1)
  353. go func() {
  354. // add the session again. this call blocks until RemoveSession makes
  355. // room for the new session
  356. chatSess2, err := sm.AddSession(context.Background(), "chat-room-1", "user-screen-name-1")
  357. assert.NoError(t, err)
  358. assert.Equal(t, chatSess1.DisplayScreenName(), chatSess2.DisplayScreenName())
  359. wg.Done()
  360. }()
  361. // wait for AddSession() to block
  362. for sm.mapMutex.TryRLock() {
  363. sm.mapMutex.RUnlock()
  364. }
  365. // AddSession() is blocked waiting for the log. this should unblock
  366. // AddSession()
  367. sm.RemoveSession(chatSess1)
  368. wg.Wait()
  369. }
  370. func TestInMemoryChatSessionManager_RemoveUserFromAllChats(t *testing.T) {
  371. sm := NewInMemoryChatSessionManager(slog.Default())
  372. user1 := NewIdentScreenName("user-screen-name-1")
  373. user1sess, err := sm.AddSession(context.Background(), "chat-room-1", "user-screen-name-1")
  374. assert.NoError(t, err)
  375. user2sess, err := sm.AddSession(context.Background(), "chat-room-1", "user-screen-name-2")
  376. assert.NoError(t, err)
  377. assert.Len(t, sm.AllSessions("chat-room-1"), 2)
  378. sm.RemoveUserFromAllChats(user1)
  379. lookup := make(map[*Session]bool)
  380. for _, session := range sm.AllSessions("chat-room-1") {
  381. lookup[session] = true
  382. }
  383. assert.False(t, lookup[user1sess])
  384. assert.True(t, lookup[user2sess])
  385. }