ggem 1.7.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1012 @@
1
+ require 'assert'
2
+ require 'ggem/cli'
3
+
4
+ require 'ggem/cli/clirb'
5
+ require 'ggem/cli/commands'
6
+ require 'ggem/gem'
7
+ require 'ggem/gemspec'
8
+ require 'ggem/git_repo'
9
+ require 'much-plugin'
10
+
11
+ class GGem::CLI
12
+
13
+ class UnitTests < Assert::Context
14
+ desc "GGem::CLI"
15
+ setup do
16
+ @cli_class = GGem::CLI
17
+ end
18
+ subject{ @cli_class }
19
+
20
+ should have_imeths :run
21
+
22
+ should "build and run an instance of itself using `run`" do
23
+ cli_spy = CLISpy.new
24
+ Assert.stub(subject, :new).with{ cli_spy }
25
+
26
+ args = [Factory.string]
27
+ subject.run(args)
28
+ assert_equal args, cli_spy.run_called_with
29
+ end
30
+
31
+ should "know its commands" do
32
+ assert_equal 6, COMMANDS.size
33
+
34
+ assert_instance_of InvalidCommand, COMMANDS[Factory.string]
35
+
36
+ assert_instance_of GenerateCommand, COMMANDS['generate']
37
+ assert_instance_of BuildCommand, COMMANDS['build']
38
+ assert_instance_of InstallCommand, COMMANDS['install']
39
+ assert_instance_of PushCommand, COMMANDS['push']
40
+ assert_instance_of TagCommand, COMMANDS['tag']
41
+ assert_instance_of ReleaseCommand, COMMANDS['release']
42
+
43
+ assert_same COMMANDS['generate'], COMMANDS['g']
44
+ assert_same COMMANDS['build'], COMMANDS['b']
45
+ assert_same COMMANDS['install'], COMMANDS['i']
46
+ assert_same COMMANDS['push'], COMMANDS['p']
47
+ assert_same COMMANDS['tag'], COMMANDS['t']
48
+ assert_same COMMANDS['release'], COMMANDS['r']
49
+ end
50
+
51
+ end
52
+
53
+ class InitTests < UnitTests
54
+ desc "when init"
55
+ setup do
56
+ @kernel_spy = KernelSpy.new
57
+ @stdout = IOSpy.new
58
+ @stderr = IOSpy.new
59
+
60
+ @cli = @cli_class.new(@kernel_spy, @stdout, @stderr)
61
+ end
62
+ subject{ @cli }
63
+
64
+ should have_imeths :run
65
+
66
+ end
67
+
68
+ class RunSetupTests < InitTests
69
+ setup do
70
+ @command_name = Factory.string
71
+ @argv = [@command_name, Factory.string]
72
+
73
+ @command_class = Class.new
74
+ @command_spy = CommandSpy.new
75
+ Assert.stub(@command_class, :new){ @command_spy }
76
+ COMMANDS.add(@command_class, @command_name)
77
+
78
+ @invalid_command = InvalidCommand.new(@command_name)
79
+ end
80
+ teardown do
81
+ COMMANDS.remove(@command_name)
82
+ end
83
+
84
+ end
85
+
86
+ class RunTests < RunSetupTests
87
+ desc "and run"
88
+ setup do
89
+ @cli.run(@argv)
90
+ end
91
+
92
+ should "have run the command" do
93
+ assert_true @command_spy.run_called
94
+ end
95
+
96
+ should "have successfully exited" do
97
+ assert_equal 0, @kernel_spy.exit_status
98
+ end
99
+
100
+ end
101
+
102
+ class RunWithNoArgsTests < RunSetupTests
103
+ desc "and run with no args"
104
+ setup do
105
+ @cli.run([])
106
+ end
107
+
108
+ should "output the invalid command's help" do
109
+ assert_equal @invalid_command.help, @stdout.read
110
+ assert_empty @stderr.read
111
+ end
112
+
113
+ should "have successfully exited" do
114
+ assert_equal 0, @kernel_spy.exit_status
115
+ end
116
+
117
+ end
118
+
119
+ class RunWithInvalidCommandTests < RunSetupTests
120
+ desc "and run with an invalid command"
121
+ setup do
122
+ @name = Factory.string
123
+ @argv.unshift(@name)
124
+ @cli.run(@argv)
125
+ end
126
+
127
+ should "output that it is invalid and output the invalid command's help" do
128
+ exp = "'#{@name}' is not a command.\n\n"
129
+ assert_equal exp, @stderr.read
130
+ assert_equal @invalid_command.help, @stdout.read
131
+ end
132
+
133
+ should "have unsuccessfully exited" do
134
+ assert_equal 1, @kernel_spy.exit_status
135
+ end
136
+
137
+ end
138
+
139
+ class RunWithCommandExitErrorTests < RunSetupTests
140
+ desc "and run with a command that error exits"
141
+ setup do
142
+ Assert.stub(@command_spy, :run){ raise CommandExitError }
143
+ @cli.run(@argv)
144
+ end
145
+
146
+ should "have unsuccessfully exited with no stderr output" do
147
+ assert_equal 1, @kernel_spy.exit_status
148
+ assert_empty @stderr.read
149
+ end
150
+
151
+ end
152
+
153
+ class RunWithHelpTests < RunSetupTests
154
+ desc "and run with the help switch"
155
+ setup do
156
+ @cli.run([ '--help' ])
157
+ end
158
+
159
+ should "output the invalid command's help" do
160
+ assert_equal @invalid_command.help, @stdout.read
161
+ assert_empty @stderr.read
162
+ end
163
+
164
+ should "have successfully exited" do
165
+ assert_equal 0, @kernel_spy.exit_status
166
+ end
167
+
168
+ end
169
+
170
+ class RunWithVersionTests < RunSetupTests
171
+ desc "and run with the version switch"
172
+ setup do
173
+ @cli.run([ '--version' ])
174
+ end
175
+
176
+ should "have output its version" do
177
+ assert_equal "#{GGem::VERSION}\n", @stdout.read
178
+ assert_empty @stderr.read
179
+ end
180
+
181
+ should "have successfully exited" do
182
+ assert_equal 0, @kernel_spy.exit_status
183
+ end
184
+
185
+ end
186
+
187
+ class RunWithErrorTests < RunSetupTests
188
+ setup do
189
+ @exception = RuntimeError.new(Factory.string)
190
+ Assert.stub(@command_spy, :run){ raise @exception }
191
+ @cli.run(@argv)
192
+ end
193
+
194
+ should "have output an error message" do
195
+ exp = "#{@exception.class}: #{@exception.message}\n" \
196
+ "#{@exception.backtrace.join("\n")}\n"
197
+ assert_equal exp, @stderr.read
198
+ assert_empty @stdout.read
199
+ end
200
+
201
+ should "have unsuccessfully exited" do
202
+ assert_equal 1, @kernel_spy.exit_status
203
+ end
204
+
205
+ end
206
+
207
+ class InvalidCommandTests < UnitTests
208
+ desc "InvalidCommand"
209
+ setup do
210
+ @name = Factory.string
211
+ @command_class = InvalidCommand
212
+ @cmd = @command_class.new(@name)
213
+ end
214
+ subject{ @cmd }
215
+
216
+ should have_readers :name, :clirb
217
+ should have_imeths :new, :run, :help
218
+
219
+ should "know its attrs" do
220
+ assert_equal @name, subject.name
221
+ assert_instance_of CLIRB, subject.clirb
222
+ end
223
+
224
+ should "set its argv and return itself using `new`" do
225
+ assert_same subject, subject.new
226
+ end
227
+
228
+ should "parse its argv on run" do
229
+ assert_raises(CLIRB::HelpExit){ subject.new.run([ '--help' ]) }
230
+ assert_raises(CLIRB::VersionExit){ subject.new.run([ '--version' ]) }
231
+ end
232
+
233
+ should "raise a help exit if its name is empty" do
234
+ cmd = @command_class.new([nil, ''].choice)
235
+ argv = [Factory.string, Factory.string]
236
+ assert_raises(CLIRB::HelpExit){ cmd.new.run(argv) }
237
+ end
238
+
239
+ should "raise an invalid command error when run" do
240
+ assert_raises(InvalidCommandError){ subject.new.run([Factory.string]) }
241
+ end
242
+
243
+ should "know its help" do
244
+ exp = "Usage: ggem [COMMAND] [options]\n\n" \
245
+ "Options: #{subject.clirb}\n" \
246
+ "Commands:\n" \
247
+ "#{COMMANDS.to_s.split("\n").map{ |l| " #{l}" }.join("\n")}\n"
248
+ assert_equal exp, subject.help
249
+ end
250
+
251
+ end
252
+
253
+ class IOCommandTests < UnitTests
254
+ setup do
255
+ @stdout, @stderr = IOSpy.new, IOSpy.new
256
+ end
257
+ subject{ @cmd }
258
+
259
+ end
260
+
261
+ class ValidCommandTests < IOCommandTests
262
+ desc "ValidCommand"
263
+ setup do
264
+ @command_class = Class.new{ include ValidCommand }
265
+ @cmd = @command_class.new
266
+ end
267
+
268
+ should have_imeths :clirb, :run, :summary
269
+
270
+ should "know its CLI.RB" do
271
+ assert_instance_of CLIRB, subject.clirb
272
+ end
273
+
274
+ should "parse its args when run" do
275
+ argv = Factory.integer(3).times.map{ Factory.string }
276
+ subject.run(argv, @stdout, @stderr)
277
+ assert_equal argv, subject.clirb.args
278
+ end
279
+
280
+ should "default its summary" do
281
+ assert_equal '', subject.summary
282
+ end
283
+
284
+ end
285
+
286
+ class GitRepoCommandTests < IOCommandTests
287
+ desc "GitRepoCommand"
288
+ setup do
289
+ @gem1_root_path = TEST_SUPPORT_PATH.join('gem1')
290
+ Assert.stub(Dir, :pwd){ @gem1_root_path}
291
+
292
+ @command_class = Class.new{ include GitRepoCommand }
293
+ @cmd = @command_class.new
294
+ end
295
+
296
+ should "be a valid, notify cmd command" do
297
+ assert_kind_of ValidCommand, subject
298
+ assert_kind_of NotifyCmdCommand, subject
299
+ end
300
+
301
+ should "build a new git repo at the current pwd root" do
302
+ gitrepo_new_called_with = nil
303
+ Assert.stub(GGem::GitRepo, :new){ |*args| gitrepo_new_called_with = args }
304
+
305
+ @command_class.new
306
+ assert_equal [Dir.pwd], gitrepo_new_called_with
307
+ end
308
+
309
+ end
310
+
311
+ module RootPathTests
312
+ include MuchPlugin
313
+
314
+ plugin_included do
315
+ setup do
316
+ @root_path = Factory.path
317
+ Assert.stub(Dir, :pwd){ @root_path }
318
+ end
319
+ end
320
+
321
+ end
322
+
323
+ module GitRepoSpyTests
324
+ include MuchPlugin
325
+
326
+ plugin_included do
327
+ include RootPathTests
328
+
329
+ setup do
330
+ @repo_spy = nil
331
+ Assert.stub(GGem::GitRepo, :new){ |*args| @repo_spy = GitRepoSpy.new(*args) }
332
+ end
333
+
334
+ end
335
+
336
+ end
337
+
338
+ class GenerateCommandTests < IOCommandTests
339
+ include GitRepoSpyTests
340
+
341
+ desc "GenerateCommand"
342
+ setup do
343
+ @name = Factory.string
344
+
345
+ @path = Factory.dir_path
346
+ Assert.stub(Dir, :pwd){ @path }
347
+
348
+ @gem_new_called_with = []
349
+ @gem_spy = GemSpy.new
350
+ @gem_class = GGem::Gem
351
+ Assert.stub(@gem_class, :new) do |*args|
352
+ @gem_new_called_with = args
353
+ @gem_spy
354
+ end
355
+
356
+ @command_class = GenerateCommand
357
+ @cmd = @command_class.new
358
+ end
359
+
360
+ should "be a valid command" do
361
+ assert_kind_of ValidCommand, subject
362
+ end
363
+
364
+ should "know its summary" do
365
+ exp = "Create a gem given a GEM-NAME"
366
+ assert_equal exp, subject.summary
367
+ end
368
+
369
+ should "know its help" do
370
+ exp = "Usage: ggem generate [options] GEM-NAME\n\n" \
371
+ "Options: #{subject.clirb}\n" \
372
+ "Description:\n" \
373
+ " #{subject.summary}"
374
+ assert_equal exp, subject.help
375
+ end
376
+
377
+ should "save a gem and initialize a git repo for it when run" do
378
+ subject.run([@name], @stdout, @stderr)
379
+
380
+ assert_equal [@path, @name], @gem_new_called_with
381
+ assert_true @gem_spy.save_called
382
+
383
+ assert_equal @gem_spy.path, @repo_spy.path
384
+ assert_true @repo_spy.run_init_cmd_called
385
+
386
+ exp = "created gem in #{@gem_spy.path}\n" \
387
+ "initialized gem git repo\n"
388
+ assert_equal exp, @stdout.read
389
+ end
390
+
391
+ should "re-raise a specific argument error on gem 'no name' errors" do
392
+ Assert.stub(@gem_class, :new){ raise GGem::Gem::NoNameError }
393
+ err = nil
394
+ begin
395
+ cmd = @command_class.new
396
+ cmd.run([])
397
+ rescue ArgumentError => err
398
+ end
399
+
400
+ assert_not_nil err
401
+ exp = "GEM-NAME must be provided"
402
+ assert_equal exp, err.message
403
+ assert_not_empty err.backtrace
404
+ end
405
+
406
+ end
407
+
408
+ class GemspecCommandTests < IOCommandTests
409
+ desc "GemspecCommand"
410
+ setup do
411
+ @gem1_root_path = TEST_SUPPORT_PATH.join('gem1')
412
+ Assert.stub(Dir, :pwd){ @gem1_root_path}
413
+
414
+ @command_class = Class.new{ include GemspecCommand }
415
+ @cmd = @command_class.new
416
+ end
417
+
418
+ should "be a valid, notify cmd command" do
419
+ assert_kind_of ValidCommand, subject
420
+ assert_kind_of NotifyCmdCommand, subject
421
+ end
422
+
423
+ should "build a new gemspec at the current pwd root" do
424
+ gemspec_new_called_with = nil
425
+ Assert.stub(GGem::Gemspec, :new){ |*args| gemspec_new_called_with = args }
426
+
427
+ @command_class.new
428
+ assert_equal [Dir.pwd], gemspec_new_called_with
429
+ end
430
+
431
+ should "complain if no gemspec file can be found at the current pwd" do
432
+ root = Factory.path
433
+ Assert.stub(Dir, :pwd){ root }
434
+
435
+ begin
436
+ cmd = @command_class.new
437
+ rescue ArgumentError => err
438
+ end
439
+ assert_not_nil err
440
+ exp = "There are no gemspecs at #{Dir.pwd}"
441
+ assert_equal exp, err.message
442
+ end
443
+
444
+ end
445
+
446
+ module GemspecSpyTests
447
+ include MuchPlugin
448
+
449
+ plugin_included do
450
+ include RootPathTests
451
+
452
+ setup do
453
+ @spec_spy = nil
454
+ Assert.stub(GGem::Gemspec, :new){ |*args| @spec_spy = GemspecSpy.new(*args) }
455
+ end
456
+ end
457
+
458
+ end
459
+
460
+ class BuildCommandTests < IOCommandTests
461
+ include GemspecSpyTests
462
+
463
+ desc "BuildCommand"
464
+ setup do
465
+ @command_class = BuildCommand
466
+ @cmd = @command_class.new
467
+ end
468
+
469
+ should "be a gemspec command" do
470
+ assert_kind_of GemspecCommand, subject
471
+ end
472
+
473
+ should "know its summary" do
474
+ exp = "Build #{@spec_spy.gem_file_name} into the " \
475
+ "#{GGem::Gemspec::BUILD_TO_DIRNAME} directory"
476
+ assert_equal exp, subject.summary
477
+ end
478
+
479
+ should "know its help" do
480
+ exp = "Usage: ggem build [options]\n\n" \
481
+ "Options: #{subject.clirb}\n" \
482
+ "Description:\n" \
483
+ " #{subject.summary}"
484
+ assert_equal exp, subject.help
485
+ end
486
+
487
+ should "call the spec's run build cmd when run" do
488
+ ENV['DEBUG'] = [nil, '1'].choice
489
+ subject.run([], @stdout, @stderr)
490
+
491
+ assert_true @spec_spy.run_build_cmd_called
492
+
493
+ exp = ENV['DEBUG'] == '1' ? "build\nbuild cmd was run\n" : ''
494
+ exp += "#{@spec_spy.name} #{@spec_spy.version} built to #{@spec_spy.gem_file}\n"
495
+ assert_equal exp, @stdout.read
496
+
497
+ ENV['DEBUG'] = nil
498
+ end
499
+
500
+ should "handle cmd errors when run" do
501
+ err_msg = Factory.string
502
+ Assert.stub(@spec_spy, :run_build_cmd){ raise GGem::Gemspec::CmdError, err_msg }
503
+
504
+ assert_raises(CommandExitError){ subject.run([], @stdout, @stderr) }
505
+ assert_equal "#{err_msg}\n", @stderr.read
506
+ end
507
+
508
+ end
509
+
510
+ class InstallCommandTests < IOCommandTests
511
+ include GemspecSpyTests
512
+
513
+ desc "InstallCommand"
514
+ setup do
515
+ @build_spy = nil
516
+ Assert.stub(BuildCommand, :new){ |*args| @build_spy = CommandSpy.new(*args) }
517
+
518
+ @command_class = InstallCommand
519
+ @argv = []
520
+ @cmd = @command_class.new
521
+ end
522
+
523
+ should "be a gemspec command" do
524
+ assert_kind_of GemspecCommand, subject
525
+ end
526
+
527
+ should "know its summary" do
528
+ exp = "Build and install #{@spec_spy.gem_file_name} into system gems"
529
+ assert_equal exp, subject.summary
530
+ end
531
+
532
+ should "know its help" do
533
+ exp = "Usage: ggem install [options]\n\n" \
534
+ "Options: #{subject.clirb}\n" \
535
+ "Description:\n" \
536
+ " #{subject.summary}"
537
+ assert_equal exp, subject.help
538
+ end
539
+
540
+ should "build a build command using its argv" do
541
+ assert @build_spy
542
+ end
543
+
544
+ should "run the build command and call the spec's run install cmds when run" do
545
+ ENV['DEBUG'] = [nil, '1'].choice
546
+ subject.run(@argv, @stdout, @stderr)
547
+
548
+ assert_true @build_spy.run_called
549
+ assert_equal @argv, @build_spy.argv
550
+ assert_true @spec_spy.run_install_cmd_called
551
+
552
+ exp = ENV['DEBUG'] == '1' ? "install\ninstall cmd was run\n" : ''
553
+ exp += "#{@spec_spy.name} #{@spec_spy.version} installed to system gems\n"
554
+ assert_includes exp, @stdout.read
555
+
556
+ ENV['DEBUG'] = nil
557
+ end
558
+
559
+ should "handle cmd errors when run" do
560
+ err_msg = Factory.string
561
+ Assert.stub(@spec_spy, :run_install_cmd){ raise GGem::Gemspec::CmdError, err_msg }
562
+
563
+ assert_raises(CommandExitError){ subject.run(@argv, @stdout, @stderr) }
564
+ assert_equal "#{err_msg}\n", @stderr.read
565
+ end
566
+
567
+ end
568
+
569
+ class PushCommandTests < IOCommandTests
570
+ include GemspecSpyTests
571
+
572
+ desc "PushCommand"
573
+ setup do
574
+ @build_spy = nil
575
+ Assert.stub(BuildCommand, :new){ |*args| @build_spy = CommandSpy.new(*args) }
576
+
577
+ @command_class = PushCommand
578
+ @argv = []
579
+ @cmd = @command_class.new
580
+ end
581
+
582
+ should "be a gemspec command" do
583
+ assert_kind_of GemspecCommand, subject
584
+ end
585
+
586
+ should "know its summary" do
587
+ exp = "Push built #{@spec_spy.gem_file_name} to #{@spec_spy.push_host}"
588
+ assert_equal exp, subject.summary
589
+ end
590
+
591
+ should "know its help" do
592
+ exp = "Usage: ggem push [options]\n\n" \
593
+ "Options: #{subject.clirb}\n" \
594
+ "Description:\n" \
595
+ " #{subject.summary}"
596
+ assert_equal exp, subject.help
597
+ end
598
+
599
+ should "build a build command using its argv" do
600
+ assert @build_spy
601
+ end
602
+
603
+ should "run the build command and call the spec's run push cmds when run" do
604
+ ENV['DEBUG'] = [nil, '1'].choice
605
+ subject.run(@argv, @stdout, @stderr)
606
+
607
+ assert_true @build_spy.run_called
608
+ assert_equal @argv, @build_spy.argv
609
+ assert_true @spec_spy.run_push_cmd_called
610
+
611
+ exp = "Pushing #{@spec_spy.gem_file_name} to #{@spec_spy.push_host}...\n"
612
+ exp += ENV['DEBUG'] == '1' ? "push\npush cmd was run\n" : ''
613
+ exp += "#{@spec_spy.gem_file_name} received.\n"
614
+ assert_equal exp, @stdout.read
615
+
616
+ ENV['DEBUG'] = nil
617
+ end
618
+
619
+ should "handle cmd errors when run" do
620
+ err_msg = Factory.string
621
+ Assert.stub(@spec_spy, :run_push_cmd){ raise GGem::Gemspec::CmdError, err_msg }
622
+
623
+ assert_raises(CommandExitError){ subject.run(@argv, @stdout, @stderr) }
624
+ assert_equal "#{err_msg}\n", @stderr.read
625
+ end
626
+
627
+ end
628
+
629
+ class TagCommandTests < IOCommandTests
630
+ include GitRepoSpyTests
631
+ include GemspecSpyTests
632
+
633
+ desc "TagCommand"
634
+ setup do
635
+ @command_class = TagCommand
636
+ @cmd = @command_class.new
637
+ end
638
+
639
+ should "be a gemspec command" do
640
+ assert_kind_of GemspecCommand, subject
641
+ end
642
+
643
+ should "know its summary" do
644
+ exp = "Tag #{@spec_spy.version_tag} and push git commits/tags"
645
+ assert_equal exp, subject.summary
646
+ end
647
+
648
+ should "know its help" do
649
+ exp = "Usage: ggem tag [options]\n\n" \
650
+ "Options: #{subject.clirb}\n" \
651
+ "Description:\n" \
652
+ " #{subject.summary}"
653
+ assert_equal exp, subject.help
654
+ end
655
+
656
+ should "call the repo's run build/push cmds when run" do
657
+ ENV['DEBUG'] = [nil, '1'].choice
658
+ subject.run([], @stdout, @stderr)
659
+
660
+ assert_true @repo_spy.run_validate_clean_cmd_called
661
+ assert_true @repo_spy.run_validate_committed_cmd_called
662
+
663
+ exp = [@spec_spy.version, @spec_spy.version_tag]
664
+ assert_equal exp, @repo_spy.run_add_version_tag_cmd_called_with
665
+
666
+ assert_true @repo_spy.run_push_cmd_called
667
+ assert_nil @repo_spy.run_rm_tag_cmd_called_with
668
+
669
+ exp = if ENV['DEBUG'] == '1'
670
+ "validate clean\nvalidate clean cmd was run\n" \
671
+ "validate committed\nvalidate committed cmd was run\n" \
672
+ "add tag\nadd tag cmd was run\n"
673
+ else
674
+ ''
675
+ end
676
+ exp += "Tagged #{@spec_spy.version_tag}.\n"
677
+ exp += ENV['DEBUG'] == '1' ? "push\npush cmd was run\n" : ''
678
+ exp += "Pushed git commits and tags.\n"
679
+ assert_equal exp, @stdout.read
680
+
681
+ ENV['DEBUG'] = nil
682
+ end
683
+
684
+ should "handle validation cmd errors when run" do
685
+ err_msg = Factory.string
686
+ err_on = [:run_validate_clean_cmd, :run_validate_committed_cmd].choice
687
+ Assert.stub(@repo_spy, err_on){ raise GGem::GitRepo::CmdError, err_msg }
688
+
689
+ assert_raises(CommandExitError){ subject.run([], @stdout, @stderr) }
690
+ exp = "There are files that need to be committed first.\n"
691
+ assert_equal exp, @stderr.read
692
+ end
693
+
694
+ should "handle non-validation cmd errors when run" do
695
+ err_msg = Factory.string
696
+ err_on = [:run_add_version_tag_cmd, :run_push_cmd].choice
697
+ Assert.stub(@repo_spy, err_on){ raise GGem::GitRepo::CmdError, err_msg }
698
+
699
+ assert_raises(CommandExitError){ subject.run([], @stdout, @stderr) }
700
+ assert_equal "#{err_msg}\n", @stderr.read
701
+ end
702
+
703
+ should "remove the version tag on push errors" do
704
+ err_msg = Factory.string
705
+ Assert.stub(@repo_spy, :run_push_cmd){ raise GGem::GitRepo::CmdError, err_msg }
706
+
707
+ assert_raises(CommandExitError){ subject.run([], @stdout, @stderr) }
708
+ assert_equal "#{err_msg}\n", @stderr.read
709
+
710
+ exp = [@spec_spy.version_tag]
711
+ assert_equal exp, @repo_spy.run_rm_tag_cmd_called_with
712
+ end
713
+
714
+ should "handle tag removal cmd errors when run" do
715
+ Assert.stub(@repo_spy, :run_push_cmd){ raise GGem::GitRepo::CmdError, Factory.string }
716
+ err_msg = Factory.string
717
+ Assert.stub(@repo_spy, :run_rm_tag_cmd){ raise GGem::GitRepo::CmdError, err_msg }
718
+
719
+ assert_raises(CommandExitError){ subject.run([], @stdout, @stderr) }
720
+ assert_equal "#{err_msg}\n", @stderr.read
721
+ end
722
+
723
+ end
724
+
725
+ class ReleaseCommandTests < IOCommandTests
726
+ include GemspecSpyTests
727
+
728
+ desc "ReleaseCommand"
729
+ setup do
730
+ @tag_spy = nil
731
+ Assert.stub(TagCommand, :new){ |*args| @tag_spy = CommandSpy.new(*args) }
732
+
733
+ @push_spy = nil
734
+ Assert.stub(PushCommand, :new){ |*args| @push_spy = CommandSpy.new(*args) }
735
+
736
+ @command_class = ReleaseCommand
737
+ @argv = []
738
+ @cmd = @command_class.new
739
+ end
740
+
741
+ should "be a gemspec command" do
742
+ assert_kind_of GemspecCommand, subject
743
+ end
744
+
745
+ should "know its summary" do
746
+ exp = "Tag #{@spec_spy.version_tag} and push built #{@spec_spy.gem_file_name} to " \
747
+ "#{@spec_spy.push_host}"
748
+ assert_equal exp, subject.summary
749
+ end
750
+
751
+ should "know its help" do
752
+ exp = "Usage: ggem release [options]\n\n" \
753
+ "Options: #{subject.clirb}\n" \
754
+ "Description:\n" \
755
+ " #{subject.summary}\n" \
756
+ " (macro for running `ggem tag && ggem push`)"
757
+ assert_equal exp, subject.help
758
+ end
759
+
760
+ should "build a tag and push command using its argv" do
761
+ [@tag_spy, @push_spy].each do |spy|
762
+ assert spy
763
+ end
764
+ end
765
+
766
+ should "run the tag and push command when run" do
767
+ subject.run(@argv, @stdout, @stderr)
768
+
769
+ [@tag_spy, @push_spy].each do |spy|
770
+ assert_true spy.run_called
771
+ assert_equal @argv, spy.argv
772
+ end
773
+ end
774
+
775
+ end
776
+
777
+ class CommandSetTests < UnitTests
778
+ desc "CommandSet"
779
+ setup do
780
+ @unknown_cmd_block_called_with = nil
781
+ @set = CommandSet.new{ |*args| @unknown_cmd_block_called_with = args }
782
+ end
783
+ subject{ @set }
784
+
785
+ should have_imeths :add, :remove, :[], :size
786
+
787
+ should "add/rm commands, be able to look them up and know its size" do
788
+ assert_equal 0, subject.size
789
+ assert_equal '', subject.to_s
790
+
791
+ subject.add(CommandSpy, 'test', 't', 'tst')
792
+ assert_equal 1, subject.size
793
+
794
+ assert_instance_of CommandSpy, subject['test']
795
+ assert_same subject['test'], subject['t']
796
+ assert_same subject['test'], subject['tst']
797
+
798
+ exp_strs = ["test (t, tst) # #{subject['test'].summary}"]
799
+ assert_equal exp_strs.join("\n"), subject.to_s
800
+
801
+ subject.add(CommandSpy, 'add1')
802
+ exp_strs << "add1 # #{subject['add1'].summary}"
803
+
804
+ @cmd_spy = CommandSpy.new
805
+ Assert.stub(@cmd_spy, :summary){ [nil, ''].choice }
806
+ Assert.stub(CommandSpy, :new){ @cmd_spy }
807
+
808
+ subject.add(CommandSpy, 'add2', 'add')
809
+ exp_strs << "add2 (add) "
810
+
811
+ subject.add(CommandSpy, 'add3')
812
+ Assert.stub(subject['add3'], :summary){ [nil, ''].choice }
813
+ exp_strs << "add3 "
814
+
815
+ assert_equal exp_strs.join("\n"), subject.to_s
816
+
817
+ subject.remove('test')
818
+ subject.remove('add1')
819
+ subject.remove('add2')
820
+ subject.remove('add3')
821
+
822
+ assert_equal 0, subject.size
823
+ assert_equal '', subject.to_s
824
+ end
825
+
826
+ should "call the given block when looking up unknown command names" do
827
+ unknown_cmd_name = Factory.string
828
+ subject[unknown_cmd_name]
829
+ assert_equal [unknown_cmd_name], @unknown_cmd_block_called_with
830
+ end
831
+
832
+ end
833
+
834
+ class CLISpy
835
+ attr_reader :run_called_with
836
+
837
+ def initialize
838
+ @run_called_with = nil
839
+ end
840
+
841
+ def run(args)
842
+ @run_called_with = args
843
+ end
844
+ end
845
+
846
+ class CommandSpy
847
+ attr_reader :argv, :stdout, :stderr, :run_called
848
+
849
+ def initialize
850
+ @argv = nil
851
+ @stdout, @stderr = nil, nil
852
+ @run_called = false
853
+ end
854
+
855
+ def run(argv, stdout = nil, stderr = nil)
856
+ @argv = argv
857
+ @stdout, @stderr = stdout, stderr
858
+ @run_called = true
859
+ end
860
+
861
+ def summary
862
+ @summary ||= Factory.string
863
+ end
864
+
865
+ def help
866
+ @help ||= Factory.text
867
+ end
868
+ end
869
+
870
+ class KernelSpy
871
+ attr_reader :exit_status
872
+
873
+ def initialize
874
+ @exit_status = nil
875
+ end
876
+
877
+ def exit(code)
878
+ @exit_status ||= code
879
+ end
880
+ end
881
+
882
+ class IOSpy
883
+ def initialize
884
+ @io = StringIO.new
885
+ end
886
+
887
+ def puts(message)
888
+ @io.puts message
889
+ end
890
+
891
+ def read
892
+ @io.rewind
893
+ @io.read
894
+ end
895
+ end
896
+
897
+ class GemSpy
898
+ attr_reader :save_called
899
+
900
+ def initialize
901
+ @save_called = false
902
+ end
903
+
904
+ def save!
905
+ @save_called = true
906
+ self
907
+ end
908
+
909
+ def path
910
+ @path ||= Factory.path
911
+ end
912
+ end
913
+
914
+ class GemspecSpy
915
+ attr_reader :name, :version, :version_tag, :push_host
916
+ attr_reader :run_build_cmd_called, :run_install_cmd_called, :run_push_cmd_called
917
+
918
+ def initialize(root_path)
919
+ @root = Pathname.new(File.expand_path(root_path))
920
+ @name = Factory.string
921
+ @version = Factory.string
922
+ @version_tag = Factory.string
923
+ @push_host = Factory.url
924
+
925
+ @run_build_cmd_called = false
926
+ @run_install_cmd_called = false
927
+ @run_push_cmd_called = false
928
+ end
929
+
930
+ def path
931
+ @root.join("#{self.name}.gemspec")
932
+ end
933
+
934
+ def gem_file_name
935
+ "#{self.name}-#{self.version}.gem"
936
+ end
937
+
938
+ def gem_file
939
+ File.join(GGem::Gemspec::BUILD_TO_DIRNAME, self.gem_file_name)
940
+ end
941
+
942
+ def run_build_cmd
943
+ @run_build_cmd_called = true
944
+ ['build', 0, 'build cmd was run']
945
+ end
946
+
947
+ def run_install_cmd
948
+ @run_install_cmd_called = true
949
+ ['install', 0, 'install cmd was run']
950
+ end
951
+
952
+ def run_push_cmd
953
+ @run_push_cmd_called = true
954
+ ['push', 0, 'push cmd was run']
955
+ end
956
+
957
+ end
958
+
959
+ class GitRepoSpy
960
+ attr_reader :path
961
+ attr_reader :run_init_cmd_called
962
+ attr_reader :run_validate_clean_cmd_called, :run_validate_committed_cmd_called
963
+ attr_reader :run_add_version_tag_cmd_called_with, :run_rm_tag_cmd_called_with
964
+ attr_reader :run_push_cmd_called
965
+
966
+ def initialize(path)
967
+ @path = path
968
+
969
+ @run_init_cmd_called = false
970
+
971
+ @run_validate_clean_cmd_called = false
972
+ @run_validate_committed_cmd_called = false
973
+
974
+ @run_add_version_tag_cmd_called_with = nil
975
+ @run_rm_tag_cmd_called_with = nil
976
+
977
+ @run_push_cmd_called = false
978
+ end
979
+
980
+ def run_init_cmd
981
+ @run_init_cmd_called = true
982
+ ['init', 0, 'init cmd was run']
983
+ end
984
+
985
+ def run_validate_clean_cmd
986
+ @run_validate_clean_cmd_called = true
987
+ ['validate clean', 0, 'validate clean cmd was run']
988
+ end
989
+
990
+ def run_validate_committed_cmd
991
+ @run_validate_committed_cmd_called = true
992
+ ['validate committed', 0, 'validate committed cmd was run']
993
+ end
994
+
995
+ def run_add_version_tag_cmd(*args)
996
+ @run_add_version_tag_cmd_called_with = args
997
+ ['add tag', 0, 'add tag cmd was run']
998
+ end
999
+
1000
+ def run_rm_tag_cmd(*args)
1001
+ @run_rm_tag_cmd_called_with = args
1002
+ ['rm tag', 0, 'rm tag cmd was run']
1003
+ end
1004
+
1005
+ def run_push_cmd
1006
+ @run_push_cmd_called = true
1007
+ ['push', 0, 'push cmd was run']
1008
+ end
1009
+
1010
+ end
1011
+
1012
+ end