lhm-shopify 3.3.5

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.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yml +34 -0
  3. data/.gitignore +17 -0
  4. data/.rubocop.yml +183 -0
  5. data/.travis.yml +21 -0
  6. data/CHANGELOG.md +216 -0
  7. data/Gemfile +5 -0
  8. data/LICENSE +27 -0
  9. data/README.md +284 -0
  10. data/Rakefile +22 -0
  11. data/bin/.gitkeep +0 -0
  12. data/dbdeployer/config.json +32 -0
  13. data/dbdeployer/install.sh +64 -0
  14. data/dev.yml +20 -0
  15. data/gemfiles/ar-2.3_mysql.gemfile +6 -0
  16. data/gemfiles/ar-3.2_mysql.gemfile +5 -0
  17. data/gemfiles/ar-3.2_mysql2.gemfile +5 -0
  18. data/gemfiles/ar-4.0_mysql2.gemfile +5 -0
  19. data/gemfiles/ar-4.1_mysql2.gemfile +5 -0
  20. data/gemfiles/ar-4.2_mysql2.gemfile +5 -0
  21. data/gemfiles/ar-5.0_mysql2.gemfile +5 -0
  22. data/lhm.gemspec +34 -0
  23. data/lib/lhm.rb +131 -0
  24. data/lib/lhm/atomic_switcher.rb +52 -0
  25. data/lib/lhm/chunk_finder.rb +32 -0
  26. data/lib/lhm/chunk_insert.rb +51 -0
  27. data/lib/lhm/chunker.rb +87 -0
  28. data/lib/lhm/cleanup/current.rb +74 -0
  29. data/lib/lhm/command.rb +48 -0
  30. data/lib/lhm/entangler.rb +117 -0
  31. data/lib/lhm/intersection.rb +51 -0
  32. data/lib/lhm/invoker.rb +98 -0
  33. data/lib/lhm/locked_switcher.rb +74 -0
  34. data/lib/lhm/migration.rb +43 -0
  35. data/lib/lhm/migrator.rb +237 -0
  36. data/lib/lhm/printer.rb +59 -0
  37. data/lib/lhm/railtie.rb +9 -0
  38. data/lib/lhm/sql_helper.rb +77 -0
  39. data/lib/lhm/sql_retry.rb +61 -0
  40. data/lib/lhm/table.rb +121 -0
  41. data/lib/lhm/table_name.rb +23 -0
  42. data/lib/lhm/test_support.rb +35 -0
  43. data/lib/lhm/throttler.rb +36 -0
  44. data/lib/lhm/throttler/slave_lag.rb +145 -0
  45. data/lib/lhm/throttler/threads_running.rb +53 -0
  46. data/lib/lhm/throttler/time.rb +29 -0
  47. data/lib/lhm/timestamp.rb +11 -0
  48. data/lib/lhm/version.rb +6 -0
  49. data/shipit.rubygems.yml +0 -0
  50. data/spec/.lhm.example +4 -0
  51. data/spec/README.md +58 -0
  52. data/spec/fixtures/bigint_table.ddl +4 -0
  53. data/spec/fixtures/composite_primary_key.ddl +7 -0
  54. data/spec/fixtures/custom_primary_key.ddl +6 -0
  55. data/spec/fixtures/destination.ddl +6 -0
  56. data/spec/fixtures/lines.ddl +7 -0
  57. data/spec/fixtures/origin.ddl +6 -0
  58. data/spec/fixtures/permissions.ddl +5 -0
  59. data/spec/fixtures/small_table.ddl +4 -0
  60. data/spec/fixtures/tracks.ddl +5 -0
  61. data/spec/fixtures/users.ddl +14 -0
  62. data/spec/fixtures/wo_id_int_column.ddl +6 -0
  63. data/spec/integration/atomic_switcher_spec.rb +93 -0
  64. data/spec/integration/chunk_insert_spec.rb +29 -0
  65. data/spec/integration/chunker_spec.rb +185 -0
  66. data/spec/integration/cleanup_spec.rb +136 -0
  67. data/spec/integration/entangler_spec.rb +66 -0
  68. data/spec/integration/integration_helper.rb +237 -0
  69. data/spec/integration/invoker_spec.rb +33 -0
  70. data/spec/integration/lhm_spec.rb +585 -0
  71. data/spec/integration/lock_wait_timeout_spec.rb +30 -0
  72. data/spec/integration/locked_switcher_spec.rb +50 -0
  73. data/spec/integration/sql_retry/lock_wait_spec.rb +125 -0
  74. data/spec/integration/sql_retry/lock_wait_timeout_test_helper.rb +101 -0
  75. data/spec/integration/table_spec.rb +91 -0
  76. data/spec/test_helper.rb +32 -0
  77. data/spec/unit/atomic_switcher_spec.rb +31 -0
  78. data/spec/unit/chunk_finder_spec.rb +73 -0
  79. data/spec/unit/chunk_insert_spec.rb +44 -0
  80. data/spec/unit/chunker_spec.rb +166 -0
  81. data/spec/unit/entangler_spec.rb +124 -0
  82. data/spec/unit/intersection_spec.rb +51 -0
  83. data/spec/unit/lhm_spec.rb +29 -0
  84. data/spec/unit/locked_switcher_spec.rb +51 -0
  85. data/spec/unit/migrator_spec.rb +146 -0
  86. data/spec/unit/printer_spec.rb +97 -0
  87. data/spec/unit/sql_helper_spec.rb +32 -0
  88. data/spec/unit/table_name_spec.rb +39 -0
  89. data/spec/unit/table_spec.rb +47 -0
  90. data/spec/unit/throttler/slave_lag_spec.rb +317 -0
  91. data/spec/unit/throttler/threads_running_spec.rb +64 -0
  92. data/spec/unit/throttler_spec.rb +124 -0
  93. data/spec/unit/unit_helper.rb +13 -0
  94. metadata +239 -0
