cf 0.1.5 → 0.6.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. data/LICENSE +1277 -30
  2. data/Rakefile +12 -1
  3. data/bin/cf +0 -3
  4. data/lib/cf.rb +6 -0
  5. data/lib/cf/cli.rb +389 -190
  6. data/lib/cf/cli/app/app.rb +45 -0
  7. data/lib/cf/cli/app/apps.rb +99 -0
  8. data/lib/cf/cli/app/base.rb +90 -0
  9. data/lib/cf/cli/app/crashes.rb +42 -0
  10. data/lib/cf/cli/app/delete.rb +95 -0
  11. data/lib/cf/cli/app/deprecated.rb +11 -0
  12. data/lib/cf/cli/app/env.rb +78 -0
  13. data/lib/cf/cli/app/files.rb +137 -0
  14. data/lib/cf/cli/app/health.rb +26 -0
  15. data/lib/cf/cli/app/instances.rb +53 -0
  16. data/lib/cf/cli/app/logs.rb +76 -0
  17. data/lib/cf/cli/app/push.rb +105 -0
  18. data/lib/cf/cli/app/push/create.rb +149 -0
  19. data/lib/cf/cli/app/push/interactions.rb +94 -0
  20. data/lib/cf/cli/app/push/sync.rb +64 -0
  21. data/lib/cf/cli/app/rename.rb +35 -0
  22. data/lib/cf/cli/app/restart.rb +20 -0
  23. data/lib/cf/cli/app/scale.rb +69 -0
  24. data/lib/cf/cli/app/start.rb +143 -0
  25. data/lib/cf/cli/app/stats.rb +67 -0
  26. data/lib/cf/cli/app/stop.rb +27 -0
  27. data/lib/cf/cli/domain/base.rb +8 -0
  28. data/lib/cf/cli/domain/domains.rb +40 -0
  29. data/lib/cf/cli/domain/map.rb +55 -0
  30. data/lib/cf/cli/domain/unmap.rb +56 -0
  31. data/lib/cf/cli/help.rb +15 -0
  32. data/lib/cf/cli/interactive.rb +105 -0
  33. data/lib/cf/cli/organization/base.rb +12 -0
  34. data/lib/cf/cli/organization/create.rb +32 -0
  35. data/lib/cf/cli/organization/delete.rb +73 -0
  36. data/lib/cf/cli/organization/org.rb +45 -0
  37. data/lib/cf/cli/organization/orgs.rb +35 -0
  38. data/lib/cf/cli/organization/rename.rb +36 -0
  39. data/lib/cf/cli/route/base.rb +8 -0
  40. data/lib/cf/cli/route/map.rb +70 -0
  41. data/lib/cf/cli/route/routes.rb +26 -0
  42. data/lib/cf/cli/route/unmap.rb +62 -0
  43. data/lib/cf/cli/service/base.rb +8 -0
  44. data/lib/cf/cli/service/bind.rb +44 -0
  45. data/lib/cf/cli/service/create.rb +107 -0
  46. data/lib/cf/cli/service/delete.rb +82 -0
  47. data/lib/cf/cli/service/rename.rb +35 -0
  48. data/lib/cf/cli/service/service.rb +40 -0
  49. data/lib/cf/cli/service/services.rb +99 -0
  50. data/lib/cf/cli/service/unbind.rb +38 -0
  51. data/lib/cf/cli/space/base.rb +19 -0
  52. data/lib/cf/cli/space/create.rb +63 -0
  53. data/lib/cf/cli/space/delete.rb +95 -0
  54. data/lib/cf/cli/space/rename.rb +39 -0
  55. data/lib/cf/cli/space/space.rb +64 -0
  56. data/lib/cf/cli/space/spaces.rb +55 -0
  57. data/lib/cf/cli/space/switch.rb +16 -0
  58. data/lib/cf/cli/start/base.rb +93 -0
  59. data/lib/cf/cli/start/colors.rb +13 -0
  60. data/lib/cf/cli/start/info.rb +124 -0
  61. data/lib/cf/cli/start/login.rb +94 -0
  62. data/lib/cf/cli/start/logout.rb +17 -0
  63. data/lib/cf/cli/start/target.rb +69 -0
  64. data/lib/cf/cli/start/target_interactions.rb +37 -0
  65. data/lib/cf/cli/start/targets.rb +16 -0
  66. data/lib/cf/cli/user/base.rb +29 -0
  67. data/lib/cf/cli/user/create.rb +39 -0
  68. data/lib/cf/cli/user/passwd.rb +43 -0
  69. data/lib/cf/cli/user/register.rb +42 -0
  70. data/lib/cf/cli/user/users.rb +32 -0
  71. data/lib/cf/constants.rb +10 -7
  72. data/lib/cf/detect.rb +113 -48
  73. data/lib/cf/errors.rb +17 -0
  74. data/lib/cf/plugin.rb +28 -12
  75. data/lib/cf/spacing.rb +89 -0
  76. data/lib/cf/spec_helper.rb +1 -0
  77. data/lib/cf/test_support.rb +6 -0
  78. data/lib/cf/version.rb +1 -1
  79. data/spec/assets/hello-sinatra/Gemfile +3 -0
  80. data/spec/assets/hello-sinatra/Gemfile.lock +17 -0
  81. data/spec/assets/hello-sinatra/config.ru +3 -0
  82. data/spec/assets/hello-sinatra/fat-cat-makes-app-larger.png +0 -0
  83. data/spec/assets/hello-sinatra/main.rb +6 -0
  84. data/spec/assets/specker_runner/specker_runner_input.rb +6 -0
  85. data/spec/assets/specker_runner/specker_runner_pause.rb +5 -0
  86. data/spec/cf/cli/app/base_spec.rb +17 -0
  87. data/spec/cf/cli/app/delete_spec.rb +188 -0
  88. data/spec/cf/cli/app/instances_spec.rb +65 -0
  89. data/spec/cf/cli/app/push/create_spec.rb +661 -0
  90. data/spec/cf/cli/app/push_spec.rb +369 -0
  91. data/spec/cf/cli/app/rename_spec.rb +104 -0
  92. data/spec/cf/cli/app/scale_spec.rb +75 -0
  93. data/spec/cf/cli/app/start_spec.rb +208 -0
  94. data/spec/cf/cli/app/stats_spec.rb +68 -0
  95. data/spec/cf/cli/domain/map_spec.rb +130 -0
  96. data/spec/cf/cli/domain/unmap_spec.rb +69 -0
  97. data/spec/cf/cli/organization/orgs_spec.rb +108 -0
  98. data/spec/cf/cli/organization/rename_spec.rb +113 -0
  99. data/spec/cf/cli/route/map_spec.rb +121 -0
  100. data/spec/cf/cli/route/unmap_spec.rb +155 -0
  101. data/spec/cf/cli/service/bind_spec.rb +25 -0
  102. data/spec/cf/cli/service/delete_spec.rb +22 -0
  103. data/spec/cf/cli/service/rename_spec.rb +105 -0
  104. data/spec/cf/cli/service/service_spec.rb +23 -0
  105. data/spec/cf/cli/service/unbind_spec.rb +25 -0
  106. data/spec/cf/cli/space/create_spec.rb +93 -0
  107. data/spec/cf/cli/space/rename_spec.rb +102 -0
  108. data/spec/cf/cli/space/spaces_spec.rb +104 -0
  109. data/spec/cf/cli/space/switch_space_spec.rb +55 -0
  110. data/spec/cf/cli/start/info_spec.rb +160 -0
  111. data/spec/cf/cli/start/login_spec.rb +142 -0
  112. data/spec/cf/cli/start/logout_spec.rb +50 -0
  113. data/spec/cf/cli/start/target_spec.rb +123 -0
  114. data/spec/cf/cli/user/create_spec.rb +54 -0
  115. data/spec/cf/cli/user/passwd_spec.rb +102 -0
  116. data/spec/cf/cli/user/register_spec.rb +140 -0
  117. data/spec/cf/cli_spec.rb +442 -0
  118. data/spec/cf/detect_spec.rb +54 -0
  119. data/spec/console_app_specker/console_app_specker_matchers_spec.rb +173 -0
  120. data/spec/console_app_specker/specker_runner_spec.rb +167 -0
  121. data/spec/features/account_lifecycle_spec.rb +85 -0
  122. data/spec/features/login_spec.rb +66 -0
  123. data/spec/features/push_flow_spec.rb +125 -0
  124. data/spec/features/switching_targets_spec.rb +32 -0
  125. data/spec/spec_helper.rb +72 -0
  126. data/spec/support/command_helper.rb +81 -0
  127. data/spec/support/config_helper.rb +15 -0
  128. data/spec/support/console_app_specker_matchers.rb +86 -0
  129. data/spec/support/fake_home_dir.rb +55 -0
  130. data/spec/support/interact_helper.rb +29 -0
  131. data/spec/support/shared_examples/errors.rb +40 -0
  132. data/spec/support/shared_examples/input.rb +14 -0
  133. data/spec/support/specker_runner.rb +80 -0
  134. data/spec/support/tracking_expector.rb +71 -0
  135. metadata +427 -66
  136. data/lib/cf/cli/app.rb +0 -595
  137. data/lib/cf/cli/command.rb +0 -444
  138. data/lib/cf/cli/dots.rb +0 -133
  139. data/lib/cf/cli/service.rb +0 -112
  140. data/lib/cf/cli/user.rb +0 -71
