xmigra 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/test/git_vcs.rb ADDED
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ GIT_PRESENT = begin
5
+ `git --version` && $?.success?
6
+ rescue
7
+ false
8
+ end
9
+
10
+ def initialize_git_repo(dir)
11
+ Dir.chdir(dir) do
12
+ do_or_die "git init", "Failed to initialize git repository"
13
+ initialize_xmigra_schema
14
+ end
15
+ end
16
+
17
+ def commit_all(msg)
18
+ do_or_die "git add -A"
19
+ do_or_die "git commit -m \"#{msg}\""
20
+ end
21
+
22
+ def commit_a_migration(desc_tail)
23
+ XMigra::NewMigrationAdder.new('.').tap do |tool|
24
+ tool.add_migration "Create #{desc_tail}"
25
+ end
26
+ commit_all "Added #{desc_tail}"
27
+ end
28
+
29
+ def get_migration_chain_head
30
+ (Pathname('.') + XMigra::SchemaManipulator::STRUCTURE_SUBDIR + XMigra::MigrationChain::HEAD_FILE).open do |f|
31
+ YAML.load(f)[XMigra::MigrationChain::LATEST_CHANGE]
32
+ end
33
+ end
34
+
35
+ def make_this_branch_master
36
+ open('.gitattributes', 'w') do |f|
37
+ f.puts("%s %s=file://%s#%s" % [
38
+ XMigra::SchemaManipulator::DBINFO_FILE,
39
+ XMigra::GitSpecifics::MASTER_HEAD_ATTRIBUTE,
40
+ Dir.pwd,
41
+ `git rev-parse --abbrev-ref HEAD`.chomp
42
+ ])
43
+ end
44
+ commit_all "Updated master database location"
45
+ end
46
+
47
+ if GIT_PRESENT
48
+ run_test "Initialize a git repository" do
49
+ 1.temp_dirs do |repo|
50
+ initialize_git_repo(repo)
51
+ end
52
+ end
53
+
54
+ run_test "XMigra recognizes git version control" do
55
+ 1.temp_dirs do |repo|
56
+ initialize_git_repo(repo)
57
+
58
+ Dir.chdir(repo) do
59
+ tool = XMigra::SchemaManipulator.new('.')
60
+ assert {tool.is_a? XMigra::MSSQLSpecifics}
61
+ end
62
+ end
63
+ end
64
+
65
+ run_test "XMigra can create a new migration" do
66
+ 1.temp_dirs do |repo|
67
+ initialize_git_repo(repo)
68
+
69
+ Dir.chdir(repo) do
70
+ tool = XMigra::NewMigrationAdder.new('.')
71
+ description = "Create the first table"
72
+ fpath = tool.add_migration(description)
73
+ assert_include fpath.to_s, description
74
+ end
75
+ end
76
+ end
77
+
78
+ run_test "XMigra can recognize a migration chain conflict" do
79
+ 1.temp_dirs do |repo|
80
+ initialize_git_repo(repo)
81
+
82
+ Dir.chdir(repo) do
83
+ commit_a_migration "first table"
84
+ do_or_die "git branch side"
85
+ commit_a_migration "foo table"
86
+ do_or_die "git checkout side 2>/dev/null"
87
+ commit_a_migration "bar table"
88
+ `git merge master` # This is not going to go well!
89
+
90
+ XMigra::SchemaManipulator.new('.').tap do |tool|
91
+ conflict = tool.get_conflict_info
92
+ assert {not conflict.nil?}
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ run_test "XMigra can fix a migration chain conflict" do
99
+ 1.temp_dirs do |repo|
100
+ initialize_git_repo(repo)
101
+
102
+ Dir.chdir(repo) do
103
+ commit_a_migration "first table"
104
+ do_or_die "git branch side"
105
+ commit_a_migration "foo table"
106
+ do_or_die "git checkout side 2>/dev/null"
107
+ commit_a_migration "bar table"
108
+ my_head = get_migration_chain_head
109
+ `git merge master` # This is not going to go well!
110
+
111
+ XMigra::SchemaManipulator.new('.').tap do |tool|
112
+ conflict = tool.get_conflict_info
113
+ assert {not conflict.nil?}
114
+ conflict.fix_conflict!
115
+
116
+ # Check that the merged migrations are at the end of the chain
117
+ assert_neq get_migration_chain_head, my_head
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ run_test "XMigra is aware of upstream branch relationship" do
124
+ 2.temp_dirs do |upstream, repo|
125
+ initialize_git_repo(upstream)
126
+
127
+ Dir.chdir(upstream) do
128
+ commit_a_migration "first table"
129
+ end
130
+
131
+ `git clone "#{upstream}" "#{repo}" 2>/dev/null`
132
+
133
+ Dir.chdir(upstream) do
134
+ commit_a_migration "foo table"
135
+ end
136
+
137
+ Dir.chdir(repo) do
138
+ commit_a_migration "bar table"
139
+
140
+ # Get head migration
141
+ downstream_head = get_migration_chain_head
142
+
143
+ `git pull origin master 2>/dev/null`
144
+
145
+ XMigra::SchemaManipulator.new('.').tap do |tool|
146
+ conflict = tool.get_conflict_info
147
+ assert {conflict}
148
+ conflict.fix_conflict!
149
+
150
+ # Check that head migration is still what it was (i.e.
151
+ # merged migrations precede local branch migrations, preventing
152
+ # modifications to upstream)
153
+ assert_eq get_migration_chain_head, downstream_head
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+ run_test "XMigra will not generate a production script from a working tree that does not match the master branch" do
160
+ 2.temp_dirs do |upstream, repo|
161
+ initialize_git_repo(upstream)
162
+
163
+ Dir.chdir(upstream) do
164
+ commit_a_migration "first table"
165
+ make_this_branch_master
166
+ end
167
+
168
+ `git clone "#{upstream.expand_path}" "#{repo}" 2>/dev/null`
169
+
170
+ Dir.chdir(upstream) do
171
+ commit_a_migration "foo table"
172
+ end
173
+
174
+ Dir.chdir(repo) do
175
+ commit_a_migration "bar table"
176
+
177
+ XMigra::SchemaUpdater.new('.').tap do |tool|
178
+ tool.production = true
179
+ assert_neq(tool.branch_use, :production)
180
+ assert_raises(XMigra::VersionControlError) {tool.update_sql}
181
+ end
182
+ end
183
+ end
184
+ end
185
+
186
+ run_test "XMigra will generate a production script from an older commit from the master branch" do
187
+ 2.temp_dirs do |upstream, repo|
188
+ initialize_git_repo(upstream)
189
+
190
+ Dir.chdir(upstream) do
191
+ commit_a_migration "first table"
192
+ make_this_branch_master
193
+ end
194
+
195
+ `git clone "#{upstream.expand_path}" "#{repo}" 2>/dev/null`
196
+
197
+ Dir.chdir(upstream) do
198
+ commit_a_migration "foo table"
199
+ end
200
+
201
+ Dir.chdir(repo) do
202
+ XMigra::SchemaUpdater.new('.').tap do |tool|
203
+ tool.production = true
204
+ assert_noraises {tool.update_sql}
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ run_test "XMigra will generate a production script even if an access object's definition changes" do
211
+ 2.temp_dirs do |upstream, repo|
212
+ initialize_git_repo(upstream)
213
+
214
+ Dir.chdir(upstream) do
215
+ commit_a_migration "first table"
216
+ make_this_branch_master
217
+ Dir.mkdir(XMigra::SchemaManipulator::ACCESS_SUBDIR)
218
+ (Pathname(XMigra::SchemaManipulator::ACCESS_SUBDIR) + 'Bar.yaml').open('w') do |f|
219
+ YAML.dump({'define'=>'stored procedure', 'sql'=>'definition goes here'}, f)
220
+ end
221
+ commit_all 'Defined Bar'
222
+ (Pathname(XMigra::SchemaManipulator::ACCESS_SUBDIR) + 'Bar.yaml').open('w') do |f|
223
+ YAML.dump({'define'=>'view', 'sql'=>'definition goes here'}, f)
224
+ end
225
+ commit_all 'Changed Bar to a view'
226
+ end
227
+
228
+ `git clone "#{upstream.expand_path}" "#{repo}" 2>/dev/null`
229
+
230
+ Dir.chdir(upstream) do
231
+ commit_a_migration "foo table"
232
+ end
233
+
234
+ Dir.chdir(repo) do
235
+ XMigra::SchemaUpdater.new('.').tap do |tool|
236
+ tool.production = true
237
+ assert_noraises {tool.update_sql}
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
data/test/runner.rb ADDED
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env ruby
2
+ require 'fileutils'
3
+ require 'pathname'
4
+ require 'stringio'
5
+ require 'tmpdir'
6
+
7
+ TESTS = %w[
8
+ git_vcs
9
+ ]
10
+
11
+ $:.unshift Pathname(__FILE__).expand_path.dirname.dirname
12
+ require 'xmigra'
13
+
14
+ $test_count = 0
15
+ $test_successes = 0
16
+ $tests_failed = []
17
+
18
+ $xmigra_test_system = 'Microsoft SQL Server'
19
+
20
+ def run_test(name, &block)
21
+ $test_count += 1
22
+
23
+ if child_pid = Process.fork
24
+ Process.wait(child_pid)
25
+
26
+ if $?.success?
27
+ print '.'
28
+ $test_successes += 1
29
+ else
30
+ print 'F'
31
+ $tests_failed << name
32
+ end
33
+ else
34
+ begin
35
+ block.call
36
+ exit! 0
37
+ rescue
38
+ puts
39
+ puts "Exception: #{$!}"
40
+ puts $!.backtrace
41
+ exit! 1
42
+ end
43
+ end
44
+ end
45
+
46
+ class Integer
47
+ def temp_dirs(prefix='')
48
+ tmpdirs = []
49
+ begin
50
+ (1..self).each do |i|
51
+ tmpdirs << Pathname(Dir.mktmpdir([prefix, ".#{i}"]))
52
+ end
53
+
54
+ yield(*tmpdirs)
55
+ ensure
56
+ tmpdirs.each do |dp|
57
+ begin
58
+ FileUtils.remove_entry dp
59
+ rescue
60
+ # Skip failure
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ def do_or_die(command, message=nil, exc_type=Exception)
68
+ output = `#{command}`
69
+ $?.success? || raise(exc_type, message || ("Unable to " + command + "\n" + output))
70
+ end
71
+
72
+ def initialize_xmigra_schema(path='.', options={})
73
+ (Pathname(path) + XMigra::SchemaManipulator::DBINFO_FILE).open('w') do |f|
74
+ YAML.dump({
75
+ 'system' => $xmigra_test_system,
76
+ }.merge(options[:db_info] || {}), f)
77
+ end
78
+ end
79
+
80
+ class AssertionFailure < Exception; end
81
+
82
+ def assert(message=nil, &block)
83
+ get_message = proc {
84
+ if !message and File.exist?(block.source_location[0])
85
+ File.read(block.source_location[0]).lines[block.source_location[1] - 1].strip
86
+ elsif message.is_a? Proc
87
+ message.call
88
+ else
89
+ message
90
+ end
91
+ }
92
+
93
+ block.call || raise(AssertionFailure, get_message.call)
94
+ end
95
+
96
+ def assert_eq(actual, expected)
97
+ assert(proc {"#{actual.inspect} was not equal to #{expected.inspect}"}) {actual == expected}
98
+ end
99
+
100
+ def assert_neq(actual, expected)
101
+ assert(proc {"Value #{actual.inspect} was unexpected"}) {actual != expected}
102
+ end
103
+
104
+ def assert_include(container, item)
105
+ assert(proc {"#{item.inspect} was not in #{container.inspect}"}) {container.include? item}
106
+ end
107
+
108
+ def assert_raises(expected_exception)
109
+ begin
110
+ yield
111
+ rescue Exception => ex
112
+ assert("#{ex.class} is not #{expected_exception}") {ex.is_a? expected_exception}
113
+ return
114
+ end
115
+
116
+ assert("No #{expected_exception} raised") {false}
117
+ end
118
+
119
+ def assert_noraises
120
+ yield
121
+ end
122
+
123
+ TESTS.each {|t| require "test/#{t}"}
124
+
125
+ puts
126
+ puts "#{$test_successes}/#{$test_count} succeeded"
127
+ puts "Failed tests:" unless $tests_failed.empty?
128
+ $tests_failed.each {|name| puts " #{name}"}
129
+
data/xmigra.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'xmigra/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "xmigra"
8
+ spec.version = XMigra::VERSION
9
+ spec.authors = ["Next IT Corporation", "Richard Weeks"]
10
+ spec.email = ["rtweeks21@gmail.com"]
11
+ spec.summary = %q{Toolkit for managing database schema evolution with version control.}
12
+ spec.description = <<-END
13
+ XMigra is a suite of tools for managing database schema evolution with
14
+ version controlled files. All database manipulations are written in
15
+ SQL (specific to the target database). Works with Git or Subversion.
16
+ Currently supports Microsoft SQL Server.
17
+ END
18
+ spec.homepage = "https://github.com/rtweeks/xmigra"
19
+ spec.license = "CC-BY-SA 4.0 Itnl."
20
+
21
+ spec.files = `git ls-files -z`.split("\x0")
22
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
23
+ spec.test_files = ["test/runner.rb"]
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.5"
27
+ spec.add_development_dependency "rake"
28
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xmigra
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Next IT Corporation
8
+ - Richard Weeks
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.5'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.5'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ description: |2
43
+ XMigra is a suite of tools for managing database schema evolution with
44
+ version controlled files. All database manipulations are written in
45
+ SQL (specific to the target database). Works with Git or Subversion.
46
+ Currently supports Microsoft SQL Server.
47
+ email:
48
+ - rtweeks21@gmail.com
49
+ executables:
50
+ - xmigra
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - ".gitignore"
55
+ - Gemfile
56
+ - LICENSE.html
57
+ - README.md
58
+ - Rakefile
59
+ - bin/xmigra
60
+ - lib/xmigra.rb
61
+ - lib/xmigra/version.rb
62
+ - test/git_vcs.rb
63
+ - test/runner.rb
64
+ - xmigra.gemspec
65
+ homepage: https://github.com/rtweeks/xmigra
66
+ licenses:
67
+ - CC-BY-SA 4.0 Itnl.
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.2.1
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Toolkit for managing database schema evolution with version control.
89
+ test_files:
90
+ - test/runner.rb