remy 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rdebugrc +3 -0
- data/.rvmrc +1 -1
- data/README.md +3 -3
- data/lib/remy/bootstrap.rb +2 -2
- data/lib/remy/chef.rb +13 -17
- data/lib/remy/config/chef.rb +56 -0
- data/lib/remy/config/node.rb +9 -0
- data/lib/remy/{remy.rb → config.rb} +17 -47
- data/lib/remy/server.rb +1 -1
- data/lib/remy/utility.rb +7 -0
- data/lib/remy/version.rb +1 -1
- data/lib/remy.rb +2 -1
- data/lib/tasks/remy.rake +5 -11
- data/remy.gemspec +1 -2
- data/spec/fixtures/chef.yml +5 -5
- data/spec/fixtures/hello_world_chef.yml +1 -1
- data/spec/fixtures/node.json +1 -0
- data/spec/remy/bootstrap_spec.rb +9 -9
- data/spec/remy/config/chef_spec.rb +360 -0
- data/spec/remy/config/node_spec.rb +20 -0
- data/spec/remy/integration/chef_spec.rb +4 -4
- data/spec/remy/utility_spec.rb +22 -0
- data/spec/remy_spec.rb +23 -251
- data/spec/spec_helper.rb +7 -4
- data/spec/tasks/remy_spec.rb +14 -0
- metadata +100 -146
- data/spec/remy/chef_spec.rb +0 -82
@@ -0,0 +1,360 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Remy::Config::Chef do
|
4
|
+
subject { Remy::Config::Chef }
|
5
|
+
before do
|
6
|
+
subject.instance_variable_set(:@config, nil)
|
7
|
+
end
|
8
|
+
|
9
|
+
after do
|
10
|
+
subject.instance_variable_set(:@config, nil)
|
11
|
+
end
|
12
|
+
|
13
|
+
def node_config(chef)
|
14
|
+
chef.instance_variable_get(:@node_config)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '.save_node_json' do
|
18
|
+
let(:node_json_output_path) { Dir.tmpdir + '/foo/chef/node.json' }
|
19
|
+
|
20
|
+
after do
|
21
|
+
FileUtils.rm node_json_output_path, :force => true
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should save the node json file to the specified directory' do
|
25
|
+
File.exist?(node_json_output_path).should be_false
|
26
|
+
subject.save_node_json(node_json_output_path)
|
27
|
+
File.exist?(node_json_output_path).should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should include chef configuration yml data as json' do
|
31
|
+
subject.configure { |config| config.yml_files = chef_fixture('foo.yml') }
|
32
|
+
subject.save_node_json(node_json_output_path)
|
33
|
+
data_from_json = JSON.parse(File.read(node_json_output_path))
|
34
|
+
data_from_json['blah'].should == 'bar'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '.config' do
|
39
|
+
describe 'with no yml files' do
|
40
|
+
it 'should return an empty mash' do
|
41
|
+
subject.config.should == Hashie::Mash.new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "yml files" do
|
46
|
+
it 'should combine multiple yaml files into a mash' do
|
47
|
+
subject.configure { |config| config.yml_files = [chef_fixture('foo.yml'), chef_fixture('bar.yml')] }
|
48
|
+
subject.config.yml_files.should == [chef_fixture('foo.yml'), chef_fixture('bar.yml')]
|
49
|
+
subject.config.blah.should == 'bar' # From foo.yml
|
50
|
+
subject.config.baz.should == 'baz' # From bar.yml
|
51
|
+
subject.config.colors.to_hash.symbolize_keys.should == {:blue => 'blue', :green => 'green', :red => 'red'}
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should return an empty array if there are no yml files' do
|
55
|
+
subject.configure {}
|
56
|
+
subject.config.yml_files.should == []
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should not raise an error if there is a file does does not exist' do
|
60
|
+
expect do
|
61
|
+
subject.configure { |config| config.yml_files = ['does_not_exist.yml'] }
|
62
|
+
end.should_not raise_error
|
63
|
+
end
|
64
|
+
end
|
65
|
+
describe 'json config files' do
|
66
|
+
before do
|
67
|
+
pending
|
68
|
+
end
|
69
|
+
it 'should combine multiple json files into a mash' do
|
70
|
+
subject.configure { |config| config.json_files = [chef_fixture('foo.json'), chef_fixture('bar.json')] }
|
71
|
+
subject.config.json_files.should == [chef_fixture('foo.json'), chef_fixture('bar.json')]
|
72
|
+
subject.config.blah.should == 'bar' # From foo.json
|
73
|
+
subject.config.baz.should == 'baz' # From bar.json
|
74
|
+
subject.config.colors.to_hash.symbolize_keys.should == {:blue => 'blue', :green => 'green', :red => 'red'}
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should return an empty array if there are no json files' do
|
78
|
+
subject.configure {}
|
79
|
+
subject.config.json_files.should == []
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should have the values in the json files override the values from the json files' do
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "cookbooks path" do
|
87
|
+
it "should work if a single cookbook path is specified" do
|
88
|
+
subject.configure { |config| config.cookbook_path = 'cookbooks' }
|
89
|
+
subject.config.cookbook_path.should == ['cookbooks']
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should work if multiple cookbook paths are specified" do
|
93
|
+
subject.configure { |config| config.cookbook_path = ['cookbooks1', 'cookbooks2'] }
|
94
|
+
subject.config.cookbook_path.should == ['cookbooks1', 'cookbooks2']
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should return an empty array if no cookbook paths are specified" do
|
98
|
+
subject.configure {}
|
99
|
+
subject.config.cookbook_path.should == []
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "specs path" do
|
104
|
+
it "should work if a single spec path is specified" do
|
105
|
+
subject.configure { |config| config.spec_path = 'specs' }
|
106
|
+
subject.config.spec_path.should == ['specs']
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should work if multiple spec paths are specified" do
|
110
|
+
subject.configure { |config| config.spec_path = ['specs1', 'specs2'] }
|
111
|
+
subject.config.spec_path.should == ['specs1', 'specs2']
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should return an empty array if no spec paths are specified" do
|
115
|
+
subject.configure {}
|
116
|
+
subject.config.spec_path.should == []
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "roles path" do
|
121
|
+
it "should work if a single file is specified" do
|
122
|
+
subject.configure { |config| config.roles_path = 'roles' }
|
123
|
+
subject.config.roles_path.should == ['roles']
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should work if multiple files are specified" do
|
127
|
+
subject.configure { |config| config.roles_path = ['roles1', 'roles2'] }
|
128
|
+
subject.config.roles_path.should == ['roles1', 'roles2']
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should return an empty array if no roles paths are specified" do
|
132
|
+
subject.configure {}
|
133
|
+
subject.config.roles_path.should == []
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "node attributes" do
|
138
|
+
it "should merge in the other node attributes from the hash" do
|
139
|
+
subject.configure { |config| config.node_attributes = {:another_node_attribute => 'red'} }
|
140
|
+
subject.config.another_node_attribute.should == 'red'
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should not blow up if there no node attributes are specified" do
|
144
|
+
lambda { subject.configure {} }.should_not raise_error
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "#remote_chef_dir" do
|
149
|
+
it "should default to /var/chef if no option is given" do
|
150
|
+
subject.configure {}
|
151
|
+
subject.config.remote_chef_dir.should == '/var/chef'
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should be able to be overriden" do
|
155
|
+
subject.configure { |config| config.remote_chef_dir = '/foo/shef' }
|
156
|
+
subject.config.remote_chef_dir.should == '/foo/shef'
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context 'with a config' do
|
162
|
+
before do
|
163
|
+
subject.configure do |config|
|
164
|
+
config.yml_files = [chef_fixture('chef.yml')]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe '.servers' do
|
169
|
+
it 'returns all servers' do
|
170
|
+
subject.servers.size.should == 3
|
171
|
+
subject.servers['db.sharespost.com'].color.should == 'yellow'
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe '.find_servers' do
|
176
|
+
it 'should return servers that match the criteria' do
|
177
|
+
subject.find_servers(:rails_env => 'demo').keys.should == ['web.sharespost.com', 'demo.sharespost.com']
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'should return all servers if there are no criteria' do
|
181
|
+
subject.find_servers.keys.should =~ ['db.sharespost.com', 'web.sharespost.com', 'demo.sharespost.com']
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'should return servers that match the criteria (with multiple criteria)' do
|
185
|
+
subject.find_servers(:rails_env => 'demo', :color => 'blue').keys.should == ['web.sharespost.com']
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should return nil if there are no servers specified in the yaml file" do
|
189
|
+
subject.configure { |config| config.yml_files = chef_fixture('hello_world_chef.yml') }
|
190
|
+
subject.find_servers(:rails_env => 'demo').should be_nil
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe '.find_server' do
|
195
|
+
it 'should return the first server that matchs the criteria' do
|
196
|
+
subject.find_server(:rails_env => 'demo').keys.should == ['web.sharespost.com']
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'should return nil if there are no servers specifie in the yml files' do
|
200
|
+
subject.configure { |config| config.yml_files = chef_fixture('hello_world_chef.yml') }
|
201
|
+
subject.find_server(:rails_env => 'demo').should be_nil
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe '.find_server_config' do
|
206
|
+
it 'should return the first server that matchs the criteria' do
|
207
|
+
subject.find_server_config(:rails_env => 'demo').to_hash.should == {"color" => "blue", "recipes" => ["recipe[hello_world]"], "rails_env" => "demo", "ip_address" => IP_ADDRESS_OF_REMY_TEST}
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'should return nil if no server info is found' do
|
211
|
+
subject.find_server_config(:rails_env => 'foo').should be_nil
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'should return nil if there are no servers in the yml files' do
|
215
|
+
subject.configure { |config| config.yml_files = chef_fixture('hello_world_chef.yml') }
|
216
|
+
subject.find_server_config(:rails_env => 'foo').should be_nil
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe '.find_server_config_by_name' do
|
221
|
+
it 'should return the server that matches the name' do
|
222
|
+
subject.find_server_config_by_name('db.sharespost.com').to_hash.should == {"encoding" => "utf8", "adapter" => "mysql2", "color" => "yellow", "rails_env" => "production", "ip_address" => "51.51.51.51"}
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'should return nil if theres no server that matches the name' do
|
226
|
+
subject.find_server_config_by_name('db.asdfjkll.com').should be_nil
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'should return nil (and not blow up) if there are no servers in the yml files' do
|
230
|
+
subject.configure { |config| config.yml_files = chef_fixture('hello_world_chef.yml') }
|
231
|
+
subject.find_server_config_by_name('db.asdfjkll.com').should be_nil
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'should return nil (and not blow up) if there is no Remy config' do
|
235
|
+
Remy.instance_variable_set('@config', nil)
|
236
|
+
subject.find_server_config_by_name('db.asdfjkll.com').should be_nil
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
describe '.cloud_config' do
|
241
|
+
it 'should return nil if it has not been specified in the yml files' do
|
242
|
+
subject.configure { |config| config.yml_files = chef_fixture('hello_world_chef.yml') }
|
243
|
+
subject.cloud_config.should be_nil
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'should return the cloud config options if present in the yml files' do
|
247
|
+
subject.cloud_config.should == Hashie::Mash.new(
|
248
|
+
:cloud_api_key => 'abcdefg12345',
|
249
|
+
:cloud_provider => 'Rackspace',
|
250
|
+
:cloud_username => 'sharespost',
|
251
|
+
:flavor_id => 4,
|
252
|
+
:image_id => 49,
|
253
|
+
:server_name => 'new-server.somedomain.com')
|
254
|
+
|
255
|
+
end
|
256
|
+
|
257
|
+
it 'should return nil if there is currently no Remy config' do
|
258
|
+
subject.instance_variable_set('@config', nil)
|
259
|
+
subject.cloud_config.should be_nil
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
describe '.bootstrap' do
|
264
|
+
it 'should return nil if it has not been specified in the yml files' do
|
265
|
+
subject.configure { |config| config.yml_files = chef_fixture('hello_world_chef.yml') }
|
266
|
+
subject.bootstrap.should be_nil
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'should return the bootstrap options if present in the yml files' do
|
270
|
+
subject.bootstrap.should == Hashie::Mash.new(
|
271
|
+
:ruby_version => '1.9.2',
|
272
|
+
:gems => {
|
273
|
+
:chef => '10.12.0',
|
274
|
+
:rspec => '2.11.0',
|
275
|
+
:bundler => '3.0.0'
|
276
|
+
})
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'should return nil if there is currently no Remy config' do
|
280
|
+
subject.instance_variable_set('@config', nil)
|
281
|
+
subject.bootstrap.should be_nil
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
describe "#initialize and the node config" do
|
287
|
+
it 'should use the top-level IP address in the yml files, if one is present, and an ip address is not passed in as an argument' do
|
288
|
+
subject.configure { |config| config.yml_files = chef_fixture('hello_world_chef.yml') }
|
289
|
+
chef = Remy::Chef.new
|
290
|
+
node_config(chef).ip_address.should == IP_ADDRESS_OF_REMY_TEST
|
291
|
+
node_config(chef).color.should == 'blue'
|
292
|
+
node_config(chef).recipes.should == ['recipe[hello_world]']
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'should allow the top-level values in the yml files (including the ip address) to be overridden' do
|
296
|
+
subject.configure { |config| config.yml_files = chef_fixture('hello_world_chef.yml') }
|
297
|
+
chef = Remy::Chef.new(:ip_address => '1.2.3.4', :color => 'green')
|
298
|
+
node_config(chef).ip_address.should == '1.2.3.4'
|
299
|
+
node_config(chef).color.should == 'green'
|
300
|
+
node_config(chef).recipes.should == ['recipe[hello_world]']
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'should return properties from the :servers section of the yml file if the ip address is found in there' do
|
304
|
+
subject.configure { |config| config.yml_files = chef_fixture('chef.yml') }
|
305
|
+
chef = Remy::Chef.new(:ip_address => '51.51.51.51')
|
306
|
+
node_config(chef).ip_address.should == '51.51.51.51'
|
307
|
+
node_config(chef).rails_env.should == 'production'
|
308
|
+
node_config(chef).color.should == 'yellow'
|
309
|
+
node_config(chef).adapter.should == 'mysql2'
|
310
|
+
node_config(chef).encoding.should == 'utf8'
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'should allow properties from the servers section of the yml file to be overridden plus additional options added' do
|
314
|
+
subject.configure { |config| config.yml_files = chef_fixture('chef.yml') }
|
315
|
+
chef = Remy::Chef.new(:ip_address => '51.51.51.51', :color => 'purple', :temperature => 'warm')
|
316
|
+
node_config(chef).color.should == 'purple' # Overrides 'yellow' from the yml files
|
317
|
+
node_config(chef).temperature.should == 'warm' # A new node attribute which is not present in the yml files
|
318
|
+
end
|
319
|
+
|
320
|
+
it 'should allow the chef args to be specified (and not merge this chef_args value into the node config)' do
|
321
|
+
subject.configure { |config| config.yml_files = chef_fixture('chef.yml') }
|
322
|
+
chef = Remy::Chef.new(:chef_args => '-l debug')
|
323
|
+
node_config(chef).chef_args.should be_nil
|
324
|
+
chef.instance_variable_get(:@chef_args).should == '-l debug'
|
325
|
+
end
|
326
|
+
|
327
|
+
it 'should allow the quiet option to be specified (and not merge this option into the node config)' do
|
328
|
+
subject.configure { |config| config.yml_files = chef_fixture('chef.yml') }
|
329
|
+
chef = Remy::Chef.new(:quiet => true)
|
330
|
+
node_config(chef).quiet.should be_nil
|
331
|
+
chef.instance_variable_get(:@quiet).should be_true
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'should not modify the global Remy config, but rather only the node config for this particular Chef node' do
|
335
|
+
subject.configure { |config| config.yml_files = chef_fixture('bar.yml') }
|
336
|
+
subject.config.another_node_attribute.should == 'hot'
|
337
|
+
chef = Remy::Chef.new(:another_node_attribute => 'cold')
|
338
|
+
node_config(chef).another_node_attribute.should == 'cold'
|
339
|
+
subject.config.another_node_attribute.should == 'hot' # Unchanged from its original value # do some checks
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
describe '#run' do
|
344
|
+
before do
|
345
|
+
subject.configure { |config| config.yml_files = chef_fixture('chef.yml') }
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'should work with a hash as its argument' do
|
349
|
+
chef = Remy::Chef.new(:ip_address => IP_ADDRESS_OF_REMY_TEST)
|
350
|
+
node_config(chef).ip_address.should == IP_ADDRESS_OF_REMY_TEST
|
351
|
+
node_config(chef).recipes.should == ['recipe[hello_world]']
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'should work with JSON as its argument' do
|
355
|
+
chef = Remy::Chef.new("{\"ip_address\":\"#{IP_ADDRESS_OF_REMY_TEST}\"}")
|
356
|
+
node_config(chef).ip_address.should == IP_ADDRESS_OF_REMY_TEST
|
357
|
+
node_config(chef).recipes.should == ['recipe[hello_world]']
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe Remy::Config::Node do
|
3
|
+
subject { Remy::Config::Node }
|
4
|
+
before do
|
5
|
+
subject.instance_variable_set(:@config, nil)
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
subject.instance_variable_set(:@config, nil)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.config' do
|
13
|
+
describe "from json file" do
|
14
|
+
it 'should extract the contents of the json file' do
|
15
|
+
subject.configure { |config| config.json_file = chef_fixture('node.json') }
|
16
|
+
subject.config.blah.should == 'bar'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Remy::Chef do
|
4
4
|
before do
|
5
|
-
Remy.configure do |config|
|
5
|
+
Remy::Config::Chef.configure do |config|
|
6
6
|
config.yml_files = ['../../fixtures/foo.yml', '../../fixtures/bar.yml', '../../fixtures/chef.yml'].map { |f| File.join(File.dirname(__FILE__), f) }
|
7
7
|
config.cookbook_path = ["../../../chef/cookbooks"].map { |f| File.join(File.dirname(__FILE__), f) }
|
8
8
|
config.spec_path = ["../../../chef/spec"].map { |f| File.join(File.dirname(__FILE__), f) }
|
@@ -11,7 +11,7 @@ describe Remy::Chef do
|
|
11
11
|
|
12
12
|
describe '#run' do
|
13
13
|
def clean_up_remote_chef_test_files(chef)
|
14
|
-
chef.remote_execute "rm -rf /tmp/hello_world.txt #{Remy.
|
14
|
+
chef.remote_execute "rm -rf /tmp/hello_world.txt #{Remy::Config::Chef.config.remote_chef_dir}" if Remy::Config::Chef.config.remote_chef_dir && Remy::Config::Chef.config.remote_chef_dir.size > 2
|
15
15
|
end
|
16
16
|
|
17
17
|
def verify_contents_of_hello_world(chef)
|
@@ -27,13 +27,13 @@ describe Remy::Chef do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'should work with a hash as its argument' do
|
30
|
-
run_chef(:ip_address =>
|
30
|
+
run_chef(:ip_address => IP_ADDRESS_OF_REMY_TEST) do |chef|
|
31
31
|
verify_contents_of_hello_world(chef)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'should work with JSON as its argument' do
|
36
|
-
run_chef("{\"ip_address\":\"#{
|
36
|
+
run_chef("{\"ip_address\":\"#{IP_ADDRESS_OF_REMY_TEST}\"}") do |chef|
|
37
37
|
verify_contents_of_hello_world(chef)
|
38
38
|
end
|
39
39
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Remy::Utility do
|
4
|
+
describe 'instance methods' do
|
5
|
+
subject do
|
6
|
+
Class.new do
|
7
|
+
include Remy::Utility
|
8
|
+
end.new
|
9
|
+
end
|
10
|
+
describe '#flatten_paths' do
|
11
|
+
let(:base_path) { }
|
12
|
+
let(:expected_paths) { [File.expand_path('a'), File.expand_path('b')] }
|
13
|
+
it 'should return a map of paths for arrays' do
|
14
|
+
subject.flatten_paths(['a'], ['b']).should == expected_paths
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should return a map of paths for strings (regression for 1.9.2)' do
|
18
|
+
subject.flatten_paths('a', 'b').should == expected_paths
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|