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.
- checksums.yaml +7 -0
- data/.bundle/config +2 -0
- data/.gitignore +3 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +28 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +122 -0
- data/Guardfile +8 -0
- data/LICENSE +202 -0
- data/README.md +221 -0
- data/Rakefile +16 -0
- data/bin/mb +6 -0
- data/lib/monkey_butler.rb +1 -0
- data/lib/monkey_butler/actions.rb +38 -0
- data/lib/monkey_butler/cli.rb +354 -0
- data/lib/monkey_butler/databases/abstract_database.rb +49 -0
- data/lib/monkey_butler/databases/cassandra_database.rb +69 -0
- data/lib/monkey_butler/databases/sqlite_database.rb +105 -0
- data/lib/monkey_butler/migrations.rb +52 -0
- data/lib/monkey_butler/project.rb +90 -0
- data/lib/monkey_butler/targets/base.rb +85 -0
- data/lib/monkey_butler/targets/cassandra/cassandra_target.rb +72 -0
- data/lib/monkey_butler/targets/cassandra/create_schema_migrations.cql.erb +16 -0
- data/lib/monkey_butler/targets/cassandra/migration.cql.erb +0 -0
- data/lib/monkey_butler/targets/cocoapods/cocoapods_target.rb +43 -0
- data/lib/monkey_butler/targets/cocoapods/podspec.erb +12 -0
- data/lib/monkey_butler/targets/maven/maven_target.rb +60 -0
- data/lib/monkey_butler/targets/maven/project/.gitignore +6 -0
- data/lib/monkey_butler/targets/maven/project/MonkeyButler.iml +19 -0
- data/lib/monkey_butler/targets/maven/project/build.gradle +54 -0
- data/lib/monkey_butler/targets/sqlite/create_monkey_butler_tables.sql.erb +15 -0
- data/lib/monkey_butler/targets/sqlite/migration.sql.erb +11 -0
- data/lib/monkey_butler/targets/sqlite/sqlite_target.rb +91 -0
- data/lib/monkey_butler/templates/Gemfile.erb +4 -0
- data/lib/monkey_butler/templates/gitignore.erb +1 -0
- data/lib/monkey_butler/util.rb +71 -0
- data/lib/monkey_butler/version.rb +3 -0
- data/logo.jpg +0 -0
- data/monkey_butler.gemspec +33 -0
- data/spec/cli_spec.rb +700 -0
- data/spec/databases/cassandra_database_spec.rb +241 -0
- data/spec/databases/sqlite_database_spec.rb +181 -0
- data/spec/migrations_spec.rb +4 -0
- data/spec/project_spec.rb +128 -0
- data/spec/sandbox/cassandra/.gitignore +2 -0
- data/spec/sandbox/cassandra/.monkey_butler.yml +7 -0
- data/spec/sandbox/cassandra/migrations/20140523123443021_create_sandbox.cql.sql +14 -0
- data/spec/sandbox/cassandra/sandbox.cql +0 -0
- data/spec/sandbox/sqlite/.gitignore +2 -0
- data/spec/sandbox/sqlite/.monkey_butler.yml +7 -0
- data/spec/sandbox/sqlite/migrations/20140523123443021_create_sandbox.sql +14 -0
- data/spec/sandbox/sqlite/sandbox.sql +0 -0
- data/spec/spec_helper.rb +103 -0
- data/spec/targets/cassandra_target_spec.rb +191 -0
- data/spec/targets/cocoapods_target_spec.rb +197 -0
- data/spec/targets/maven_target_spec.rb +156 -0
- data/spec/targets/sqlite_target_spec.rb +103 -0
- data/spec/util_spec.rb +13 -0
- 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
|