UNPKG

12.9 kBtext/coffeescriptView Raw
1_ = require('lodash')
2sinon = require('sinon')
3chai = require('chai')
4chai.use(require('sinon-chai'))
5expect = chai.expect
6Command = require('../lib/command')
7Option = require('../lib/option')
8Signature = require('../lib/signature')
9settings = require('../lib/settings')
10state = require('../lib/state')
11utils = require('../lib/utils')
12
13describe 'Command:', ->
14
15 describe '#constructor()', ->
16
17 describe 'signature option', ->
18
19 it 'should throw an error if no signature', ->
20 expect ->
21 new Command(action: _.noop)
22 .to.throw(Error)
23
24 it 'should throw an error if signature is not a string', ->
25 expect ->
26 new Command
27 signature: new Signature([ 1, 2, 3 ])
28 .to.throw(Error)
29
30 describe 'action option', ->
31
32 it 'should throw an error if no action', ->
33 expect ->
34 new Command
35 signature: new Signature('foo')
36 .to.throw(Error)
37
38 it 'should throw an error if action is not a function', ->
39 expect ->
40 new Command
41 signature: new Signature('foo')
42 action: 'bar'
43 .to.throw(Error)
44
45 describe 'options option', ->
46
47 it 'should throw an error if not array', ->
48 expect ->
49 new Command
50 signature: new Signature('hello')
51 action: _.noop
52 options: { hello: boolean: true }
53 .to.throw(Error)
54
55 it 'should default to an empty array', ->
56 command = new Command
57 signature: new Signature('hello')
58 action: _.noop
59
60 expect(command.options).to.deep.equal([])
61
62 it 'should parse each option', ->
63 command = new Command
64 signature: new Signature('hello')
65 action: _.noop
66 options: [
67 new Option
68 signature: new Signature('quiet')
69 boolean: true
70 new Option
71 signature: new Signature('config')
72 parameter: 'name'
73 ]
74
75 expect(command.options).to.be.an.instanceof(Array)
76 for option in command.options
77 expect(option).to.be.an.instanceof(Option)
78
79 describe '#option()', ->
80
81 it 'should throw an error if option is not an instance of Option', ->
82 command = new Command
83 signature: new Signature('hello')
84 action: _.noop
85
86 expect ->
87 command.option({ option: 'world' })
88 .to.throw(Error)
89
90 it 'should add an option', ->
91 command = new Command
92 signature: new Signature('hello')
93 action: _.noop
94
95 expect(command.options).to.have.length(0)
96 command.option new Option
97 signature: new Signature('quiet')
98 boolean: true
99 expect(command.options).to.have.length(1)
100 expect(command.options[0].signature.toString()).to.equal('quiet')
101
102 it 'should not add duplicated options', ->
103 command = new Command
104 signature: new Signature('hello')
105 action: _.noop
106
107 option = new Option
108 signature: new Signature('quiet')
109 boolean: true
110
111 expect(command.options).to.have.length(0)
112 command.option(option)
113 expect(command.options).to.have.length(1)
114 command.option(option)
115 expect(command.options).to.have.length(1)
116
117 describe '#applyPermissions()', ->
118
119 beforeEach ->
120 state.permissions = {}
121
122 describe 'given a command without permissions', ->
123
124 beforeEach ->
125 @command = new Command
126 signature: new Signature('hello')
127 action: _.noop
128
129 it 'should call the callback without errors', ->
130 spy = sinon.spy()
131 @command.applyPermissions(spy)
132 expect(spy).to.have.been.calledOnce
133 expect(spy.firstCall.args).to.deep.equal([])
134
135 describe 'given a command with permissions', ->
136
137 beforeEach ->
138 @command = new Command
139 signature: new Signature('hello')
140 action: _.noop
141 permission: 'user'
142
143 it 'should continue if permission is found and does not return an error', ->
144 state.permissions.user = (done) -> done()
145 spy = sinon.spy()
146 @command.applyPermissions(spy)
147 expect(spy).to.have.been.calledOnce
148 expect(spy.firstCall.args).to.deep.equal([])
149
150 it 'should return an error if permission was not found', ->
151 spy = sinon.spy()
152 @command.applyPermissions(spy)
153 expect(spy).to.have.been.calledOnce
154 args = spy.firstCall.args
155 expect(args[0]).to.be.an.instanceof(Error)
156 expect(args[0].message).to.equal('Permission not found: user')
157
158 it 'should return an error if permission is found and returns an error', ->
159 state.permissions.user = (done) ->
160 error = new Error('You are not a user!')
161 done(error)
162 spy = sinon.spy()
163 @command.applyPermissions(spy)
164 expect(spy).to.have.been.calledOnce
165 args = spy.firstCall.args
166 expect(args[0]).to.be.an.instanceof(Error)
167 expect(args[0].message).to.equal('You are not a user!')
168
169 describe '#execute()', ->
170
171 beforeEach ->
172 state.globalOptions = []
173
174 it 'should execute the action', (done) ->
175 spy = sinon.spy()
176
177 command = new Command
178 signature: new Signature('foo <bar>')
179 action: spy
180
181 command.execute command: 'foo hello', (error) ->
182 expect(error).to.not.exist
183 expect(spy).to.have.been.calledOnce
184 expect(spy).to.have.been.calledWith(bar: 'hello')
185 done()
186
187 it 'should call action within the context of command', (done) ->
188 spy = sinon.spy()
189
190 command = new Command
191 signature: new Signature('foo')
192 action: spy
193
194 command.execute command: 'foo', (error) ->
195 expect(error).to.not.exist
196 expect(spy).to.have.been.calledOn(command)
197 done()
198
199 it 'should pass empty objects if nullary command', (done) ->
200 spy = sinon.spy()
201
202 command = new Command
203 signature: new Signature('foo')
204 action: spy
205
206 command.execute command: 'foo', (error) ->
207 expect(error).to.not.exist
208 expect(spy).to.have.been.calledWith({}, {})
209 done()
210
211 it 'should pass an empty object as the second argument if no options', (done) ->
212 spy = sinon.spy()
213
214 command = new Command
215 signature: new Signature('foo <bar>')
216 action: spy
217
218 expect(state.globalOptions).to.deep.equal([])
219 command.execute command: 'foo baz', (error) ->
220 expect(error).to.not.exist
221 expect(spy).to.have.been.calledWith(bar: 'baz', {})
222 done()
223
224 it 'should parse global options', (done) ->
225 spy = sinon.spy()
226
227 command = new Command
228 signature: new Signature('foo <bar>')
229 action: spy
230
231 state.globalOptions.push new Option
232 signature: new Signature('quiet')
233 boolean: true
234
235 command.execute {
236 command: 'foo baz'
237 options:
238 quiet: true
239 }, (error) ->
240 expect(error).to.not.exist
241 expect(spy).to.have.been.calledWith {
242 bar: 'baz'
243 }, {
244 quiet: true
245 }
246 done()
247
248 it 'should parse command options', (done) ->
249 spy = sinon.spy()
250
251 command = new Command
252 signature: new Signature('foo <bar>')
253 action: spy
254 options: [
255 new Option
256 signature: new Signature('quiet')
257 boolean: true
258 ]
259
260 command.execute {
261 command: 'foo baz'
262 options:
263 quiet: true
264 }, (error) ->
265 expect(error).to.not.exist
266 expect(spy).to.have.been.calledWith {
267 bar: 'baz'
268 }, {
269 quiet: true
270 }
271 done()
272
273 it 'should return an error if lacking a required option', (done) ->
274 command = new Command
275 signature: new Signature('foo <bar>')
276 action: _.noop
277 options: [
278 new Option
279 signature: new Signature('quiet')
280 boolean: true
281 required: 'You have to pass this option'
282 ]
283
284 command.execute {
285 command: 'foo baz'
286 }, (error) ->
287 expect(error).to.be.an.instanceof(Error)
288 expect(error.message).to.equal('You have to pass this option')
289 done()
290
291 it 'should parse global and command options', (done) ->
292 spy = sinon.spy()
293
294 state.globalOptions.push new Option
295 signature: new Signature('config')
296 parameter: 'name'
297 boolean: false
298 alias: 'c'
299
300 command = new Command
301 signature: new Signature('foo <bar>')
302 action: spy
303 options: [
304 new Option
305 signature: new Signature('quiet')
306 boolean: true
307 ]
308
309 command.execute {
310 command: 'foo baz'
311 options:
312 quiet: true
313 c: 'hello.conf'
314 }, (error) ->
315 expect(error).to.not.exist
316 expect(spy).to.have.been.calledWith {
317 bar: 'baz'
318 }, {
319 quiet: true
320 config: 'hello.conf'
321 }
322 done()
323
324 it 'should give precedence to command options', (done) ->
325 spy = sinon.spy()
326
327 state.globalOptions.push new Option
328 signature: new Signature('config')
329 parameter: 'name'
330
331 command = new Command
332 signature: new Signature('foo <bar>')
333 action: spy
334 options: [
335 new Option
336 signature: new Signature('config')
337 boolean: true
338 ]
339
340 command.execute {
341 command: 'foo baz'
342 options:
343 config: true
344 }, (error) ->
345 expect(error).to.not.exist
346 expect(spy).to.have.been.calledWith {
347 bar: 'baz'
348 }, {
349 config: true
350 }
351 done()
352
353 it 'should be able to call the done callback manually', (done) ->
354 command = new Command
355 signature: new Signature('foo <bar>')
356 action: (params, options, callback) ->
357 return callback(null, 123)
358
359 command.execute command: 'foo bar', (error, data) ->
360 expect(error).to.not.exist
361 expect(data).to.equal(123)
362 done()
363
364 it 'should be able to call the done callback with an error', (done) ->
365 cliError = new Error('Test error')
366
367 command = new Command
368 signature: new Signature('foo <bar>')
369 action: (params, options, callback) ->
370 return callback(cliError)
371
372 command.execute command: 'foo bar', (error) ->
373 expect(error).to.deep.equal(cliError)
374 done()
375
376 it 'should call the action if no callback', (done) ->
377 spy = sinon.spy()
378
379 command = new Command
380 signature: new Signature('foo <bar>')
381 action: spy
382
383 command.execute command: 'foo bar', (error) ->
384 expect(error).to.not.exist
385 expect(spy).to.have.been.calledOnce
386 done()
387
388 describe 'given an action that throws an error', ->
389
390 beforeEach ->
391 @command = new Command
392 signature: new Signature('hello')
393 action: ->
394 throw new Error('Command Error')
395
396 it 'should catch the error and send it to the callback', (done) ->
397 @command.execute command: 'hello', (error) ->
398 expect(error).to.be.an.instanceof(Error)
399 expect(error.message).to.equal('Command Error')
400 done()
401
402 describe 'given a command with the root property', ->
403
404 beforeEach ->
405 @actionSpy = sinon.spy()
406 @command = new Command
407 signature: new Signature('foo')
408 action: @actionSpy
409 root: true
410
411 describe 'given the user is root', ->
412
413 beforeEach ->
414 @utilsIsElevatedStub = sinon.stub(utils, 'isElevated')
415 @utilsIsElevatedStub.yields(null, true)
416
417 afterEach ->
418 @utilsIsElevatedStub.restore()
419
420 it 'should execute the action normally', (done) ->
421 @command.execute command: 'foo', (error) =>
422 expect(error).to.not.exist
423 expect(@actionSpy).to.have.been.called
424 done()
425
426 describe 'given the user is not root', ->
427
428 beforeEach ->
429 @utilsIsElevatedStub = sinon.stub(utils, 'isElevated')
430 @utilsIsElevatedStub.yields(null, false)
431
432 afterEach ->
433 @utilsIsElevatedStub.restore()
434
435 it 'should not execute the action', (done) ->
436 @command.execute command: 'foo', =>
437 expect(@actionSpy).to.not.have.been.called
438 done()
439
440 it 'should return an error', (done) ->
441 @command.execute command: 'foo', (error) ->
442 expect(error).to.be.an.instanceof(Error)
443 expect(error.message).to.equal('You need admin privileges to run this command')
444 expect(error.code).to.equal('EACCES')
445 done()
446
447 describe 'given a command with permissions', ->
448
449 beforeEach ->
450 state.permissions = {}
451
452 @actionSpy = sinon.spy()
453 @command = new Command
454 signature: new Signature('foo')
455 action: @actionSpy
456 permission: 'user'
457
458 it 'should call the action if permission is found and does not return an error', (done) ->
459 state.permissions.user = (done) -> done()
460 @command.execute command: 'foo', (error) =>
461 expect(error).to.not.exist
462 expect(@actionSpy).to.have.been.called
463 done()
464
465 it 'should not call the action if permission is found and returns an error', (done) ->
466 state.permissions.user = (done) ->
467 error = new Error('You are not a user!')
468 done(error)
469
470 @command.execute command: 'foo', (error) =>
471 expect(error).to.be.an.instanceof(Error)
472 expect(error.message).to.equal('You are not a user!')
473 expect(@actionSpy).to.not.have.been.called
474 done()
475
476 it 'should return an error if permission is not found', (done) ->
477 @command.execute command: 'foo', (error) =>
478 expect(error).to.be.an.instanceof(Error)
479 expect(error.message).to.equal('Permission not found: user')
480 expect(@actionSpy).to.not.have.been.called
481 done()
482
483 describe '#isWildcard()', ->
484
485 it 'should return true if is wildcard', ->
486 command = new Command
487 signature: new Signature(settings.signatures.wildcard)
488 action: _.noop
489
490 expect(command.isWildcard()).to.be.true
491
492 it 'should return false if not wildcard', ->
493 command = new Command
494 signature: new Signature('foo bar')
495 action: _.noop
496
497 expect(command.isWildcard()).to.be.false