monkey_butler 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.bundle/config +2 -0
  3. data/.gitignore +3 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +28 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +122 -0
  8. data/Guardfile +8 -0
  9. data/LICENSE +202 -0
  10. data/README.md +221 -0
  11. data/Rakefile +16 -0
  12. data/bin/mb +6 -0
  13. data/lib/monkey_butler.rb +1 -0
  14. data/lib/monkey_butler/actions.rb +38 -0
  15. data/lib/monkey_butler/cli.rb +354 -0
  16. data/lib/monkey_butler/databases/abstract_database.rb +49 -0
  17. data/lib/monkey_butler/databases/cassandra_database.rb +69 -0
  18. data/lib/monkey_butler/databases/sqlite_database.rb +105 -0
  19. data/lib/monkey_butler/migrations.rb +52 -0
  20. data/lib/monkey_butler/project.rb +90 -0
  21. data/lib/monkey_butler/targets/base.rb +85 -0
  22. data/lib/monkey_butler/targets/cassandra/cassandra_target.rb +72 -0
  23. data/lib/monkey_butler/targets/cassandra/create_schema_migrations.cql.erb +16 -0
  24. data/lib/monkey_butler/targets/cassandra/migration.cql.erb +0 -0
  25. data/lib/monkey_butler/targets/cocoapods/cocoapods_target.rb +43 -0
  26. data/lib/monkey_butler/targets/cocoapods/podspec.erb +12 -0
  27. data/lib/monkey_butler/targets/maven/maven_target.rb +60 -0
  28. data/lib/monkey_butler/targets/maven/project/.gitignore +6 -0
  29. data/lib/monkey_butler/targets/maven/project/MonkeyButler.iml +19 -0
  30. data/lib/monkey_butler/targets/maven/project/build.gradle +54 -0
  31. data/lib/monkey_butler/targets/sqlite/create_monkey_butler_tables.sql.erb +15 -0
  32. data/lib/monkey_butler/targets/sqlite/migration.sql.erb +11 -0
  33. data/lib/monkey_butler/targets/sqlite/sqlite_target.rb +91 -0
  34. data/lib/monkey_butler/templates/Gemfile.erb +4 -0
  35. data/lib/monkey_butler/templates/gitignore.erb +1 -0
  36. data/lib/monkey_butler/util.rb +71 -0
  37. data/lib/monkey_butler/version.rb +3 -0
  38. data/logo.jpg +0 -0
  39. data/monkey_butler.gemspec +33 -0
  40. data/spec/cli_spec.rb +700 -0
  41. data/spec/databases/cassandra_database_spec.rb +241 -0
  42. data/spec/databases/sqlite_database_spec.rb +181 -0
  43. data/spec/migrations_spec.rb +4 -0
  44. data/spec/project_spec.rb +128 -0
  45. data/spec/sandbox/cassandra/.gitignore +2 -0
  46. data/spec/sandbox/cassandra/.monkey_butler.yml +7 -0
  47. data/spec/sandbox/cassandra/migrations/20140523123443021_create_sandbox.cql.sql +14 -0
  48. data/spec/sandbox/cassandra/sandbox.cql +0 -0
  49. data/spec/sandbox/sqlite/.gitignore +2 -0
  50. data/spec/sandbox/sqlite/.monkey_butler.yml +7 -0
  51. data/spec/sandbox/sqlite/migrations/20140523123443021_create_sandbox.sql +14 -0
  52. data/spec/sandbox/sqlite/sandbox.sql +0 -0
  53. data/spec/spec_helper.rb +103 -0
  54. data/spec/targets/cassandra_target_spec.rb +191 -0
  55. data/spec/targets/cocoapods_target_spec.rb +197 -0
  56. data/spec/targets/maven_target_spec.rb +156 -0
  57. data/spec/targets/sqlite_target_spec.rb +103 -0
  58. data/spec/util_spec.rb +13 -0
  59. metadata +260 -0
