Skip to content

Commit ab37a33

Browse files
committed
refactor(realtime): add test case for clean dangling user
Signed-off-by: BoHong Li <[email protected]>
1 parent d8b18ee commit ab37a33

File tree

7 files changed

+169
-26
lines changed

7 files changed

+169
-26
lines changed

lib/realtime.js

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const ot = require('./ot')
2424
const { ProcessQueue } = require('./processQueue')
2525
const { RealtimeClientConnection } = require('./realtimeClientConnection')
2626
const { UpdateDirtyNoteJob } = require('./realtimeUpdateDirtyNoteJob')
27+
const { CleanDanglingUserJob } = require('./realtimeCleanDanglingUserJob')
2728

2829
// public
2930
const realtime = {
@@ -164,29 +165,8 @@ function finishUpdateNote (note, _note, callback) {
164165
})
165166
}
166167

167-
// clean when user not in any rooms or user not in connected list
168-
setInterval(function () {
169-
async.each(Object.keys(users), function (key, callback) {
170-
var socket = realtime.io.sockets.connected[key]
171-
if ((!socket && users[key]) ||
172-
(socket && (!socket.rooms || socket.rooms.length <= 0))) {
173-
if (config.debug) {
174-
logger.info('cleaner found redundant user: ' + key)
175-
}
176-
if (!socket) {
177-
socket = {
178-
id: key
179-
}
180-
}
181-
if (!disconnectProcessQueue.checkTaskIsInQueue(socket.id)) {
182-
exports.queueForDisconnect(socket)
183-
}
184-
}
185-
return callback(null, null)
186-
}, function (err) {
187-
if (err) return logger.error('cleaner error', err)
188-
})
189-
}, 60000)
168+
const cleanDanglingUserJob = new CleanDanglingUserJob(realtime)
169+
cleanDanglingUserJob.start()
190170

191171
var saverSleep = false
192172
// save note revision in interval

lib/realtimeCleanDanglingUserJob.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'use strict'
2+
3+
const async = require('async')
4+
const config = require('./config')
5+
const logger = require('./logger')
6+
7+
/**
8+
* clean when user not in any rooms or user not in connected list
9+
*/
10+
class CleanDanglingUserJob {
11+
constructor (realtime) {
12+
this.realtime = realtime
13+
}
14+
15+
start () {
16+
if (this.timer) return
17+
this.timer = setInterval(this.cleanDanglingUser.bind(this), 60000)
18+
}
19+
20+
stop () {
21+
if (!this.timer) return
22+
clearInterval(this.timer)
23+
this.timer = undefined
24+
}
25+
26+
cleanDanglingUser () {
27+
const users = this.realtime.getUserPool()
28+
async.each(Object.keys(users), (key, callback) => {
29+
const socket = this.realtime.io.sockets.connected[key]
30+
if ((!socket && users[key]) ||
31+
(socket && (!socket.rooms || socket.rooms.length <= 0))) {
32+
if (config.debug) {
33+
logger.info('cleaner found redundant user: ' + key)
34+
}
35+
if (!socket) {
36+
return callback(null, null)
37+
}
38+
if (!this.realtime.disconnectProcessQueue.checkTaskIsInQueue(socket.id)) {
39+
this.realtime.queueForDisconnect(socket)
40+
}
41+
}
42+
return callback(null, null)
43+
}, function (err) {
44+
if (err) return logger.error('cleaner error', err)
45+
})
46+
}
47+
}
48+
49+
exports.CleanDanglingUserJob = CleanDanglingUserJob
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/* eslint-env node, mocha */
2+
'use strict'
3+
4+
const assert = require('assert')
5+
const mock = require('mock-require')
6+
const sinon = require('sinon')
7+
const {removeModuleFromRequireCache, makeMockSocket} = require('./utils')
8+
9+
describe('cleanDanglingUser', function () {
10+
let clock
11+
beforeEach(() => {
12+
clock = sinon.useFakeTimers()
13+
mock('../../lib/processQueue', require('../testDoubles/ProcessQueueFake'))
14+
mock('../../lib/logger', {
15+
error: () => {},
16+
info: () => {}
17+
})
18+
mock('../../lib/history', {})
19+
mock('../../lib/models', {
20+
Revision: {
21+
saveAllNotesRevision: () => {
22+
}
23+
}
24+
})
25+
mock('../../lib/config', {
26+
debug: true
27+
})
28+
mock('../../lib/realtimeUpdateDirtyNoteJob', require('../testDoubles/realtimeUpdateDirtyNoteJobStub'))
29+
})
30+
31+
afterEach(() => {
32+
clock.restore()
33+
removeModuleFromRequireCache('../../lib/realtime')
34+
mock.stopAll()
35+
sinon.restore()
36+
})
37+
38+
it('should ', (done) => {
39+
const realtime = require('../../lib/realtime')
40+
const queueForDisconnectSpy = sinon.spy(realtime, 'queueForDisconnect')
41+
realtime.io = {
42+
to: sinon.stub().callsFake(function () {
43+
return {
44+
emit: sinon.fake()
45+
}
46+
}),
47+
sockets: {
48+
connected: {}
49+
}
50+
}
51+
let user1Socket = makeMockSocket()
52+
let user2Socket = makeMockSocket()
53+
54+
user1Socket.rooms.push('room1')
55+
56+
realtime.io.sockets.connected[user1Socket.id] = user1Socket
57+
realtime.io.sockets.connected[user2Socket.id] = user2Socket
58+
59+
realtime.users[user1Socket.id] = user1Socket
60+
realtime.users[user2Socket.id] = user2Socket
61+
clock.tick(60000)
62+
clock.restore()
63+
setTimeout(() => {
64+
assert(queueForDisconnectSpy.called)
65+
done()
66+
}, 50)
67+
})
68+
})

test/realtime/dirtyNoteUpdate.test.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ describe('realtime#update note is dirty timer', function () {
6666
realtime.notes['note2'] = note2
6767

6868
clock.tick(1000)
69-
7069
setTimeout(() => {
7170
assert(note2.server.isDirty === false)
7271
done()
@@ -126,5 +125,4 @@ describe('realtime#update note is dirty timer', function () {
126125
done()
127126
}, 50)
128127
})
129-
130128
})

test/realtime/utils.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ function makeMockSocket (headers, query) {
2424
return broadCastChannelCache[channel]
2525
}
2626
},
27-
disconnect: sinon.fake()
27+
disconnect: sinon.fake(),
28+
rooms: []
2829
}
2930
}
3031

test/testDoubles/ProcessQueueFake.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict'
2+
3+
class ProcessQueueFake {
4+
constructor () {
5+
this.taskMap = new Map()
6+
this.queue = []
7+
}
8+
9+
start () {
10+
11+
}
12+
13+
stop () {
14+
15+
}
16+
17+
checkTaskIsInQueue (id) {
18+
return this.taskMap.has(id)
19+
}
20+
21+
push (id, processFunc) {
22+
this.queue.push({
23+
id: id,
24+
processFunc: processFunc
25+
})
26+
this.taskMap.set(id, true)
27+
}
28+
29+
process () {
30+
31+
}
32+
}
33+
34+
exports.ProcessQueueFake = ProcessQueueFake
35+
exports.ProcessQueue = ProcessQueueFake
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use strict'
2+
3+
class UpdateDirtyNoteJobStub {
4+
start() {
5+
}
6+
7+
stop() {
8+
}
9+
}
10+
11+
exports.UpdateDirtyNoteJobStub = UpdateDirtyNoteJobStub
12+
exports.UpdateDirtyNoteJob = UpdateDirtyNoteJobStub

0 commit comments

Comments
 (0)