@@ -9,12 +9,25 @@ module CF
9
9
  @@plugins = []
10
10
 
11
11
  def self.load_all
12
- # auto-load gems with 'vmc-plugin' in their name
13
- enabled =
14
- Set.new(
15
- Gem::Specification.find_all { |s|
16
- s.name =~ /vmc-plugin/
17
- }.collect(&:name))
12
+ # auto-load gems with 'cf-plugin' in their name
13
+ matching =
14
+ if Gem::Specification.respond_to? :find_all
15
+ Gem::Specification.find_all do |s|
16
+ s.name =~ /cf-plugin/
17
+ end
18
+ else
19
+ Gem.source_index.find_name(/cf-plugin/)
20
+ end
21
+
22
+ enabled = Set.new(matching.collect(&:name))
23
+
24
+ cf_gems = Gem.loaded_specs["cf"]
25
+ ((cf_gems && cf_gems.dependencies) || Gem.loaded_specs.values).each do |dep|
26
+ if dep.name =~ /cf-plugin/
27
+ require "#{dep.name}/plugin"
28
+ enabled.delete dep.name
29
+ end
30
+ end
18
31
 
19
32
  # allow explicit enabling/disabling of gems via config
20
33
  plugins = File.expand_path(CF::PLUGINS_FILE)