data/spec/cli_spec.rb ADDED
@@ -0,0 +1,700 @@
1
+ require 'spec_helper'
2
+ require 'monkey_butler/databases/sqlite_database'
3
+
4
+ describe MonkeyButler::CLI, "#init" do
5
+ describe "#init" do
6
+ let(:thor_class) { MonkeyButler::CLI }
7
+ let!(:project_root) { Dir.mktmpdir }
8
+
9
+ context 'when no PATH is given' do
10
+ it "prints an argument error to stderr" do
11
+ output = invoke!([:init])
12
+ output[:stderr].should =~ /ERROR: "\w+ init" was called with no arguments/
13
+ end
14
+ end
15
+
16
+ context 'when the PATH given already exists' do
17
+ context "and contains existing content" do
18
+ it "aborts with an error" do
19
+ Dir.mktmpdir do |path|
20
+ File.open(File.join(path, 'sdasdasd'), 'w+')
21
+ output = invoke!([:init, path])
22
+ output[:stderr].should =~ /Cannot create repository into non-empty path/
23
+ end
24
+ end
25
+ end
26
+
27
+ context "and is a regular file" do
28
+ it "aborts with an error" do
29
+ path = Tempfile.new('monkey_butler').path
30
+ output = invoke!([:init, path])
31
+ output[:stderr].should =~ /Cannot create repository: regular file exists at path/
32
+ end
33
+ end
34
+
35
+ context "and is empty" do
36
+ it "reports the path already exists" do
37
+ Dir.mktmpdir do |path|
38
+ output = invoke!([:init, path])
39
+ output[:stderr].should be_empty
40
+ output[:stdout].should =~ /exist\s+$/
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ context 'when the PATH given does not exist' do
47
+ before(:each) do
48
+ FileUtils.remove_entry project_root
49
+ end
50
+
51
+ it "creates the path" do
52
+ output = invoke!([:init, project_root])
53
+ output[:stderr].should be_empty
54
+ output[:stdout].should =~ /create\s+$/
55
+ File.exists?(project_root).should be_true
56
+ File.directory?(project_root).should be_true
57
+ end
58
+
59
+ it "populates .gitignore" do
60
+ invoke!([:init, project_root, '--name=MonkeyButler'])
61
+ path = File.join(project_root, '.gitignore')
62
+ expect(File.exists?(project_root)).to be_true
63
+ content = File.read(path)
64
+ content.should include('.DS_Store')
65
+ content.should =~ /^MonkeyButler.sqlite$/
66
+ end
67
+
68
+ describe '.monkey_butler.yml' do
69
+ let(:args) { [:init, project_root, '--name=MonkeyButler'] }
70
+ before(:each) do
71
+ invoke!(args)
72
+ @yaml_path = File.join(project_root, '.monkey_butler.yml')
73
+ end
74
+
75
+ it "is created" do
76
+ expect(File.exists?(@yaml_path)).to be_true
77
+ end
78
+
79
+ it "is valid YAML" do
80
+ expect { YAML.load(File.read(@yaml_path)) }.not_to raise_error
81
+ end
82
+
83
+ it "configures the project name" do
84
+ config = YAML.load(File.read(@yaml_path))
85
+ config['name'].should == 'MonkeyButler'
86
+ end
87
+
88
+ it "includes a nested config dictionary" do
89
+ config = YAML.load(File.read(@yaml_path))
90
+ config['config'].should == {}
91
+ end
92
+
93
+ it "configures default adapters" do
94
+ config = YAML.load(File.read(@yaml_path))
95
+ config['config'].should == {}
96
+ end
97
+
98
+ context "when config option is specified" do
99
+ let(:args) { [:init, project_root, '--name=MonkeyButler', '--config', 'foo:bar'] }
100
+
101
+ it "parses the arguments as a hash of config vars" do
102
+ config = YAML.load(File.read(@yaml_path))
103
+ config['config'].should == {"foo" => "bar"}
104
+ end
105
+ end
106
+ end
107
+
108
+ it "creates an empty database" do
109
+ invoke!([:init, project_root, '--name=MonkeyButler'])
110
+ path = File.join(project_root, 'MonkeyButler.db')
111
+ expect(File.exists?(project_root)).to be_true
112
+ end
113
+
114
+ it "creates an empty schema" do
115
+ invoke!([:init, project_root, '--name=MonkeyButler'])
116
+ path = File.join(project_root, 'MonkeyButler.sql')
117
+ expect(File.exists?(project_root)).to be_true
118
+ end
119
+
120
+ it "generates an initial migration" do
121
+ invoke!([:init, project_root, '--name=MonkeyButler'])
122
+ filename = Dir.entries(File.join(project_root, 'migrations')).detect { |f| f =~ /create_monkey_butler.sql$/ }
123
+ expect(filename).not_to be_nil
124
+ path = File.join(project_root, 'migrations', filename)
125
+ expect(File.exists?(path)).to be_true
126
+ end
127
+
128
+ it "initializes a Git repository at the destination path" do
129
+ invoke!([:init, project_root, '--name=MonkeyButler'])
130
+ path = File.join(project_root, '.git')
131
+ expect(File.exists?(path)).to be_true
132
+ end
133
+
134
+ def stub_bundler
135
+ MonkeyButler::CLI.any_instance.stub(:bundle) do
136
+ File.open(File.join(project_root, 'Gemfile.lock'), 'w')
137
+ end
138
+ end
139
+
140
+ context "when --bundler option is given" do
141
+ before(:each) do
142
+ stub_bundler
143
+ end
144
+
145
+ it "creates a Gemfile" do
146
+ invoke!([:init, project_root, '--bundler'])
147
+ path = File.join(project_root, 'Gemfile')
148
+ expect(File.exists?(path)).to be_true
149
+ end
150
+
151
+ it "omits the option from the YAML" do
152
+ invoke!([:init, project_root, '--bundler'])
153
+ project = YAML.load File.read(File.join(project_root, '.monkey_butler.yml'))
154
+ expect(project['bundler']).to be_nil
155
+ end
156
+ end
157
+
158
+ context "when --targets option is given" do
159
+ context "when cocoapods is specified" do
160
+ it "informs the user that the targets are being initialized" do
161
+ output = invoke!([:init, project_root, "--config", 'cocoapods.repo:whatever', '--pretend', '--targets', 'cocoapods'])
162
+ output[:stdout].should =~ /Initializing target 'cocoapods'.../
163
+ end
164
+
165
+ it "writes any changes written to the config back to the file" do
166
+ expect(Thor::LineEditor).to receive(:readline).with("What is the name of your Cocoapods specs repo? ", {}).and_return("layerhq")
167
+ invoke!([:init, project_root, '--targets', 'cocoapods'])
168
+ project = YAML.load File.read(File.join(project_root, '.monkey_butler.yml'))
169
+ expect(project['config']['cocoapods.repo']).to eql('layerhq')
170
+ end
171
+
172
+ context "when --bundler is given" do
173
+ it "appends CocoaPods to the Gemfile" do
174
+ stub_bundler
175
+ invoke!([:init, project_root, "--config", 'cocoapods.repo:whatever', '--bundler', '--targets', 'cocoapods'])
176
+ gemfile_content = File.read(File.join(project_root, 'Gemfile'))
177
+ gemfile_content.should =~ /gem 'cocoapods'/
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+
186
+ describe MonkeyButler::CLI do
187
+ let!(:project_root) { clone_temp_sandbox }
188
+ let(:project) { MonkeyButler::Project.load(project_root) }
189
+ let(:schema_path) { File.join(project_root, project.schema_path) }
190
+
191
+ def add_migration(sql, ext = '.sql')
192
+ path = File.join(project_root, 'migrations', random_migration_name + ext)
193
+ File.open(path, 'w+') do |f|
194
+ f << sql
195
+ end
196
+ File.basename(path)
197
+ end
198
+
199
+ def create_sandbox_migration_path
200
+ Dir.entries(File.join(project_root, 'migrations')).detect { |f| f =~ /create_sandbox\.sql/ }
201
+ end
202
+
203
+ describe '#load' do
204
+ context "when the schema is empty" do
205
+ before(:each) do
206
+ File.truncate(schema_path, 0)
207
+ end
208
+
209
+ it "fails with an error" do
210
+ output = invoke!(%w{load})
211
+ output[:stderr].should =~ /Cannot load database: empty schema found at/
212
+ end
213
+ end
214
+
215
+ context "when the schema contains invalid SQL" do
216
+ before(:each) do
217
+ @timestamp = MonkeyButler::Util.migration_timestamp
218
+ sql = <<-SQL
219
+ CREATE TABLE this_is_invalid;
220
+ SQL
221
+ File.open(schema_path, 'w+') do |f|
222
+ f << MonkeyButler::Util.strip_leading_whitespace(sql)
223
+ end
224
+ end
225
+
226
+ it "fails with an error" do
227
+ output = invoke!(%w{load})
228
+ output[:stderr].should =~ /Failed loading schema: Error: near line 1: near ";": syntax error/
229
+ end
230
+ end
231
+
232
+ context "when the schema is populated" do
233
+ before(:each) do
234
+ @timestamp = MonkeyButler::Util.migration_timestamp
235
+ sql = <<-SQL
236
+ #{MonkeyButler::Databases::SqliteDatabase.create_schema_migrations_sql}
237
+ INSERT INTO schema_migrations(version) VALUES ('#{@timestamp}');
238
+ SQL
239
+ File.open(schema_path, 'w+') do |f|
240
+ f << MonkeyButler::Util.strip_leading_whitespace(sql)
241
+ end
242
+ end
243
+
244
+ it "loads without error" do
245
+ output = invoke!(%w{load})
246
+ output[:stdout].should =~ /executing sqlite3 sandbox.sqlite < sandbox.sql/
247
+ output[:stderr].should be_empty
248
+ end
249
+
250
+ it "reports the current version of the database" do
251
+ output = invoke!(%w{load})
252
+ output[:stdout].should =~ /Loaded schema at version #{@timestamp}/
253
+ end
254
+
255
+ context "when a database argument is given" do
256
+ it "loads into the specified database" do
257
+ db_path = Tempfile.new('specific.db').path
258
+ output = invoke!(%W{load -d #{db_path}})
259
+ output[:stdout].should_not =~ /truncate sandbox.sqlite/
260
+ output[:stdout].should =~ /executing sqlite3 #{db_path} < sandbox.sql/
261
+ output[:stdout].should =~ /Loaded schema at version #{@timestamp}/
262
+ output[:stderr].should be_empty
263
+ end
264
+ end
265
+ end
266
+ end
267
+
268
+ def sqlite_url_for_path(path)
269
+ URI("sqlite://#{path}")
270
+ end
271
+
272
+ describe "#migrate" do
273
+ before(:each) do
274
+ @migration_paths = [
275
+ create_sandbox_migration_path,
276
+ add_migration('CREATE TABLE table1 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);'),
277
+ add_migration('CREATE TABLE table2 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);'),
278
+ add_migration('CREATE TABLE table3 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);')
279
+ ]
280
+ end
281
+
282
+ context "when the database is empty" do
283
+ context "when a database argument is given" do
284
+ it "applies all migrations to the specified database" do
285
+ db_path = Tempfile.new('specific.db').path
286
+ output = invoke!(%W{migrate -d #{db_path}})
287
+
288
+ db = MonkeyButler::Databases::SqliteDatabase.new(sqlite_url_for_path(db_path))
289
+ expected_versions = MonkeyButler::Util.migrations_by_version(@migration_paths).keys.sort
290
+ target_version = expected_versions.max
291
+ expect(db.current_version).to eql(target_version)
292
+ expect(db.all_versions).to eql(expected_versions)
293
+ end
294
+
295
+ context "when the dump option is given" do
296
+ it "dumps the database after migrations" do
297
+ db_path = Tempfile.new('specific.db').path
298
+ File.truncate(schema_path, 0)
299
+ output = invoke!(%W{migrate -d #{db_path} --dump})
300
+ output[:stdout].should =~ /Dumping schema from database/
301
+ File.size(schema_path).should_not == 0
302
+ end
303
+ end
304
+ end
305
+
306
+ it "applies all migrations" do
307
+ output = invoke!(%w{migrate})
308
+
309
+ db = MonkeyButler::Databases::SqliteDatabase.new(sqlite_url_for_path(project_root + '/sandbox.sqlite'))
310
+ expected_versions = MonkeyButler::Util.migrations_by_version(@migration_paths).keys.sort
311
+ target_version = expected_versions.max
312
+ expect(db.current_version).to eql(target_version)
313
+ expect(db.all_versions).to eql(expected_versions)
314
+ end
315
+
316
+ it "informs the user of target version" do
317
+ output = invoke!(%w{migrate})
318
+ expected_versions = MonkeyButler::Util.migrations_by_version(@migration_paths).keys.sort
319
+ target_version = expected_versions.max
320
+ output[:stdout].should =~ %r{Migrating new database to #{target_version}}
321
+ end
322
+
323
+ it "informs the user of all migrations applied" do
324
+ output = invoke!(%w{migrate})
325
+ output[:stdout].should =~ /Migrating database.../
326
+ @migration_paths.each do |path|
327
+ output[:stdout].should =~ %r{applying migration:\s+migrations\/#{path}}
328
+ end
329
+ end
330
+
331
+ it "displays migraiton complete message" do
332
+ output = invoke!(%w{migrate})
333
+ expected_versions = MonkeyButler::Util.migrations_by_version(@migration_paths).keys.sort
334
+ target_version = expected_versions.max
335
+ output[:stdout].should =~ /Migration to version #{target_version} complete./
336
+ end
337
+
338
+ pending "dumps the schema and schema_migrations rows"
339
+ pending "adds the schema file to the Git staging area"
340
+ end
341
+
342
+ context "when the database is up to date" do
343
+ before(:each) do
344
+ versions = MonkeyButler::Util.migration_versions_from_paths(@migration_paths)
345
+ database = MonkeyButler::Databases::SqliteDatabase.create(sqlite_url_for_path(project_root + '/sandbox.sqlite'))
346
+ versions.each { |version| database.insert_version(version) }
347
+ end
348
+
349
+ it "informs the user the database is up to date" do
350
+ output = invoke!(%w{migrate})
351
+ output[:stdout].should =~ /Database is up to date./
352
+ end
353
+ end
354
+
355
+ context "when some migrations have been applied" do
356
+ before(:each) do
357
+ versions = MonkeyButler::Util.migration_versions_from_paths(@migration_paths)
358
+ database = MonkeyButler::Databases::SqliteDatabase.create(sqlite_url_for_path(project_root + '/sandbox.sqlite'))
359
+ @current_version = versions.first
360
+ database.insert_version(@current_version)
361
+ end
362
+
363
+ it "displays the current version of the database" do
364
+ output = invoke!(%w{migrate})
365
+ expected_versions = MonkeyButler::Util.migrations_by_version(@migration_paths).keys.sort
366
+ target_version = expected_versions.max
367
+ output[:stdout].should =~ %r{Migrating from #{@current_version} to #{target_version}}
368
+ end
369
+
370
+ it "displays the migrations to be applied" do
371
+ output = invoke!(%w{migrate})
372
+ output[:stdout].should =~ /Migrating database/
373
+ @migration_paths[1,3].each do |path|
374
+ output[:stdout].should =~ %r{applying migration:\s+migrations\/#{path}}
375
+ end
376
+ end
377
+ end
378
+ end
379
+
380
+ describe '#status' do
381
+ before(:each) do
382
+ @migration_paths = [
383
+ create_sandbox_migration_path,
384
+ add_migration('CREATE TABLE table1 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);'),
385
+ add_migration('CREATE TABLE table2 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);'),
386
+ add_migration('CREATE TABLE table3 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);')
387
+ ]
388
+ end
389
+
390
+ context "when the database is up to date" do
391
+ before(:each) do
392
+ versions = MonkeyButler::Util.migration_versions_from_paths(@migration_paths)
393
+ path = project_root + '/sandbox.sqlite'
394
+ uri = URI("sqlite://#{path}")
395
+ database = MonkeyButler::Databases::SqliteDatabase.create(uri)
396
+ versions.each { |version| database.insert_version(version) }
397
+ end
398
+
399
+ it "displays the current version" do
400
+ output = invoke!(%w{status})
401
+ output[:stdout].should =~ /Current version\:/
402
+ end
403
+
404
+ it "displays up to date status" do
405
+ output = invoke!(%w{status})
406
+ output[:stdout].should =~ /Database is up to date./
407
+ end
408
+ end
409
+
410
+ context "when the database is empty" do
411
+ it "informs the user the database is missing the schema migrations table" do
412
+ output = invoke!(%w{status})
413
+ output[:stdout].should =~ /New database/
414
+ output[:stdout].should =~ /The database at 'sandbox.sqlite' does not have a 'schema_migrations' table./
415
+ end
416
+
417
+ it "displays the migrations to be applied" do
418
+ output = invoke!(%w{status})
419
+ output[:stdout].should =~ /Migrations to be applied/
420
+ output[:stdout].should =~ /\(use "mb migrate" to apply\)/
421
+ @migration_paths.each do |path|
422
+ output[:stdout].should =~ %r{pending migration:\s+migrations\/#{path}}
423
+ end
424
+ end
425
+ end
426
+
427
+ context "when there are unapplied migrations" do
428
+ before(:each) do
429
+ versions = MonkeyButler::Util.migration_versions_from_paths(@migration_paths)
430
+ path = project_root + '/sandbox.sqlite'
431
+ uri = URI("sqlite://#{path}")
432
+ database = MonkeyButler::Databases::SqliteDatabase.create(uri)
433
+ @current_version = versions.first
434
+ database.insert_version(@current_version)
435
+ end
436
+
437
+ it "displays the current version of the database" do
438
+ output = invoke!(%w{status})
439
+ output[:stdout].should =~ %r{Current version: #{@current_version}}
440
+ end
441
+
442
+ it "displays the migrations to be applied" do
443
+ output = invoke!(%w{status})
444
+ output[:stdout].should =~ /The database at 'sandbox.sqlite' is 3 versions behind /
445
+ output[:stdout].should =~ /Migrations to be applied/
446
+ output[:stdout].should =~ /\(use "mb migrate" to apply\)/
447
+ @migration_paths[1,3].each do |path|
448
+ output[:stdout].should =~ %r{pending migration:\s+migrations\/#{path}}
449
+ end
450
+ end
451
+ end
452
+ end
453
+
454
+ describe '#validate' do
455
+ before(:each) do
456
+ sql = MonkeyButler::Util.strip_leading_whitespace <<-SQL
457
+ #{MonkeyButler::Databases::SqliteDatabase.create_schema_migrations_sql}
458
+ INSERT INTO schema_migrations(version) VALUES ('#{MonkeyButler::Util.migration_timestamp}');
459
+ SQL
460
+ File.open(schema_path, 'w+') { |f| f << sql }
461
+ add_migration('CREATE TABLE table1 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);')
462
+ add_migration('CREATE TABLE table2 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);')
463
+ end
464
+
465
+ context "when there is a schema to and migrations to apply" do
466
+ it "informs the user validation was successful" do
467
+ output = invoke!(%w{validate})
468
+ output[:stdout].should =~ /Validation successful./
469
+ end
470
+ end
471
+
472
+ context "when the git url is not configured" do
473
+ before(:each) do
474
+ Dir.chdir(project_root) do
475
+ system("git remote remove origin")
476
+ end
477
+ end
478
+
479
+ it "fails with error" do
480
+ output = invoke!(['validate'])
481
+ output[:stderr].should =~ /Invalid configuration: git does not have a remote named 'origin'./
482
+ end
483
+ end
484
+ end
485
+
486
+ describe '#new' do
487
+ # TODO: The only test here should be that a dummy database adapter was invoked....
488
+ context "when using a SQLite database" do
489
+ it "generates a new migration with the given name" do
490
+ invoke!(%w{new add_column_to_table})
491
+ migration = Dir.entries(File.join(project_root, 'migrations')).detect { |f| f =~ /add_column_to_table\.sql/ }
492
+ migration.should_not be_nil
493
+ end
494
+ end
495
+
496
+ context "when using a Cassandra database" do
497
+ it "generates a migration"
498
+ end
499
+ end
500
+
501
+ describe '#generate' do
502
+ before(:each) do
503
+ sql = MonkeyButler::Util.strip_leading_whitespace <<-SQL
504
+ #{MonkeyButler::Databases::SqliteDatabase.create_schema_migrations_sql}
505
+ INSERT INTO schema_migrations(version) VALUES ('#{MonkeyButler::Util.migration_timestamp}');
506
+ SQL
507
+ File.open(schema_path, 'w+') { |f| f << sql }
508
+ add_migration('CREATE TABLE table1 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);')
509
+ add_migration('CREATE TABLE table2 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);')
510
+ end
511
+
512
+ context "when no target option is not given" do
513
+ it "invokes the default targets for the project" do
514
+ output = invoke!(%w{generate})
515
+ output[:stdout].should =~ /Invoking target 'cocoapods'/
516
+ output[:stdout].should_not =~ /Invoking target 'java'/
517
+ output[:stderr].should == ""
518
+ end
519
+ end
520
+
521
+ context "when target option is given" do
522
+ it "invokes the specified targets" do
523
+ output = invoke!(%w{generate -t maven})
524
+ output[:stdout].should =~ /Invoking target 'maven'/
525
+ output[:stdout].should_not =~ /Invoking target 'cocoapods'/
526
+ end
527
+ end
528
+ end
529
+
530
+ describe '#package' do
531
+ before(:each) do
532
+ sql = MonkeyButler::Util.strip_leading_whitespace <<-SQL
533
+ #{MonkeyButler::Databases::SqliteDatabase.create_schema_migrations_sql}
534
+ INSERT INTO schema_migrations(version) VALUES ('#{MonkeyButler::Util.migration_timestamp}');
535
+ SQL
536
+ File.open(schema_path, 'w+') { |f| f << sql }
537
+ add_migration('CREATE TABLE table1 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);')
538
+ add_migration('CREATE TABLE table2 (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);')
539
+ end
540
+
541
+ def stub_questions
542
+ Thor::LineEditor.stub(:readline).and_return("n")
543
+ end
544
+
545
+ it "invokes validation" do
546
+ stub_questions
547
+ output = invoke!(%w{package --pretend})
548
+ output[:stdout].should =~ /Validation successful/
549
+ end
550
+
551
+ it "invokes generation" do
552
+ stub_questions
553
+ output = invoke!(%w{package --pretend})
554
+ output[:stdout].should =~ /Invoking target 'cocoapods'/
555
+ end
556
+
557
+ it "adds the project to git" do
558
+ stub_questions
559
+ output = invoke!(%w{package --pretend})
560
+ output[:stdout].should =~ /git add . from "."/
561
+ end
562
+
563
+ it "runs git status" do
564
+ stub_questions
565
+ output = invoke!(%w{package --pretend})
566
+ output[:stdout].should =~ /git status from "."/
567
+ end
568
+
569
+ describe "diff" do
570
+ context "when no diff option is given" do
571
+ it "asks if you want to see the diff" do
572
+ expect(Thor::LineEditor).to receive(:readline).with("Review package diff? [y, n] ", {:limited_to=>["y", "n"]}).and_return("n")
573
+ stub_questions
574
+ output = invoke!(%w{package --pretend})
575
+ end
576
+ end
577
+
578
+ context "when --diff is specified" do
579
+ it "shows the diff" do
580
+ stub_questions
581
+ output = invoke!(%w{package --pretend --diff})
582
+ output[:stdout].should =~ /git diff --cached from "."/
583
+ end
584
+ end
585
+
586
+ context "when --no-diff is specified" do
587
+ it "should not ask to review the diff" do
588
+ expect(Thor::LineEditor).not_to receive(:readline).with("Review package diff? [y, n] ", {:limited_to=>["y", "n"]})
589
+ stub_questions
590
+ output = invoke!(%w{package --pretend --no-diff})
591
+ end
592
+ end
593
+ end
594
+
595
+ describe "commit" do
596
+ context "when no commit option is given" do
597
+ it "asks if you want to commit" do
598
+ expect(Thor::LineEditor).to receive(:readline).with("Commit package artifacts? [y, n] ", {:limited_to=>["y", "n"]}).and_return("n")
599
+ stub_questions
600
+ output = invoke!(%w{package --pretend})
601
+ end
602
+ end
603
+
604
+ context "when --commit is specified" do
605
+ it "commits the changes" do
606
+ stub_questions
607
+ output = invoke!(%w{package --pretend --commit})
608
+ output[:stdout].should =~ /git commit -m 'Packaging release/
609
+ end
610
+ end
611
+
612
+ context "when --no-commit is specified" do
613
+ it "commits the changes" do
614
+ expect(Thor::LineEditor).not_to receive(:readline).with("Commit package artifacts? [y, n] ", {:limited_to=>["y", "n"]})
615
+ stub_questions
616
+ output = invoke!(%w{package --pretend --no-commit})
617
+ output[:stdout].should_not =~ /git commit -m 'Packaging release/
618
+ end
619
+ end
620
+
621
+ it "tags a release for the latest version" do
622
+ db = MonkeyButler::Databases::SqliteDatabase.new(sqlite_url_for_path(project_root + '/sandbox.sqlite'))
623
+ migrations = MonkeyButler::Migrations.new(project_root + '/migrations', db)
624
+
625
+ stub_questions
626
+ output = invoke!(%w{package --pretend --commit})
627
+ output[:stdout].should =~ /git tag #{migrations.latest_version}/
628
+ end
629
+
630
+ context "when a tag for the version already exists" do
631
+ it "tags a point release" do
632
+ db = MonkeyButler::Databases::SqliteDatabase.new(URI(project_root + '/sandbox.sqlite'))
633
+ migrations = MonkeyButler::Migrations.new(project_root + '/migrations', db)
634
+ Dir.chdir(project_root) do
635
+ `echo '' > foo`
636
+ `git add foo && git commit -m 'fake commit' .`
637
+ `git tag #{migrations.latest_version}`
638
+ end
639
+ point_release = "#{migrations.latest_version}.1"
640
+
641
+ stub_questions
642
+ output = invoke!(%w{package --commit --quiet})
643
+ Dir.chdir(project_root) do
644
+ project.git_latest_tag.should == point_release
645
+ end
646
+ end
647
+ end
648
+ end
649
+ end
650
+
651
+ describe '#push' do
652
+ it "pushes to git" do
653
+ output = invoke!(%w{push --pretend})
654
+ output[:stdout].should =~ /git push origin master --tags/
655
+ end
656
+ end
657
+
658
+ describe "#config" do
659
+ before(:each) do
660
+ project = MonkeyButler::Project.load(project_root)
661
+ project.config['this'] = 'that'
662
+ project.config['option'] = 'value'
663
+ end
664
+
665
+ context "when called with no arguments" do
666
+ it "enumerates all config variables" do
667
+ output = invoke!(%w{config})
668
+ output[:stdout].should =~ /this=that/
669
+ output[:stdout].should =~ /option=value/
670
+ end
671
+ end
672
+
673
+ context "when called with one argument" do
674
+ it "reads the value for that option" do
675
+ output = invoke!(%w{config this})
676
+ output[:stdout].should =~ /this=that/
677
+ output[:stdout].should_not =~ /option=value/
678
+ end
679
+ end
680
+
681
+ context "when called with two arguments" do
682
+ it "sets the value for the specified option" do
683
+ invoke!(%w{config this value})
684
+ yaml = YAML.load(File.read(File.join(project_root, '.monkey_butler.yml')))
685
+ yaml['config']['this'].should == 'value'
686
+ end
687
+ end
688
+ end
689
+ end
690
+
691
+ describe MonkeyButler::CLI, "#target integration" do
692
+ let!(:project_root) { clone_temp_sandbox }
693
+
694
+ context "when the current directory has a monkey_butler.yml file" do
695
+ it "allows the database target to register options" do
696
+ output = invoke!(%w{help dump})
697
+ output[:stdout].should =~ /#{Regexp.escape "-d, [--database=DATABASE] # Set target DATABASE"}/
698
+ end
699
+ end
700
+ end