UserSessionsManagerTests.coffee 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. sinon = require('sinon')
  2. chai = require('chai')
  3. should = chai.should()
  4. expect = chai.expect
  5. modulePath = "../../../../app/js/Features/User/UserSessionsManager.js"
  6. SandboxedModule = require('sandboxed-module')
  7. describe 'UserSessionsManager', ->
  8. beforeEach ->
  9. @user =
  10. _id: "abcd"
  11. email: "user@example.com"
  12. @sessionId = 'some_session_id'
  13. @rclient =
  14. multi: sinon.stub()
  15. exec: sinon.stub()
  16. get: sinon.stub()
  17. del: sinon.stub()
  18. sadd: sinon.stub()
  19. srem: sinon.stub()
  20. smembers: sinon.stub()
  21. mget: sinon.stub()
  22. expire: sinon.stub()
  23. @rclient.multi.returns(@rclient)
  24. @rclient.get.returns(@rclient)
  25. @rclient.del.returns(@rclient)
  26. @rclient.sadd.returns(@rclient)
  27. @rclient.srem.returns(@rclient)
  28. @rclient.smembers.returns(@rclient)
  29. @rclient.expire.returns(@rclient)
  30. @rclient.exec.callsArgWith(0, null)
  31. @redis =
  32. createClient: () => @rclient
  33. @logger =
  34. err: sinon.stub()
  35. error: sinon.stub()
  36. log: sinon.stub()
  37. @settings =
  38. redis:
  39. web: {}
  40. @UserSessionsManager = SandboxedModule.require modulePath, requires:
  41. "redis-sharelatex": @redis
  42. "logger-sharelatex": @logger
  43. "settings-sharelatex": @settings
  44. describe '_sessionSetKey', ->
  45. it 'should build the correct key', ->
  46. result = @UserSessionsManager._sessionSetKey(@user)
  47. result.should.equal 'UserSessions:abcd'
  48. describe '_sessionKey', ->
  49. it 'should build the correct key', ->
  50. result = @UserSessionsManager._sessionKey(@sessionId)
  51. result.should.equal 'sess:some_session_id'
  52. describe 'trackSession', ->
  53. beforeEach ->
  54. @call = (callback) =>
  55. @UserSessionsManager.trackSession @user, @sessionId, callback
  56. @rclient.exec.callsArgWith(0, null)
  57. @_checkSessions = sinon.stub(@UserSessionsManager, '_checkSessions').returns(null)
  58. afterEach ->
  59. @_checkSessions.restore()
  60. it 'should not produce an error', (done) ->
  61. @call (err) =>
  62. expect(err).to.not.be.instanceof Error
  63. done()
  64. it 'should call the appropriate redis methods', (done) ->
  65. @call (err) =>
  66. @rclient.multi.callCount.should.equal 1
  67. @rclient.sadd.callCount.should.equal 1
  68. @rclient.expire.callCount.should.equal 1
  69. @rclient.exec.callCount.should.equal 1
  70. done()
  71. it 'should call _checkSessions', (done) ->
  72. @call (err) =>
  73. @_checkSessions.callCount.should.equal 1
  74. done()
  75. describe 'when rclient produces an error', ->
  76. beforeEach ->
  77. @rclient.exec.callsArgWith(0, new Error('woops'))
  78. it 'should produce an error', (done) ->
  79. @call (err) =>
  80. expect(err).to.be.instanceof Error
  81. done()
  82. it 'should not call _checkSessions', (done) ->
  83. @call (err) =>
  84. @_checkSessions.callCount.should.equal 0
  85. done()
  86. describe 'when no user is supplied', ->
  87. beforeEach ->
  88. @call = (callback) =>
  89. @UserSessionsManager.trackSession null, @sessionId, callback
  90. it 'should not produce an error', (done) ->
  91. @call (err) =>
  92. expect(err).to.not.be.instanceof Error
  93. expect(err).to.equal null
  94. done()
  95. it 'should not call the appropriate redis methods', (done) ->
  96. @call (err) =>
  97. @rclient.multi.callCount.should.equal 0
  98. @rclient.sadd.callCount.should.equal 0
  99. @rclient.expire.callCount.should.equal 0
  100. @rclient.exec.callCount.should.equal 0
  101. done()
  102. it 'should not call _checkSessions', (done) ->
  103. @call (err) =>
  104. @_checkSessions.callCount.should.equal 0
  105. done()
  106. describe 'when no sessionId is supplied', ->
  107. beforeEach ->
  108. @call = (callback) =>
  109. @UserSessionsManager.trackSession @user, null, callback
  110. it 'should not produce an error', (done) ->
  111. @call (err) =>
  112. expect(err).to.not.be.instanceof Error
  113. expect(err).to.equal null
  114. done()
  115. it 'should not call the appropriate redis methods', (done) ->
  116. @call (err) =>
  117. @rclient.multi.callCount.should.equal 0
  118. @rclient.sadd.callCount.should.equal 0
  119. @rclient.expire.callCount.should.equal 0
  120. @rclient.exec.callCount.should.equal 0
  121. done()
  122. it 'should not call _checkSessions', (done) ->
  123. @call (err) =>
  124. @_checkSessions.callCount.should.equal 0
  125. done()
  126. describe 'untrackSession', ->
  127. beforeEach ->
  128. @call = (callback) =>
  129. @UserSessionsManager.untrackSession @user, @sessionId, callback
  130. @rclient.exec.callsArgWith(0, null)
  131. @_checkSessions = sinon.stub(@UserSessionsManager, '_checkSessions').returns(null)
  132. afterEach ->
  133. @_checkSessions.restore()
  134. it 'should not produce an error', (done) ->
  135. @call (err) =>
  136. expect(err).to.not.be.instanceof Error
  137. expect(err).to.equal undefined
  138. done()
  139. it 'should call the appropriate redis methods', (done) ->
  140. @call (err) =>
  141. @rclient.multi.callCount.should.equal 1
  142. @rclient.srem.callCount.should.equal 1
  143. @rclient.expire.callCount.should.equal 1
  144. @rclient.exec.callCount.should.equal 1
  145. done()
  146. it 'should call _checkSessions', (done) ->
  147. @call (err) =>
  148. @_checkSessions.callCount.should.equal 1
  149. done()
  150. describe 'when rclient produces an error', ->
  151. beforeEach ->
  152. @rclient.exec.callsArgWith(0, new Error('woops'))
  153. it 'should produce an error', (done) ->
  154. @call (err) =>
  155. expect(err).to.be.instanceof Error
  156. done()
  157. it 'should not call _checkSessions', (done) ->
  158. @call (err) =>
  159. @_checkSessions.callCount.should.equal 0
  160. done()
  161. describe 'when no user is supplied', ->
  162. beforeEach ->
  163. @call = (callback) =>
  164. @UserSessionsManager.untrackSession null, @sessionId, callback
  165. it 'should not produce an error', (done) ->
  166. @call (err) =>
  167. expect(err).to.not.be.instanceof Error
  168. expect(err).to.equal null
  169. done()
  170. it 'should not call the appropriate redis methods', (done) ->
  171. @call (err) =>
  172. @rclient.multi.callCount.should.equal 0
  173. @rclient.srem.callCount.should.equal 0
  174. @rclient.expire.callCount.should.equal 0
  175. @rclient.exec.callCount.should.equal 0
  176. done()
  177. it 'should not call _checkSessions', (done) ->
  178. @call (err) =>
  179. @_checkSessions.callCount.should.equal 0
  180. done()
  181. describe 'when no sessionId is supplied', ->
  182. beforeEach ->
  183. @call = (callback) =>
  184. @UserSessionsManager.untrackSession @user, null, callback
  185. it 'should not produce an error', (done) ->
  186. @call (err) =>
  187. expect(err).to.not.be.instanceof Error
  188. expect(err).to.equal null
  189. done()
  190. it 'should not call the appropriate redis methods', (done) ->
  191. @call (err) =>
  192. @rclient.multi.callCount.should.equal 0
  193. @rclient.srem.callCount.should.equal 0
  194. @rclient.expire.callCount.should.equal 0
  195. @rclient.exec.callCount.should.equal 0
  196. done()
  197. it 'should not call _checkSessions', (done) ->
  198. @call (err) =>
  199. @_checkSessions.callCount.should.equal 0
  200. done()
  201. ##
  202. describe 'revokeAllUserSessions', ->
  203. beforeEach ->
  204. @sessionKeys = ['sess:one', 'sess:two']
  205. @retain = []
  206. @rclient.smembers.callsArgWith(1, null, @sessionKeys)
  207. @rclient.exec.callsArgWith(0, null)
  208. @call = (callback) =>
  209. @UserSessionsManager.revokeAllUserSessions @user, @retain, callback
  210. it 'should not produce an error', (done) ->
  211. @call (err) =>
  212. expect(err).to.not.be.instanceof Error
  213. expect(err).to.equal null
  214. done()
  215. it 'should call the appropriate redis methods', (done) ->
  216. @call (err) =>
  217. @rclient.smembers.callCount.should.equal 1
  218. @rclient.multi.callCount.should.equal 1
  219. @rclient.del.callCount.should.equal 1
  220. expect(@rclient.del.firstCall.args[0]).to.deep.equal @sessionKeys
  221. @rclient.srem.callCount.should.equal 1
  222. expect(@rclient.srem.firstCall.args[1]).to.deep.equal @sessionKeys
  223. @rclient.exec.callCount.should.equal 1
  224. done()
  225. describe 'when a session is retained', ->
  226. beforeEach ->
  227. @sessionKeys = ['sess:one', 'sess:two', 'sess:three', 'sess:four']
  228. @retain = ['two']
  229. @rclient.smembers.callsArgWith(1, null, @sessionKeys)
  230. @rclient.exec.callsArgWith(0, null)
  231. @call = (callback) =>
  232. @UserSessionsManager.revokeAllUserSessions @user, @retain, callback
  233. it 'should not produce an error', (done) ->
  234. @call (err) =>
  235. expect(err).to.not.be.instanceof Error
  236. expect(err).to.equal null
  237. done()
  238. it 'should call the appropriate redis methods', (done) ->
  239. @call (err) =>
  240. @rclient.smembers.callCount.should.equal 1
  241. @rclient.multi.callCount.should.equal 1
  242. @rclient.del.callCount.should.equal 1
  243. @rclient.srem.callCount.should.equal 1
  244. @rclient.exec.callCount.should.equal 1
  245. done()
  246. it 'should remove all sessions except for the retained one', (done) ->
  247. @call (err) =>
  248. expect(@rclient.del.firstCall.args[0]).to.deep.equal(['sess:one', 'sess:three', 'sess:four'])
  249. expect(@rclient.srem.firstCall.args[1]).to.deep.equal(['sess:one', 'sess:three', 'sess:four'])
  250. done()
  251. describe 'when rclient produces an error', ->
  252. beforeEach ->
  253. @rclient.exec.callsArgWith(0, new Error('woops'))
  254. it 'should produce an error', (done) ->
  255. @call (err) =>
  256. expect(err).to.be.instanceof Error
  257. done()
  258. describe 'when no user is supplied', ->
  259. beforeEach ->
  260. @call = (callback) =>
  261. @UserSessionsManager.revokeAllUserSessions null, @retain, callback
  262. it 'should not produce an error', (done) ->
  263. @call (err) =>
  264. expect(err).to.not.be.instanceof Error
  265. expect(err).to.equal null
  266. done()
  267. it 'should not call the appropriate redis methods', (done) ->
  268. @call (err) =>
  269. @rclient.smembers.callCount.should.equal 0
  270. @rclient.multi.callCount.should.equal 0
  271. @rclient.del.callCount.should.equal 0
  272. @rclient.srem.callCount.should.equal 0
  273. @rclient.exec.callCount.should.equal 0
  274. done()
  275. describe 'when there are no keys to delete', ->
  276. beforeEach ->
  277. @rclient.smembers.callsArgWith(1, null, [])
  278. it 'should not produce an error', (done) ->
  279. @call (err) =>
  280. expect(err).to.not.be.instanceof Error
  281. expect(err).to.equal null
  282. done()
  283. it 'should not do the delete operation', (done) ->
  284. @call (err) =>
  285. @rclient.smembers.callCount.should.equal 1
  286. @rclient.multi.callCount.should.equal 0
  287. @rclient.del.callCount.should.equal 0
  288. @rclient.srem.callCount.should.equal 0
  289. @rclient.exec.callCount.should.equal 0
  290. done()
  291. describe 'touch', ->
  292. beforeEach ->
  293. @rclient.expire.callsArgWith(2, null)
  294. @call = (callback) =>
  295. @UserSessionsManager.touch @user, callback
  296. it 'should not produce an error', (done) ->
  297. @call (err) =>
  298. expect(err).to.not.be.instanceof Error
  299. expect(err).to.equal null
  300. done()
  301. it 'should call rclient.expire', (done) ->
  302. @call (err) =>
  303. @rclient.expire.callCount.should.equal 1
  304. done()
  305. describe 'when rclient produces an error', ->
  306. beforeEach ->
  307. @rclient.expire.callsArgWith(2, new Error('woops'))
  308. it 'should produce an error', (done) ->
  309. @call (err) =>
  310. expect(err).to.be.instanceof Error
  311. done()
  312. describe 'when no user is supplied', ->
  313. beforeEach ->
  314. @call = (callback) =>
  315. @UserSessionsManager.touch null, callback
  316. it 'should not produce an error', (done) ->
  317. @call (err) =>
  318. expect(err).to.not.be.instanceof Error
  319. expect(err).to.equal null
  320. done()
  321. it 'should not call expire', (done) ->
  322. @call (err) =>
  323. @rclient.expire.callCount.should.equal 0
  324. done()
  325. describe 'getAllUserSessions', ->
  326. beforeEach ->
  327. @sessionKeys = ['sess:one', 'sess:two', 'sess:three']
  328. @sessions = [
  329. '{"user": {"ip_address": "a", "session_created": "b"}}',
  330. '{"passport": {"user": {"ip_address": "c", "session_created": "d"}}}'
  331. ]
  332. @exclude = ['two']
  333. @rclient.smembers.callsArgWith(1, null, @sessionKeys)
  334. @rclient.mget.callsArgWith(1, null, @sessions)
  335. @call = (callback) =>
  336. @UserSessionsManager.getAllUserSessions @user, @exclude, callback
  337. it 'should not produce an error', (done) ->
  338. @call (err, sessions) =>
  339. expect(err).to.equal null
  340. done()
  341. it 'should get sessions', (done) ->
  342. @call (err, sessions) =>
  343. expect(sessions).to.deep.equal [
  344. { ip_address: 'a', session_created: 'b' },
  345. { ip_address: 'c', session_created: 'd' }
  346. ]
  347. done()
  348. it 'should have called rclient.smembers', (done) ->
  349. @call (err, sessions) =>
  350. @rclient.smembers.callCount.should.equal 1
  351. done()
  352. it 'should have called rclient.mget', (done) ->
  353. @call (err, sessions) =>
  354. @rclient.mget.callCount.should.equal 1
  355. done()
  356. describe 'when there are no other sessions', ->
  357. beforeEach ->
  358. @sessionKeys = ['sess:two']
  359. @rclient.smembers.callsArgWith(1, null, @sessionKeys)
  360. it 'should not produce an error', (done) ->
  361. @call (err, sessions) =>
  362. expect(err).to.equal null
  363. done()
  364. it 'should produce an empty list of sessions', (done) ->
  365. @call (err, sessions) =>
  366. expect(sessions).to.deep.equal []
  367. done()
  368. it 'should have called rclient.smembers', (done) ->
  369. @call (err, sessions) =>
  370. @rclient.smembers.callCount.should.equal 1
  371. done()
  372. it 'should not have called rclient.mget', (done) ->
  373. @call (err, sessions) =>
  374. @rclient.mget.callCount.should.equal 0
  375. done()
  376. describe 'when smembers produces an error', ->
  377. beforeEach ->
  378. @rclient.smembers.callsArgWith(1, new Error('woops'))
  379. it 'should produce an error', (done) ->
  380. @call (err, sessions) =>
  381. expect(err).to.not.equal null
  382. expect(err).to.be.instanceof Error
  383. done()
  384. it 'should not have called rclient.mget', (done) ->
  385. @call (err, sessions) =>
  386. @rclient.mget.callCount.should.equal 0
  387. done()
  388. describe 'when mget produces an error', ->
  389. beforeEach ->
  390. @rclient.mget.callsArgWith(1, new Error('woops'))
  391. it 'should produce an error', (done) ->
  392. @call (err, sessions) =>
  393. expect(err).to.not.equal null
  394. expect(err).to.be.instanceof Error
  395. done()
  396. describe '_checkSessions', ->
  397. beforeEach ->
  398. @call = (callback) =>
  399. @UserSessionsManager._checkSessions @user, callback
  400. @sessionKeys = ['one', 'two']
  401. @rclient.smembers.callsArgWith(1, null, @sessionKeys)
  402. @rclient.get.callsArgWith(1, null, 'some-value')
  403. @rclient.srem.callsArgWith(2, null, {})
  404. it 'should not produce an error', (done) ->
  405. @call (err) =>
  406. expect(err).to.not.be.instanceof Error
  407. expect(err).to.equal undefined
  408. done()
  409. it 'should call the appropriate redis methods', (done) ->
  410. @call (err) =>
  411. @rclient.smembers.callCount.should.equal 1
  412. @rclient.get.callCount.should.equal 2
  413. @rclient.srem.callCount.should.equal 0
  414. done()
  415. describe 'when one of the keys is not present in redis', ->
  416. beforeEach ->
  417. @rclient.get.onCall(0).callsArgWith(1, null, 'some-val')
  418. @rclient.get.onCall(1).callsArgWith(1, null, null)
  419. it 'should not produce an error', (done) ->
  420. @call (err) =>
  421. expect(err).to.not.be.instanceof Error
  422. expect(err).to.equal undefined
  423. done()
  424. it 'should remove that key from the set', (done) ->
  425. @call (err) =>
  426. @rclient.smembers.callCount.should.equal 1
  427. @rclient.get.callCount.should.equal 2
  428. @rclient.srem.callCount.should.equal 1
  429. @rclient.srem.firstCall.args[1].should.equal 'two'
  430. done()
  431. describe 'when no user is supplied', ->
  432. beforeEach ->
  433. @call = (callback) =>
  434. @UserSessionsManager._checkSessions null, callback
  435. it 'should not produce an error', (done) ->
  436. @call (err) =>
  437. expect(err).to.not.be.instanceof Error
  438. expect(err).to.equal null
  439. done()
  440. it 'should not call redis methods', (done) ->
  441. @call (err) =>
  442. @rclient.smembers.callCount.should.equal 0
  443. @rclient.get.callCount.should.equal 0
  444. done()
  445. describe 'when one of the get operations produces an error', ->
  446. beforeEach ->
  447. @rclient.get.onCall(0).callsArgWith(1, new Error('woops'), null)
  448. @rclient.get.onCall(1).callsArgWith(1, null, null)
  449. it 'should produce an error', (done) ->
  450. @call (err) =>
  451. expect(err).to.be.instanceof Error
  452. done()
  453. it 'should call the right redis methods, bailing out early', (done) ->
  454. @call (err) =>
  455. @rclient.smembers.callCount.should.equal 1
  456. @rclient.get.callCount.should.equal 1
  457. @rclient.srem.callCount.should.equal 0
  458. done()