@@ -28,13 +41,16 @@ module CF
28
41
  # we require this file specifically so people can require the gem
29
42
  # without it plugging into CF
30
43
  enabled.each do |gemname|
31
- require "#{gemname}/plugin"
44
+ begin
45
+ require "#{gemname}/plugin"
46
+ rescue Gem::LoadError => e
47
+ puts "Failed to load #{gemname}:"
48
+ puts " #{e}"
49
+ puts
50
+ puts "You may need to update or remove this plugin."
51
+ puts
52
+ end
32
53
  end
33
54
  end
34
55
  end
35
-
36
- def self.Plugin(target = CLI, &blk)
37
- # SUPER FANCY PLUGIN SYSTEM
38
- target.class_eval &blk
39
- end
40
56
  end
@@ -0,0 +1,89 @@
1
+ module CF
2
+ module Spacing
3
+ @@indentation = 0
4
+
5
+ def indented
6
+ @@indentation += 1
7
+ yield
8
+ ensure
9
+ @@indentation -= 1
10
+ end
11
+
12
+ def line(msg = "")
13
+ return puts "" if msg.empty?
14
+
15
+ start_line(msg)
16
+ puts ""
17
+ end
18
+
19
+ def start_line(msg)
20
+ print " " * @@indentation unless quiet?
21
+ print msg
22
+ end
23
+
24
+ def lines(blob)
25
+ blob.each_line do |line|
26
+ start_line(line)
27
+ end
28
+
29
+ line
30
+ end
31
+
32
+ def quiet?
33
+ false
34
+ end
35
+
36
+ def spaced(vals)
37
+ num = 0
38
+ vals.each do |val|
39
+ line unless quiet? || num == 0
40
+ yield val
41
+ num += 1
42
+ end
43
+ end
44
+
45
+ def tabular(*rows)
46
+ spacings = []
47
+ rows.each do |row|
48
+ next unless row
49
+
50
+ row.each.with_index do |col, i|
51
+ next unless col
52
+
53
+ width = text_width(col)
54
+
55
+ if !spacings[i] || width > spacings[i]
56
+ spacings[i] = width
57
+ end
58
+ end
59
+ end
60
+
61
+ columns = spacings.size
62
+ rows.each do |row|
63
+ next unless row
64
+
65
+ row.each.with_index do |col, i|
66
+ next unless col
67
+
68
+ start_line justify(col, spacings[i])
69
+ print " " unless i + 1 == columns
70
+ end
71
+
72
+ line
73
+ end
74
+ end
75
+
76
+ def trim_escapes(str)
77
+ str.gsub(/\e\[\d+m/, "")
78
+ end
79
+
80
+ def text_width(str)
81
+ trim_escapes(str).size
82
+ end
83
+
84
+ def justify(str, width)
85
+ trimmed = trim_escapes(str)
86
+ str.ljust(width + (str.size - trimmed.size))
87
+ end
88
+ end
89
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path("../../../spec/spec_helper", __FILE__)
@@ -0,0 +1,6 @@
1
+ module CF::TestSupport
2
+ end
3
+
4
+ Dir[File.expand_path('../../../spec/support/**/*.rb', __FILE__)].each do |file|
5
+ require file
6
+ end
@@ -1,3 +1,3 @@
1
1
  module CF
2
- VERSION = "0.1.5"
2
+ VERSION = "0.6.0.rc1".freeze
3
3
  end
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "sinatra"
@@ -0,0 +1,17 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ rack (1.5.2)
5
+ rack-protection (1.3.2)
6
+ rack
7
+ sinatra (1.3.4)
8
+ rack (~> 1.4)
9
+ rack-protection (~> 1.3)
10
+ tilt (~> 1.3, >= 1.3.3)
11
+ tilt (1.3.3)
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ sinatra
@@ -0,0 +1,3 @@
1
+ require './main'
2
+
3
+ run Main.new
@@ -0,0 +1,6 @@
1
+ require "rubygems"
2
+ require "sinatra"
3
+
4
+ get "/" do
5
+ "Hello, world!"
6
+ end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $stdout.sync = true
4
+ print "started"
5
+ typed = gets
6
+ print "received #{typed}"
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ print "started"
4
+ sleep 0.5
5
+ print " finished"
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+ require "cf/cli/app/base"
3
+
4
+ describe CF::App::Base do
5
+ describe '#human_size' do
6
+ let(:base) { CF::App::Base.new }
7
+
8
+ it { base.human_size(1_023).should == "1023.0B" }
9
+ it { base.human_size(1_024).should == "1.0K" }
10
+ it { base.human_size(1_024 * 1_024).should == "1.0M" }
11
+ it { base.human_size(1_024 * 1_024 * 1.5).should == "1.5M" }
12
+ it { base.human_size(1_024 * 1_024 * 1_024).should == "1.0G" }
13
+ it { base.human_size(1_024 * 1_024 * 1_024 * 1.5).should == "1.5G" }
14
+ it { base.human_size(1_024 * 1_024 * 1_024 * 1.234, 3).should == "1.234G" }
15
+ it { base.human_size(31395840).should == "29.9M" } # tests against floating point errors
16
+ end
17
+ end
@@ -0,0 +1,188 @@
1
+ require 'spec_helper'
2
+ require "cf/cli/app/delete"
3
+
4
+ describe CF::App::Delete do
5
+ let(:global) { { :color => false, :quiet => true } }
6
+ let(:inputs) { {} }
7
+ let(:given) { {} }
8
+ let(:client) { fake_client }
9
+ let(:app) {}
10
+ let(:new_name) { "some-new-name" }
11
+
12
+ before do
13
+ any_instance_of(CF::CLI) do |cli|
14
+ stub(cli).client { client }
15
+ stub(cli).precondition { nil }
16
+ end
17
+ end
18
+
19
+ subject { Mothership.new.invoke(:delete, inputs, given, global) }
20
+
21
+ describe 'metadata' do
22
+ let(:command) { Mothership.commands[:delete] }
23
+
24
+ describe 'command' do
25
+ subject { command }
26
+ its(:description) { should eq "Delete an application" }
27
+ it { expect(Mothership::Help.group(:apps, :manage)).to include(subject) }
28
+ end
29
+
30
+ include_examples 'inputs must have descriptions'
31
+
32
+ describe 'arguments' do
33
+ subject { command.arguments }
34
+ it 'has the correct argument order' do
35
+ should eq([{ :type => :splat, :value => nil, :name => :apps }])
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'when there are no apps' do
41
+ context 'and an app is given' do
42
+ let(:given) { { :app => "some-app" } }
43
+ it { expect { subject }.to raise_error(CF::UserError, "Unknown app 'some-app'.") }
44
+ end
45
+
46
+ context 'and an app is not given' do
47
+ it { expect { subject }.to raise_error(CF::UserError, "No applications.") }
48
+ end
49
+ end
50
+
51
+ context 'when there are apps' do
52
+ let(:client) { fake_client(:apps => apps) }
53
+ let(:apps) { [basic_app, app_with_orphans, app_without_orphans] }
54
+ let(:service_1) { fake :service_instance }
55
+ let(:service_2) { fake :service_instance }
56
+ let(:basic_app) { fake(:app, :name => "basic_app") }
57
+ let(:app_with_orphans) {
58
+ fake :app,
59
+ :name => "app_with_orphans",
60
+ :service_bindings => [
61
+ fake(:service_binding, :service_instance => service_1),
62
+ fake(:service_binding, :service_instance => service_2)
63
+ ]
64
+ }
65
+ let(:app_without_orphans) {
66
+ fake :app,
67
+ :name => "app_without_orphans",
68
+ :service_bindings => [
69
+ fake(:service_binding, :service_instance => service_1)
70
+ ]
71
+ }
72
+
73
+ context 'and no app is given' do
74
+ it 'asks for the app' do
75
+ mock_ask("Delete which application?", anything) { basic_app }
76
+ stub_ask { true }
77
+ stub(basic_app).delete!
78
+ subject
79
+ end
80
+ end
81
+
82
+ context 'and a basic app is given' do
83
+ let(:deleted_app) { basic_app }
84
+ let(:given) { { :app => deleted_app.name } }
85
+
86
+ context 'and it asks for confirmation' do
87
+ context 'and the user answers no' do
88
+ it 'does not delete the application' do
89
+ mock_ask("Really delete #{deleted_app.name}?", anything) { false }
90
+ dont_allow(deleted_app).delete!
91
+ subject
92
+ end
93
+ end
94
+
95
+ context 'and the user answers yes' do
96
+ it 'deletes the application' do
97
+ mock_ask("Really delete #{deleted_app.name}?", anything) { true }
98
+ mock(deleted_app).delete!
99
+ subject
100
+ end
101
+ end
102
+ end
103
+
104
+ context 'and --force is given' do
105
+ let(:global) { { :force => true, :color => false, :quiet => true } }
106
+
107
+ it 'deletes the application without asking to confirm' do
108
+ dont_allow_ask
109
+ mock(deleted_app).delete!
110
+ subject
111
+ end
112
+ end
113
+ end
114
+
115
+ context 'and an app with orphaned services is given' do
116
+ let(:deleted_app) { app_with_orphans }
117
+ let(:inputs) { { :app => deleted_app } }
118
+
119
+ context 'and it asks for confirmation' do
120
+ context 'and the user answers yes' do
121
+ it 'asks to delete orphaned services' do
122
+ stub_ask("Really delete #{deleted_app.name}?", anything) { true }
123
+ stub(deleted_app).delete!
124
+
125
+ stub(service_2).invalidate!
126
+
127
+ mock_ask("Delete orphaned service #{service_2.name}?", anything) { true }
128
+
129
+ any_instance_of(CF::App::Delete) do |del|
130
+ mock(del).invoke :delete_service, :service => service_2,
131
+ :really => true
132
+ end
133
+
134
+ subject
135
+ end
136
+ end
137
+
138
+ context 'and the user answers no' do
139
+ it 'does not ask to delete orphaned serivces, or delete them' do
140
+ stub_ask("Really delete #{deleted_app.name}?", anything) { false }
141
+ dont_allow(deleted_app).delete!
142
+
143
+ stub(service_2).invalidate!
144
+
145
+ dont_allow_ask("Delete orphaned service #{service_2.name}?")
146
+
147
+ any_instance_of(CF::App::Delete) do |del|
148
+ dont_allow(del).invoke(:delete_service, anything)
149
+ end
150
+
151
+ subject
152
+ end
153
+ end
154
+ end
155
+
156
+ context 'and --force is given' do
157
+ let(:global) { { :force => true, :color => false, :quiet => true } }
158
+
159
+ it 'does not delete orphaned services' do
160
+ dont_allow_ask
161
+ stub(deleted_app).delete!
162
+
163
+ any_instance_of(CF::App::Delete) do |del|
164
+ dont_allow(del).invoke(:delete_service, anything)
165
+ end
166
+
167
+ subject
168
+ end
169
+ end
170
+
171
+ context 'and --delete-orphaned is given' do
172
+ let(:inputs) { { :app => deleted_app, :delete_orphaned => true } }
173
+
174
+ it 'deletes the orphaned services' do
175
+ stub_ask("Really delete #{deleted_app.name}?", anything) { true }
176
+ stub(deleted_app).delete!
177
+
178
+ any_instance_of(CF::App::Delete) do |del|
179
+ mock(del).invoke :delete_service, :service => service_2,
180
+ :really => true
181
+ end
182
+
183
+ subject
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+ require 'stringio'
3
+
4
+ describe CF::App::Stats do
5
+ let(:global) { { :color => false } }
6
+ let(:inputs) { { :app => apps[0] } }
7
+ let(:given) { {} }
8
+ let(:client) { fake_client(:apps => apps) }
9
+ let(:apps) { [fake(:app, :name => "basic_app")] }
10
+ let(:time) { Time.local(2012, 11, 1, 2, 30) }
11
+
12
+ before do
13
+ any_instance_of(CF::CLI) do |cli|
14
+ stub(cli).client { client }
15
+ stub(cli).precondition { nil }
16
+ end
17
+ stub(client.base).instances(anything) do
18
+ {
19
+ "12" => {:state => "STOPPED", :since => time.to_i, :debug_ip => "foo", :debug_port => "bar", :console_ip => "baz", :console_port => "qux"},
20
+ "1" => {:state => "STOPPED", :since => time.to_i, :debug_ip => "foo", :debug_port => "bar", :console_ip => "baz", :console_port => "qux"},
21
+ "2" => {:state => "STARTED", :since => time.to_i, :debug_ip => "foo", :debug_port => "bar", :console_ip => "baz", :console_port => "qux"}
22
+ }
23
+ end
24
+ end
25
+
26
+ subject do
27
+ capture_output do
28
+ Mothership.new.invoke(:instances, inputs, given, global)
29
+ end
30
+ end
31
+
32
+ describe 'metadata' do
33
+ let(:command) { Mothership.commands[:instances] }
34
+
35
+ describe 'command' do
36
+ subject { command }
37
+ its(:description) { should eq "List an app's instances" }
38
+ it { expect(Mothership::Help.group(:apps, :info)).to include(subject) }
39
+ end
40
+
41
+ include_examples 'inputs must have descriptions'
42
+
43
+ describe 'arguments' do
44
+ subject { command.arguments }
45
+ it 'has no arguments' do
46
+ should eq([{:type=>:splat, :value=>nil, :name=>:apps}])
47
+ end
48
+ end
49
+ end
50
+
51
+ it 'prints out the instances in the correct order' do
52
+ subject
53
+ expect(output).to say("instance #1")
54
+ expect(output).to say("instance #2")
55
+ expect(output).to say("instance #12")
56
+ end
57
+
58
+ it 'prints out one of the instances correctly' do
59
+ subject
60
+ expect(output).to say("instance #2: started")
61
+ expect(output).to say(" started: #{time.strftime("%F %r")}")
62
+ expect(output).to say(" debugger: port bar at foo")
63
+ expect(output).to say(" console: port qux at baz")
64
+ end
65
+ end