monkey_butler 1.2.2

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.
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