migration_bundler 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) 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 +49 -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 +219 -0
  11. data/Rakefile +16 -0
  12. data/bin/mb +6 -0
  13. data/lib/migration_bundler/actions.rb +38 -0
  14. data/lib/migration_bundler/cli.rb +355 -0
  15. data/lib/migration_bundler/databases/abstract_database.rb +54 -0
  16. data/lib/migration_bundler/databases/cassandra_database.rb +88 -0
  17. data/lib/migration_bundler/databases/sqlite_database.rb +116 -0
  18. data/lib/migration_bundler/migrations.rb +52 -0
  19. data/lib/migration_bundler/project.rb +90 -0
  20. data/lib/migration_bundler/targets/base.rb +85 -0
  21. data/lib/migration_bundler/targets/cassandra/cassandra_target.rb +73 -0
  22. data/lib/migration_bundler/targets/cassandra/create_schema_migrations.cql.erb +16 -0
  23. data/lib/migration_bundler/targets/cassandra/migration.cql.erb +0 -0
  24. data/lib/migration_bundler/targets/cocoapods/cocoapods_target.rb +43 -0
  25. data/lib/migration_bundler/targets/cocoapods/podspec.erb +12 -0
  26. data/lib/migration_bundler/targets/maven/maven_target.rb +62 -0
  27. data/lib/migration_bundler/targets/maven/project/.gitignore +6 -0
  28. data/lib/migration_bundler/targets/maven/project/MonkeyButler.iml +19 -0
  29. data/lib/migration_bundler/targets/maven/project/build.gradle +54 -0
  30. data/lib/migration_bundler/targets/sqlite/create_migration_bundler_tables.sql.erb +15 -0
  31. data/lib/migration_bundler/targets/sqlite/migration.sql.erb +11 -0
  32. data/lib/migration_bundler/targets/sqlite/sqlite_target.rb +92 -0
  33. data/lib/migration_bundler/templates/Gemfile.erb +4 -0
  34. data/lib/migration_bundler/templates/gitignore.erb +1 -0
  35. data/lib/migration_bundler/util.rb +71 -0
  36. data/lib/migration_bundler/version.rb +3 -0
  37. data/lib/migration_bundler.rb +1 -0
  38. data/migration_bundler.gemspec +33 -0
  39. data/spec/cli_spec.rb +700 -0
  40. data/spec/databases/cassandra_database_spec.rb +260 -0
  41. data/spec/databases/sqlite_database_spec.rb +198 -0
  42. data/spec/migrations_spec.rb +4 -0
  43. data/spec/project_spec.rb +128 -0
  44. data/spec/sandbox/cassandra/.gitignore +2 -0
  45. data/spec/sandbox/cassandra/.migration_bundler.yml +9 -0
  46. data/spec/sandbox/cassandra/migrations/20140523123443021_create_sandbox.cql.sql +14 -0
  47. data/spec/sandbox/cassandra/sandbox.cql +0 -0
  48. data/spec/sandbox/sqlite/.gitignore +2 -0
  49. data/spec/sandbox/sqlite/.migration_bundler.yml +9 -0
  50. data/spec/sandbox/sqlite/migrations/20140523123443021_create_sandbox.sql +14 -0
  51. data/spec/sandbox/sqlite/sandbox.sql +0 -0
  52. data/spec/spec_helper.rb +103 -0
  53. data/spec/targets/cassandra_target_spec.rb +191 -0
  54. data/spec/targets/cocoapods_target_spec.rb +197 -0
  55. data/spec/targets/maven_target_spec.rb +156 -0
  56. data/spec/targets/sqlite_target_spec.rb +103 -0
  57. data/spec/util_spec.rb +13 -0
  58. metadata +260 -0
@@ -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