ConnectedUsersManagerTests.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. no-return-assign,
  5. no-unused-vars,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. const should = require('chai').should()
  15. const SandboxedModule = require('sandboxed-module')
  16. const assert = require('assert')
  17. const path = require('path')
  18. const sinon = require('sinon')
  19. const modulePath = path.join(__dirname, '../../../app/js/ConnectedUsersManager')
  20. const { expect } = require('chai')
  21. const tk = require('timekeeper')
  22. describe('ConnectedUsersManager', function () {
  23. beforeEach(function () {
  24. this.settings = {
  25. redis: {
  26. realtime: {
  27. key_schema: {
  28. clientsInProject({ project_id }) {
  29. return `clients_in_project:${project_id}`
  30. },
  31. connectedUser({ project_id, client_id }) {
  32. return `connected_user:${project_id}:${client_id}`
  33. }
  34. }
  35. }
  36. }
  37. }
  38. this.rClient = {
  39. auth() {},
  40. setex: sinon.stub(),
  41. sadd: sinon.stub(),
  42. get: sinon.stub(),
  43. srem: sinon.stub(),
  44. del: sinon.stub(),
  45. smembers: sinon.stub(),
  46. expire: sinon.stub(),
  47. hset: sinon.stub(),
  48. hgetall: sinon.stub(),
  49. exec: sinon.stub(),
  50. multi: () => {
  51. return this.rClient
  52. }
  53. }
  54. tk.freeze(new Date())
  55. this.ConnectedUsersManager = SandboxedModule.require(modulePath, {
  56. requires: {
  57. 'settings-sharelatex': this.settings,
  58. 'logger-sharelatex': { log() {} },
  59. 'redis-sharelatex': {
  60. createClient: () => {
  61. return this.rClient
  62. }
  63. }
  64. }
  65. })
  66. this.client_id = '32132132'
  67. this.project_id = 'dskjh2u21321'
  68. this.user = {
  69. _id: 'user-id-123',
  70. first_name: 'Joe',
  71. last_name: 'Bloggs',
  72. email: 'joe@example.com'
  73. }
  74. return (this.cursorData = {
  75. row: 12,
  76. column: 9,
  77. doc_id: '53c3b8c85fee64000023dc6e'
  78. })
  79. })
  80. afterEach(function () {
  81. return tk.reset()
  82. })
  83. describe('updateUserPosition', function () {
  84. beforeEach(function () {
  85. return this.rClient.exec.callsArgWith(0)
  86. })
  87. it('should set a key with the date and give it a ttl', function (done) {
  88. return this.ConnectedUsersManager.updateUserPosition(
  89. this.project_id,
  90. this.client_id,
  91. this.user,
  92. null,
  93. (err) => {
  94. this.rClient.hset
  95. .calledWith(
  96. `connected_user:${this.project_id}:${this.client_id}`,
  97. 'last_updated_at',
  98. Date.now()
  99. )
  100. .should.equal(true)
  101. return done()
  102. }
  103. )
  104. })
  105. it('should set a key with the user_id', function (done) {
  106. return this.ConnectedUsersManager.updateUserPosition(
  107. this.project_id,
  108. this.client_id,
  109. this.user,
  110. null,
  111. (err) => {
  112. this.rClient.hset
  113. .calledWith(
  114. `connected_user:${this.project_id}:${this.client_id}`,
  115. 'user_id',
  116. this.user._id
  117. )
  118. .should.equal(true)
  119. return done()
  120. }
  121. )
  122. })
  123. it('should set a key with the first_name', function (done) {
  124. return this.ConnectedUsersManager.updateUserPosition(
  125. this.project_id,
  126. this.client_id,
  127. this.user,
  128. null,
  129. (err) => {
  130. this.rClient.hset
  131. .calledWith(
  132. `connected_user:${this.project_id}:${this.client_id}`,
  133. 'first_name',
  134. this.user.first_name
  135. )
  136. .should.equal(true)
  137. return done()
  138. }
  139. )
  140. })
  141. it('should set a key with the last_name', function (done) {
  142. return this.ConnectedUsersManager.updateUserPosition(
  143. this.project_id,
  144. this.client_id,
  145. this.user,
  146. null,
  147. (err) => {
  148. this.rClient.hset
  149. .calledWith(
  150. `connected_user:${this.project_id}:${this.client_id}`,
  151. 'last_name',
  152. this.user.last_name
  153. )
  154. .should.equal(true)
  155. return done()
  156. }
  157. )
  158. })
  159. it('should set a key with the email', function (done) {
  160. return this.ConnectedUsersManager.updateUserPosition(
  161. this.project_id,
  162. this.client_id,
  163. this.user,
  164. null,
  165. (err) => {
  166. this.rClient.hset
  167. .calledWith(
  168. `connected_user:${this.project_id}:${this.client_id}`,
  169. 'email',
  170. this.user.email
  171. )
  172. .should.equal(true)
  173. return done()
  174. }
  175. )
  176. })
  177. it('should push the client_id on to the project list', function (done) {
  178. return this.ConnectedUsersManager.updateUserPosition(
  179. this.project_id,
  180. this.client_id,
  181. this.user,
  182. null,
  183. (err) => {
  184. this.rClient.sadd
  185. .calledWith(`clients_in_project:${this.project_id}`, this.client_id)
  186. .should.equal(true)
  187. return done()
  188. }
  189. )
  190. })
  191. it('should add a ttl to the project set so it stays clean', function (done) {
  192. return this.ConnectedUsersManager.updateUserPosition(
  193. this.project_id,
  194. this.client_id,
  195. this.user,
  196. null,
  197. (err) => {
  198. this.rClient.expire
  199. .calledWith(
  200. `clients_in_project:${this.project_id}`,
  201. 24 * 4 * 60 * 60
  202. )
  203. .should.equal(true)
  204. return done()
  205. }
  206. )
  207. })
  208. it('should add a ttl to the connected user so it stays clean', function (done) {
  209. return this.ConnectedUsersManager.updateUserPosition(
  210. this.project_id,
  211. this.client_id,
  212. this.user,
  213. null,
  214. (err) => {
  215. this.rClient.expire
  216. .calledWith(
  217. `connected_user:${this.project_id}:${this.client_id}`,
  218. 60 * 15
  219. )
  220. .should.equal(true)
  221. return done()
  222. }
  223. )
  224. })
  225. return it('should set the cursor position when provided', function (done) {
  226. return this.ConnectedUsersManager.updateUserPosition(
  227. this.project_id,
  228. this.client_id,
  229. this.user,
  230. this.cursorData,
  231. (err) => {
  232. this.rClient.hset
  233. .calledWith(
  234. `connected_user:${this.project_id}:${this.client_id}`,
  235. 'cursorData',
  236. JSON.stringify(this.cursorData)
  237. )
  238. .should.equal(true)
  239. return done()
  240. }
  241. )
  242. })
  243. })
  244. describe('markUserAsDisconnected', function () {
  245. beforeEach(function () {
  246. return this.rClient.exec.callsArgWith(0)
  247. })
  248. it('should remove the user from the set', function (done) {
  249. return this.ConnectedUsersManager.markUserAsDisconnected(
  250. this.project_id,
  251. this.client_id,
  252. (err) => {
  253. this.rClient.srem
  254. .calledWith(`clients_in_project:${this.project_id}`, this.client_id)
  255. .should.equal(true)
  256. return done()
  257. }
  258. )
  259. })
  260. it('should delete the connected_user string', function (done) {
  261. return this.ConnectedUsersManager.markUserAsDisconnected(
  262. this.project_id,
  263. this.client_id,
  264. (err) => {
  265. this.rClient.del
  266. .calledWith(`connected_user:${this.project_id}:${this.client_id}`)
  267. .should.equal(true)
  268. return done()
  269. }
  270. )
  271. })
  272. return it('should add a ttl to the connected user set so it stays clean', function (done) {
  273. return this.ConnectedUsersManager.markUserAsDisconnected(
  274. this.project_id,
  275. this.client_id,
  276. (err) => {
  277. this.rClient.expire
  278. .calledWith(
  279. `clients_in_project:${this.project_id}`,
  280. 24 * 4 * 60 * 60
  281. )
  282. .should.equal(true)
  283. return done()
  284. }
  285. )
  286. })
  287. })
  288. describe('_getConnectedUser', function () {
  289. it('should return a connected user if there is a user object', function (done) {
  290. const cursorData = JSON.stringify({ cursorData: { row: 1 } })
  291. this.rClient.hgetall.callsArgWith(1, null, {
  292. connected_at: new Date(),
  293. user_id: this.user._id,
  294. last_updated_at: `${Date.now()}`,
  295. cursorData
  296. })
  297. return this.ConnectedUsersManager._getConnectedUser(
  298. this.project_id,
  299. this.client_id,
  300. (err, result) => {
  301. result.connected.should.equal(true)
  302. result.client_id.should.equal(this.client_id)
  303. return done()
  304. }
  305. )
  306. })
  307. it('should return a not connected user if there is no object', function (done) {
  308. this.rClient.hgetall.callsArgWith(1, null, null)
  309. return this.ConnectedUsersManager._getConnectedUser(
  310. this.project_id,
  311. this.client_id,
  312. (err, result) => {
  313. result.connected.should.equal(false)
  314. result.client_id.should.equal(this.client_id)
  315. return done()
  316. }
  317. )
  318. })
  319. return it('should return a not connected user if there is an empty object', function (done) {
  320. this.rClient.hgetall.callsArgWith(1, null, {})
  321. return this.ConnectedUsersManager._getConnectedUser(
  322. this.project_id,
  323. this.client_id,
  324. (err, result) => {
  325. result.connected.should.equal(false)
  326. result.client_id.should.equal(this.client_id)
  327. return done()
  328. }
  329. )
  330. })
  331. })
  332. return describe('getConnectedUsers', function () {
  333. beforeEach(function () {
  334. this.users = ['1234', '5678', '9123', '8234']
  335. this.rClient.smembers.callsArgWith(1, null, this.users)
  336. this.ConnectedUsersManager._getConnectedUser = sinon.stub()
  337. this.ConnectedUsersManager._getConnectedUser
  338. .withArgs(this.project_id, this.users[0])
  339. .callsArgWith(2, null, {
  340. connected: true,
  341. client_age: 2,
  342. client_id: this.users[0]
  343. })
  344. this.ConnectedUsersManager._getConnectedUser
  345. .withArgs(this.project_id, this.users[1])
  346. .callsArgWith(2, null, {
  347. connected: false,
  348. client_age: 1,
  349. client_id: this.users[1]
  350. })
  351. this.ConnectedUsersManager._getConnectedUser
  352. .withArgs(this.project_id, this.users[2])
  353. .callsArgWith(2, null, {
  354. connected: true,
  355. client_age: 3,
  356. client_id: this.users[2]
  357. })
  358. return this.ConnectedUsersManager._getConnectedUser
  359. .withArgs(this.project_id, this.users[3])
  360. .callsArgWith(2, null, {
  361. connected: true,
  362. client_age: 11,
  363. client_id: this.users[3]
  364. })
  365. }) // connected but old
  366. return it('should only return the users in the list which are still in redis and recently updated', function (done) {
  367. return this.ConnectedUsersManager.getConnectedUsers(
  368. this.project_id,
  369. (err, users) => {
  370. users.length.should.equal(2)
  371. users[0].should.deep.equal({
  372. client_id: this.users[0],
  373. client_age: 2,
  374. connected: true
  375. })
  376. users[1].should.deep.equal({
  377. client_id: this.users[2],
  378. client_age: 3,
  379. connected: true
  380. })
  381. return done()
  382. }
  383. )
  384. })
  385. })
  386. })