UNPKG

12.5 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 parse global and command options', (done) ->
274 spy = sinon.spy()
275
276 state.globalOptions.push new Option
277 signature: new Signature('config')
278 parameter: 'name'
279 boolean: false
280 alias: 'c'
281
282 command = new Command
283 signature: new Signature('foo <bar>')
284 action: spy
285 options: [
286 new Option
287 signature: new Signature('quiet')
288 boolean: true
289 ]
290
291 command.execute {
292 command: 'foo baz'
293 options:
294 quiet: true
295 c: 'hello.conf'
296 }, (error) ->
297 expect(error).to.not.exist
298 expect(spy).to.have.been.calledWith {
299 bar: 'baz'
300 }, {
301 quiet: true
302 config: 'hello.conf'
303 }
304 done()
305
306 it 'should give precedence to command options', (done) ->
307 spy = sinon.spy()
308
309 state.globalOptions.push new Option
310 signature: new Signature('config')
311 parameter: 'name'
312
313 command = new Command
314 signature: new Signature('foo <bar>')
315 action: spy
316 options: [
317 new Option
318 signature: new Signature('config')
319 boolean: true
320 ]
321
322 command.execute {
323 command: 'foo baz'
324 options:
325 config: true
326 }, (error) ->
327 expect(error).to.not.exist
328 expect(spy).to.have.been.calledWith {
329 bar: 'baz'
330 }, {
331 config: true
332 }
333 done()
334
335 it 'should be able to call the done callback manually', (done) ->
336 command = new Command
337 signature: new Signature('foo <bar>')
338 action: (params, options, callback) ->
339 return callback(null, 123)
340
341 command.execute command: 'foo bar', (error, data) ->
342 expect(error).to.not.exist
343 expect(data).to.equal(123)
344 done()
345
346 it 'should be able to call the done callback with an error', (done) ->
347 cliError = new Error('Test error')
348
349 command = new Command
350 signature: new Signature('foo <bar>')
351 action: (params, options, callback) ->
352 return callback(cliError)
353
354 command.execute command: 'foo bar', (error) ->
355 expect(error).to.deep.equal(cliError)
356 done()
357
358 it 'should call the action if no callback', (done) ->
359 spy = sinon.spy()
360
361 command = new Command
362 signature: new Signature('foo <bar>')
363 action: spy
364
365 command.execute command: 'foo bar', (error) ->
366 expect(error).to.not.exist
367 expect(spy).to.have.been.calledOnce
368 done()
369
370 describe 'given an action that throws an error', ->
371
372 beforeEach ->
373 @command = new Command
374 signature: new Signature('hello')
375 action: ->
376 throw new Error('Command Error')
377
378 it 'should catch the error and send it to the callback', (done) ->
379 @command.execute command: 'hello', (error) ->
380 expect(error).to.be.an.instanceof(Error)
381 expect(error.message).to.equal('Command Error')
382 done()
383
384 describe 'given a command with the root property', ->
385
386 beforeEach ->
387 @actionSpy = sinon.spy()
388 @command = new Command
389 signature: new Signature('foo')
390 action: @actionSpy
391 root: true
392
393 describe 'given the user is root', ->
394
395 beforeEach ->
396 @utilsIsElevatedStub = sinon.stub(utils, 'isElevated')
397 @utilsIsElevatedStub.yields(null, true)
398
399 afterEach ->
400 @utilsIsElevatedStub.restore()
401
402 it 'should execute the action normally', (done) ->
403 @command.execute command: 'foo', (error) =>
404 expect(error).to.not.exist
405 expect(@actionSpy).to.have.been.called
406 done()
407
408 describe 'given the user is not root', ->
409
410 beforeEach ->
411 @utilsIsElevatedStub = sinon.stub(utils, 'isElevated')
412 @utilsIsElevatedStub.yields(null, false)
413
414 afterEach ->
415 @utilsIsElevatedStub.restore()
416
417 it 'should not execute the action', (done) ->
418 @command.execute command: 'foo', =>
419 expect(@actionSpy).to.not.have.been.called
420 done()
421
422 it 'should return an error', (done) ->
423 @command.execute command: 'foo', (error) ->
424 expect(error).to.be.an.instanceof(Error)
425 expect(error.message).to.equal('You need admin privileges to run this command')
426 expect(error.code).to.equal('EACCES')
427 done()
428
429 describe 'given a command with permissions', ->
430
431 beforeEach ->
432 state.permissions = {}
433
434 @actionSpy = sinon.spy()
435 @command = new Command
436 signature: new Signature('foo')
437 action: @actionSpy
438 permission: 'user'
439
440 it 'should call the action if permission is found and does not return an error', (done) ->
441 state.permissions.user = (done) -> done()
442 @command.execute command: 'foo', (error) =>
443 expect(error).to.not.exist
444 expect(@actionSpy).to.have.been.called
445 done()
446
447 it 'should not call the action if permission is found and returns an error', (done) ->
448 state.permissions.user = (done) ->
449 error = new Error('You are not a user!')
450 done(error)
451
452 @command.execute command: 'foo', (error) =>
453 expect(error).to.be.an.instanceof(Error)
454 expect(error.message).to.equal('You are not a user!')
455 expect(@actionSpy).to.not.have.been.called
456 done()
457
458 it 'should return an error if permission is not found', (done) ->
459 @command.execute command: 'foo', (error) =>
460 expect(error).to.be.an.instanceof(Error)
461 expect(error.message).to.equal('Permission not found: user')
462 expect(@actionSpy).to.not.have.been.called
463 done()
464
465 describe '#isWildcard()', ->
466
467 it 'should return true if is wildcard', ->
468 command = new Command
469 signature: new Signature(settings.signatures.wildcard)
470 action: _.noop
471
472 expect(command.isWildcard()).to.be.true
473
474 it 'should return false if not wildcard', ->
475 command = new Command
476 signature: new Signature('foo bar')
477 action: _.noop
478
479 expect(command.isWildcard()).to.be.false