1 | 'use strict'
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | const chai = require('chai')
|
8 | const sinon = require('sinon')
|
9 | chai.use(require('sinon-chai'))
|
10 |
|
11 | const expect = chai.expect
|
12 |
|
13 | const isCircular = require('is-circular')
|
14 |
|
15 |
|
16 | const Brain = require('../src/brain')
|
17 | const User = require('../src/user')
|
18 |
|
19 | describe('Brain', function () {
|
20 | beforeEach(function () {
|
21 | this.clock = sinon.useFakeTimers()
|
22 | this.mockRobot = {
|
23 | emit () {},
|
24 | on () {}
|
25 | }
|
26 |
|
27 |
|
28 |
|
29 | sinon.stub(this.mockRobot, 'on').withArgs('running').callsArg(1)
|
30 |
|
31 | this.brain = new Brain(this.mockRobot)
|
32 |
|
33 | this.user1 = this.brain.userForId('1', { name: 'Guy One' })
|
34 | this.user2 = this.brain.userForId('2', { name: 'Guy One Two' })
|
35 | this.user3 = this.brain.userForId('3', { name: 'Girl Three' })
|
36 | })
|
37 |
|
38 | afterEach(function () {
|
39 | this.clock.restore()
|
40 | })
|
41 |
|
42 | describe('Unit Tests', function () {
|
43 | describe('#mergeData', function () {
|
44 | it('performs a proper merge with the new data taking precedent', function () {
|
45 | this.brain.data = {
|
46 | 1: 'old',
|
47 | 2: 'old'
|
48 | }
|
49 |
|
50 | this.brain.mergeData({ 2: 'new' })
|
51 |
|
52 | expect(this.brain.data).to.deep.equal({
|
53 | 1: 'old',
|
54 | 2: 'new'
|
55 | })
|
56 | })
|
57 |
|
58 | it('emits a loaded event with the new data', function () {
|
59 | sinon.spy(this.brain, 'emit')
|
60 | this.brain.mergeData({})
|
61 | expect(this.brain.emit).to.have.been.calledWith('loaded', this.brain.data)
|
62 | })
|
63 |
|
64 | it('coerces loaded data into User objects', function () {
|
65 | this.brain.mergeData({ users: { 4: { name: 'new', id: '4' } } })
|
66 | const user = this.brain.userForId('4')
|
67 | expect(user.constructor.name).to.equal('User')
|
68 | expect(user.id).to.equal('4')
|
69 | expect(user.name).to.equal('new')
|
70 | expect(isCircular(this.brain)).to.be.false
|
71 | })
|
72 | })
|
73 |
|
74 | describe('#save', () => it('emits a save event', function () {
|
75 | sinon.spy(this.brain, 'emit')
|
76 | this.brain.save()
|
77 | expect(this.brain.emit).to.have.been.calledWith('save', this.brain.data)
|
78 | }))
|
79 |
|
80 | describe('#resetSaveInterval', () => it('updates the auto-save interval', function () {
|
81 | sinon.spy(this.brain, 'save')
|
82 |
|
83 | this.brain.resetSaveInterval(10)
|
84 |
|
85 | this.brain.setAutoSave(true)
|
86 |
|
87 | this.clock.tick(5000)
|
88 |
|
89 | expect(this.brain.save).to.not.have.been.called
|
90 | this.clock.tick(5000)
|
91 |
|
92 | expect(this.brain.save).to.have.been.calledOnce
|
93 | }))
|
94 |
|
95 | describe('#close', function () {
|
96 | it('saves', function () {
|
97 | sinon.spy(this.brain, 'save')
|
98 | this.brain.close()
|
99 | expect(this.brain.save).to.have.been.calledOnce
|
100 | })
|
101 |
|
102 | it('emits a close event', function () {
|
103 | sinon.spy(this.brain, 'emit')
|
104 | this.brain.close()
|
105 | expect(this.brain.emit).to.have.been.calledWith('close')
|
106 | })
|
107 |
|
108 | it('saves before emitting the close event', function () {
|
109 | sinon.spy(this.brain, 'save')
|
110 | sinon.spy(this.brain, 'emit').withArgs('close')
|
111 | this.brain.close()
|
112 | expect(this.brain.save).to.have.been.calledBefore(this.brain.emit)
|
113 | })
|
114 |
|
115 | it('stops auto-saving', function () {
|
116 |
|
117 | this.brain.setAutoSave(true)
|
118 | this.brain.close()
|
119 |
|
120 |
|
121 | sinon.spy(this.brain, 'save')
|
122 |
|
123 | this.clock.tick(2 * 5000)
|
124 | expect(this.brain.save).to.not.have.been.called
|
125 | })
|
126 | })
|
127 |
|
128 | describe('#get', function () {
|
129 | it('returns the saved value', function () {
|
130 | this.brain.data._private['test-key'] = 'value'
|
131 | expect(this.brain.get('test-key')).to.equal('value')
|
132 | })
|
133 |
|
134 | it('returns null if object is not found', function () {
|
135 | expect(this.brain.get('not a real key')).to.be.null
|
136 | })
|
137 | })
|
138 |
|
139 | describe('#set', function () {
|
140 | it('saves the value', function () {
|
141 | this.brain.set('test-key', 'value')
|
142 | expect(this.brain.data._private['test-key']).to.equal('value')
|
143 | })
|
144 |
|
145 | it('sets multiple keys at once if an object is provided', function () {
|
146 | this.brain.data._private = {
|
147 | key1: 'val1',
|
148 | key2: 'val1'
|
149 | }
|
150 |
|
151 | this.brain.set({
|
152 | key2: 'val2',
|
153 | key3: 'val2'
|
154 | })
|
155 |
|
156 | expect(this.brain.data._private).to.deep.equal({
|
157 | key1: 'val1',
|
158 | key2: 'val2',
|
159 | key3: 'val2'
|
160 | })
|
161 | })
|
162 |
|
163 |
|
164 |
|
165 | it('emits loaded', function () {
|
166 | sinon.spy(this.brain, 'emit')
|
167 | this.brain.set('test-key', 'value')
|
168 | expect(this.brain.emit).to.have.been.calledWith('loaded', this.brain.data)
|
169 | })
|
170 |
|
171 | it('returns the brain', function () {
|
172 | expect(this.brain.set('test-key', 'value')).to.equal(this.brain)
|
173 | })
|
174 | })
|
175 |
|
176 | describe('#remove', () => it('removes the specified key', function () {
|
177 | this.brain.data._private['test-key'] = 'value'
|
178 | this.brain.remove('test-key')
|
179 | expect(this.brain.data._private).to.not.include.keys('test-key')
|
180 | }))
|
181 |
|
182 | describe('#userForId', function () {
|
183 | it('returns the user object', function () {
|
184 | expect(this.brain.userForId(1)).to.equal(this.user1)
|
185 | })
|
186 |
|
187 | it('does an exact match', function () {
|
188 | const user4 = this.brain.userForId('FOUR')
|
189 | expect(this.brain.userForId('four')).to.not.equal(user4)
|
190 | })
|
191 |
|
192 |
|
193 |
|
194 | it('recreates the user if the room option differs from the user object', function () {
|
195 | expect(this.brain.userForId(1).room).to.be.undefined
|
196 |
|
197 |
|
198 | const newUser1 = this.brain.userForId(1, { room: 'room1' })
|
199 | expect(newUser1).to.not.equal(this.user1)
|
200 |
|
201 |
|
202 | const newUser2 = this.brain.userForId(1, { room: 'room2' })
|
203 | expect(newUser2).to.not.equal(newUser1)
|
204 | })
|
205 |
|
206 | describe('when there is no matching user ID', function () {
|
207 | it('creates a new User', function () {
|
208 | expect(this.brain.data.users).to.not.include.key('all-new-user')
|
209 | const newUser = this.brain.userForId('all-new-user')
|
210 | expect(newUser).to.be.instanceof(User)
|
211 | expect(newUser.id).to.equal('all-new-user')
|
212 | expect(this.brain.data.users).to.include.key('all-new-user')
|
213 | })
|
214 |
|
215 | it('passes the provided options to the new User', function () {
|
216 | const newUser = this.brain.userForId('all-new-user', { name: 'All New User', prop: 'mine' })
|
217 | expect(newUser.name).to.equal('All New User')
|
218 | expect(newUser.prop).to.equal('mine')
|
219 | })
|
220 | })
|
221 | })
|
222 |
|
223 | describe('#userForName', function () {
|
224 | it('returns the user with a matching name', function () {
|
225 | expect(this.brain.userForName('Guy One')).to.equal(this.user1)
|
226 | })
|
227 |
|
228 | it('does a case-insensitive match', function () {
|
229 | expect(this.brain.userForName('guy one')).to.equal(this.user1)
|
230 | })
|
231 |
|
232 | it('returns null if no user matches', function () {
|
233 | expect(this.brain.userForName('not a real user')).to.be.null
|
234 | })
|
235 | })
|
236 |
|
237 | describe('#usersForRawFuzzyName', function () {
|
238 | it('does a case-insensitive match', function () {
|
239 | expect(this.brain.usersForRawFuzzyName('guy')).to.have.members([this.user1, this.user2])
|
240 | })
|
241 |
|
242 | it('returns all matching users (prefix match) when there is not an exact match (case-insensitive)', function () {
|
243 | expect(this.brain.usersForRawFuzzyName('Guy')).to.have.members([this.user1, this.user2])
|
244 | })
|
245 |
|
246 | it('returns all matching users (prefix match) when there is an exact match (case-insensitive)', function () {
|
247 |
|
248 | expect(this.brain.usersForRawFuzzyName('Guy One')).to.deep.equal([this.user1, this.user2])
|
249 |
|
250 | expect(this.brain.usersForRawFuzzyName('guy one')).to.deep.equal([this.user1, this.user2])
|
251 | })
|
252 |
|
253 | it('returns an empty array if no users match', function () {
|
254 | const result = this.brain.usersForRawFuzzyName('not a real user')
|
255 | expect(result).to.be.an('array')
|
256 | expect(result).to.be.empty
|
257 | })
|
258 | })
|
259 |
|
260 | describe('#usersForFuzzyName', function () {
|
261 | it('does a case-insensitive match', function () {
|
262 | expect(this.brain.usersForFuzzyName('guy')).to.have.members([this.user1, this.user2])
|
263 | })
|
264 |
|
265 | it('returns all matching users (prefix match) when there is not an exact match', function () {
|
266 | expect(this.brain.usersForFuzzyName('Guy')).to.have.members([this.user1, this.user2])
|
267 | })
|
268 |
|
269 | it('returns just the user when there is an exact match (case-insensitive)', function () {
|
270 |
|
271 | expect(this.brain.usersForFuzzyName('Guy One')).to.deep.equal([this.user1])
|
272 |
|
273 | expect(this.brain.usersForFuzzyName('guy one')).to.deep.equal([this.user1])
|
274 | })
|
275 |
|
276 | it('returns an empty array if no users match', function () {
|
277 | const result = this.brain.usersForFuzzyName('not a real user')
|
278 | expect(result).to.be.an('array')
|
279 | expect(result).to.be.empty
|
280 | })
|
281 | })
|
282 | })
|
283 |
|
284 | describe('Auto-Save', function () {
|
285 | it('is on by default', function () {
|
286 | expect(this.brain.autoSave).to.equal(true)
|
287 | })
|
288 |
|
289 | it('automatically saves every 5 seconds when turned on', function () {
|
290 | sinon.spy(this.brain, 'save')
|
291 |
|
292 | this.brain.setAutoSave(true)
|
293 |
|
294 | this.clock.tick(5000)
|
295 | expect(this.brain.save).to.have.been.called
|
296 | })
|
297 |
|
298 | it('does not auto-save when turned off', function () {
|
299 | sinon.spy(this.brain, 'save')
|
300 |
|
301 | this.brain.setAutoSave(false)
|
302 |
|
303 | this.clock.tick(2 * 5000)
|
304 | expect(this.brain.save).to.not.have.been.called
|
305 | })
|
306 | })
|
307 |
|
308 | describe('User Searching', function () {
|
309 | it('finds users by ID', function () {
|
310 | expect(this.brain.userForId('1')).to.equal(this.user1)
|
311 | })
|
312 |
|
313 | it('finds users by exact name', function () {
|
314 | expect(this.brain.userForName('Guy One')).to.equal(this.user1)
|
315 | })
|
316 |
|
317 | it('finds users by fuzzy name (prefix match)', function () {
|
318 | const result = this.brain.usersForFuzzyName('Guy')
|
319 | expect(result).to.have.members([this.user1, this.user2])
|
320 | expect(result).to.not.have.members([this.user3])
|
321 | })
|
322 |
|
323 | it('returns User objects, not POJOs', function () {
|
324 | expect(this.brain.userForId('1').constructor.name).to.equal('User')
|
325 | for (const user of this.brain.usersForFuzzyName('Guy')) {
|
326 | expect(user.constructor.name).to.equal('User')
|
327 | }
|
328 |
|
329 | for (const user of this.brain.usersForRawFuzzyName('Guy One')) {
|
330 | expect(user.constructor.name).to.equal('User')
|
331 | }
|
332 |
|
333 | expect(isCircular(this.brain)).to.be.false
|
334 | })
|
335 | })
|
336 | })
|