session_test.go 66 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297
  1. package state
  2. import (
  3. "context"
  4. "math"
  5. "net/netip"
  6. "sync"
  7. "testing"
  8. "time"
  9. "github.com/mk6i/open-oscar-server/wire"
  10. "github.com/stretchr/testify/assert"
  11. )
  12. func TestSession_UsesFeedbag(t *testing.T) {
  13. s := NewSession()
  14. if s.UsesFeedbag() {
  15. t.Fatalf("UsesFeedbag() = true; want false")
  16. }
  17. s.SetUsesFeedbag()
  18. if !s.UsesFeedbag() {
  19. t.Fatalf("UsesFeedbag() = false; want true")
  20. }
  21. // idempotent
  22. s.SetUsesFeedbag()
  23. if !s.UsesFeedbag() {
  24. t.Fatalf("UsesFeedbag() = false after second SetUsesFeedbag; want true")
  25. }
  26. }
  27. func TestSession_IncrementAndGetWarning(t *testing.T) {
  28. s := NewSession().AddInstance()
  29. var wg sync.WaitGroup
  30. wg.Add(1)
  31. go func() {
  32. defer wg.Done()
  33. s.Session().ScaleWarningAndRateLimit(1, 1)
  34. s.Session().ScaleWarningAndRateLimit(2, 1)
  35. s.Session().ScaleWarningAndRateLimit(3, 1)
  36. }()
  37. assert.Equal(t, uint16(1), <-s.WarningCh())
  38. assert.Equal(t, uint16(3), <-s.WarningCh())
  39. assert.Equal(t, uint16(6), <-s.WarningCh())
  40. wg.Wait()
  41. }
  42. func TestSession_SetAndGetInvisible(t *testing.T) {
  43. s := NewSession().AddInstance()
  44. assert.False(t, s.Invisible())
  45. s.SetUserStatusBitmask(wire.OServiceUserStatusInvisible)
  46. assert.True(t, s.Invisible())
  47. }
  48. func TestSession_SetAndGetScreenName(t *testing.T) {
  49. s := NewSession().AddInstance()
  50. assert.Empty(t, s.IdentScreenName())
  51. sn := NewIdentScreenName("user-screen-name")
  52. s.Session().SetIdentScreenName(sn)
  53. assert.Equal(t, sn, s.IdentScreenName())
  54. }
  55. func TestSession_SetAndGetChatRoomCookie(t *testing.T) {
  56. s := NewSession().AddInstance()
  57. assert.Empty(t, s.ChatRoomCookie())
  58. sn := "the-chat-cookie"
  59. s.Session().SetChatRoomCookie(sn)
  60. assert.Equal(t, sn, s.ChatRoomCookie())
  61. }
  62. func TestSession_SetAndGetUIN(t *testing.T) {
  63. s := NewSession().AddInstance()
  64. assert.Empty(t, s.UIN())
  65. uin := uint32(100003)
  66. s.Session().SetUIN(uin)
  67. assert.Equal(t, uin, s.UIN())
  68. }
  69. func TestSession_SetAndGetClientID(t *testing.T) {
  70. s := NewSession().AddInstance()
  71. assert.Empty(t, s.ClientID())
  72. clientID := "AIM Client ID"
  73. s.SetClientID(clientID)
  74. assert.Equal(t, clientID, s.ClientID())
  75. }
  76. func TestSession_SetAndGetKerberosAuth(t *testing.T) {
  77. s := NewSession().AddInstance()
  78. assert.False(t, s.KerberosAuth())
  79. s.SetKerberosAuth(true)
  80. assert.True(t, s.KerberosAuth())
  81. s.SetKerberosAuth(false)
  82. assert.False(t, s.KerberosAuth())
  83. }
  84. func TestSession_SetAndGetRemoteAddr(t *testing.T) {
  85. s := NewSession().AddInstance()
  86. assert.Empty(t, s.RemoteAddr())
  87. remoteAddr, _ := netip.ParseAddrPort("1.2.3.4:1234")
  88. s.SetRemoteAddr(&remoteAddr)
  89. assert.Equal(t, &remoteAddr, s.RemoteAddr())
  90. }
  91. func TestSession_TLVUserInfo(t *testing.T) {
  92. tests := []struct {
  93. name string
  94. givenSessionFn func() *SessionInstance
  95. want wire.TLVUserInfo
  96. }{
  97. {
  98. name: "user is active and visible",
  99. givenSessionFn: func() *SessionInstance {
  100. s := NewSession().AddInstance()
  101. s.Session().SetSignonTime(time.Unix(1, 0))
  102. s.Session().SetIdentScreenName(NewIdentScreenName("xXAIMUSERXx"))
  103. s.Session().SetDisplayScreenName("xXAIMUSERXx")
  104. s.Session().ScaleWarningAndRateLimit(10, 1)
  105. s.SetUserInfoFlag(wire.OServiceUserFlagOSCARFree)
  106. return s
  107. },
  108. want: wire.TLVUserInfo{
  109. ScreenName: "xXAIMUSERXx",
  110. WarningLevel: 10,
  111. TLVBlock: wire.TLVBlock{
  112. TLVList: wire.TLVList{
  113. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  114. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
  115. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  116. wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)),
  117. },
  118. },
  119. },
  120. },
  121. {
  122. name: "user is on ICQ",
  123. givenSessionFn: func() *SessionInstance {
  124. s := NewSession().AddInstance()
  125. s.Session().SetSignonTime(time.Unix(1, 0))
  126. s.Session().SetIdentScreenName(NewIdentScreenName("1000003"))
  127. s.Session().SetDisplayScreenName("1000003")
  128. s.SetUserInfoFlag(wire.OServiceUserFlagICQ)
  129. return s
  130. },
  131. want: wire.TLVUserInfo{
  132. ScreenName: "1000003",
  133. TLVBlock: wire.TLVBlock{
  134. TLVList: wire.TLVList{
  135. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  136. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, wire.OServiceUserFlagOSCARFree|wire.OServiceUserFlagICQ),
  137. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  138. wire.NewTLVBE(wire.OServiceUserInfoICQDC, wire.ICQDCInfo{}),
  139. wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)),
  140. },
  141. },
  142. },
  143. },
  144. {
  145. name: "user has away message set - all instances away",
  146. givenSessionFn: func() *SessionInstance {
  147. sg := NewSession()
  148. s := sg.AddInstance()
  149. s.Session().SetSignonTime(time.Unix(1, 0))
  150. s.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  151. // Add a second instance that is also away
  152. s2 := sg.AddInstance()
  153. s2.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  154. return s
  155. },
  156. want: wire.TLVUserInfo{
  157. TLVBlock: wire.TLVBlock{
  158. TLVList: wire.TLVList{
  159. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  160. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x30)),
  161. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  162. wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)),
  163. },
  164. },
  165. },
  166. },
  167. {
  168. name: "user has one instance away, one not away - away flag not set",
  169. givenSessionFn: func() *SessionInstance {
  170. sg := NewSession()
  171. // Create the NOT away instance first so it's used as the base
  172. s2 := sg.AddInstance()
  173. s2.Session().SetSignonTime(time.Unix(1, 0))
  174. // s2 is NOT away - it has default flags only (OServiceUserFlagOSCARFree)
  175. // Now create the away instance
  176. s := sg.AddInstance()
  177. s.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  178. // Since s2 is the first instance and is not away, and allAway() returns false,
  179. // the unavailable flag should not be set
  180. return s
  181. },
  182. want: wire.TLVUserInfo{
  183. TLVBlock: wire.TLVBlock{
  184. TLVList: wire.TLVList{
  185. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  186. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x10)),
  187. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  188. wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)),
  189. },
  190. },
  191. },
  192. },
  193. {
  194. name: "user has two instances away, second goes off away - away flag not set",
  195. givenSessionFn: func() *SessionInstance {
  196. sg := NewSession()
  197. sg.SetSignonTime(time.Unix(1, 0))
  198. // Set the first instance as away
  199. s1 := sg.AddInstance()
  200. s1.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  201. // Set the second instance as away
  202. s2 := sg.AddInstance()
  203. s2.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  204. // Make the second instance as not away
  205. s2.ClearUserInfoFlag(wire.OServiceUserFlagUnavailable)
  206. return s1
  207. },
  208. want: wire.TLVUserInfo{
  209. TLVBlock: wire.TLVBlock{
  210. TLVList: wire.TLVList{
  211. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  212. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x10)),
  213. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  214. wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)),
  215. },
  216. },
  217. },
  218. },
  219. {
  220. name: "user is invisible",
  221. givenSessionFn: func() *SessionInstance {
  222. s := NewSession().AddInstance()
  223. s.Session().SetSignonTime(time.Unix(1, 0))
  224. s.SetUserStatusBitmask(wire.OServiceUserStatusInvisible)
  225. return s
  226. },
  227. want: wire.TLVUserInfo{
  228. TLVBlock: wire.TLVBlock{
  229. TLVList: wire.TLVList{
  230. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  231. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
  232. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0100)),
  233. wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)),
  234. },
  235. },
  236. },
  237. },
  238. {
  239. name: "user is idle",
  240. givenSessionFn: func() *SessionInstance {
  241. s := NewSession().AddInstance()
  242. // sign on at t=0m
  243. timeBegin := time.Unix(0, 0)
  244. s.Session().SetSignonTime(timeBegin)
  245. // set idle for 1m at t=+5m (ergo user idled @ t=+4m)
  246. timeIdle := timeBegin.Add(5 * time.Minute)
  247. s.Session().SetNowFn(func() time.Time { return timeIdle })
  248. s.SetIdle(1 * time.Minute)
  249. // now it's t=+10m, ergo idle time should be t10-t4=6m
  250. timeNow := timeBegin.Add(10 * time.Minute)
  251. s.Session().SetNowFn(func() time.Time { return timeNow })
  252. return s
  253. },
  254. want: wire.TLVUserInfo{
  255. TLVBlock: wire.TLVBlock{
  256. TLVList: wire.TLVList{
  257. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(0)),
  258. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
  259. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  260. wire.NewTLVBE(wire.OServiceUserInfoIdleTime, uint16(6)),
  261. wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)),
  262. },
  263. },
  264. },
  265. },
  266. {
  267. name: "user goes idle then returns",
  268. givenSessionFn: func() *SessionInstance {
  269. s := NewSession().AddInstance()
  270. s.Session().SetSignonTime(time.Unix(1, 0))
  271. s.SetIdle(1 * time.Second)
  272. s.UnsetIdle()
  273. return s
  274. },
  275. want: wire.TLVUserInfo{
  276. TLVBlock: wire.TLVBlock{
  277. TLVList: wire.TLVList{
  278. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  279. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
  280. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  281. wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)),
  282. },
  283. },
  284. },
  285. },
  286. {
  287. name: "user has capabilities",
  288. givenSessionFn: func() *SessionInstance {
  289. s := NewSession().AddInstance()
  290. s.Session().SetSignonTime(time.Unix(1, 0))
  291. s.SetCaps([][16]byte{
  292. {
  293. // chat: "748F2420-6287-11D1-8222-444553540000"
  294. 0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
  295. 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00,
  296. },
  297. {
  298. // chat2: "748F2420-6287-11D1-8222-444553540000"
  299. 0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
  300. 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01,
  301. },
  302. })
  303. return s
  304. },
  305. want: wire.TLVUserInfo{
  306. TLVBlock: wire.TLVBlock{
  307. TLVList: wire.TLVList{
  308. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  309. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
  310. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  311. wire.NewTLVBE(wire.OServiceUserInfoOscarCaps, []byte{
  312. // chat: "748F2420-6287-11D1-8222-444553540000"
  313. 0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
  314. 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00,
  315. // chat: "748F2420-6287-11D1-8222-444553540000"
  316. 0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
  317. 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01,
  318. }),
  319. wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)),
  320. },
  321. },
  322. },
  323. },
  324. {
  325. name: "user has buddy icon",
  326. givenSessionFn: func() *SessionInstance {
  327. s := NewSession().AddInstance()
  328. s.Session().SetSignonTime(time.Unix(1, 0))
  329. return s
  330. },
  331. want: wire.TLVUserInfo{
  332. WarningLevel: 0,
  333. TLVBlock: wire.TLVBlock{
  334. TLVList: wire.TLVList{
  335. wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
  336. wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
  337. wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
  338. wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)),
  339. },
  340. },
  341. },
  342. },
  343. }
  344. for _, tt := range tests {
  345. t.Run(tt.name, func(t *testing.T) {
  346. s := tt.givenSessionFn()
  347. assert.Equal(t, tt.want, s.Session().TLVUserInfo())
  348. })
  349. }
  350. }
  351. func TestSession_SendAndRecvMessage_ExpectSessSendOK(t *testing.T) {
  352. s := NewSession().AddInstance()
  353. s.SetSignonComplete()
  354. msg := wire.SNACMessage{
  355. Frame: wire.SNACFrame{
  356. FoodGroup: wire.ICBM,
  357. },
  358. }
  359. var wg sync.WaitGroup
  360. wg.Add(1)
  361. go func() {
  362. defer wg.Done()
  363. defer s.CloseInstance()
  364. status := s.RelayMessageToInstance(msg)
  365. assert.Equal(t, SessSendOK, status)
  366. }()
  367. loop:
  368. for {
  369. select {
  370. case m := <-s.ReceiveMessage():
  371. assert.Equal(t, msg, m)
  372. case <-s.Closed():
  373. break loop
  374. }
  375. }
  376. wg.Wait()
  377. }
  378. func TestSession_SendMessage_SessSendClosed(t *testing.T) {
  379. s := NewSession().AddInstance()
  380. s.CloseInstance()
  381. if res := s.RelayMessageToInstance(wire.SNACMessage{}); res != SessSendClosed {
  382. t.Fatalf("expected SessSendClosed, got %+v", res)
  383. }
  384. }
  385. func TestSession_SendMessage_SessQueueFull(t *testing.T) {
  386. s := NewSession().AddInstance()
  387. s.SetSignonComplete()
  388. // Fill up the message channel (default buffer size is 1000)
  389. for i := 0; i < 1000; i++ {
  390. assert.Equal(t, SessSendOK, s.RelayMessageToInstance(wire.SNACMessage{}))
  391. }
  392. assert.Equal(t, SessQueueFull, s.RelayMessageToInstance(wire.SNACMessage{}))
  393. }
  394. func TestSession_Close_Twice(t *testing.T) {
  395. s := NewSession().AddInstance()
  396. s.CloseInstance()
  397. s.CloseInstance() // make sure close is idempotent
  398. // Check that the session is closed by trying to relay a message
  399. if res := s.RelayMessageToInstance(wire.SNACMessage{}); res != SessSendClosed {
  400. t.Fatalf("expected SessSendClosed, got %+v", res)
  401. }
  402. select {
  403. case <-s.Closed():
  404. case <-time.After(1 * time.Second):
  405. t.Fatalf("channel is not closed")
  406. }
  407. }
  408. func TestSession_Closed(t *testing.T) {
  409. s := NewSession().AddInstance()
  410. select {
  411. case <-s.Closed():
  412. assert.Fail(t, "channel is closed")
  413. default:
  414. // channel is open by default
  415. }
  416. s.Session().CloseSession()
  417. <-s.Closed()
  418. }
  419. func TestSession_EvaluateRateLimit_ObserveRateChanges(t *testing.T) {
  420. classParams := [5]wire.RateClass{
  421. {
  422. ID: 1,
  423. WindowSize: 80,
  424. ClearLevel: 2500,
  425. AlertLevel: 2000,
  426. LimitLevel: 1500,
  427. DisconnectLevel: 800,
  428. MaxLevel: 6000,
  429. },
  430. {
  431. ID: 2,
  432. WindowSize: 80,
  433. ClearLevel: 3000,
  434. AlertLevel: 2000,
  435. LimitLevel: 1500,
  436. DisconnectLevel: 1000,
  437. MaxLevel: 6000,
  438. },
  439. {
  440. ID: 3,
  441. WindowSize: 20,
  442. ClearLevel: 5100,
  443. AlertLevel: 5000,
  444. LimitLevel: 4000,
  445. DisconnectLevel: 3000,
  446. MaxLevel: 6000,
  447. },
  448. {
  449. ID: 4,
  450. WindowSize: 20,
  451. ClearLevel: 5500,
  452. AlertLevel: 5300,
  453. LimitLevel: 4200,
  454. DisconnectLevel: 3000,
  455. MaxLevel: 8000,
  456. },
  457. {
  458. ID: 5,
  459. WindowSize: 10,
  460. ClearLevel: 5500,
  461. AlertLevel: 5300,
  462. LimitLevel: 4200,
  463. DisconnectLevel: 3000,
  464. MaxLevel: 8000,
  465. },
  466. }
  467. rateClasses := wire.NewRateLimitClasses(classParams)
  468. t.Run("we can action every 5 seconds indefinitely without getting rate limited", func(t *testing.T) {
  469. now := time.Now()
  470. instance := NewSession().AddInstance()
  471. instance.Session().SetRateClasses(now, rateClasses)
  472. rateClass := rateClasses.Get(3)
  473. instance.Session().SubscribeRateLimits([]wire.RateLimitClassID{rateClass.ID})
  474. for i := 0; i < 100; i++ {
  475. now = now.Add(5 * time.Second)
  476. have := instance.Session().EvaluateRateLimit(now, rateClass.ID)
  477. assert.Equal(t, wire.RateLimitStatusClear, have)
  478. }
  479. })
  480. t.Run("reach disconnect threshold", func(t *testing.T) {
  481. now := time.Now()
  482. sess := NewSession()
  483. sess.SetRateClasses(now, rateClasses)
  484. sess.AddInstance()
  485. sess.AddInstance()
  486. sess.AddInstance()
  487. rateClass := rateClasses.Get(3)
  488. sess.SubscribeRateLimits([]wire.RateLimitClassID{rateClass.ID})
  489. // record some event in the rate limiter
  490. want := []wire.RateLimitStatus{
  491. wire.RateLimitStatusClear,
  492. wire.RateLimitStatusClear,
  493. wire.RateLimitStatusClear,
  494. wire.RateLimitStatusClear,
  495. wire.RateLimitStatusAlert,
  496. wire.RateLimitStatusAlert,
  497. wire.RateLimitStatusAlert,
  498. wire.RateLimitStatusAlert,
  499. wire.RateLimitStatusAlert,
  500. wire.RateLimitStatusLimited,
  501. wire.RateLimitStatusLimited,
  502. wire.RateLimitStatusLimited,
  503. wire.RateLimitStatusLimited,
  504. wire.RateLimitStatusLimited,
  505. wire.RateLimitStatusLimited,
  506. wire.RateLimitStatusLimited,
  507. wire.RateLimitStatusLimited,
  508. wire.RateLimitStatusDisconnect,
  509. }
  510. for i := 0; i < len(want); i++ {
  511. now = now.Add(1 * time.Second)
  512. have := sess.EvaluateRateLimit(now, rateClass.ID)
  513. assert.Equal(t, want[i], have)
  514. }
  515. for _, instance := range sess.Instances() {
  516. select {
  517. case <-instance.Closed():
  518. default:
  519. t.Error("expected session to be closed")
  520. }
  521. }
  522. })
  523. t.Run("reach rate limit threshold, wait for clear threshold", func(t *testing.T) {
  524. now := time.Now()
  525. instance := NewSession().AddInstance()
  526. instance.Session().SetRateClasses(now, rateClasses)
  527. rateClass := rateClasses.Get(3)
  528. instance.Session().SubscribeRateLimits([]wire.RateLimitClassID{rateClass.ID})
  529. // first reach the rate limit threshold
  530. want := []wire.RateLimitStatus{
  531. wire.RateLimitStatusClear,
  532. wire.RateLimitStatusClear,
  533. wire.RateLimitStatusClear,
  534. wire.RateLimitStatusClear,
  535. wire.RateLimitStatusAlert,
  536. wire.RateLimitStatusAlert,
  537. wire.RateLimitStatusAlert,
  538. wire.RateLimitStatusAlert,
  539. wire.RateLimitStatusAlert,
  540. wire.RateLimitStatusLimited,
  541. }
  542. for i := 0; i < len(want); i++ {
  543. now = now.Add(1 * time.Second)
  544. have := instance.Session().EvaluateRateLimit(now, rateClass.ID)
  545. assert.Equal(t, want[i], have)
  546. if i > 0 && want[i-1] != want[i] {
  547. classChanges, rateChanges := instance.Session().ObserveRateChanges(now)
  548. assert.Empty(t, classChanges)
  549. if assert.NotEmpty(t, rateChanges) {
  550. rateDelta := rateChanges[0]
  551. assert.Equal(t, rateClass, rateDelta.RateClass)
  552. assert.Equal(t, want[i], rateDelta.CurrentStatus)
  553. assert.True(t, rateDelta.Subscribed)
  554. if want[i] == wire.RateLimitStatusLimited {
  555. assert.True(t, rateDelta.LimitedNow)
  556. }
  557. }
  558. }
  559. }
  560. // this is a rearranged moving average formula that determines how many
  561. // milliseconds it will take to reach the clear threshold
  562. rateLimitStates := instance.RateLimitStates()
  563. timeToRecover := int(math.Ceil((time.Duration(rateClass.ClearLevel*rateClass.WindowSize-rateLimitStates[rateClass.ID-1].CurrentLevel*(rateClass.WindowSize-1)) * time.Millisecond).Seconds()))
  564. assert.True(t, timeToRecover > 0)
  565. // indicate the time rate limiting kicked in
  566. timeLimited := now
  567. for i := 0; i < timeToRecover; i++ {
  568. now = now.Add(1 * time.Second)
  569. classDelta, stateDelta := instance.Session().ObserveRateChanges(now)
  570. assert.Empty(t, classDelta)
  571. if i == timeToRecover-1 {
  572. // assert that the clear threshold has been met.
  573. assert.ElementsMatch(t, stateDelta, []RateClassState{
  574. {
  575. RateClass: rateClass,
  576. CurrentLevel: 5140,
  577. CurrentStatus: wire.RateLimitStatusClear,
  578. LastTime: timeLimited,
  579. Subscribed: true,
  580. LimitedNow: false,
  581. }})
  582. } else {
  583. // assert that no changed have been observed, it's still rate-limited
  584. assert.Nil(t, stateDelta)
  585. }
  586. }
  587. })
  588. t.Run("observe a rate class change", func(t *testing.T) {
  589. now := time.Now()
  590. instance := NewSession().AddInstance()
  591. instance.Session().SetRateClasses(now, rateClasses)
  592. rateClass := rateClasses.Get(3)
  593. instance.Session().SubscribeRateLimits([]wire.RateLimitClassID{rateClass.ID})
  594. now = now.Add(1 * time.Second)
  595. classDelta, stateDelta := instance.Session().ObserveRateChanges(now)
  596. assert.Empty(t, classDelta)
  597. assert.Empty(t, stateDelta)
  598. paramsCopy := classParams
  599. paramsCopy[rateClass.ID-1].LimitLevel++
  600. newRateClasses := wire.NewRateLimitClasses(paramsCopy)
  601. now = now.Add(1 * time.Second)
  602. instance.Session().SetRateClasses(now, newRateClasses)
  603. now = now.Add(1 * time.Second)
  604. classDelta, stateDelta = instance.Session().ObserveRateChanges(now)
  605. assert.Equal(t, classDelta[0].RateClass, newRateClasses.Get(rateClass.ID))
  606. assert.Empty(t, stateDelta)
  607. })
  608. t.Run("as a bot, I can action every second indefinitely without getting rate limited", func(t *testing.T) {
  609. now := time.Now()
  610. instance := NewSession().AddInstance()
  611. instance.SetUserInfoFlag(wire.OServiceUserFlagBot)
  612. instance.Session().SetRateClasses(now, rateClasses)
  613. for i := 0; i < 100; i++ {
  614. now = now.Add(1 * time.Second)
  615. have := instance.Session().EvaluateRateLimit(now, wire.RateLimitClassID(1))
  616. assert.Equal(t, wire.RateLimitStatusClear, have)
  617. }
  618. })
  619. }
  620. func TestSession_SetAndGetFoodGroupVersions(t *testing.T) {
  621. versions := [wire.MDir + 1]uint16{}
  622. versions[wire.Feedbag] = 1
  623. versions[wire.OService] = 2
  624. s := NewSession().AddInstance()
  625. s.SetFoodGroupVersions(versions)
  626. assert.Equal(t, versions, s.FoodGroupVersions())
  627. }
  628. func TestSession_SetAndGetTypingEventsEnabled(t *testing.T) {
  629. s := NewSession().AddInstance()
  630. assert.False(t, s.TypingEventsEnabled())
  631. s.Session().SetTypingEventsEnabled(true)
  632. assert.True(t, s.TypingEventsEnabled())
  633. s.Session().SetTypingEventsEnabled(false)
  634. assert.False(t, s.TypingEventsEnabled())
  635. }
  636. func TestSession_SetAndGetMultiConnFlag(t *testing.T) {
  637. s := NewSession().AddInstance()
  638. assert.Zero(t, s.MultiConnFlag())
  639. s.SetMultiConnFlag(wire.MultiConnFlagsOldClient)
  640. assert.Equal(t, wire.MultiConnFlagsOldClient, s.MultiConnFlag())
  641. s.SetMultiConnFlag(wire.MultiConnFlagsRecentClient)
  642. assert.Equal(t, wire.MultiConnFlagsRecentClient, s.MultiConnFlag())
  643. s.SetMultiConnFlag(wire.MultiConnFlagsSingleClient)
  644. assert.Equal(t, wire.MultiConnFlagsSingleClient, s.MultiConnFlag())
  645. }
  646. func TestSession_SetAndGetLastWarnLevel(t *testing.T) {
  647. s := NewSession().AddInstance()
  648. assert.Zero(t, s.Warning())
  649. level := uint16(500)
  650. s.Session().SetWarning(level)
  651. assert.Equal(t, level, s.Warning())
  652. }
  653. func TestInstance_Active(t *testing.T) {
  654. tests := []struct {
  655. name string
  656. setupInstance func() *SessionInstance
  657. expectedActive bool
  658. }{
  659. {
  660. name: "active instance - not closed, not idle, no away message",
  661. setupInstance: func() *SessionInstance {
  662. sg := NewSession()
  663. instance := &SessionInstance{
  664. session: sg,
  665. closed: false,
  666. idle: false,
  667. awayMsg: "",
  668. signonComplete: true,
  669. }
  670. return instance
  671. },
  672. expectedActive: true,
  673. },
  674. {
  675. name: "inactive instance - closed",
  676. setupInstance: func() *SessionInstance {
  677. sg := NewSession()
  678. instance := &SessionInstance{
  679. session: sg,
  680. closed: true,
  681. idle: false,
  682. awayMsg: "",
  683. }
  684. return instance
  685. },
  686. expectedActive: false,
  687. },
  688. {
  689. name: "inactive instance - idle",
  690. setupInstance: func() *SessionInstance {
  691. sg := NewSession()
  692. instance := &SessionInstance{
  693. session: sg,
  694. closed: false,
  695. idle: true,
  696. awayMsg: "",
  697. }
  698. return instance
  699. },
  700. expectedActive: false,
  701. },
  702. {
  703. name: "inactive instance - has away message",
  704. setupInstance: func() *SessionInstance {
  705. sg := NewSession()
  706. instance := &SessionInstance{
  707. session: sg,
  708. closed: false,
  709. idle: false,
  710. awayMsg: "I'm away",
  711. userInfoBitmask: wire.OServiceUserFlagUnavailable,
  712. }
  713. return instance
  714. },
  715. expectedActive: false,
  716. },
  717. {
  718. name: "inactive instance - closed and idle",
  719. setupInstance: func() *SessionInstance {
  720. sg := NewSession()
  721. instance := &SessionInstance{
  722. session: sg,
  723. closed: true,
  724. idle: true,
  725. awayMsg: "",
  726. }
  727. return instance
  728. },
  729. expectedActive: false,
  730. },
  731. {
  732. name: "inactive instance - closed and has away message",
  733. setupInstance: func() *SessionInstance {
  734. sg := NewSession()
  735. instance := &SessionInstance{
  736. session: sg,
  737. closed: true,
  738. idle: false,
  739. awayMsg: "I'm away",
  740. userInfoBitmask: wire.OServiceUserFlagUnavailable,
  741. }
  742. return instance
  743. },
  744. expectedActive: false,
  745. },
  746. {
  747. name: "inactive instance - idle and has away message",
  748. setupInstance: func() *SessionInstance {
  749. sg := NewSession()
  750. instance := &SessionInstance{
  751. session: sg,
  752. closed: false,
  753. idle: true,
  754. awayMsg: "I'm away",
  755. userInfoBitmask: wire.OServiceUserFlagUnavailable,
  756. }
  757. return instance
  758. },
  759. expectedActive: false,
  760. },
  761. {
  762. name: "inactive instance - closed, idle, and has away message",
  763. setupInstance: func() *SessionInstance {
  764. sg := NewSession()
  765. instance := &SessionInstance{
  766. session: sg,
  767. closed: true,
  768. idle: true,
  769. awayMsg: "I'm away",
  770. userInfoBitmask: wire.OServiceUserFlagUnavailable,
  771. }
  772. return instance
  773. },
  774. expectedActive: false,
  775. },
  776. {
  777. name: "inactive instance - signon not complete",
  778. setupInstance: func() *SessionInstance {
  779. sg := NewSession()
  780. instance := &SessionInstance{
  781. session: sg,
  782. closed: false,
  783. idle: false,
  784. awayMsg: "",
  785. signonComplete: false,
  786. }
  787. return instance
  788. },
  789. expectedActive: false,
  790. },
  791. {
  792. name: "inactive instance - signon not complete and idle",
  793. setupInstance: func() *SessionInstance {
  794. sg := NewSession()
  795. instance := &SessionInstance{
  796. session: sg,
  797. closed: false,
  798. idle: true,
  799. awayMsg: "",
  800. signonComplete: false,
  801. }
  802. return instance
  803. },
  804. expectedActive: false,
  805. },
  806. }
  807. for _, tt := range tests {
  808. t.Run(tt.name, func(t *testing.T) {
  809. instance := tt.setupInstance()
  810. assert.Equal(t, tt.expectedActive, instance.active())
  811. })
  812. }
  813. }
  814. func TestSessionGroup_AllInactive(t *testing.T) {
  815. tests := []struct {
  816. name string
  817. setupSessionGroup func() *Session
  818. expectedResult bool
  819. }{
  820. {
  821. name: "no instances - should return true",
  822. setupSessionGroup: func() *Session {
  823. return NewSession()
  824. },
  825. expectedResult: true,
  826. },
  827. {
  828. name: "one active instance - should return false",
  829. setupSessionGroup: func() *Session {
  830. sg := NewSession()
  831. instance := sg.AddInstance()
  832. instance.closed = false
  833. instance.idle = false
  834. instance.awayMsg = ""
  835. instance.signonComplete = true
  836. return sg
  837. },
  838. expectedResult: false,
  839. },
  840. {
  841. name: "one closed instance - should return true",
  842. setupSessionGroup: func() *Session {
  843. sg := NewSession()
  844. instance := sg.AddInstance()
  845. instance.closed = true
  846. instance.idle = false
  847. instance.awayMsg = ""
  848. return sg
  849. },
  850. expectedResult: true,
  851. },
  852. {
  853. name: "one idle instance - should return true",
  854. setupSessionGroup: func() *Session {
  855. sg := NewSession()
  856. instance := sg.AddInstance()
  857. instance.closed = false
  858. instance.idle = true
  859. instance.awayMsg = ""
  860. return sg
  861. },
  862. expectedResult: true,
  863. },
  864. {
  865. name: "one instance with away message - should return true",
  866. setupSessionGroup: func() *Session {
  867. sg := NewSession()
  868. instance := sg.AddInstance()
  869. instance.closed = false
  870. instance.idle = false
  871. instance.awayMsg = "I'm away"
  872. return sg
  873. },
  874. expectedResult: true,
  875. },
  876. {
  877. name: "multiple instances - all inactive - should return true",
  878. setupSessionGroup: func() *Session {
  879. sg := NewSession()
  880. // Add closed instance
  881. instance1 := sg.AddInstance()
  882. instance1.closed = true
  883. instance1.idle = false
  884. instance1.awayMsg = ""
  885. // Add idle instance
  886. instance2 := sg.AddInstance()
  887. instance2.closed = false
  888. instance2.idle = true
  889. instance2.awayMsg = ""
  890. // Add instance with away message
  891. instance3 := sg.AddInstance()
  892. instance3.closed = false
  893. instance3.idle = false
  894. instance3.awayMsg = "I'm away"
  895. return sg
  896. },
  897. expectedResult: true,
  898. },
  899. {
  900. name: "multiple instances - one active - should return false",
  901. setupSessionGroup: func() *Session {
  902. sg := NewSession()
  903. // Add closed instance
  904. instance1 := sg.AddInstance()
  905. instance1.closed = true
  906. instance1.idle = false
  907. instance1.awayMsg = ""
  908. // Add active instance
  909. instance2 := sg.AddInstance()
  910. instance2.closed = false
  911. instance2.idle = false
  912. instance2.awayMsg = ""
  913. instance2.signonComplete = true
  914. // Add idle instance
  915. instance3 := sg.AddInstance()
  916. instance3.closed = false
  917. instance3.idle = true
  918. instance3.awayMsg = ""
  919. return sg
  920. },
  921. expectedResult: false,
  922. },
  923. {
  924. name: "multiple instances - all active - should return false",
  925. setupSessionGroup: func() *Session {
  926. sg := NewSession()
  927. // Add first active instance
  928. instance1 := sg.AddInstance()
  929. instance1.closed = false
  930. instance1.idle = false
  931. instance1.awayMsg = ""
  932. instance1.signonComplete = true
  933. // Add second active instance
  934. instance2 := sg.AddInstance()
  935. instance2.closed = false
  936. instance2.idle = false
  937. instance2.awayMsg = ""
  938. instance2.signonComplete = true
  939. return sg
  940. },
  941. expectedResult: false,
  942. },
  943. {
  944. name: "mixed scenarios - some closed, some idle, some away, one active - should return false",
  945. setupSessionGroup: func() *Session {
  946. sg := NewSession()
  947. // Add closed instance
  948. instance1 := sg.AddInstance()
  949. instance1.closed = true
  950. instance1.idle = false
  951. instance1.awayMsg = ""
  952. // Add idle instance
  953. instance2 := sg.AddInstance()
  954. instance2.closed = false
  955. instance2.idle = true
  956. instance2.awayMsg = ""
  957. // Add instance with away message
  958. instance3 := sg.AddInstance()
  959. instance3.closed = false
  960. instance3.idle = false
  961. instance3.awayMsg = "I'm away"
  962. // Add active instance
  963. instance4 := sg.AddInstance()
  964. instance4.closed = false
  965. instance4.idle = false
  966. instance4.awayMsg = ""
  967. instance4.signonComplete = true
  968. return sg
  969. },
  970. expectedResult: false,
  971. },
  972. }
  973. for _, tt := range tests {
  974. t.Run(tt.name, func(t *testing.T) {
  975. sg := tt.setupSessionGroup()
  976. assert.Equal(t, tt.expectedResult, sg.Inactive())
  977. })
  978. }
  979. }
  980. func TestSessionGroup_InstanceCount(t *testing.T) {
  981. tests := []struct {
  982. name string
  983. setupGroup func() *Session
  984. expectedCount int
  985. }{
  986. {
  987. name: "empty session group should return 0",
  988. setupGroup: func() *Session {
  989. return NewSession()
  990. },
  991. expectedCount: 0,
  992. },
  993. {
  994. name: "one instance should return 1",
  995. setupGroup: func() *Session {
  996. sg := NewSession()
  997. sg.AddInstance()
  998. return sg
  999. },
  1000. expectedCount: 1,
  1001. },
  1002. {
  1003. name: "multiple instances should return correct count",
  1004. setupGroup: func() *Session {
  1005. sg := NewSession()
  1006. for i := 0; i < 3; i++ {
  1007. sg.AddInstance()
  1008. }
  1009. return sg
  1010. },
  1011. expectedCount: 3,
  1012. },
  1013. {
  1014. name: "instance count decreases after removal",
  1015. setupGroup: func() *Session {
  1016. sg := NewSession()
  1017. sg.AddInstance()
  1018. instance2 := sg.AddInstance()
  1019. sg.AddInstance()
  1020. // Remove one instance
  1021. sg.RemoveInstance(instance2)
  1022. return sg
  1023. },
  1024. expectedCount: 2,
  1025. },
  1026. {
  1027. name: "instance count is correct after multiple add/remove operations",
  1028. setupGroup: func() *Session {
  1029. sg := NewSession()
  1030. instance1 := sg.AddInstance()
  1031. sg.AddInstance()
  1032. sg.RemoveInstance(instance1)
  1033. sg.AddInstance()
  1034. return sg
  1035. },
  1036. expectedCount: 2,
  1037. },
  1038. }
  1039. for _, tt := range tests {
  1040. t.Run(tt.name, func(t *testing.T) {
  1041. sg := tt.setupGroup()
  1042. assert.Equal(t, tt.expectedCount, sg.InstanceCount())
  1043. })
  1044. }
  1045. }
  1046. func TestSessionGroup_Instances(t *testing.T) {
  1047. tests := []struct {
  1048. name string
  1049. setupGroup func() *Session
  1050. expectedCount int
  1051. expectedAll bool // whether all instances should be returned (including non-signed-in)
  1052. }{
  1053. {
  1054. name: "empty session group should return empty slice",
  1055. setupGroup: func() *Session {
  1056. return NewSession()
  1057. },
  1058. expectedCount: 0,
  1059. expectedAll: true,
  1060. },
  1061. {
  1062. name: "returns all instances including non-signed-in",
  1063. setupGroup: func() *Session {
  1064. sg := NewSession()
  1065. instance1 := sg.AddInstance()
  1066. instance1.SetSignonComplete()
  1067. _ = sg.AddInstance()
  1068. // instance2 has not completed signon
  1069. return sg
  1070. },
  1071. expectedCount: 2,
  1072. expectedAll: true,
  1073. },
  1074. {
  1075. name: "returns all instances with mixed signon states",
  1076. setupGroup: func() *Session {
  1077. sg := NewSession()
  1078. instance1 := sg.AddInstance()
  1079. instance1.SetSignonComplete()
  1080. _ = sg.AddInstance()
  1081. // instance2 has not completed signon
  1082. instance3 := sg.AddInstance()
  1083. instance3.SetSignonComplete()
  1084. return sg
  1085. },
  1086. expectedCount: 3,
  1087. expectedAll: true,
  1088. },
  1089. }
  1090. for _, tt := range tests {
  1091. t.Run(tt.name, func(t *testing.T) {
  1092. sg := tt.setupGroup()
  1093. instances := sg.Instances()
  1094. assert.Equal(t, tt.expectedCount, len(instances), "should return all instances")
  1095. if tt.expectedAll {
  1096. // Verify that instances() returns all instances regardless of their state
  1097. // This is the key change: it should return all instances, not just live ones
  1098. assert.Equal(t, sg.InstanceCount(), len(instances), "Instances() should return all instances")
  1099. }
  1100. })
  1101. }
  1102. }
  1103. func TestSession_SetAndGetProfile(t *testing.T) {
  1104. s := NewSession().AddInstance()
  1105. profile := s.Session().Profile()
  1106. assert.Empty(t, profile.ProfileText)
  1107. assert.Empty(t, profile.MIMEType)
  1108. assert.True(t, profile.UpdateTime.IsZero())
  1109. profileTime := time.Unix(1234567890, 0)
  1110. newProfile := UserProfile{
  1111. ProfileText: "My profile text",
  1112. MIMEType: "text/plain",
  1113. UpdateTime: profileTime,
  1114. }
  1115. s.SetProfile(newProfile)
  1116. retrievedProfile := s.Session().Profile()
  1117. assert.Equal(t, newProfile, retrievedProfile)
  1118. assert.Equal(t, "My profile text", retrievedProfile.ProfileText)
  1119. assert.Equal(t, "text/plain", retrievedProfile.MIMEType)
  1120. assert.Equal(t, profileTime, retrievedProfile.UpdateTime)
  1121. }
  1122. func TestSession_Profile(t *testing.T) {
  1123. tests := []struct {
  1124. name string
  1125. setupSession func() *Session
  1126. expectedProfile UserProfile
  1127. }{
  1128. {
  1129. name: "no instances - returns empty profile",
  1130. setupSession: func() *Session {
  1131. return NewSession()
  1132. },
  1133. expectedProfile: UserProfile{},
  1134. },
  1135. {
  1136. name: "one instance with empty profile - returns empty profile",
  1137. setupSession: func() *Session {
  1138. s := NewSession()
  1139. s.AddInstance()
  1140. return s
  1141. },
  1142. expectedProfile: UserProfile{},
  1143. },
  1144. {
  1145. name: "one instance with non-empty profile - returns that profile",
  1146. setupSession: func() *Session {
  1147. s := NewSession()
  1148. instance := s.AddInstance()
  1149. profileTime := time.Unix(1234567890, 0)
  1150. instance.SetProfile(UserProfile{
  1151. ProfileText: "My profile",
  1152. MIMEType: "text/plain",
  1153. UpdateTime: profileTime,
  1154. })
  1155. return s
  1156. },
  1157. expectedProfile: UserProfile{
  1158. ProfileText: "My profile",
  1159. MIMEType: "text/plain",
  1160. UpdateTime: time.Unix(1234567890, 0),
  1161. },
  1162. },
  1163. {
  1164. name: "multiple instances, all empty - returns empty profile",
  1165. setupSession: func() *Session {
  1166. s := NewSession()
  1167. s.AddInstance()
  1168. s.AddInstance()
  1169. s.AddInstance()
  1170. return s
  1171. },
  1172. expectedProfile: UserProfile{},
  1173. },
  1174. {
  1175. name: "multiple instances, one non-empty - returns that one",
  1176. setupSession: func() *Session {
  1177. s := NewSession()
  1178. s.AddInstance() // empty instance
  1179. instance2 := s.AddInstance()
  1180. instance2.SetProfile(UserProfile{
  1181. ProfileText: "Profile 2",
  1182. MIMEType: "text/plain",
  1183. UpdateTime: time.Unix(1234567890, 0),
  1184. })
  1185. s.AddInstance() // empty instance
  1186. return s
  1187. },
  1188. expectedProfile: UserProfile{
  1189. ProfileText: "Profile 2",
  1190. MIMEType: "text/plain",
  1191. UpdateTime: time.Unix(1234567890, 0),
  1192. },
  1193. },
  1194. {
  1195. name: "multiple instances, multiple non-empty - returns most recent UpdateTime",
  1196. setupSession: func() *Session {
  1197. s := NewSession()
  1198. instance1 := s.AddInstance()
  1199. instance1.SetProfile(UserProfile{
  1200. ProfileText: "Profile 1",
  1201. MIMEType: "text/plain",
  1202. UpdateTime: time.Unix(1234567900, 0), // later time - should be returned
  1203. })
  1204. instance2 := s.AddInstance()
  1205. instance2.SetProfile(UserProfile{
  1206. ProfileText: "Profile 2",
  1207. MIMEType: "text/plain",
  1208. UpdateTime: time.Unix(1234567890, 0), // earlier time
  1209. })
  1210. instance3 := s.AddInstance()
  1211. instance3.SetProfile(UserProfile{
  1212. ProfileText: "Profile 3",
  1213. MIMEType: "text/plain",
  1214. UpdateTime: time.Unix(1234567895, 0), // middle time
  1215. })
  1216. return s
  1217. },
  1218. expectedProfile: UserProfile{
  1219. ProfileText: "Profile 1",
  1220. MIMEType: "text/plain",
  1221. UpdateTime: time.Unix(1234567900, 0),
  1222. },
  1223. },
  1224. {
  1225. name: "first instance empty, later instances have profiles - returns most recent non-empty",
  1226. setupSession: func() *Session {
  1227. s := NewSession()
  1228. s.AddInstance() // empty instance
  1229. instance2 := s.AddInstance()
  1230. instance2.SetProfile(UserProfile{
  1231. ProfileText: "Profile 2",
  1232. MIMEType: "text/plain",
  1233. UpdateTime: time.Unix(1234567890, 0), // earlier
  1234. })
  1235. instance3 := s.AddInstance()
  1236. instance3.SetProfile(UserProfile{
  1237. ProfileText: "Profile 3",
  1238. MIMEType: "text/plain",
  1239. UpdateTime: time.Unix(1234567900, 0), // later time - should be returned
  1240. })
  1241. return s
  1242. },
  1243. expectedProfile: UserProfile{
  1244. ProfileText: "Profile 3",
  1245. MIMEType: "text/plain",
  1246. UpdateTime: time.Unix(1234567900, 0),
  1247. },
  1248. },
  1249. {
  1250. name: "profile with empty ProfileText is considered empty",
  1251. setupSession: func() *Session {
  1252. s := NewSession()
  1253. instance := s.AddInstance()
  1254. instance.SetProfile(UserProfile{
  1255. ProfileText: "",
  1256. MIMEType: "text/plain",
  1257. UpdateTime: time.Unix(1234567890, 0),
  1258. })
  1259. return s
  1260. },
  1261. expectedProfile: UserProfile{},
  1262. },
  1263. {
  1264. name: "profile with null byte ProfileText is considered empty",
  1265. setupSession: func() *Session {
  1266. s := NewSession()
  1267. instance := s.AddInstance()
  1268. instance.SetProfile(UserProfile{
  1269. ProfileText: "\x00",
  1270. MIMEType: "text/plain",
  1271. UpdateTime: time.Unix(1234567890, 0),
  1272. })
  1273. return s
  1274. },
  1275. expectedProfile: UserProfile{},
  1276. },
  1277. }
  1278. for _, tt := range tests {
  1279. t.Run(tt.name, func(t *testing.T) {
  1280. s := tt.setupSession()
  1281. profile := s.Profile()
  1282. assert.Equal(t, tt.expectedProfile, profile)
  1283. })
  1284. }
  1285. }
  1286. func TestSession_SetAndGetMemberSince(t *testing.T) {
  1287. s := NewSession().AddInstance()
  1288. assert.True(t, s.Session().MemberSince().IsZero())
  1289. memberTime := time.Unix(1234567890, 0)
  1290. s.Session().SetMemberSince(memberTime)
  1291. assert.Equal(t, memberTime, s.Session().MemberSince())
  1292. }
  1293. func TestSession_SetAndGetOfflineMsgCount(t *testing.T) {
  1294. s := NewSession().AddInstance()
  1295. assert.Zero(t, s.OfflineMsgCount())
  1296. count := 5
  1297. s.Session().SetOfflineMsgCount(count)
  1298. assert.Equal(t, count, s.OfflineMsgCount())
  1299. count = 10
  1300. s.Session().SetOfflineMsgCount(count)
  1301. assert.Equal(t, count, s.OfflineMsgCount())
  1302. }
  1303. func TestSession_ScaleWarningAndRateLimit(t *testing.T) {
  1304. t.Run("scale up", func(t *testing.T) {
  1305. classParams := [5]wire.RateClass{
  1306. {},
  1307. {},
  1308. {
  1309. ID: 3,
  1310. WindowSize: 20,
  1311. ClearLevel: 5100,
  1312. AlertLevel: 5000,
  1313. LimitLevel: 4000,
  1314. DisconnectLevel: 3000,
  1315. MaxLevel: 6000,
  1316. },
  1317. {},
  1318. {},
  1319. }
  1320. rateClasses := wire.NewRateLimitClasses(classParams)
  1321. now := time.Now()
  1322. instance := NewSession().AddInstance()
  1323. instance.Session().SetRateClasses(now, rateClasses)
  1324. var wg sync.WaitGroup
  1325. wg.Add(1)
  1326. ctx, cancel := context.WithCancel(t.Context())
  1327. go func() {
  1328. defer wg.Done()
  1329. for {
  1330. select {
  1331. case <-ctx.Done():
  1332. return
  1333. case <-instance.WarningCh():
  1334. }
  1335. }
  1336. }()
  1337. rateLimitStates := instance.RateLimitStates()
  1338. assert.Equal(t, int32(5000), rateLimitStates[2].AlertLevel)
  1339. assert.Equal(t, int32(5100), rateLimitStates[2].ClearLevel)
  1340. assert.Equal(t, int32(4000), rateLimitStates[2].LimitLevel)
  1341. instance.Session().ScaleWarningAndRateLimit(100, 3)
  1342. rateLimitStates = instance.RateLimitStates()
  1343. assert.Equal(t, int32(5085), rateLimitStates[2].AlertLevel)
  1344. assert.Equal(t, int32(5175), rateLimitStates[2].ClearLevel)
  1345. assert.Equal(t, int32(4185), rateLimitStates[2].LimitLevel)
  1346. instance.Session().ScaleWarningAndRateLimit(100, 3)
  1347. rateLimitStates = instance.RateLimitStates()
  1348. assert.Equal(t, int32(5170), rateLimitStates[2].AlertLevel)
  1349. assert.Equal(t, int32(5250), rateLimitStates[2].ClearLevel)
  1350. assert.Equal(t, int32(4370), rateLimitStates[2].LimitLevel)
  1351. instance.Session().ScaleWarningAndRateLimit(100, 3)
  1352. rateLimitStates = instance.RateLimitStates()
  1353. assert.Equal(t, int32(5255), rateLimitStates[2].AlertLevel)
  1354. assert.Equal(t, int32(5325), rateLimitStates[2].ClearLevel)
  1355. assert.Equal(t, int32(4555), rateLimitStates[2].LimitLevel)
  1356. instance.Session().ScaleWarningAndRateLimit(100, 3)
  1357. rateLimitStates = instance.RateLimitStates()
  1358. assert.Equal(t, int32(5340), rateLimitStates[2].AlertLevel)
  1359. assert.Equal(t, int32(5400), rateLimitStates[2].ClearLevel)
  1360. assert.Equal(t, int32(4740), rateLimitStates[2].LimitLevel)
  1361. instance.Session().ScaleWarningAndRateLimit(100, 3)
  1362. rateLimitStates = instance.RateLimitStates()
  1363. assert.Equal(t, int32(5425), rateLimitStates[2].AlertLevel)
  1364. assert.Equal(t, int32(5475), rateLimitStates[2].ClearLevel)
  1365. assert.Equal(t, int32(4925), rateLimitStates[2].LimitLevel)
  1366. instance.Session().ScaleWarningAndRateLimit(100, 3)
  1367. rateLimitStates = instance.RateLimitStates()
  1368. assert.Equal(t, int32(5510), rateLimitStates[2].AlertLevel)
  1369. assert.Equal(t, int32(5550), rateLimitStates[2].ClearLevel)
  1370. assert.Equal(t, int32(5110), rateLimitStates[2].LimitLevel)
  1371. instance.Session().ScaleWarningAndRateLimit(100, 3)
  1372. rateLimitStates = instance.RateLimitStates()
  1373. assert.Equal(t, int32(5595), rateLimitStates[2].AlertLevel)
  1374. assert.Equal(t, int32(5625), rateLimitStates[2].ClearLevel)
  1375. assert.Equal(t, int32(5295), rateLimitStates[2].LimitLevel)
  1376. instance.Session().ScaleWarningAndRateLimit(100, 3)
  1377. rateLimitStates = instance.RateLimitStates()
  1378. assert.Equal(t, int32(5680), rateLimitStates[2].AlertLevel)
  1379. assert.Equal(t, int32(5700), rateLimitStates[2].ClearLevel)
  1380. assert.Equal(t, int32(5480), rateLimitStates[2].LimitLevel)
  1381. instance.Session().ScaleWarningAndRateLimit(100, 3)
  1382. rateLimitStates = instance.RateLimitStates()
  1383. assert.Equal(t, int32(5765), rateLimitStates[2].AlertLevel)
  1384. assert.Equal(t, int32(5775), rateLimitStates[2].ClearLevel)
  1385. assert.Equal(t, int32(5665), rateLimitStates[2].LimitLevel)
  1386. instance.Session().ScaleWarningAndRateLimit(100, 3)
  1387. rateLimitStates = instance.RateLimitStates()
  1388. assert.Equal(t, int32(5850), rateLimitStates[2].AlertLevel)
  1389. assert.Equal(t, int32(5850), rateLimitStates[2].ClearLevel)
  1390. assert.Equal(t, int32(5850), rateLimitStates[2].LimitLevel)
  1391. instance.Session().ScaleWarningAndRateLimit(100, 3)
  1392. rateLimitStates = instance.RateLimitStates()
  1393. assert.Equal(t, int32(5850), rateLimitStates[2].AlertLevel)
  1394. assert.Equal(t, int32(5850), rateLimitStates[2].ClearLevel)
  1395. assert.Equal(t, int32(5850), rateLimitStates[2].LimitLevel)
  1396. cancel()
  1397. wg.Wait()
  1398. })
  1399. t.Run("scale down", func(t *testing.T) {
  1400. currentClassParams := [5]wire.RateClass{
  1401. {},
  1402. {},
  1403. {
  1404. ID: 3,
  1405. WindowSize: 20,
  1406. ClearLevel: 5100,
  1407. AlertLevel: 5000,
  1408. LimitLevel: 4000,
  1409. DisconnectLevel: 3000,
  1410. MaxLevel: 6000,
  1411. },
  1412. {},
  1413. {},
  1414. }
  1415. rateClasses := wire.NewRateLimitClasses(currentClassParams)
  1416. now := time.Now()
  1417. instance := NewSession().AddInstance()
  1418. instance.Session().SetRateClasses(now, rateClasses)
  1419. var wg sync.WaitGroup
  1420. wg.Add(1)
  1421. ctx, cancel := context.WithCancel(t.Context())
  1422. go func() {
  1423. defer wg.Done()
  1424. for {
  1425. select {
  1426. case <-ctx.Done():
  1427. return
  1428. case <-instance.WarningCh():
  1429. }
  1430. }
  1431. }()
  1432. for i := 0; i < 10; i++ {
  1433. instance.Session().ScaleWarningAndRateLimit(100, 3)
  1434. }
  1435. rateLimitStates := instance.RateLimitStates()
  1436. assert.Equal(t, int32(5850), rateLimitStates[2].AlertLevel)
  1437. assert.Equal(t, int32(5850), rateLimitStates[2].ClearLevel)
  1438. assert.Equal(t, int32(5850), rateLimitStates[2].LimitLevel)
  1439. instance.Session().ScaleWarningAndRateLimit(-100, 3)
  1440. rateLimitStates = instance.RateLimitStates()
  1441. assert.Equal(t, int32(5765), rateLimitStates[2].AlertLevel)
  1442. assert.Equal(t, int32(5775), rateLimitStates[2].ClearLevel)
  1443. assert.Equal(t, int32(5665), rateLimitStates[2].LimitLevel)
  1444. instance.Session().ScaleWarningAndRateLimit(-100, 3)
  1445. rateLimitStates = instance.RateLimitStates()
  1446. assert.Equal(t, int32(5680), rateLimitStates[2].AlertLevel)
  1447. assert.Equal(t, int32(5700), rateLimitStates[2].ClearLevel)
  1448. assert.Equal(t, int32(5480), rateLimitStates[2].LimitLevel)
  1449. instance.Session().ScaleWarningAndRateLimit(-100, 3)
  1450. rateLimitStates = instance.RateLimitStates()
  1451. assert.Equal(t, int32(5595), rateLimitStates[2].AlertLevel)
  1452. assert.Equal(t, int32(5625), rateLimitStates[2].ClearLevel)
  1453. assert.Equal(t, int32(5295), rateLimitStates[2].LimitLevel)
  1454. instance.Session().ScaleWarningAndRateLimit(-100, 3)
  1455. rateLimitStates = instance.RateLimitStates()
  1456. assert.Equal(t, int32(5510), rateLimitStates[2].AlertLevel)
  1457. assert.Equal(t, int32(5550), rateLimitStates[2].ClearLevel)
  1458. assert.Equal(t, int32(5110), rateLimitStates[2].LimitLevel)
  1459. instance.Session().ScaleWarningAndRateLimit(-100, 3)
  1460. rateLimitStates = instance.RateLimitStates()
  1461. assert.Equal(t, int32(5425), rateLimitStates[2].AlertLevel)
  1462. assert.Equal(t, int32(5475), rateLimitStates[2].ClearLevel)
  1463. assert.Equal(t, int32(4925), rateLimitStates[2].LimitLevel)
  1464. instance.Session().ScaleWarningAndRateLimit(-100, 3)
  1465. rateLimitStates = instance.RateLimitStates()
  1466. assert.Equal(t, int32(5340), rateLimitStates[2].AlertLevel)
  1467. assert.Equal(t, int32(5400), rateLimitStates[2].ClearLevel)
  1468. assert.Equal(t, int32(4740), rateLimitStates[2].LimitLevel)
  1469. instance.Session().ScaleWarningAndRateLimit(-100, 3)
  1470. rateLimitStates = instance.RateLimitStates()
  1471. assert.Equal(t, int32(5255), rateLimitStates[2].AlertLevel)
  1472. assert.Equal(t, int32(5325), rateLimitStates[2].ClearLevel)
  1473. assert.Equal(t, int32(4555), rateLimitStates[2].LimitLevel)
  1474. instance.Session().ScaleWarningAndRateLimit(-100, 3)
  1475. rateLimitStates = instance.RateLimitStates()
  1476. assert.Equal(t, int32(5170), rateLimitStates[2].AlertLevel)
  1477. assert.Equal(t, int32(5250), rateLimitStates[2].ClearLevel)
  1478. assert.Equal(t, int32(4370), rateLimitStates[2].LimitLevel)
  1479. instance.Session().ScaleWarningAndRateLimit(-100, 3)
  1480. rateLimitStates = instance.RateLimitStates()
  1481. assert.Equal(t, int32(5085), rateLimitStates[2].AlertLevel)
  1482. assert.Equal(t, int32(5175), rateLimitStates[2].ClearLevel)
  1483. assert.Equal(t, int32(4185), rateLimitStates[2].LimitLevel)
  1484. instance.Session().ScaleWarningAndRateLimit(-100, 3)
  1485. rateLimitStates = instance.RateLimitStates()
  1486. assert.Equal(t, int32(5000), rateLimitStates[2].AlertLevel)
  1487. assert.Equal(t, int32(5100), rateLimitStates[2].ClearLevel)
  1488. assert.Equal(t, int32(4000), rateLimitStates[2].LimitLevel)
  1489. instance.Session().ScaleWarningAndRateLimit(-100, 3)
  1490. rateLimitStates = instance.RateLimitStates()
  1491. assert.Equal(t, int32(5000), rateLimitStates[2].AlertLevel)
  1492. assert.Equal(t, int32(5100), rateLimitStates[2].ClearLevel)
  1493. assert.Equal(t, int32(4000), rateLimitStates[2].LimitLevel)
  1494. cancel()
  1495. wg.Wait()
  1496. })
  1497. t.Run("increment 100%", func(t *testing.T) {
  1498. classParams := [5]wire.RateClass{
  1499. {},
  1500. {},
  1501. {
  1502. ID: 3,
  1503. WindowSize: 20,
  1504. ClearLevel: 5100,
  1505. AlertLevel: 5000,
  1506. LimitLevel: 4000,
  1507. DisconnectLevel: 3000,
  1508. MaxLevel: 6000,
  1509. },
  1510. {},
  1511. {},
  1512. }
  1513. rateClasses := wire.NewRateLimitClasses(classParams)
  1514. now := time.Now()
  1515. instance := NewSession().AddInstance()
  1516. instance.Session().SetRateClasses(now, rateClasses)
  1517. var wg sync.WaitGroup
  1518. wg.Add(1)
  1519. ctx, cancel := context.WithCancel(t.Context())
  1520. go func() {
  1521. defer wg.Done()
  1522. for {
  1523. select {
  1524. case <-ctx.Done():
  1525. return
  1526. case <-instance.WarningCh():
  1527. }
  1528. }
  1529. }()
  1530. rateLimitStates := instance.RateLimitStates()
  1531. assert.Equal(t, int32(5000), rateLimitStates[2].AlertLevel)
  1532. assert.Equal(t, int32(5100), rateLimitStates[2].ClearLevel)
  1533. assert.Equal(t, int32(4000), rateLimitStates[2].LimitLevel)
  1534. instance.Session().ScaleWarningAndRateLimit(1000, 3)
  1535. rateLimitStates = instance.RateLimitStates()
  1536. assert.Equal(t, int32(5850), rateLimitStates[2].AlertLevel)
  1537. assert.Equal(t, int32(5850), rateLimitStates[2].ClearLevel)
  1538. assert.Equal(t, int32(5850), rateLimitStates[2].LimitLevel)
  1539. cancel()
  1540. wg.Wait()
  1541. })
  1542. }
  1543. func TestSession_RunOnce(t *testing.T) {
  1544. t.Run("runs function on first call", func(t *testing.T) {
  1545. s := NewSession()
  1546. callCount := 0
  1547. err := s.RunOnce(func() error {
  1548. callCount++
  1549. return nil
  1550. })
  1551. assert.NoError(t, err)
  1552. assert.Equal(t, 1, callCount)
  1553. })
  1554. t.Run("does not run function on subsequent calls", func(t *testing.T) {
  1555. s := NewSession()
  1556. callCount := 0
  1557. // First call
  1558. err1 := s.RunOnce(func() error {
  1559. callCount++
  1560. return nil
  1561. })
  1562. // Second call
  1563. err2 := s.RunOnce(func() error {
  1564. callCount++
  1565. return nil
  1566. })
  1567. // Third call
  1568. err3 := s.RunOnce(func() error {
  1569. callCount++
  1570. return nil
  1571. })
  1572. assert.NoError(t, err1)
  1573. assert.NoError(t, err2)
  1574. assert.NoError(t, err3)
  1575. assert.Equal(t, 1, callCount, "function should only be called once")
  1576. })
  1577. t.Run("returns error from function", func(t *testing.T) {
  1578. s := NewSession()
  1579. expectedErr := assert.AnError
  1580. err := s.RunOnce(func() error {
  1581. return expectedErr
  1582. })
  1583. assert.Error(t, err)
  1584. assert.Equal(t, expectedErr, err)
  1585. })
  1586. }
  1587. func TestSession_CloseInstance(t *testing.T) {
  1588. s := NewSession()
  1589. sessionCloseCount := 0
  1590. s.OnSessionClose(func() {
  1591. sessionCloseCount++
  1592. })
  1593. instance1CloseCount := 0
  1594. instance2CloseCount := 0
  1595. instance3CloseCount := 0
  1596. instance1 := s.AddInstance()
  1597. instance2 := s.AddInstance()
  1598. instance3 := s.AddInstance()
  1599. instance1.OnClose(func() {
  1600. // ensure instance is removed from the session before calling this func
  1601. assert.Equal(t, 2, s.InstanceCount())
  1602. instance1CloseCount++
  1603. })
  1604. instance2.OnClose(func() {
  1605. assert.Equal(t, 1, s.InstanceCount())
  1606. instance2CloseCount++
  1607. })
  1608. instance3.OnClose(func() {
  1609. instance3CloseCount++
  1610. })
  1611. // Close instance1 (instances 2 and 3 remain)
  1612. instance1.CloseInstance()
  1613. instance2.CloseInstance()
  1614. instance3.CloseInstance()
  1615. assert.Equal(t, 1, instance1CloseCount, "instance1 onInstanceCloseFn should only be called once")
  1616. assert.Equal(t, 1, instance2CloseCount, "instance2 onInstanceCloseFn should only be called once")
  1617. assert.Equal(t, 0, instance3CloseCount, "instance3 onInstanceCloseFn should not be called because it's the last instance")
  1618. assert.Equal(t, 1, sessionCloseCount, "session onSessCloseFn should not be called")
  1619. }
  1620. func TestSession_CloseSession(t *testing.T) {
  1621. s := NewSession()
  1622. sessionCloseCount := 0
  1623. s.OnSessionClose(func() {
  1624. sessionCloseCount++
  1625. })
  1626. instance1CloseCount := 0
  1627. instance2CloseCount := 0
  1628. instance3CloseCount := 0
  1629. instance1 := s.AddInstance()
  1630. instance2 := s.AddInstance()
  1631. instance3 := s.AddInstance()
  1632. instance1.OnClose(func() {
  1633. instance1CloseCount++
  1634. })
  1635. instance2.OnClose(func() {
  1636. instance2CloseCount++
  1637. })
  1638. instance3.OnClose(func() {
  1639. instance3CloseCount++
  1640. })
  1641. s.CloseSession()
  1642. assert.Equal(t, 0, instance1CloseCount, "instance1 onInstanceCloseFn should not be called")
  1643. assert.Equal(t, 0, instance2CloseCount, "instance2 onInstanceCloseFn should not be called")
  1644. assert.Equal(t, 0, instance3CloseCount, "instance3 onInstanceCloseFn should not be called")
  1645. assert.Equal(t, 1, sessionCloseCount, "session onSessCloseFn should only be called once")
  1646. }
  1647. func TestSession_AwayMessage(t *testing.T) {
  1648. tests := []struct {
  1649. name string
  1650. setupSession func() *Session
  1651. expectedResult string
  1652. }{
  1653. {
  1654. name: "no instances - should return empty string",
  1655. setupSession: func() *Session {
  1656. return NewSession()
  1657. },
  1658. expectedResult: "",
  1659. },
  1660. {
  1661. name: "one instance not away - should return empty string",
  1662. setupSession: func() *Session {
  1663. sg := NewSession()
  1664. _ = sg.AddInstance()
  1665. // instance has no away message and is not set as away
  1666. return sg
  1667. },
  1668. expectedResult: "",
  1669. },
  1670. {
  1671. name: "one instance away via SetUserInfoFlag - should return away message",
  1672. setupSession: func() *Session {
  1673. sg := NewSession()
  1674. instance := sg.AddInstance()
  1675. instance.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1676. instance.SetAwayMessage("I'm away")
  1677. return sg
  1678. },
  1679. expectedResult: "I'm away",
  1680. },
  1681. {
  1682. name: "one instance away via SetUserStatusBitmask - should return away message",
  1683. setupSession: func() *Session {
  1684. sg := NewSession()
  1685. instance := sg.AddInstance()
  1686. instance.SetUserStatusBitmask(wire.OServiceUserStatusAway)
  1687. instance.SetAwayMessage("I'm away")
  1688. return sg
  1689. },
  1690. expectedResult: "I'm away",
  1691. },
  1692. {
  1693. name: "multiple instances - not all away - should return away message from away instance",
  1694. setupSession: func() *Session {
  1695. sg := NewSession()
  1696. instance1 := sg.AddInstance()
  1697. instance1.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1698. instance1.SetAwayMessage("I'm away")
  1699. _ = sg.AddInstance()
  1700. // instance2 has no away message and is not set as away
  1701. return sg
  1702. },
  1703. expectedResult: "I'm away",
  1704. },
  1705. {
  1706. name: "multiple instances - all away - should return latest away message",
  1707. setupSession: func() *Session {
  1708. sg := NewSession()
  1709. baseTime := time.Now()
  1710. callCount := 0
  1711. sg.nowFn = func() time.Time {
  1712. callCount++
  1713. return baseTime.Add(time.Duration(callCount) * time.Second)
  1714. }
  1715. instance1 := sg.AddInstance()
  1716. instance1.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1717. instance1.SetAwayMessage("First away message")
  1718. instance2 := sg.AddInstance()
  1719. instance2.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1720. instance2.SetAwayMessage("Second away message")
  1721. return sg
  1722. },
  1723. expectedResult: "Second away message",
  1724. },
  1725. {
  1726. name: "multiple instances - all away after multiple updates - should return latest away message",
  1727. setupSession: func() *Session {
  1728. sg := NewSession()
  1729. baseTime := time.Now()
  1730. callCount := 0
  1731. sg.nowFn = func() time.Time {
  1732. callCount++
  1733. return baseTime.Add(time.Duration(callCount) * time.Second)
  1734. }
  1735. instance1 := sg.AddInstance()
  1736. instance1.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1737. instance1.SetAwayMessage("First away message")
  1738. instance2 := sg.AddInstance()
  1739. instance2.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1740. instance2.SetAwayMessage("Second away message")
  1741. // Update instance1's away status again (this will update awayTime)
  1742. instance1.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1743. instance1.SetAwayMessage("Third away message")
  1744. return sg
  1745. },
  1746. expectedResult: "Third away message",
  1747. },
  1748. {
  1749. name: "multiple instances - different away methods - should return latest away message",
  1750. setupSession: func() *Session {
  1751. sg := NewSession()
  1752. baseTime := time.Now()
  1753. callCount := 0
  1754. sg.nowFn = func() time.Time {
  1755. callCount++
  1756. return baseTime.Add(time.Duration(callCount) * time.Second)
  1757. }
  1758. instance1 := sg.AddInstance()
  1759. instance1.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1760. instance1.SetAwayMessage("First away message")
  1761. instance2 := sg.AddInstance()
  1762. instance2.SetUserStatusBitmask(wire.OServiceUserStatusAway)
  1763. instance2.SetAwayMessage("Second away message")
  1764. return sg
  1765. },
  1766. expectedResult: "Second away message",
  1767. },
  1768. {
  1769. name: "instance sets away message then clears message - should return empty string",
  1770. setupSession: func() *Session {
  1771. sg := NewSession()
  1772. instance := sg.AddInstance()
  1773. instance.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1774. instance.SetAwayMessage("I'm away")
  1775. instance.SetAwayMessage("") // clear away message (but still away)
  1776. return sg
  1777. },
  1778. expectedResult: "",
  1779. },
  1780. {
  1781. name: "instance sets away message then clears away status - should return empty string",
  1782. setupSession: func() *Session {
  1783. sg := NewSession()
  1784. instance := sg.AddInstance()
  1785. instance.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1786. instance.SetAwayMessage("I'm away")
  1787. instance.ClearUserInfoFlag(wire.OServiceUserFlagUnavailable) // clear away status
  1788. return sg
  1789. },
  1790. expectedResult: "",
  1791. },
  1792. {
  1793. name: "multiple instances - one away with message, one away without message - should return message from most recent",
  1794. setupSession: func() *Session {
  1795. sg := NewSession()
  1796. baseTime := time.Now()
  1797. callCount := 0
  1798. sg.nowFn = func() time.Time {
  1799. callCount++
  1800. return baseTime.Add(time.Duration(callCount) * time.Second)
  1801. }
  1802. instance1 := sg.AddInstance()
  1803. instance1.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1804. instance1.SetAwayMessage("I'm away")
  1805. instance2 := sg.AddInstance()
  1806. instance2.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1807. // instance2 is away but has no message, and was set away after instance1
  1808. return sg
  1809. },
  1810. expectedResult: "", // instance2 has more recent awayTime but no message
  1811. },
  1812. {
  1813. name: "multiple instances - one away with message set later - should return that message",
  1814. setupSession: func() *Session {
  1815. sg := NewSession()
  1816. baseTime := time.Now()
  1817. callCount := 0
  1818. sg.nowFn = func() time.Time {
  1819. callCount++
  1820. return baseTime.Add(time.Duration(callCount) * time.Second)
  1821. }
  1822. instance1 := sg.AddInstance()
  1823. instance1.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1824. instance1.SetAwayMessage("I'm away")
  1825. instance2 := sg.AddInstance()
  1826. instance2.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1827. // instance2 is away but has no message
  1828. // Now update instance1's away status to make it more recent
  1829. instance1.SetUserInfoFlag(wire.OServiceUserFlagUnavailable)
  1830. return sg
  1831. },
  1832. expectedResult: "I'm away", // instance1 has more recent awayTime and has a message
  1833. },
  1834. }
  1835. for _, tt := range tests {
  1836. t.Run(tt.name, func(t *testing.T) {
  1837. sg := tt.setupSession()
  1838. result := sg.AwayMessage()
  1839. assert.Equal(t, tt.expectedResult, result)
  1840. })
  1841. }
  1842. }
  1843. func TestSession_Caps(t *testing.T) {
  1844. // Helper function to compare capability slices (order-independent)
  1845. capsEqual := func(a, b [][16]byte) bool {
  1846. if len(a) != len(b) {
  1847. return false
  1848. }
  1849. capMap := make(map[[16]byte]bool)
  1850. for _, cap := range a {
  1851. capMap[cap] = true
  1852. }
  1853. for _, cap := range b {
  1854. if !capMap[cap] {
  1855. return false
  1856. }
  1857. }
  1858. return true
  1859. }
  1860. tests := []struct {
  1861. name string
  1862. setupSession func() *Session
  1863. expectedCaps [][16]byte
  1864. expectedCount int
  1865. }{
  1866. {
  1867. name: "empty session with no instances - should return empty slice",
  1868. setupSession: func() *Session {
  1869. return NewSession()
  1870. },
  1871. expectedCaps: [][16]byte{},
  1872. expectedCount: 0,
  1873. },
  1874. {
  1875. name: "single instance with no capabilities - should return empty slice",
  1876. setupSession: func() *Session {
  1877. sg := NewSession()
  1878. _ = sg.AddInstance()
  1879. return sg
  1880. },
  1881. expectedCaps: [][16]byte{},
  1882. expectedCount: 0,
  1883. },
  1884. {
  1885. name: "single instance with one cap - should return that cap",
  1886. setupSession: func() *Session {
  1887. sg := NewSession()
  1888. instance := sg.AddInstance()
  1889. cap1 := [16]byte{0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}
  1890. instance.SetCaps([][16]byte{cap1})
  1891. return sg
  1892. },
  1893. expectedCaps: [][16]byte{
  1894. {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
  1895. },
  1896. expectedCount: 1,
  1897. },
  1898. {
  1899. name: "single instance with multiple capabilities - should return all capabilities",
  1900. setupSession: func() *Session {
  1901. sg := NewSession()
  1902. instance := sg.AddInstance()
  1903. cap1 := [16]byte{0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}
  1904. cap2 := [16]byte{0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01}
  1905. cap3 := [16]byte{0x76, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x02}
  1906. instance.SetCaps([][16]byte{cap1, cap2, cap3})
  1907. return sg
  1908. },
  1909. expectedCaps: [][16]byte{
  1910. {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
  1911. {0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01},
  1912. {0x76, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x02},
  1913. },
  1914. expectedCount: 3,
  1915. },
  1916. {
  1917. name: "multiple instances with no overlapping capabilities - should return union of all capabilities",
  1918. setupSession: func() *Session {
  1919. sg := NewSession()
  1920. instance1 := sg.AddInstance()
  1921. cap1 := [16]byte{0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}
  1922. instance1.SetCaps([][16]byte{cap1})
  1923. instance2 := sg.AddInstance()
  1924. cap2 := [16]byte{0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01}
  1925. instance2.SetCaps([][16]byte{cap2})
  1926. instance3 := sg.AddInstance()
  1927. cap3 := [16]byte{0x76, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x02}
  1928. instance3.SetCaps([][16]byte{cap3})
  1929. return sg
  1930. },
  1931. expectedCaps: [][16]byte{
  1932. {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
  1933. {0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01},
  1934. {0x76, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x02},
  1935. },
  1936. expectedCount: 3,
  1937. },
  1938. {
  1939. name: "multiple instances with overlapping capabilities - should deduplicate",
  1940. setupSession: func() *Session {
  1941. sg := NewSession()
  1942. cap1 := [16]byte{0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}
  1943. cap2 := [16]byte{0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01}
  1944. cap3 := [16]byte{0x76, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x02}
  1945. instance1 := sg.AddInstance()
  1946. instance1.SetCaps([][16]byte{cap1, cap2})
  1947. instance2 := sg.AddInstance()
  1948. instance2.SetCaps([][16]byte{cap2, cap3}) // cap2 overlaps
  1949. return sg
  1950. },
  1951. expectedCaps: [][16]byte{
  1952. {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
  1953. {0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01},
  1954. {0x76, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x02},
  1955. },
  1956. expectedCount: 3,
  1957. },
  1958. {
  1959. name: "multiple instances with all same capabilities - should return unique capabilities",
  1960. setupSession: func() *Session {
  1961. sg := NewSession()
  1962. cap1 := [16]byte{0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}
  1963. cap2 := [16]byte{0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01}
  1964. instance1 := sg.AddInstance()
  1965. instance1.SetCaps([][16]byte{cap1, cap2})
  1966. instance2 := sg.AddInstance()
  1967. instance2.SetCaps([][16]byte{cap1, cap2}) // same caps
  1968. instance3 := sg.AddInstance()
  1969. instance3.SetCaps([][16]byte{cap1, cap2}) // same caps
  1970. return sg
  1971. },
  1972. expectedCaps: [][16]byte{
  1973. {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
  1974. {0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01},
  1975. },
  1976. expectedCount: 2,
  1977. },
  1978. {
  1979. name: "multiple instances with some having capabilities and some not - should return union",
  1980. setupSession: func() *Session {
  1981. sg := NewSession()
  1982. _ = sg.AddInstance()
  1983. // instance1 has no caps
  1984. instance2 := sg.AddInstance()
  1985. cap1 := [16]byte{0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}
  1986. instance2.SetCaps([][16]byte{cap1})
  1987. _ = sg.AddInstance()
  1988. // instance3 has no caps
  1989. instance4 := sg.AddInstance()
  1990. cap2 := [16]byte{0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01}
  1991. instance4.SetCaps([][16]byte{cap2})
  1992. return sg
  1993. },
  1994. expectedCaps: [][16]byte{
  1995. {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
  1996. {0x75, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x01},
  1997. },
  1998. expectedCount: 2,
  1999. },
  2000. }
  2001. for _, tt := range tests {
  2002. t.Run(tt.name, func(t *testing.T) {
  2003. sg := tt.setupSession()
  2004. result := sg.Caps()
  2005. assert.Equal(t, tt.expectedCount, len(result), "cap count should match")
  2006. assert.True(t, capsEqual(tt.expectedCaps, result), "capabilities should match (order-independent)")
  2007. })
  2008. }
  2009. }
  2010. func TestSession_InstanceNumberAssignment(t *testing.T) {
  2011. t.Run("first instance gets number 1", func(t *testing.T) {
  2012. s := NewSession()
  2013. instance := s.AddInstance()
  2014. assert.Equal(t, uint8(1), instance.Num())
  2015. })
  2016. t.Run("multiple instances get sequential numbers", func(t *testing.T) {
  2017. s := NewSession()
  2018. instance1 := s.AddInstance()
  2019. instance2 := s.AddInstance()
  2020. instance3 := s.AddInstance()
  2021. assert.Equal(t, uint8(1), instance1.Num())
  2022. assert.Equal(t, uint8(2), instance2.Num())
  2023. assert.Equal(t, uint8(3), instance3.Num())
  2024. })
  2025. t.Run("removed instance numbers are reused", func(t *testing.T) {
  2026. s := NewSession()
  2027. instance1 := s.AddInstance()
  2028. instance2 := s.AddInstance()
  2029. instance3 := s.AddInstance()
  2030. assert.Equal(t, uint8(1), instance1.Num())
  2031. assert.Equal(t, uint8(2), instance2.Num())
  2032. assert.Equal(t, uint8(3), instance3.Num())
  2033. // Remove instance 2
  2034. s.RemoveInstance(instance2)
  2035. // New instance should reuse number 2
  2036. instance4 := s.AddInstance()
  2037. assert.Equal(t, uint8(2), instance4.Num())
  2038. // Verify all instance numbers are unique
  2039. instances := s.Instances()
  2040. instanceNums := make(map[uint8]bool)
  2041. for _, inst := range instances {
  2042. assert.False(t, instanceNums[inst.Num()], "instance number %d should be unique", inst.Num())
  2043. instanceNums[inst.Num()] = true
  2044. }
  2045. })
  2046. t.Run("finds lowest available number", func(t *testing.T) {
  2047. s := NewSession()
  2048. // Create instances 1, 2, 3
  2049. instance1 := s.AddInstance()
  2050. instance2 := s.AddInstance()
  2051. instance3 := s.AddInstance()
  2052. assert.Equal(t, uint8(1), instance1.Num())
  2053. assert.Equal(t, uint8(2), instance2.Num())
  2054. assert.Equal(t, uint8(3), instance3.Num())
  2055. // Remove instance 1
  2056. s.RemoveInstance(instance1)
  2057. // New instance should get number 1 (lowest available)
  2058. instance4 := s.AddInstance()
  2059. assert.Equal(t, uint8(1), instance4.Num())
  2060. // Remove instance 2
  2061. s.RemoveInstance(instance2)
  2062. // New instance should get number 2 (lowest available)
  2063. instance5 := s.AddInstance()
  2064. assert.Equal(t, uint8(2), instance5.Num())
  2065. // Verify instance 3 still has its number
  2066. assert.Equal(t, uint8(3), instance3.Num())
  2067. })
  2068. t.Run("panics when all instance numbers are taken", func(t *testing.T) {
  2069. s := NewSession()
  2070. // Fill up all 255 instance numbers
  2071. instances := make([]*SessionInstance, 255)
  2072. for i := 0; i < 255; i++ {
  2073. instances[i] = s.AddInstance()
  2074. }
  2075. // Verify we have 255 instances
  2076. assert.Equal(t, 255, s.InstanceCount())
  2077. // Try to create one more - should panic
  2078. assert.PanicsWithValue(t, "all instance numbers are taken (max 255 instances per session)", func() {
  2079. s.AddInstance()
  2080. })
  2081. })
  2082. }