@@ -0,0 +1,73 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
5
+
6
+ describe Lhm::ChunkFinder do
7
+ before(:each) do
8
+ @origin = Lhm::Table.new('foo')
9
+ @destination = Lhm::Table.new('bar')
10
+ @migration = Lhm::Migration.new(@origin, @destination)
11
+ @connection = mock()
12
+ end
13
+
14
+ describe '#validate' do
15
+ describe 'when start is greater than limit' do
16
+ it 'raises' do
17
+ assert_raises { Lhm::ChunkFinder.new(@connection, @migration, {start: 2, limit: 1}).validate }
18
+ end
19
+ end
20
+
21
+ describe 'when start is greater than limit' do
22
+ it 'does not raise' do
23
+ Lhm::ChunkFinder.new(@connection, @migration, {start: 1, limit: 2}).validate # does not raise
24
+ end
25
+ end
26
+ end
27
+
28
+ describe '#start' do
29
+ describe 'when initialized with 5' do
30
+ before(:each) do
31
+ @instance = Lhm::ChunkFinder.new(@connection, @migration, {start: 5, limit: 6})
32
+ end
33
+
34
+ it 'returns 5' do
35
+ assert_equal @instance.start, 5
36
+ end
37
+ end
38
+
39
+ describe 'when initialized with nil and the min(id) is 22' do
40
+ before(:each) do
41
+ @connection.expects(:select_value).returns(22)
42
+ @instance = Lhm::ChunkFinder.new(@migration, @connection, {limit: 6})
43
+ end
44
+
45
+ it 'returns 22' do
46
+ assert_equal @instance.start, 22
47
+ end
48
+ end
49
+ end
50
+
51
+ describe '#limit' do
52
+ describe 'when initialized with 6' do
53
+ before(:each) do
54
+ @instance = Lhm::ChunkFinder.new(@connection, @migration, {start: 5, limit: 6})
55
+ end
56
+
57
+ it 'returns 6' do
58
+ assert_equal @instance.limit, 6
59
+ end
60
+ end
61
+
62
+ describe 'when initialized with nil and the max(id) is 33' do
63
+ before(:each) do
64
+ @connection.expects(:select_value).returns(33)
65
+ @instance = Lhm::ChunkFinder.new(@migration, @connection, {start: 5})
66
+ end
67
+
68
+ it 'returns 33' do
69
+ assert_equal @instance.limit, 33
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,44 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
5
+
6
+ require 'lhm/chunk_insert'
7
+
8
+ describe Lhm::ChunkInsert do
9
+ before(:each) do
10
+ @connection = stub(:connection)
11
+ @origin = Lhm::Table.new('foo')
12
+ @destination = Lhm::Table.new('bar')
13
+ end
14
+
15
+ describe "#sql" do
16
+ describe "when migration has no conditions" do
17
+ before { @migration = Lhm::Migration.new(@origin, @destination) }
18
+
19
+ it "uses a simple where clause" do
20
+ assert_equal(
21
+ Lhm::ChunkInsert.new(@migration, @connection, 1, 2).sql,
22
+ "insert ignore into `bar` () select from `foo` where `foo`.`id` between 1 and 2"
23
+ )
24
+ end
25
+ end
26
+
27
+ describe "when migration has a WHERE condition" do
28
+ before do
29
+ @migration = Lhm::Migration.new(
30
+ @origin,
31
+ @destination,
32
+ "where foo.created_at > '2013-07-10' or foo.baz = 'quux'"
33
+ )
34
+ end
35
+
36
+ it "combines the clause with the chunking WHERE condition" do
37
+ assert_equal(
38
+ Lhm::ChunkInsert.new(@migration, @connection, 1, 2).sql,
39
+ "insert ignore into `bar` () select from `foo` where (foo.created_at > '2013-07-10' or foo.baz = 'quux') and `foo`.`id` between 1 and 2"
40
+ )
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,166 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
5
+
6
+ require 'lhm/table'
7
+ require 'lhm/migration'
8
+ require 'lhm/chunker'
9
+ require 'lhm/throttler'
10
+
11
+ describe Lhm::Chunker do
12
+ include UnitHelper
13
+
14
+ before(:each) do
15
+ @origin = Lhm::Table.new('foo')
16
+ @destination = Lhm::Table.new('bar')
17
+ @migration = Lhm::Migration.new(@origin, @destination)
18
+ @connection = mock()
19
+ # This is a poor man's stub
20
+ @throttler = Object.new
21
+ def @throttler.run
22
+ # noop
23
+ end
24
+ def @throttler.stride
25
+ 1
26
+ end
27
+
28
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
29
+ :start => 1,
30
+ :limit => 10)
31
+ end
32
+
33
+ describe '#run' do
34
+
35
+ it 'detects the max id to use in the chunk using the stride and use it if it is lower than the limit' do
36
+ def @throttler.stride
37
+ 5
38
+ end
39
+
40
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 4/)).returns(7)
41
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 4/)).returns(21)
42
+ @connection.expects(:update).with(regexp_matches(/between 1 and 7/)).returns(2)
43
+ @connection.expects(:update).with(regexp_matches(/between 8 and 10/)).returns(2)
44
+
45
+ @chunker.run
46
+ end
47
+
48
+
49
+ it 'chunks the result set according to the stride size' do
50
+ def @throttler.stride
51
+ 2
52
+ end
53
+
54
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/)).returns(2)
55
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 1/)).returns(4)
56
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 5 order by id limit 1 offset 1/)).returns(6)
57
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 7 order by id limit 1 offset 1/)).returns(8)
58
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 1/)).returns(10)
59
+
60
+ @connection.expects(:update).with(regexp_matches(/between 1 and 2/)).returns(2)
61
+ @connection.expects(:update).with(regexp_matches(/between 3 and 4/)).returns(2)
62
+ @connection.expects(:update).with(regexp_matches(/between 5 and 6/)).returns(2)
63
+ @connection.expects(:update).with(regexp_matches(/between 7 and 8/)).returns(2)
64
+ @connection.expects(:update).with(regexp_matches(/between 9 and 10/)).returns(2)
65
+
66
+ @chunker.run
67
+ end
68
+
69
+ it 'handles stride changes during execution' do
70
+ # roll our own stubbing
71
+ def @throttler.stride
72
+ @run_count ||= 0
73
+ @run_count = @run_count + 1
74
+ if @run_count > 1
75
+ 3
76
+ else
77
+ 2
78
+ end
79
+ end
80
+
81
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/)).returns(2)
82
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 2/)).returns(5)
83
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 2/)).returns(8)
84
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 2/)).returns(nil)
85
+
86
+ @connection.expects(:update).with(regexp_matches(/between 1 and 2/)).returns(2)
87
+ @connection.expects(:update).with(regexp_matches(/between 3 and 5/)).returns(2)
88
+ @connection.expects(:update).with(regexp_matches(/between 6 and 8/)).returns(2)
89
+ @connection.expects(:update).with(regexp_matches(/between 9 and 10/)).returns(2)
90
+
91
+ @chunker.run
92
+ end
93
+
94
+ it 'correctly copies single record tables' do
95
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
96
+ :start => 1,
97
+ :limit => 1)
98
+
99
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 0/)).returns(nil)
100
+ @connection.expects(:update).with(regexp_matches(/between 1 and 1/)).returns(1)
101
+
102
+ @chunker.run
103
+ end
104
+
105
+ it 'copies the last record of a table, even it is the start of the last chunk' do
106
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
107
+ :start => 2,
108
+ :limit => 10)
109
+ def @throttler.stride
110
+ 2
111
+ end
112
+
113
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 2 order by id limit 1 offset 1/)).returns(3)
114
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 4 order by id limit 1 offset 1/)).returns(5)
115
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 1/)).returns(7)
116
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 1/)).returns(9)
117
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 10 order by id limit 1 offset 1/)).returns(nil)
118
+
119
+ @connection.expects(:update).with(regexp_matches(/between 2 and 3/)).returns(2)
120
+ @connection.expects(:update).with(regexp_matches(/between 4 and 5/)).returns(2)
121
+ @connection.expects(:update).with(regexp_matches(/between 6 and 7/)).returns(2)
122
+ @connection.expects(:update).with(regexp_matches(/between 8 and 9/)).returns(2)
123
+ @connection.expects(:update).with(regexp_matches(/between 10 and 10/)).returns(1)
124
+
125
+ @chunker.run
126
+ end
127
+
128
+
129
+ it 'separates filter conditions from chunking conditions' do
130
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
131
+ :start => 1,
132
+ :limit => 2)
133
+ def @throttler.stride
134
+ 2
135
+ end
136
+
137
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/)).returns(2)
138
+ @connection.expects(:update).with(regexp_matches(/where \(foo.created_at > '2013-07-10' or foo.baz = 'quux'\) and `foo`/)).returns(1)
139
+
140
+ def @migration.conditions
141
+ "where foo.created_at > '2013-07-10' or foo.baz = 'quux'"
142
+ end
143
+
144
+ @chunker.run
145
+ end
146
+
147
+ it "doesn't mess with inner join filters" do
148
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
149
+ :start => 1,
150
+ :limit => 2)
151
+
152
+ def @throttler.stride
153
+ 2
154
+ end
155
+
156
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/)).returns(2)
157
+ @connection.expects(:update).with(regexp_matches(/inner join bar on foo.id = bar.foo_id and/)).returns(1)
158
+
159
+ def @migration.conditions
160
+ 'inner join bar on foo.id = bar.foo_id'
161
+ end
162
+
163
+ @chunker.run
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,124 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
5
+
6
+ require 'lhm/table'
7
+ require 'lhm/migration'
8
+ require 'lhm/entangler'
9
+
10
+ describe Lhm::Entangler do
11
+ include UnitHelper
12
+
13
+ before(:each) do
14
+ @origin = Lhm::Table.new('origin')
15
+ @destination = Lhm::Table.new('destination')
16
+ @migration = Lhm::Migration.new(@origin, @destination)
17
+ @entangler = Lhm::Entangler.new(@migration)
18
+ end
19
+
20
+ describe 'activation' do
21
+ before(:each) do
22
+ @origin.columns['info'] = { :type => 'varchar(255)' }
23
+ @origin.columns['tags'] = { :type => 'varchar(255)' }
24
+
25
+ @destination.columns['info'] = { :type => 'varchar(255)' }
26
+ @destination.columns['tags'] = { :type => 'varchar(255)' }
27
+ end
28
+
29
+ it 'should create insert trigger to destination table' do
30
+ ddl = %Q{
31
+ create trigger `lhmt_ins_origin`
32
+ after insert on `origin` for each row
33
+ replace into `destination` (`info`, `tags`) /* large hadron migration */
34
+ values (`NEW`.`info`, `NEW`.`tags`)
35
+ }
36
+
37
+ @entangler.entangle.must_include strip(ddl)
38
+ end
39
+
40
+ it 'should create an update trigger to the destination table' do
41
+ ddl = %Q{
42
+ create trigger `lhmt_upd_origin`
43
+ after update on `origin` for each row
44
+ replace into `destination` (`info`, `tags`) /* large hadron migration */
45
+ values (`NEW`.`info`, `NEW`.`tags`)
46
+ }
47
+
48
+ @entangler.entangle.must_include strip(ddl)
49
+ end
50
+
51
+ it 'should create a delete trigger to the destination table' do
52
+ ddl = %Q{
53
+ create trigger `lhmt_del_origin`
54
+ after delete on `origin` for each row
55
+ delete ignore from `destination` /* large hadron migration */
56
+ where `destination`.`id` = OLD.`id`
57
+ }
58
+
59
+ @entangler.entangle.must_include strip(ddl)
60
+ end
61
+
62
+ it 'should retry trigger creation when it hits a lock wait timeout' do
63
+ connection = mock()
64
+ tries = 1
65
+ @entangler = Lhm::Entangler.new(@migration, connection, retriable: {base_interval: 0, tries: tries})
66
+ connection.expects(:execute).times(tries).raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
67
+
68
+ assert_raises(Mysql2::Error) { @entangler.before }
69
+ end
70
+
71
+ it 'should not retry trigger creation with other mysql errors' do
72
+ connection = mock()
73
+ connection.expects(:execute).once.raises(Mysql2::Error, 'The MySQL server is running with the --read-only option so it cannot execute this statement.')
74
+
75
+ @entangler = Lhm::Entangler.new(@migration, connection, retriable: {base_interval: 0})
76
+ assert_raises(Mysql2::Error) { @entangler.before }
77
+ end
78
+
79
+ it 'should succesfully finish after retrying' do
80
+ connection = mock()
81
+ connection.stubs(:execute).raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction').then.returns(true)
82
+ @entangler = Lhm::Entangler.new(@migration, connection, retriable: {base_interval: 0})
83
+
84
+ assert @entangler.before
85
+ end
86
+
87
+ it 'should retry as many times as specified by configuration' do
88
+ connection = mock()
89
+ connection.expects(:execute).times(5).raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
90
+ @entangler = Lhm::Entangler.new(@migration, connection, retriable: {tries: 5, base_interval: 0})
91
+
92
+ assert_raises(Mysql2::Error) { @entangler.before }
93
+ end
94
+
95
+ describe 'super long table names' do
96
+ before(:each) do
97
+ @origin = Lhm::Table.new('a' * 64)
98
+ @destination = Lhm::Table.new('b' * 64)
99
+ @migration = Lhm::Migration.new(@origin, @destination)
100
+ @entangler = Lhm::Entangler.new(@migration)
101
+ end
102
+
103
+ it 'should use truncated names' do
104
+ @entangler.trigger(:ins).length.must_be :<=, 64
105
+ @entangler.trigger(:upd).length.must_be :<=, 64
106
+ @entangler.trigger(:del).length.must_be :<=, 64
107
+ end
108
+ end
109
+ end
110
+
111
+ describe 'removal' do
112
+ it 'should remove insert trigger' do
113
+ @entangler.untangle.must_include('drop trigger if exists `lhmt_ins_origin`')
114
+ end
115
+
116
+ it 'should remove update trigger' do
117
+ @entangler.untangle.must_include('drop trigger if exists `lhmt_upd_origin`')
118
+ end
119
+
120
+ it 'should remove delete trigger' do
121
+ @entangler.untangle.must_include('drop trigger if exists `lhmt_del_origin`')
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
5
+
6
+ require 'lhm/table'
7
+ require 'lhm/migrator'
8
+
9
+ describe Lhm::Intersection do
10
+ include UnitHelper
11
+
12
+ it 'should not have dropped changes' do
13
+ origin = Lhm::Table.new('origin')
14
+ origin.columns['dropped'] = varchar
15
+ origin.columns['retained'] = varchar
16
+
17
+ destination = Lhm::Table.new('destination')
18
+ destination.columns['retained'] = varchar
19
+
20
+ intersection = Lhm::Intersection.new(origin, destination)
21
+ intersection.destination.include?('dropped').must_equal(false)
22
+ end
23
+
24
+ it 'should have unchanged columns' do
25
+ origin = Lhm::Table.new('origin')
26
+ origin.columns['dropped'] = varchar
27
+ origin.columns['retained'] = varchar
28
+
29
+ destination = Lhm::Table.new('destination')
30
+ destination.columns['retained'] = varchar
31
+
32
+ intersection = Lhm::Intersection.new(origin, destination)
33
+ intersection.destination.must_equal(['retained'])
34
+ end
35
+
36
+ it 'should have renamed columns' do
37
+ origin = Lhm::Table.new('origin')
38
+ origin.columns['old_name'] = varchar
39
+
40
+ destination = Lhm::Table.new('destination')
41
+ destination.columns['new_name'] = varchar
42
+
43
+ intersection = Lhm::Intersection.new(origin, destination, { 'old_name' => 'new_name' })
44
+ intersection.origin.must_equal(['old_name'])
45
+ intersection.destination.must_equal(['new_name'])
46
+ end
47
+
48
+ def varchar
49
+ { :metadata => 'VARCHAR(255)' }
50
+ end
51
+ end