migration_bundler 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.bundle/config +2 -0
- data/.gitignore +3 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +49 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +122 -0
- data/Guardfile +8 -0
- data/LICENSE +202 -0
- data/README.md +219 -0
- data/Rakefile +16 -0
- data/bin/mb +6 -0
- data/lib/migration_bundler/actions.rb +38 -0
- data/lib/migration_bundler/cli.rb +355 -0
- data/lib/migration_bundler/databases/abstract_database.rb +54 -0
- data/lib/migration_bundler/databases/cassandra_database.rb +88 -0
- data/lib/migration_bundler/databases/sqlite_database.rb +116 -0
- data/lib/migration_bundler/migrations.rb +52 -0
- data/lib/migration_bundler/project.rb +90 -0
- data/lib/migration_bundler/targets/base.rb +85 -0
- data/lib/migration_bundler/targets/cassandra/cassandra_target.rb +73 -0
- data/lib/migration_bundler/targets/cassandra/create_schema_migrations.cql.erb +16 -0
- data/lib/migration_bundler/targets/cassandra/migration.cql.erb +0 -0
- data/lib/migration_bundler/targets/cocoapods/cocoapods_target.rb +43 -0
- data/lib/migration_bundler/targets/cocoapods/podspec.erb +12 -0
- data/lib/migration_bundler/targets/maven/maven_target.rb +62 -0
- data/lib/migration_bundler/targets/maven/project/.gitignore +6 -0
- data/lib/migration_bundler/targets/maven/project/MonkeyButler.iml +19 -0
- data/lib/migration_bundler/targets/maven/project/build.gradle +54 -0
- data/lib/migration_bundler/targets/sqlite/create_migration_bundler_tables.sql.erb +15 -0
- data/lib/migration_bundler/targets/sqlite/migration.sql.erb +11 -0
- data/lib/migration_bundler/targets/sqlite/sqlite_target.rb +92 -0
- data/lib/migration_bundler/templates/Gemfile.erb +4 -0
- data/lib/migration_bundler/templates/gitignore.erb +1 -0
- data/lib/migration_bundler/util.rb +71 -0
- data/lib/migration_bundler/version.rb +3 -0
- data/lib/migration_bundler.rb +1 -0
- data/migration_bundler.gemspec +33 -0
- data/spec/cli_spec.rb +700 -0
- data/spec/databases/cassandra_database_spec.rb +260 -0
- data/spec/databases/sqlite_database_spec.rb +198 -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/.migration_bundler.yml +9 -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/.migration_bundler.yml +9 -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/spec_helper.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
require 'migration_bundler'
|
5
|
+
|
6
|
+
require 'rspec/core/shared_context'
|
7
|
+
require 'tempfile'
|
8
|
+
require 'digest'
|
9
|
+
require 'byebug'
|
10
|
+
|
11
|
+
module GlobalContext
|
12
|
+
extend RSpec::Core::SharedContext
|
13
|
+
|
14
|
+
# Assume that most specs will describe a Thor subclass
|
15
|
+
let(:thor_class) { subject.class }
|
16
|
+
end
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.before do
|
20
|
+
ARGV.replace []
|
21
|
+
MigrationBundler::Project.clear
|
22
|
+
end
|
23
|
+
|
24
|
+
config.include GlobalContext
|
25
|
+
|
26
|
+
def capture(stream)
|
27
|
+
begin
|
28
|
+
stream = stream.to_s
|
29
|
+
eval "$#{stream} = StringIO.new"
|
30
|
+
yield
|
31
|
+
result = eval("$#{stream}").string
|
32
|
+
ensure
|
33
|
+
eval("$#{stream} = #{stream.upcase}")
|
34
|
+
end
|
35
|
+
|
36
|
+
result
|
37
|
+
end
|
38
|
+
|
39
|
+
def capture_output(proc, &block)
|
40
|
+
error = nil
|
41
|
+
content = capture(:stdout) do
|
42
|
+
error = capture(:stderr) do
|
43
|
+
proc.call
|
44
|
+
end
|
45
|
+
end
|
46
|
+
yield content, error if block_given?
|
47
|
+
{ stdout: content, stderr: error }
|
48
|
+
end
|
49
|
+
|
50
|
+
# def source_root
|
51
|
+
# File.join(File.dirname(__FILE__), "fixtures")
|
52
|
+
# end
|
53
|
+
|
54
|
+
def sandbox_root
|
55
|
+
Pathname.new File.join(File.dirname(__FILE__), "sandbox")
|
56
|
+
end
|
57
|
+
|
58
|
+
def clone_temp_sandbox(database = :sqlite)
|
59
|
+
Dir.mktmpdir.tap do |path|
|
60
|
+
FileUtils.cp_r Dir.glob(sandbox_root + "#{database}/."), path
|
61
|
+
Dir.chdir(path) do
|
62
|
+
system("git init -q .")
|
63
|
+
system("git remote add origin git@github.com:layerhq/migration_bundler_sandbox.git")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def random_migration_name
|
69
|
+
timestamp = MigrationBundler::Util.migration_timestamp + rand(1..1000)
|
70
|
+
MigrationBundler::Util.migration_named(Digest::SHA256.hexdigest(Time.now.to_s), timestamp)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Requires `thor_class` and `project_root`
|
74
|
+
def invoke!(args = [], options = {:capture => true})
|
75
|
+
output = nil
|
76
|
+
# Some commands work with a directory that doesn't yet exist
|
77
|
+
dir = File.exists?(project_root) ? project_root : '.'
|
78
|
+
Dir.chdir(dir) do
|
79
|
+
if options[:capture]
|
80
|
+
output = capture_output(proc { thor_class.start(args) })
|
81
|
+
else
|
82
|
+
thor_class.start(args)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def invoke_target!(command, options = {})
|
88
|
+
output = nil
|
89
|
+
# Some commands work with a directory that doesn't yet exist
|
90
|
+
dir = File.exists?(project_root) ? project_root : '.'
|
91
|
+
Dir.chdir(dir) do
|
92
|
+
if options.delete(:capture)
|
93
|
+
output = capture_output proc do
|
94
|
+
target = thor_class.new([], options)
|
95
|
+
target.invoke(command)
|
96
|
+
end
|
97
|
+
else
|
98
|
+
target = thor_class.new([], options)
|
99
|
+
target.invoke(command)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'migration_bundler/targets/cassandra/cassandra_target'
|
3
|
+
require 'migration_bundler/databases/cassandra_database'
|
4
|
+
|
5
|
+
describe MigrationBundler::Targets::CassandraTarget do
|
6
|
+
let(:thor_class) { MigrationBundler::CLI }
|
7
|
+
let!(:project_root) { clone_temp_sandbox(:cassandra) }
|
8
|
+
let(:project) { MigrationBundler::Project.load(project_root) }
|
9
|
+
let(:database) { MigrationBundler::Databases::CassandraDatabase.new(project.database_url) }
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
database.drop
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#init" do
|
16
|
+
let(:path) do
|
17
|
+
Dir.mktmpdir.tap { |path| FileUtils.remove_entry(path) }
|
18
|
+
end
|
19
|
+
|
20
|
+
it "generates a new migration with the given name" do
|
21
|
+
invoke!(["init", "-d", "cassandra://localhost:9170/sandbox", path])
|
22
|
+
migration = Dir.entries(File.join(path, 'migrations')).detect { |f| f =~ /\d{15}_create_.+\.cql/ }
|
23
|
+
migration.should_not be_nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#new" do
|
28
|
+
it "generates a new migration with the given name" do
|
29
|
+
invoke!(%w{new add_column_to_table})
|
30
|
+
migration = Dir.entries(File.join(project_root, 'migrations')).detect { |f| f =~ /add_column_to_table\.cql/ }
|
31
|
+
migration.should_not be_nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#dump" do
|
36
|
+
before(:each) do
|
37
|
+
database = MigrationBundler::Databases::CassandraDatabase.new(project.database_url)
|
38
|
+
database.create_migrations_table
|
39
|
+
database.insert_version(123)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "dumps the database" do
|
43
|
+
invoke!(%w{dump})
|
44
|
+
content = File.read(File.join(project_root, project.schema_path))
|
45
|
+
content.should =~ /CREATE KEYSPACE sandbox/
|
46
|
+
end
|
47
|
+
|
48
|
+
it "dumps the schema_migrations rows" do
|
49
|
+
invoke!(%w{dump})
|
50
|
+
content = File.read(File.join(project_root, project.schema_path))
|
51
|
+
content.should =~ /INSERT INTO schema_migrations \(partition_key, version\) VALUES \(0, 123\);/
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when there are extra keyspaces specified" do
|
55
|
+
before(:each) do
|
56
|
+
database.client.execute "CREATE KEYSPACE IF NOT EXISTS extra1 WITH replication = {'class' : 'SimpleStrategy', 'replication_factor' : 1};"
|
57
|
+
database.client.execute "CREATE KEYSPACE IF NOT EXISTS extra2 WITH replication = {'class' : 'SimpleStrategy', 'replication_factor' : 1};"
|
58
|
+
end
|
59
|
+
|
60
|
+
it "dumps the additional keyspaces" do
|
61
|
+
project.config['cassandra.keyspaces'] = %w{extra1 extra2}
|
62
|
+
project.save!(project_root)
|
63
|
+
invoke!(%w{dump})
|
64
|
+
content = File.read(File.join(project_root, project.schema_path))
|
65
|
+
content.should =~ /CREATE KEYSPACE extra1/
|
66
|
+
content.should =~ /CREATE KEYSPACE extra2/
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#load" do
|
72
|
+
before(:each) do
|
73
|
+
@database = MigrationBundler::Databases::CassandraDatabase.new(project.database_url)
|
74
|
+
@database.create_migrations_table
|
75
|
+
invoke!(%w{dump})
|
76
|
+
@database.drop
|
77
|
+
end
|
78
|
+
|
79
|
+
it "loads the database" do
|
80
|
+
expect(@database.migrations_table?).to be_false
|
81
|
+
invoke!(%w{load})
|
82
|
+
expect(@database.migrations_table?).to be_true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#status" do
|
87
|
+
context "when the database is empty" do
|
88
|
+
before(:each) do
|
89
|
+
database.drop
|
90
|
+
end
|
91
|
+
|
92
|
+
it "shows status" do
|
93
|
+
output = invoke!(%w{status})
|
94
|
+
output[:stdout].should =~ /New database/
|
95
|
+
output[:stdout].should =~ /The database at 'cassandra:\/\/localhost:9042\/sandbox' does not have a 'schema_migrations' table./
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context "when the database is up to date" do
|
100
|
+
before(:each) do
|
101
|
+
database.create_migrations_table
|
102
|
+
database.insert_version(12345)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "tells the user the database is up to date" do
|
106
|
+
output = invoke!(%w{status})
|
107
|
+
output[:stdout].should =~ /Current version: 12345/
|
108
|
+
output[:stdout].should =~ /Database is up to date./
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "when there are migrations to apply" do
|
113
|
+
before(:each) do
|
114
|
+
File.open(File.join(project_root, 'migrations/20140523123443021_add_another_table.cql'), 'w') do |f|
|
115
|
+
f << "CREATE TABLE IF NOT EXISTS sandbox.another_table (some_column VARINT, PRIMARY KEY (some_column));"
|
116
|
+
end
|
117
|
+
database.create_migrations_table
|
118
|
+
database.insert_version(12345)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "tells the user there is a migration to apply" do
|
122
|
+
output = invoke!(%w{status})
|
123
|
+
output[:stdout].should =~ /The database at 'cassandra:\/\/localhost:9042\/sandbox' is 1 version behind 20140523123443021/
|
124
|
+
output[:stdout].should =~ /pending migration: migrations\/20140523123443021_add_another_table.cql/
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "#migrate" do
|
130
|
+
context "when the database is empty" do
|
131
|
+
before(:each) do
|
132
|
+
database.drop
|
133
|
+
end
|
134
|
+
|
135
|
+
it "shows migrate" do
|
136
|
+
output = invoke!(%w{migrate})
|
137
|
+
output[:stdout].should =~ /Database is up to date./
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context "when the database is up to date" do
|
142
|
+
before(:each) do
|
143
|
+
database.create_migrations_table
|
144
|
+
database.insert_version(12345)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "tells the user the database is up to date" do
|
148
|
+
output = invoke!(%w{migrate})
|
149
|
+
output[:stdout].should =~ /Database is up to date./
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "when there are migrations to apply" do
|
154
|
+
before(:each) do
|
155
|
+
File.open(File.join(project_root, 'migrations/20140523123443021_add_another_table.cql'), 'w') do |f|
|
156
|
+
f << "CREATE TABLE IF NOT EXISTS sandbox.another_table (some_column VARINT, PRIMARY KEY (some_column));"
|
157
|
+
end
|
158
|
+
database.create_migrations_table
|
159
|
+
database.insert_version(12345)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "tells the user there is a migration to apply" do
|
163
|
+
output = invoke!(%w{migrate})
|
164
|
+
output[:stdout].should =~ /applying migration: migrations\/20140523123443021_add_another_table.cql/
|
165
|
+
output[:stdout].should =~ /Migration to version 20140523123443021 complete./
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe "#drop" do
|
171
|
+
before(:each) do
|
172
|
+
database.create_migrations_table
|
173
|
+
database.insert_version(123)
|
174
|
+
database.client.execute "CREATE KEYSPACE IF NOT EXISTS extra1 WITH replication = {'class' : 'SimpleStrategy', 'replication_factor' : 1};"
|
175
|
+
database.client.execute "CREATE KEYSPACE IF NOT EXISTS extra2 WITH replication = {'class' : 'SimpleStrategy', 'replication_factor' : 1};"
|
176
|
+
project.config['cassandra.keyspaces'] = %w{extra1 extra2}
|
177
|
+
project.save!(project_root)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "drops all keyspaces" do
|
181
|
+
invoke!(%w{drop})
|
182
|
+
database.client.use(:system)
|
183
|
+
rows = database.client.execute('SELECT keyspace_name FROM schema_columnfamilies')
|
184
|
+
keyspaces = []
|
185
|
+
rows.each { |row| keyspaces << row['keyspace_name'] }
|
186
|
+
keyspaces.should_not include('sandbox')
|
187
|
+
keyspaces.should_not include('extra1')
|
188
|
+
keyspaces.should_not include('extra2')
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'migration_bundler/targets/cocoapods/cocoapods_target'
|
3
|
+
|
4
|
+
module Pod
|
5
|
+
class Spec
|
6
|
+
attr_accessor :name, :version, :summary, :homepage, :author, :source, :license, :resource_bundles, :requires_arc
|
7
|
+
|
8
|
+
def initialize(hash = {})
|
9
|
+
yield self if block_given?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe MigrationBundler::Targets::CocoapodsTarget do
|
15
|
+
let(:thor_class) { MigrationBundler::Targets::CocoapodsTarget }
|
16
|
+
let!(:project_root) { clone_temp_sandbox }
|
17
|
+
|
18
|
+
before(:each) do
|
19
|
+
Dir.chdir(project_root) do
|
20
|
+
system("git config user.email developer@layer.com")
|
21
|
+
system("git config user.name 'Layer Developer'")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#init" do
|
26
|
+
it "asks for the name of the cocoapods repository" do
|
27
|
+
remove_cocoapods_repo_from_config
|
28
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the name of your Cocoapods specs repo? ", {}).and_return("layerhq")
|
29
|
+
invoke!(['init'])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#generate' do
|
34
|
+
let!(:podspec) do
|
35
|
+
invoke!(['generate'])
|
36
|
+
eval(File.read(File.join(project_root, 'sandbox.podspec')))
|
37
|
+
end
|
38
|
+
|
39
|
+
it "has name" do
|
40
|
+
expect(podspec.name).to eq('sandbox')
|
41
|
+
end
|
42
|
+
|
43
|
+
it "has version" do
|
44
|
+
expect(podspec.version).to eq('20140523123443021')
|
45
|
+
end
|
46
|
+
|
47
|
+
it "has summary" do
|
48
|
+
expect(podspec.summary).to eq('Packages the database schema and migrations for sandbox')
|
49
|
+
end
|
50
|
+
|
51
|
+
it "has homepage" do
|
52
|
+
expect(podspec.homepage).to eq('http://github.com/layerhq')
|
53
|
+
end
|
54
|
+
|
55
|
+
it "has author" do
|
56
|
+
expect(podspec.author).to eq({"Layer Developer"=>"developer@layer.com"})
|
57
|
+
end
|
58
|
+
|
59
|
+
it "has source" do
|
60
|
+
expect(podspec.source).to eq({:git=>"git@github.com:layerhq/migration_bundler_sandbox.git", :tag=>"20140523123443021"})
|
61
|
+
end
|
62
|
+
|
63
|
+
it "has license" do
|
64
|
+
expect(podspec.license).to eq('Commercial')
|
65
|
+
end
|
66
|
+
|
67
|
+
it "has resource_bundles" do
|
68
|
+
expect(podspec.resource_bundles).to eq({"sandbox"=>["migrations/*", "sandbox.sql"]})
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#push" do
|
73
|
+
context "when cocoapods.repo is not configured" do
|
74
|
+
it "fails with an error" do
|
75
|
+
remove_cocoapods_repo_from_config
|
76
|
+
output = invoke!(['push'])
|
77
|
+
output[:stderr].should =~ /Invalid configuration: cocoapods.repo is not configured./
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "when cocoapods.repo is configured" do
|
82
|
+
it "invokes pod push" do
|
83
|
+
output = invoke!(['push', '--pretend'])
|
84
|
+
output[:stdout].should =~ /run\s+pod repo push --allow-warnings example_specs_repo sandbox.podspec/
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#validate" do
|
90
|
+
context "when the cocoapods repo is not configured" do
|
91
|
+
before(:each) do
|
92
|
+
remove_cocoapods_repo_from_config
|
93
|
+
end
|
94
|
+
|
95
|
+
it "fails with error" do
|
96
|
+
output = invoke!(['validate'])
|
97
|
+
output[:stderr].should =~ /Invalid configuration: cocoapods.repo is not configured./
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#push" do
|
103
|
+
context "when there is no repo configured" do
|
104
|
+
before(:each) do
|
105
|
+
remove_cocoapods_repo_from_config
|
106
|
+
end
|
107
|
+
|
108
|
+
it "fails validation" do
|
109
|
+
output = invoke!(['push'])
|
110
|
+
output[:stderr].should =~ /Invalid configuration: cocoapods.repo is not configured./
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "when there is a repo configured" do
|
115
|
+
def with_temporary_cocoapods_repo
|
116
|
+
path = Dir.mktmpdir
|
117
|
+
temp_specs_repo_at_path(File.join(path, 'master'))
|
118
|
+
temp_specs_repo_at_path(File.join(path, 'example_specs_repo'))
|
119
|
+
|
120
|
+
ENV['CP_REPOS_DIR'] = path
|
121
|
+
yield path
|
122
|
+
ENV.delete('CP_REPOS_DIR')
|
123
|
+
end
|
124
|
+
|
125
|
+
it "pushes to cocoapods" do
|
126
|
+
git_repo_path = path_for_temp_bare_git_repo
|
127
|
+
Dir.chdir(project_root) do
|
128
|
+
`git remote set-url origin file://#{git_repo_path}`
|
129
|
+
end
|
130
|
+
|
131
|
+
# Generate the podspec
|
132
|
+
invoke!(['generate'])
|
133
|
+
|
134
|
+
Dir.chdir(project_root) do
|
135
|
+
`git remote set-url origin file://#{git_repo_path}`
|
136
|
+
`git add .`
|
137
|
+
`git commit --no-status -m 'Adding files' .`
|
138
|
+
`git tag 20140523123443021`
|
139
|
+
`git push -q origin master --tags`
|
140
|
+
end
|
141
|
+
|
142
|
+
with_temporary_cocoapods_repo do |repo_path|
|
143
|
+
output = invoke!(%w{push --quiet})
|
144
|
+
|
145
|
+
Dir.chdir(repo_path) do
|
146
|
+
pushed_podspec_path = File.join(repo_path, 'example_specs_repo', 'sandbox', '20140523123443021', 'sandbox.podspec')
|
147
|
+
File.exists?(pushed_podspec_path).should be_true
|
148
|
+
content = File.read(pushed_podspec_path)
|
149
|
+
content.should =~ /20140523123443021/
|
150
|
+
content.should =~ /resource_bundles/
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def remove_cocoapods_repo_from_config
|
158
|
+
yaml_path = File.join(project_root, '.migration_bundler.yml')
|
159
|
+
project = YAML.load(File.read(yaml_path))
|
160
|
+
project['config'].delete 'cocoapods.repo'
|
161
|
+
File.open(yaml_path, 'w') { |f| f << YAML.dump(project) }
|
162
|
+
end
|
163
|
+
|
164
|
+
def path_for_temp_bare_git_repo
|
165
|
+
git_repo_path = Dir.mktmpdir
|
166
|
+
Dir.chdir(git_repo_path) do
|
167
|
+
`git init --bare`
|
168
|
+
end
|
169
|
+
git_repo_path
|
170
|
+
end
|
171
|
+
|
172
|
+
def temp_specs_repo_at_path(path)
|
173
|
+
FileUtils.mkdir_p path
|
174
|
+
Dir.chdir(path) do
|
175
|
+
run("git init -q .")
|
176
|
+
raise "Failed to init git repo" unless $?.exitstatus.zero?
|
177
|
+
run("echo '' > README.md")
|
178
|
+
run("git add README.md")
|
179
|
+
run("git commit --no-status -m 'Add README.md' .")
|
180
|
+
raise "Failed to touch README.md" unless $?.exitstatus.zero?
|
181
|
+
remote_url = "file://#{path_for_temp_bare_git_repo}"
|
182
|
+
run("git remote add origin #{remote_url}")
|
183
|
+
raise "Failed to set origin remote url" unless $?.exitstatus.zero?
|
184
|
+
run("git push -q -u origin master")
|
185
|
+
raise "Failed to push to temp repo" unless $?.exitstatus.zero?
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def run(command, echo = false)
|
190
|
+
if echo
|
191
|
+
puts "Executing `#{command}`"
|
192
|
+
system(command)
|
193
|
+
else
|
194
|
+
`#{command}`
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'migration_bundler/targets/base'
|
3
|
+
require 'migration_bundler/targets/maven/maven_target'
|
4
|
+
|
5
|
+
describe MigrationBundler::Targets::MavenTarget do
|
6
|
+
let(:thor_class) { MigrationBundler::Targets::MavenTarget }
|
7
|
+
let!(:project_root) { clone_temp_sandbox }
|
8
|
+
|
9
|
+
describe "#init" do
|
10
|
+
it "asks for Maven Java repository URL and credentials" do
|
11
|
+
remove_maven_repo_from_config
|
12
|
+
remove_maven_username_from_config
|
13
|
+
remove_maven_password_from_config
|
14
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the URL of your Java Maven repo? ", {}).and_return(File.join(project_root, "maven"))
|
15
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the username for your Java Maven repo? ", {}).and_return("none")
|
16
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the password for your Java Maven repo? ", {}).and_return("none")
|
17
|
+
invoke!(['init'])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#generate' do
|
22
|
+
before(:all) do
|
23
|
+
# Put config into the YAML
|
24
|
+
yaml_path = File.join(project_root, '.migration_bundler.yml')
|
25
|
+
project = YAML.load(File.read(yaml_path))
|
26
|
+
project['config']['maven.url'] = 'maven'
|
27
|
+
project['config']['maven.username'] = 'none'
|
28
|
+
project['config']['maven.password'] = 'none'
|
29
|
+
File.open(yaml_path, 'w') { |f| f << YAML.dump(project) }
|
30
|
+
|
31
|
+
invoke!(['generate', '--quiet'])
|
32
|
+
end
|
33
|
+
|
34
|
+
# Shadow the let! declarations so we can use before(:all)
|
35
|
+
def thor_class
|
36
|
+
MigrationBundler::Targets::MavenTarget
|
37
|
+
end
|
38
|
+
|
39
|
+
def project_root
|
40
|
+
@project_root ||= clone_temp_sandbox
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should have project/build.gradle" do
|
44
|
+
expect(File.directory?(File.join(project_root, 'project'))).to eq(true)
|
45
|
+
expect(File.file?(File.join(project_root, 'project/build.gradle'))).to eq(true)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should have a schema file in resources/schema" do
|
49
|
+
expect(File.directory?(File.join(project_root, 'project/src/main/resources/schema'))).to eq(true)
|
50
|
+
expect(File.file?(File.join(project_root, 'project/src/main/resources/schema/schema.sql'))).to eq(true)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should have migration files in resources/migrations" do
|
54
|
+
expect(File.directory?(File.join(project_root, 'project/src/main/resources/migrations'))).to eq(true)
|
55
|
+
expect(Dir.entries(File.join(project_root, 'project/src/main/resources/migrations')).size).not_to eq(2)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should have project/build/libs/MigrationBundler-[version].jar files" do
|
59
|
+
expect(File.file?(built_jar_path)).to eq(true)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should have a schema packaged in the JAR" do
|
63
|
+
jar_schema = `jar tf #{built_jar_path} | grep -e schema/.*[.]sql`.strip
|
64
|
+
expect(jar_schema).to eq("schema/schema.sql")
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should have all migrations packaged in the JAR" do
|
68
|
+
# Create a hash of all expected migrations
|
69
|
+
expected_migrations = Hash.new
|
70
|
+
Dir.foreach(File.join(project_root, "migrations")) do |entry|
|
71
|
+
if !entry.end_with? ".sql" then
|
72
|
+
next
|
73
|
+
end
|
74
|
+
expected_migrations[entry] = true
|
75
|
+
end
|
76
|
+
|
77
|
+
# Create a array of all migrations packaged in the JAR
|
78
|
+
jar_migrations = `jar tf #{built_jar_path} | grep -e migrations/.*[.]sql`
|
79
|
+
jar_migration_array = jar_migrations.split(/\r?\n/)
|
80
|
+
|
81
|
+
# Verify that the expected and actual have the same size
|
82
|
+
expect(jar_migration_array.length).to eq(expected_migrations.length)
|
83
|
+
|
84
|
+
# Verify that all JAR'd migrations were expected
|
85
|
+
jar_migration_array.each { |jar_migration|
|
86
|
+
length = jar_migration.length - "migrations/".length
|
87
|
+
migration = jar_migration[-length, length]
|
88
|
+
expect(expected_migrations[migration]).to eq(true)
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should clear java project between `generate` invocations" do
|
93
|
+
# Create a random file in the project directory.
|
94
|
+
yaml_path = File.join(project_root, '.migration_bundler.yml')
|
95
|
+
project = YAML.load(File.read(yaml_path))
|
96
|
+
File.open(File.join(project_root, 'project/deleteme.txt'), 'w') { |f| f << YAML.dump(project) }
|
97
|
+
expect(File.file?(File.join(project_root, 'project/deleteme.txt'))).to eq(true)
|
98
|
+
invoke!(['generate', '--quiet'])
|
99
|
+
expect(File.file?(File.join(project_root, 'project/deleteme.txt'))).to eq(false)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '#push' do
|
104
|
+
before(:each) do
|
105
|
+
set_config
|
106
|
+
invoke!(%w{generate --quiet})
|
107
|
+
invoke!(%w{push --quiet})
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should have MigrationBundler-[version].jar file in the local Maven" do
|
111
|
+
expect(File.file?(maven_jar_path)).to eq(true)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def remove_maven_repo_from_config
|
118
|
+
yaml_path = File.join(project_root, '.migration_bundler.yml')
|
119
|
+
project = YAML.load(File.read(yaml_path))
|
120
|
+
project['config'].delete 'maven.url'
|
121
|
+
File.open(yaml_path, 'w') { |f| f << YAML.dump(project) }
|
122
|
+
end
|
123
|
+
|
124
|
+
def remove_maven_username_from_config
|
125
|
+
yaml_path = File.join(project_root, '.migration_bundler.yml')
|
126
|
+
project = YAML.load(File.read(yaml_path))
|
127
|
+
project['config'].delete 'maven.username'
|
128
|
+
File.open(yaml_path, 'w') { |f| f << YAML.dump(project) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def remove_maven_password_from_config
|
132
|
+
yaml_path = File.join(project_root, '.migration_bundler.yml')
|
133
|
+
project = YAML.load(File.read(yaml_path))
|
134
|
+
project['config'].delete 'maven.password'
|
135
|
+
File.open(yaml_path, 'w') { |f| f << YAML.dump(project) }
|
136
|
+
end
|
137
|
+
|
138
|
+
def set_config
|
139
|
+
remove_maven_repo_from_config
|
140
|
+
remove_maven_username_from_config
|
141
|
+
remove_maven_password_from_config
|
142
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the URL of your Java Maven repo? ", {}).and_return(File.join(project_root, "maven"))
|
143
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the username for your Java Maven repo? ", {}).and_return("none")
|
144
|
+
expect(Thor::LineEditor).to receive(:readline).with("What is the password for your Java Maven repo? ", {}).and_return("none")
|
145
|
+
invoke!(['init'])
|
146
|
+
end
|
147
|
+
|
148
|
+
def maven_jar_path
|
149
|
+
File.join(project_root, "maven/com/layer/MigrationBundler/20140523123443021/MigrationBundler-20140523123443021.jar")
|
150
|
+
end
|
151
|
+
|
152
|
+
def built_jar_path
|
153
|
+
File.join(project_root, "project/build/libs/MigrationBundler-20140523123443021.jar")
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|