xmigra 1.0.1

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